Changeset 467


Ignore:
Timestamp:
09/19/08 17:13:03 (5 years ago)
Author:
cbalan
Message:

ResourceTools?: - Upgrade to ResourceTools?-0.3

Location:
trunk/plugins
Files:
1 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/plugins/projectstatusplugin/setup.py

    r447 r467  
    88 
    99setup(name="TracProjectStatus", 
    10       version="0.2", 
     10      version="0.3", 
    1111      packages=['tracprojectstatus'],  
    1212      author="Catalin Balan",  
  • trunk/plugins/projectstatusplugin/tracprojectstatus/templates/macros_psoutline.html

    r447 r467  
    88        </div> 
    99        <div> 
    10                 <label><b>Status:</b></label><span class="status ${ps.status.upper()}"> ${ps.status}</span> 
     10                <label><b>Status:</b></label><span class="status ${ps.ps_ryg_status.upper()}"> ${ps.ps_ryg_status}</span> 
    1111        </div> 
    12         <div py:if="ps.state and ps.state.upper()!='ACTIVE'"> 
    13                 <label><b>Document's state:</b></label><span class="${ps.state}"> ${ps.state}</span> 
     12        <div py:if="ps.ps_state and ps.ps_state.upper()!='ACTIVE'"> 
     13                <label><b>Document's state:</b></label><span class="${ps.ps_state}"> ${ps.ps_state}</span> 
    1414        </div> 
    15         <div py:if="ps.summary"> 
    16                 <label><b>Summary:</b></label><span> ${ps.summary}</span> 
     15        <div py:if="ps.ps_summary"> 
     16                <label><b>Summary:</b></label><span> ${ps.ps_summary}</span> 
    1717        </div> 
    1818</div> 
  • trunk/plugins/projectstatusplugin/tracprojectstatus/templates/ps_index.html

    r447 r467  
    4040                <ul> 
    4141                        <li>Date: <a href="${ps.reports.Active[0].href}">${ps_dateformat(ps.reports.Active[0].date)}</a></li> 
    42                         <li>Status: <span class="status_${ps.reports.Active[0].status.upper()}"> ${ps.reports.Active[0].status} </span></li> 
    43                         <li>Summary: ${Markup(ps.reports.Active[0].summary)}</li> 
     42                        <li>Status: <span class="status_${ps.reports.Active[0].ps_ryg_status.upper()}"> ${ps.reports.Active[0].ps_ryg_status} </span></li> 
     43                        <li>Summary: ${Markup(ps.reports.Active[0].ps_summary)}</li> 
    4444                </ul> 
    4545                </py:if> 
     
    5959                <tr py:for="project_status in ps.reports.Draft"> 
    6060                                        <td><a href="${project_status.href}">${ps_dateformat(project_status.date)}</a></td> 
    61                     <td class="status_${project_status.status.upper()}" >${project_status.status}</td> 
    62                     <td>${Markup(project_status.summary)}</td>                       
     61                    <td class="status_${project_status.ps_ryg_status.upper()}" >${project_status.ps_ryg_status}</td> 
     62                    <td>${Markup(project_status.ps_summary)}</td>                       
    6363                </tr> 
    6464            </tbody> 
     
    8484                                <tr py:for="project_status in ps.reports.Active"> 
    8585                                        <td><a href="${project_status.href}">${ps_dateformat(project_status.date)}</a></td> 
    86                                         <td class="status_${project_status.status.upper()}">${project_status.status}</td> 
    87                                         <td>${Markup(project_status.summary)}</td>                                       
     86                                        <td class="status_${project_status.ps_ryg_status.upper()}">${project_status.ps_ryg_status}</td> 
     87                                        <td>${Markup(project_status.ps_summary)}</td>                                    
    8888                                </tr> 
    8989                        </tbody> 
  • trunk/plugins/projectstatusplugin/tracprojectstatus/web_ui.py

    r447 r467  
    1515from trac.resource import get_resource_url 
    1616 
    17 from tracresourcetools.api import MetadataSystem, IMetadataHandler 
    18 from tracwikitools.web_ui import DefaultWikiCustomFieldHandler 
     17from tracresourcetools.api import ResourcePropertySystem, ResourceProperty 
     18from tracwikitools.api import IWikiCustomFieldsProvider 
    1919 
    2020class ProjectStatusModule(Component): 
    21     implements(ITemplateProvider, INavigationContributor, IRequestHandler, IMetadataHandler) 
     21    implements(ITemplateProvider, INavigationContributor, IRequestHandler, IWikiCustomFieldsProvider) 
    2222 
    2323    status_wikiPath = Option('status_report', 'wiki_prefix', 'StatusReport/', 
    2424        """Prefix used by this component to store status reports in trac wiki system""") 
    2525     
    26     # IMetadataHandler 
    27     def get_handled_metadata(self, resource, context_data={}): 
    28         if resource.realm == 'wiki': 
    29             meta=MetadataSystem(self.env) 
    30             page_type=meta.get_metadata(resource, 'type', 
    31                                        default_value=context_data.get('type'), 
    32                                        lookup_handler=False) 
    33             if page_type =='status-report': 
    34                 yield 'ps_ryg_status' 
    35                 yield 'ps_summary' 
    36                 yield 'ps_state'  
    37     def get_supported_operations(self, metadata, context_data={}): 
    38         yield 'init_metadata' 
    39         yield 'render_metadata' 
    40     def init_metadata(self, metadata, context_data={}): 
    41         if metadata.name=='ps_ryg_status': 
    42             metadata.data.update(type='status-report', element_type='select', 
    43                                  label='Overall Status', options='Not_set|Green|Yellow|Red', 
    44                                  order=1) 
    45         elif metadata.name=='ps_summary': 
    46             metadata.data.update(type='status-report', element_type='textarea', 
    47                                  label='Summary', cols=100, rows=2, 
    48                                  order=2) 
    49         elif metadata.name=='ps_state': 
    50             metadata.data.update(type='status-report', element_type='select', label='State', 
    51                                  options='Draft|Active', position="after-wikitext", 
    52                                  order=3) 
    53     def render_metadata(self, req, metadata, context_data={}): 
    54         return DefaultWikiCustomFieldHandler(self.env).render_metadata(req, metadata) 
    55  
    56  
     26    ryg_status = ResourceProperty('ps_ryg_status', default_value= 'Not_set', 
     27                                  element_type='select', label='Overall Status', options=['Not_set','Green','Yellow','Red'], 
     28                                  order=1) 
     29    summary = ResourceProperty('ps_summary',  
     30                                element_type='textarea', label='Summary', cols=100, rows=2, 
     31                                order=2) 
     32    state = ResourceProperty('ps_state',  
     33                             element_type='select', label='State', 
     34                             options=['Draft','Active'], 
     35                             position='after-wikitext', 
     36                             order=3) 
     37     
     38    # IWikiCustomFieldsProvider 
     39    def get_wiki_custom_fields(self, resource, resource_properties_cache): 
     40        if resource_properties_cache.get('wiki_type', resource).value=='status-report': 
     41            yield self.ryg_status 
     42            yield self.summary 
     43            yield self.state 
     44     
    5745    # INavigationContributor methods 
    5846    def get_active_navigation_item(self, req): 
     
    9482 
    9583        # last statuses 
    96         data.update(reports=self._get_projects_statuses(req)) 
     84        data.update(reports=self._get_project_statuses(req)) 
    9785         
    9886        # adding stylesheet & js 
     
    10593        return 'ps_index.html', {'ps': data}, None 
    10694     
    107     def _get_projects_statuses(self, req, limit=None, filters={}): 
     95    def _get_project_statuses(self, req, limit=None, filters={}): 
    10896        reports = {} 
    109         meta = MetadataSystem(self.env) 
    110         for res in meta.get_resources_with_metadatas(dict(type='status-report', **filters), limit=limit): 
     97        rp_system = ResourcePropertySystem(self.env) 
     98        for res in rp_system.get_resources_with_properties(wiki_type='status-report', limit = limit, **filters): 
    11199            report = {} 
    112             report.update(status=meta.get_metadata(res, 'ps_ryg_status', default_value='Not_set', lookup_handler=False), 
    113                           summary=meta.get_metadata(res, 'ps_summary', lookup_handler=False), 
    114                           state=meta.get_metadata(res, 'ps_state', default_value='Draft', lookup_handler=False), 
    115                           date=res.id.replace(self.status_wikiPath,'')[:8], 
     100            report.update(self._get_project_status(req, res)) 
     101            report.update(date=res.id.replace(self.status_wikiPath,'')[:8], 
    116102                          href = get_resource_url(self.env, res, req.href), 
    117103                          name=res.id) 
    118             if not reports.has_key(report['state']): 
    119                 reports[report['state']]=[] 
    120             reports[report['state']].append(report) 
    121              
     104            reports.setdefault(report[self.state.name],[]).append(report) 
    122105        return reports 
    123          
     106 
     107    def _get_project_status(self, req, resource): 
     108        rp_cache = ResourcePropertySystem(self.env).get_properties_cache(req) 
     109        for prop in rp_cache.get_resource_properties(resource, self.ryg_status, self.summary, self.state): 
     110            yield prop.name, prop.value 
  • trunk/plugins/projectstatusplugin/tracprojectstatus/wiki.py

    r447 r467  
    1212from trac.web.chrome import Chrome, add_stylesheet 
    1313 
    14 from tracresourcetools.api import MetadataSystem 
    1514from tracprojectstatus.web_ui import ProjectStatusModule  
    1615 
     
    2726    def expand_macro(self, formatter, name, content): 
    2827        req = formatter.req 
    29         res = formatter.resource.realm and formatter.resource or Resource('wiki', req.path_info[6:]) 
    30         meta = MetadataSystem(self.env) 
     28        resource = formatter.resource.realm and formatter.resource or Resource('wiki', req.path_info[6:]) 
    3129        ps_module = ProjectStatusModule(self.env) 
    3230         
    3331        data={} 
    34         data.update(status=meta.get_metadata(res, 'ps_ryg_status', default_value=req.args.get('ps_ryg_status'),lookup_handler=False), 
    35                     summary=Markup(meta.get_metadata(res, 'ps_summary', default_value=req.args.get('ps_summary'),lookup_handler=False)), 
    36                     state=meta.get_metadata(res, 'ps_state', default_value='Draft', lookup_handler=False), 
    37                     date=res.id.replace(ps_module.status_wikiPath,'')[:8]) 
     32        data.update(ps_module._get_project_status(req, resource)) 
     33        data[ps_module.summary.name] = Markup(data.get(ps_module.summary.name)) 
     34        data['date']=resource.id.replace(ps_module.status_wikiPath,'')[:8] 
    3835   
    3936        add_stylesheet(req, 'tracprojectstatus/css/project_status.css') 
    40    
    4137        return Chrome(self.env).render_template(req, 'macros_psoutline.html', {'ps':data}) 
    4238 
     
    6157         
    6258        # @todo: Please fix this very inefficient last report lookup.   
    63         project_statuses = ProjectStatusModule(self.env)._get_projects_statuses(formatter.req, limit=1, filters={'ps_state':'Active'}).get('Active',[]) 
     59        project_statuses = ProjectStatusModule(self.env)._get_project_statuses(formatter.req, limit=1, filters={'ps_state':'Active'}).get('Active',[]) 
    6460         
    6561        for project_status in project_statuses:  
     
    7066            add_stylesheet(formatter.req, 'tracprojectstatus/css/project_status.css') 
    7167            return html.span(html.a(label, href=project_status['href']), 
    72                            class_="status_%s"%(project_status['status'].upper())) 
     68                           class_="status_%s"%(project_status['ps_ryg_status'].upper()))    
    7369         
    7470        return html.span(html.a("No status report created.", href=formatter.req.href.status('index'))) 
  • trunk/plugins/resourcetoolsplugin/setup.py

    r447 r467  
    88 
    99setup(name="TracResourceToolsPlugin", 
    10       version="0.2", 
     10      version="0.3", 
    1111      packages=['tracresourcetools'], 
    1212      url="http://code.optaros.com/trac/oforge", 
     
    1616            'tracresourcetools.api = tracresourcetools.api', 
    1717            'tracresourcetools.model = tracresourcetools.model']}, 
    18       test_suite = 'tracresourcetools.tests' 
     18      test_suite = 'tracresourcetools.tests.suite' 
    1919) 
  • trunk/plugins/resourcetoolsplugin/tracresourcetools/api.py

    r447 r467  
    33# Copyright 2008 Optaros, Inc. 
    44# 
    5 import traceback  
    6 from StringIO import StringIO 
    75 
    86from trac.core import * 
    97from trac.resource import * 
    10 from trac.util.translation import _ 
    11 from trac.web.href import Href 
    128 
    13 from tracresourcetools.model import MetadataModel 
     9from tracresourcetools.model import ResourcePropertyModel, StrResourcePropertyModel, ListResourcePropertyModel 
    1410 
    15  
    16 class DontSaveMetadataException(TracError): 
    17     """Raised by a metadata handler when a metadata should not be saved.""" 
    18  
    19 class IMetadataChangeListener(Interface): 
    20     def metadata_changed(resource, name, old_value, new_value): 
    21         """Fired when MetadataSystem#set_metadata is called, after the  
     11class IResourcePropertyChangeListener(Interface): 
     12    def resource_property_changed(resource, name, old_value, new_value): 
     13        """Fired when ResourcePropertySystem#set_sesource_property is called, after the  
    2214        change/creation. 
    2315         
     
    2820        @return: None     
    2921        """ 
    30     def metadata_deleted(resource, name, old_value): 
    31         """Fired when MetadataSystem#delete_metadata is called, after 
    32         the metadata is deleted. 
     22         
     23    def resource_property_deleted(resource, name, old_value): 
     24        """Fired when ResourcePropertySystem#delete_resource_property is called, after 
     25        the ResourceProperty is deleted. 
    3326         
    3427        @param resource: Resource 
     
    3730        @return: None 
    3831        """ 
     32         
     33class ResourceProperty(object): 
     34    """Resource property descriptor.""" 
     35     
     36    MODEL_CLASS = StrResourcePropertyModel 
     37    registry={} 
    3938 
    40 class IMetadataHandler(Interface): 
    41     def get_handled_metadata(resource, context_data={}): 
    42         """Returns a list of handled metadata names  
    43         for a specific resource as [str, ...] 
     39    def __init__(self, name, env=None, **kwargs): 
     40        """Resource property object initialization. 
     41         
     42        @param name: str 
     43        @param env: Environment 
     44        """ 
     45        self.name = name 
     46        self.data = kwargs 
     47        self.env = env 
     48                
     49        self.registry[self.name]=self 
     50          
     51    def __get__(self, instance, owner): 
     52        """Setting current env. 
     53         
     54        @param instance: Component 
     55        @param owner: class 
     56        """ 
     57        if self.env is None: 
     58            env = getattr(instance, 'env') 
     59            if not env is  None: 
     60                self.env = env 
     61        return self 
     62         
     63    def get(self, resource, env = None, _cache=None, req=None, **kwargs): 
     64        """Return the resource property model object for specified resource. 
    4465         
    4566        @param resource: Resource 
    46         @return: Generator  
    4767        """ 
     68        data = self.data.copy() 
     69        data.update(kwargs) 
     70         
     71        if self.env is None: 
     72            self.env = env 
     73         
     74        if req and _cache is None: 
     75            _cache = ResourcePropertySystem(self.env).get_properties_cache(req) 
     76        if isinstance(_cache, ResourcePropertiesCache): 
     77            return _cache.get(self, resource, **data) 
     78          
     79        return self.MODEL_CLASS(self.env, resource, self.name, **data) 
    4880     
    49     def get_supported_operations(metadata, context_data={}): 
    50         """Return a list with supported operations for a  
    51         specific metadata as [str, ...]. 
     81    def __getitem__(self, resource): 
     82        return self.get(resource) 
     83     
     84    def __set__(self, instance, value): 
     85        raise TracError("Not implemented.") 
     86    def __setitem__(self, resource, value): 
     87        raise TracError("Not implemented.")  
     88    def __contains__(self, resource): 
     89        raise TracError("Not implemented.") 
     90     
     91class ListResourceProperty(ResourceProperty): 
     92    MODEL_CLASS = ListResourcePropertyModel 
     93     
     94class ResourcePropertiesCache(object): 
     95    """Caches resource properties model objects.""" 
     96    def __init__(self, env=None): 
     97        self.env = env 
     98        self.property_cache={} 
    5299         
    53         @param metadata: Metadata 
    54         @param context_data: dict 
     100    def get(self, resource_property, resource, **kwargs): 
     101        """Returns resource property model object from cache. 
     102         
     103        @param resource_property: ResourceProperty 
     104        @param resource: Resource 
     105        @return: ResourcePropertyModel 
     106        """ 
     107        if isinstance(resource_property, str): 
     108            if resource_property in ResourceProperty.registry: 
     109                resource_property = ResourceProperty.registry[resource_property] 
     110            else:  
     111                raise TracError("Undefined resource property %s"%(resource_property)) 
     112         
     113        if not (resource_property.name, resource) in self.property_cache: 
     114            self.property_cache[(resource_property.name, resource)] = resource_property.get(resource, env = self.env, **kwargs) 
     115        return self.property_cache[(resource_property.name, resource)] 
     116     
     117    def get_resource_properties(self, resource, *args, **kwargs): 
     118        """Returns all defined resource property objects for  
     119        a specific resource. 
     120                 
     121        @param resource: Resource 
     122        @return: Generator 
     123        """ 
     124        _cache_failed = {} 
     125        for resource_property in (len(args)==0 and ResourceProperty.registry.values() or args): 
     126            if (resource_property.name, resource) in self.property_cache: 
     127                yield self.property_cache[(resource_property.name, resource)] 
     128            else: 
     129                _cache_failed[resource_property.name] = resource_property 
     130         
     131        for resource_property_model in ResourcePropertyModel.get_resource_properties(self.env, resource, _cache_failed.copy(), **kwargs): 
     132            self.property_cache[(resource_property_model.name, resource)]=resource_property_model 
     133            _cache_failed.pop(resource_property_model.name) 
     134            yield resource_property_model  
     135         
     136        # Defaults 
     137        for resource_property in _cache_failed.values(): 
     138            model = resource_property.get(resource, env = self.env, fetch=False)  
     139            self.property_cache[(resource_property.name, resource)] = model 
     140            yield model 
     141             
     142class ResourcePropertySystem(Component): 
     143    implements(IResourceManager) 
     144     
     145    change_listeners = ExtensionPoint(IResourcePropertyChangeListener) 
     146     
     147    def get_resource_property(self, name, **kwargs): 
     148        """Returns resource property object. 
     149         
     150        @param name: str 
     151        @return: ResourceProperty  
     152        """ 
     153        if name in ResourceProperty.registry: 
     154            if ResourceProperty.registry[name].env is None: 
     155                ResourceProperty.registry[name].env = self.env 
     156            return ResourceProperty.registry[name] 
     157        else: 
     158            raise TracError("Property not defined.") 
     159     
     160    def get_resources_with_properties(self, **kwargs): 
     161        """Returns all resources with resource_properties_dict. 
     162         
     163        @param resource_properties_dict: dict 
    55164        @return: generator 
    56165        """ 
    57      
    58     def init_metadata(metadata, context_data={}): 
    59         """Called by Metadata.__init__ method. metadata._fetched=True flag 
    60         can be used here in order to avoid MetadataModel fetch. Useful only  
    61         for 'virtual' metadata, when persistence is handled different  
    62         than 'MetadataModel' way.  
    63          
    64         @param metadata: Metadata 
    65         @param context_data: dict 
    66         @return: None 
    67         """ 
    68      
    69     def get_metadata(metadata, default_value=None, context_data={}): 
    70         """Called by Metadata.get_metadata method.  
    71          
    72         @param metadata: Metadata 
    73         @param default_value: str  
    74         @param context_data: dict 
    75         @return: None 
    76         """ 
    77          
    78     def set_metadata(metadata, value, context_data={}): 
    79         """Called by Metadata.set_metadata.  DontSaveMetadataException exception  
    80         can be raised in order to avoid MetadataModel update. Useful when  
    81         persistence is handled different than 'MetadataModel' way. 
    82          
    83         @param metadata: Metadata 
    84         @param value: str 
    85         @param context_data: dict 
    86         @raise DontSaveMetadataException: can be raised in order to bypass MetadataModel update. 
    87         @return: None 
    88         """ 
    89      
    90     def render_metadata(req, metadata, context_data={}): 
    91         """Returns a genshi stream for handled metadata, or None. 
     166        return ResourcePropertyModel.get_resources_with_properties(self.env, **kwargs) 
     167 
     168    def get_properties_cache(self, req=None): 
     169        """Returns request properties cache. 
    92170         
    93171        @param req: Request 
    94         @param metadata: Metadata 
    95         @param context_data: dict 
    96         @return: Stream 
     172        @return: ResourcePropertiesCache 
    97173        """ 
     174        if req is None: 
     175            return ResourcePropertiesCache(env=self.env) 
    98176         
    99 class Metadata(object): 
    100     """Object representing a Resource's metadata.""" 
    101  
    102     def __init__(self, env, resource, name, handler=None, context_data={}): 
    103         """Metadata object initialization. 
     177        if 'resource_properties_cache' not in req.callbacks: 
     178            req.callbacks['resource_properties_cache'] = lambda req:ResourcePropertiesCache(env=self.env) 
    104179         
    105         @param env: Environment 
    106         @param resource: Resource 
    107         @param name: str 
    108         @param handler: IMetadataHandler 
    109         @param context_data: dict 
    110         """ 
    111         self.env = env 
    112         self.name = name 
    113         self.resource = resource 
    114          
    115         # Metadata model 
    116         self.model = MetadataModel(env, resource, name=name, fetch=False) 
    117         self._fetched = False 
    118          
    119         # Dict that can be use to store various info 
    120         self.data={} 
    121          
    122         # IMetadataHandler implementation component  
    123         self.handler = handler 
    124         if self.is_operation_supported('init_metadata', context_data): 
    125             self.handler.init_metadata(self, context_data) 
    126          
    127     def set(self, value, context_data={}): 
    128         """Set metadata's value. 
    129          
    130         @param value: str 
    131         @param context_data: dict 
    132         @return: None  
    133         """ 
    134         self._fetch_model() 
    135  
    136         # Notify handler   
    137         old_value = self.model.value 
    138         self.model.value = value 
    139         if self.is_operation_supported('set_metadata', context_data): 
    140             try: 
    141                 self.handler.set_metadata(self, value, context_data) 
    142             except DontSaveMetadataException, e: 
    143                 return  
    144              
    145         # Update model  
    146         self.model.update() 
    147          
    148         # Notify change listeners about the update 
    149         for listener in MetadataSystem(self.env).change_listeners: 
    150             listener.metadata_changed(self.resource, self.name, old_value, self.model.value)  
    151      
    152     def get(self, default_value=None, context_data={}): 
    153         """Returns metadata's value. 
    154          
    155         @param default_value:  
    156         @param context_data: dict 
    157         @return: None 
    158         """ 
    159         self._fetch_model() 
    160          
    161         # Notify handler 
    162         if self.is_operation_supported('get_metadata', context_data): 
    163             return self.handler.get_metadata(self, default_value, context_data) 
    164          
    165         # No handler, original model value 
    166         if self.model.value: 
    167             return self.model.value 
    168         return default_value 
    169      
    170     def render(self, req, context_data={}): 
    171         """Render metadata, if operation supported by handler. 
    172          
    173         @param req: Request 
    174         @param context_data: dict 
    175         @return: Stream 
    176         """ 
    177         if self.is_render_supported(context_data): 
    178             return self.handler.render_metadata(req, self, context_data) 
    179      
    180     def is_render_supported(self, context_data={}): 
    181         """Helper method especially for templates. Returns True  
    182         if render operation is supported.  
    183          
    184         @param context_data: dict 
    185         @return: bool 
    186         """ 
    187         return self.is_operation_supported('render_metadata', context_data) 
    188      
    189     def is_operation_supported(self, operation_name, context_data={}): 
    190         """Returns `True` if operation_name is supported by current handler. 
    191          
    192         @param operation_name: str 
    193         @param context_data: dict 
    194         @rtype: bool 
    195         """  
    196         if self.handler and hasattr(self.handler, 'get_supported_operations'): 
    197             return operation_name in self.handler.get_supported_operations(self, context_data) 
    198         return False 
    199      
    200     # Internals 
    201     def _fetch_model(self): 
    202         if self._fetched is False: 
    203             try: 
    204                 self.model._fetch(self.model.resource.id) 
    205             except ResourceNotFound, e: 
    206                 out = StringIO() 
    207                 traceback.print_exc(file=out) 
    208                 self.env.log.error('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 
    209             self._fetched=True 
    210          
    211 class MetadataSystem(Component): 
    212     implements(IResourceManager) 
    213      
    214     metadata_handlers = ExtensionPoint(IMetadataHandler) 
    215     change_listeners = ExtensionPoint(IMetadataChangeListener) 
    216      
    217     def get_metadatas(self, resource, filter=lambda metadata:True, context_data={}): 
    218         """Returns all metadatas objects  
    219         handled by IMetadataHandler implementations. 
    220           
    221         @param resource: Resource 
    222         @param filter: function 
    223         @param context_data: dict 
    224         @return: Generator 
    225         """         
    226         for handler in self.metadata_handlers: 
    227             for metadata_name in handler.get_handled_metadata(resource, context_data): 
    228                 metadata = Metadata(self.env, resource, metadata_name, handler, context_data) 
    229                 if filter(metadata): 
    230                     yield metadata  
    231      
    232     def get_resources_with_metadatas(self, metadatas_dict, limit=None, context_data={}): 
    233         """Returns all resources with metadatas_dict. 
    234          
    235         @param metadatas_dict: dict 
    236         @param limit: int 
    237         @param context_data: dict 
    238         @return: list 
    239         """ 
    240         return MetadataModel.get_resources_with_metadatas(self.env, metadatas_dict, limit=limit) 
    241  
    242     # Metadata helpers 
    243     def get_metadata(self, resource, name, default_value=None, handler=None, lookup_handler=True, context_data={}): 
    244         """Returns metadata value. 
    245          
    246         @param resource: Resource 
    247         @param name: str 
    248         @param default_value: str 
    249         @param handler: IMetadataHandler 
    250         @param lookup_handler: bool 
    251         @param context_data: dict 
    252         @return: str 
    253         """ 
    254         if lookup_handler and handler is None: 
    255             for metadata in self.get_metadatas(resource, lambda x:x.name==name, context_data): 
    256                 return metadata.get(default_value) 
    257          
    258         return Metadata(self.env, resource, name, handler=handler).get(default_value) 
    259  
    260     def set_metadata(self, resource, name, value, handler=None, lookup_handler=True, context_data={}): 
    261         """Update metadata's value. 
    262          
    263         @param resource: Resource 
    264         @param name: str 
    265         @param value: str 
    266         @param handler: IMetadataHandler 
    267         @param lookup_handler: bool 
    268         @param context_data: dict 
    269         @return: None 
    270         """ 
    271         if lookup_handler and handler is None: 
    272             for metadata in self.get_metadatas(resource, lambda x:x.name==name, context_data): 
    273                 return metadata.set(value) 
    274         return Metadata(self.env, resource, name, handler=handler).set(value) 
    275      
    276     def delete_metadata(self, resource, name): 
    277         """Delete a resource metadata. 
    278          
    279         @param resource: Resource 
    280         @param name: str 
    281         @return: None 
    282         """ 
    283         return MetadataModel(self.env, resource, name, fetch=False).delete() 
    284                              
     180        return req.resource_properties_cache 
     181           
    285182    # IResourceManager methods 
    286183    def get_resource_realms(self): 
    287         yield 'metadata' 
    288  
     184        yield 'property' 
    289185    def get_resource_url(self, resource, href, **kwargs): 
    290186        """@todo: implement  IResourceMananger.get_resource_url """ 
  • trunk/plugins/resourcetoolsplugin/tracresourcetools/model.py

    r447 r467  
    1212from trac.db import Table, Column, Index 
    1313from trac.util.translation import _ 
    14  
    15 class MetadataModel(object): 
    16  
    17     def __init__(self, env, parent_resource, name=None, db=None, default_value=None, fetch=True): 
    18         """MetadataModel initialization. fetch=False flag can be used  
    19         in order to bypass value fetch from db.  
    20          
    21         @param env: Environment 
    22         @param parent_resource: Resource 
    23         @param name: str 
    24         @param db: Db 
    25         @param default_value: str 
    26         @param fetch: bool 
    27         """ 
     14from trac.util.compat import groupby 
     15 
     16class ResourcePropertyModel(object): 
     17    def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, **kwargs): 
    2818        self.env = env 
    29         self.resource = Resource(parent_resource).child('metadata', name) 
    30         self.parent_realm = self.resource.parent.realm 
    31         self.parent_id = self.resource.parent.id 
    32  
     19        self.resource = Resource(resource).child('property', name) 
     20        self.resource_realm = self.resource.parent.realm 
     21        self.resource_id = self.resource.parent.id 
    3322        self.name = name 
    3423        self.value = default_value 
    35         self.index = None 
    36         self.uniq_id = None 
    37          
     24        self.data = kwargs 
    3825        if self.resource.id and fetch: 
    3926            try: 
     
    4229                out = StringIO() 
    4330                traceback.print_exc(file=out) 
    44                 env.log.error('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 
    45  
     31                env.log.debug('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 
     32 
     33    # Internals 
    4634    def _set_name(self, val): 
    47         """Updates metadata's name. 
    48          
    49         @param val: str 
    50         @return: None 
    51         """ 
    5235        self.resource.id = val 
    53  
    5436    name = property(lambda self: self.resource.id, _set_name) 
    5537 
    5638    def _fetch(self, name, db=None): 
    57         """Metadata's value fetch. If index_ fields not None, then  
    58         value's type will be List. 
    59          
    60         @param name: str 
     39        raise TracError('Not implemented') 
     40     
     41    def _load(self, **kargs): 
     42        raise TracError('Not implemented') 
     43     
     44    def _handle_ta(self, db=None): 
     45        handle_ta = db is None 
     46        return handle_ta, handle_ta and self.env.get_db_cnx() or db  
     47     
     48    # Public methods  
     49    def insert(self, name=None, value=None, db=None): 
     50        raise TracError('Not implemented') 
     51 
     52    def update(self, db=None): 
     53        handle_ta, db = self._handle_ta(db) 
     54        self.delete(db) 
     55        self.insert(self.name, self.value, db) 
     56        if handle_ta: 
     57            db.commit() 
     58     
     59    def delete(self, db=None): 
     60        handle_ta, db = self._handle_ta(db) 
     61        cursor = db.cursor() 
     62        cursor.execute("DELETE FROM resource_custom " 
     63                       "WHERE resource_realm=%s AND resource_id=%s " 
     64                       "AND name=%s", (self.resource_realm, self.resource_id, self.name)) 
     65        if handle_ta: 
     66            db.commit() 
     67     
     68    def get(self): 
     69        return self.value 
     70     
     71    def set(self, value, db=None): 
     72        #notify change listeners here  
     73        self.value = value 
     74        self.update(db) 
     75     
     76    @classmethod 
     77    def select(cls, env, *args, **kwargs ): 
     78        """Returns all properties for a specific resource. 
     79 
     80        >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties 
     81        >>> select(env, type='status-report') # all properties with name='type' and value='status-report'. 
     82                 
     83        @param cls: Class 
     84        @param env: Environment 
     85        @param resource: Resource 
     86        @return: Generator 
     87        """ 
     88        raise TracError("Not implemented.") 
     89     
     90    @classmethod             
     91    def delete_all(cls, env, resource, db=None): 
     92        """Removes all resource_custom records for a specific resource. 
     93         
     94        @param cls: Class 
     95        @param env: Environment 
     96        @param resource: Resource 
    6197        @param db: Db 
    6298        @return: None 
    6399        """ 
    64         if not db: 
    65             db = self.env.get_db_cnx() 
    66          
     100        handle_ta = db is None 
     101        db = handle_ta and env.get_db_cnx() or db 
     102         
     103        cursor = db.cursor()     
     104        cursor.execute("DELETE FROM resource_custom " 
     105                       "WHERE resource_realm=%s AND resource_id=%s", (resource.realm, resource.id)) 
     106        if handle_ta: 
     107            db.commit() 
     108     
     109    @classmethod 
     110    def get_resources_with_properties(cls, env, limit=None, **kwargs): 
     111        """Returns resources with properties. 
     112         
     113        >>> list(ResourcePropertyModel.get_resources_with_properties(env, prop1_name='prop_value1', prop2_name='prop_value2')) 
     114        [<Resource u'wiki:Page1'>, <Resource u'wiki:Page2'>] 
     115         
     116        @param cls: Type 
     117        @param env: Environment 
     118        @return: Generator  
     119        """ 
     120        db = env.get_db_cnx() 
     121        cursor = db.cursor() 
     122         
     123        # dict to list attr_dict = { k1:v1, k2:v2, ... } -> [k1,v1,k2,v2..., len(attr_dict)] 
     124        kwargs_list = [] 
     125        for k, v in kwargs.items(): 
     126            kwargs_list.append(k.startswith('NOT_') and k[4:] or k) 
     127            kwargs_list.append(v) 
     128        kwargs_list.append(len(kwargs)) 
     129         
     130        def _get_condition(k,v): 
     131            return "name=%s AND value" + (k.startswith('NOT_') and 'NOT' or ' ') + " LIKE %s" 
     132         
     133        cursor.execute("SELECT resource_realm, resource_id " 
     134                       "FROM resource_custom " 
     135                       "WHERE " + " OR ".join([ _get_condition(k,v) for k,v in kwargs.items()]) + " " 
     136                       "GROUP BY resource_realm, resource_id " 
     137                       "HAVING count(*)=%s " 
     138                       "ORDER BY resource_realm, resource_id DESC" 
     139                       +(limit and " LIMIT %s" or ""), kwargs_list+(limit and [limit] or [])) 
     140         
     141        for reasource_realm, resource_id in cursor: 
     142            yield Resource(reasource_realm, resource_id) 
     143         
     144        cursor.close() 
     145             
     146    @classmethod 
     147    def get_resource_properties(cls, env, resource, resource_properties, **kwargs): 
     148        """Returns resource property model objects for resource. 
     149         
     150        @param env: Environment 
     151        @param resource: Resource 
     152        @param resource_properties: dict 
     153        @return: Generator 
     154        """ 
     155        db = env.get_db_cnx() or db 
     156        cursor = db.cursor()     
     157         
     158        cursor.execute("SELECT id, name, value, property_index " 
     159                       "FROM resource_custom " 
     160                       "WHERE resource_realm=%s AND resource_id=%s " 
     161                       "ORDER BY name, property_index", 
     162                       (resource.realm, resource.id))         
     163         
     164        kwargs['fetch'] = False 
     165        for name, data in groupby(cursor, lambda row: row[1]): 
     166            if name in resource_properties: 
     167                rp_model = resource_properties[name].get(resource, env=env, **kwargs) 
     168                 
     169                rp_model._load(iterator = data) 
     170                env.log.debug("get_resource_properties: Loaded resource property: %s, value=%s"%(rp_model.resource, rp_model.value)) 
     171                yield rp_model 
     172         
     173        cursor.close() 
     174         
     175class StrResourcePropertyModel(ResourcePropertyModel): 
     176    def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, uniq_id=None, **kwargs): 
     177        self.uniq_id = uniq_id 
     178        ResourcePropertyModel.__init__(self, env, resource, name=name, db=db, default_value=default_value, fetch=fetch, **kwargs) 
     179  
     180    def _fetch(self, name, db=None): 
     181        handle_ta, db = self._handle_ta(db)  
    67182        self.name = name 
    68183        cursor = db.cursor() 
    69         cursor.execute("SELECT id, name, value, index_ " 
    70                        "FROM metadata " 
    71                        "WHERE parent_realm=%s AND parent_id=%s AND name=%s " 
    72                        "ORDER BY index_", 
    73                        (self.parent_realm, self.parent_id, name)) 
    74         rows = cursor.fetchall() 
     184        cursor.execute("SELECT id, name, value, property_index " 
     185                       "FROM resource_custom " 
     186                       "WHERE resource_realm=%s AND resource_id=%s AND name=%s " 
     187                       "ORDER BY property_index", 
     188                       (self.resource_realm, self.resource_id, name)) 
     189        self._load(iterator = cursor) 
     190        self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value)) 
    75191        cursor.close() 
    76  
    77         if not rows: 
    78             raise ResourceNotFound(_("Metadata '%(name)s' does not exist.", 
    79                                      name=self.name), _('Invalid metadata')) 
    80         if len(rows)==1 and rows[0][3] is None: 
    81             row = rows[0] 
    82             self.uniq_id = row[0] 
    83             self.value = row[2] 
    84             self.index = row[3] and int(row[3]) or 0 
    85         else: 
    86             self.value=[] 
    87             for row in rows: 
    88                 self.value.append(row[2]) 
    89                  
    90  
    91     def insert(self, name, value, db=None): 
    92         """Creates new metadata table record. If value's type is List, then  
    93         for each item in value, a new metadata record is created, where  
    94         index_ is item's position in value list.     
    95                  
    96         @param name: str 
    97         @param value: str 
    98         @param db: Db 
    99         @return: None  
    100         """ 
    101          
    102         handle_ta = False 
    103         if not db: 
    104             db = self.env.get_db_cnx() 
    105             handle_ta = True 
    106          
    107         cursor = db.cursor() 
    108         if isinstance(value, list): 
    109             cursor.executemany("INSERT INTO metadata " 
    110                        "(parent_realm, parent_id, name, value, index_) " 
    111                        "VALUES (%s,%s,%s,%s,%s)", 
    112                        [(self.parent_realm, self.parent_id, name, value_item, idx) for idx, value_item in enumerate(value)]) 
    113         else: 
    114             cursor.execute("INSERT INTO metadata " 
    115                        "(parent_realm, parent_id, name, value) " 
     192             
     193    def _load(self, **kwargs): 
     194        if 'iterator' in kwargs: 
     195            for uniq_id, name, value, property_index in kwargs['iterator']: 
     196                self.uniq_id, self.value, self.index = uniq_id, value, property_index 
     197                return   
     198             
     199        self.uniq_id = kwargs.get('uniq_id', self.uniq_id) 
     200        self.value = kwargs.get('value', self.value) 
     201        self.index = kwargs.get('property_index') and int(kwargs.get('property_index')) or 0  
     202 
     203    def insert(self, name=None, value=None, db=None): 
     204        handle_ta, db = self._handle_ta(db) 
     205        cursor = db.cursor() 
     206        cursor.execute("INSERT INTO resource_custom " 
     207                       "(resource_realm, resource_id, name, value) " 
    116208                       "VALUES (%s,%s,%s,%s)", 
    117                        (self.parent_realm, self.parent_id, name, value)) 
    118          
    119             self.uniq_id = db.get_last_id(cursor, 'metadata') 
    120          
     209                       (self.resource_realm, self.resource_id, name, value)) 
     210        self.uniq_id = db.get_last_id(cursor, 'resource_custom') 
    121211        self.resource.id = self.name = name 
    122212        self.value = value 
    123         self.env.log.info('New metadata: %s by %s', self.name, self.value) 
    124  
     213        self.env.log.info('New resource property: %s by %s', self.name, self.value) 
    125214        if handle_ta: 
    126215            db.commit() 
    127216                    
    128217    def update(self, db=None): 
    129         """Update metadata's value. Update approach is used only when uniq_id is not None.  
    130         Delete/Insert approch is used in rest. 
    131          
    132         @param db: Db 
    133         @return: None 
    134         """ 
    135         if self.uniq_id is None or isinstance(self.value, list): 
     218        if self.uniq_id is None: 
    136219            self.delete(db) 
    137220            return self.insert(self.name, self.value, db) 
    138          
    139         handle_ta = False 
    140         if not db: 
    141             db = self.env.get_db_cnx() 
    142             handle_ta = True 
    143          
    144         cursor = db.cursor() 
     221        handle_ta, db = self._handle_ta(db) 
     222        cursor = db.cursor() 
     223        cursor.execute("UPDATE resource_custom SET value=%s WHERE id=%s", (self.value, self.uniq_id)) 
     224        if handle_ta: 
     225            db.commit() 
     226 
     227    def delete(self, db=None): 
    145228        if self.uniq_id: 
    146             cursor.execute("UPDATE metadata SET value=%s WHERE id=%s", (self.value, self.uniq_id)) 
     229            handle_ta, db = self._handle_ta(db)            
     230            cursor = db.cursor() 
     231            cursor.execute("DELETE FROM resource_custom WHERE id=%s", (self.uniq_id,)) 
     232            if handle_ta: 
     233                db.commit() 
    147234        else: 
    148             cursor.execute("UPDATE metadata SET value=%s " 
    149                            "WHERE parent_realm=%s AND parent_id=%s " 
    150                                "AND name=%s", (self.value, self.parent_realm, self.parent_id, self.name)) 
    151         if handle_ta: 
    152             db.commit() 
    153  
    154     def delete(self, db=None): 
    155         """Remove metadata records. 
    156          
    157         @param db: Db 
    158         @return: None 
    159         """ 
    160         handle_ta = False 
    161         if not db: 
    162             db = self.env.get_db_cnx() 
    163             handle_ta = True 
    164              
    165         cursor = db.cursor() 
    166         if self.uniq_id: 
    167             cursor.execute("DELETE FROM metadata WHERE id=%s", (self.uniq_id,)) 
    168         else: 
    169             cursor.execute("DELETE FROM metadata " 
    170                            "WHERE parent_realm=%s AND parent_id=%s " 
    171                                "AND name=%s", (self.parent_realm, self.parent_id, self.name)) 
    172         if handle_ta: 
    173             db.commit() 
    174        
    175     # Class methods 
    176     def select(cls, env, parent_resource, db=None): 
    177         """Returns all metadatas for a specific parent_resource. 
    178          
     235            ResourcePropertyModel.delete(self, db=db) 
     236             
     237    @classmethod 
     238    def select(cls, env, *args, **kwargs ): 
     239        """Returns all properties for a specific resource. 
     240        @attention: Not stable, will be changed/dropped soon.  
     241         
     242        >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties 
     243                 
    179244        @param cls: Class 
    180245        @param env: Environment 
    181         @param parent_resource: Resource 
    182         @param db: Db 
     246        @param resource: Resource 
    183247        @return: Generator 
    184248        """ 
    185         handle_ta = False 
    186         if not db: 
    187             db = env.get_db_cnx() 
    188             handle_ta = True 
    189  
    190         cursor = db.cursor() 
    191         cursor.execute("SELECT name " 
    192                        "FROM metadata " 
    193                        "WHERE parent_realm=%s AND parent_id=%s " 
    194                        "GROUP BY name", (parent_resource.realm, parent_resource.id)) 
    195         for name, in cursor.fetchall(): 
    196             yield MetadataModel(env, parent_resource, name) 
    197  
    198     def delete_all(cls, env, parent_resource, db=None): 
    199         """Removes all metadata records for a specific parent_record. 
    200          
    201         @param cls: Class 
    202         @param env: Environment 
    203         @param parent_resource: Resource 
    204         @param db: Db 
    205         @return: None 
    206         """ 
    207         handle_ta = False 
    208         if not db: 
    209             db = env.get_db_cnx() 
    210             handle_ta = True 
    211              
    212         cursor = db.cursor()     
    213         cursor.execute("DELETE FROM metadata " 
    214                        "WHERE parent_realm=%s AND parent_id=%s", (parent_resource.realm, parent_resource.id)) 
    215         if handle_ta: 
    216             db.commit() 
    217      
    218     def get_resources_with_metadatas(cls, env, metadatas_dict=None, db=None, limit=None): 
    219         """Returns all resources with metadata matching metadata_dict. limit flag can be used  
    220         in order to limit generator's size. 
    221          
    222         @param cls: Class 
    223         @param env: Environment 
    224         @param metadatas_dict: dict 
    225         @param db: Db 
    226         @param limit: int 
    227         @return: Generator  
    228         """ 
    229         if metadatas_dict is None: 
    230             return [] 
    231          
    232         handle_ta = False 
    233         if not db: 
    234             db = env.get_db_cnx() 
    235             handle_ta = True 
    236              
    237         # dict to list metadatas_dict = { k1:v1, k2:v2, ... } -> [k1,v1,k2,v2..., len(attr_dict)] 
    238         metadatas_list=[] 
    239         for k, v in metadatas_dict.items(): 
    240             metadatas_list.append(k.startswith('NOT_') and k[4:] or k) 
    241             metadatas_list.append(v) 
    242         metadatas_list.append(len(metadatas_dict)) 
    243          
    244         def _get_condition(k,v): 
    245             return "name=%s AND value " + (k.startswith('NOT_') and 'NOT' or '') + " LIKE %s" 
    246          
    247         cursor = db.cursor() 
    248         cursor.execute("SELECT parent_realm, parent_id, parent_version " 
    249                        "FROM metadata " 
    250                        "WHERE " + " OR ".join([ _get_condition(k,v) for k,v in metadatas_dict.items()]) +" " 
    251                        "GROUP BY parent_realm, parent_id, parent_version " 
    252                        "HAVING count(*)=%s " 
    253                        "ORDER BY parent_realm, parent_id DESC" 
    254                        +(limit and " LIMIT %s" or ""), metadatas_list+(limit and [limit] or [])) 
    255          
    256         return [Resource(parent_realm, parent_id, version=parent_version)  
    257                     for parent_realm, parent_id, parent_version in cursor] 
    258      
    259     select = classmethod(select) 
    260     delete_all = classmethod(delete_all) 
    261     get_resources_with_metadatas = classmethod(get_resources_with_metadatas) 
    262      
    263 class MetadataModelProvider(Component): 
     249        resource = Resource() 
     250        if 'resource' in kwargs: 
     251             resource = kwargs['resource'] 
     252         
     253        db = env.get_db_cnx()  
     254        cursor = db.cursor() 
     255        cursor.execute("SELECT id, name, value, property_index " 
     256                        "FROM resource_custom " 
     257                        "WHERE resource_realm=%s AND resource_id=%s " 
     258                        "ORDER BY name, property_index", 
     259                       (resource.realm, resource.id)) 
     260         
     261        for uniq_id, name, value, property_index in cursor: 
     262            yield StrResourcePropertyModel(env, resource, name=name, default_value=value, fetch=False, uniq_id=uniq_id) 
     263 
     264class ListResourcePropertyModel(ResourcePropertyModel): 
     265    def _fetch(self, name, db=None): 
     266        handle_ta, db = self._handle_ta(db)  
     267        self.name = name 
     268        cursor = db.cursor() 
     269        cursor.execute("SELECT id, name, value, property_index " 
     270                       "FROM resource_custom " 
     271                       "WHERE resource_realm=%s AND resource_id=%s AND name=%s " 
     272                       "ORDER BY property_index", 
     273                       (self.resource_realm, self.resource_id, name)) 
     274        self._load(iterator=cursor) 
     275        self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value)) 
     276        cursor.close() 
     277             
     278    def _load(self, **kwargs): 
     279        if 'iterator' in kwargs: 
     280            self.value=[] 
     281            for uniq_id, name, value, property_index in kwargs['iterator']: 
     282                self.value.append(value) 
     283            return 
     284        raise ResourceNotFound(_("Property '%(name)s' does not exist.", 
     285                                     name=self.name), _('Invalid property')) 
     286 
     287    def set(self, value, db=None): 
     288        if isinstance(value, unicode): 
     289            value=value.split(' ')   
     290        self.value = value 
     291        self.update(db) 
     292 
     293 
     294    def insert(self, name=None, value=None, db=None): 
     295        handle_ta, db = self._handle_ta(db) 
     296         
     297        cursor = db.cursor() 
     298        cursor.executemany("INSERT INTO resource_custom " 
     299                       "(resource_realm, resource_id, name, value, property_index) " 
     300                       "VALUES (%s,%s,%s,%s,%s)", 
     301                       [(self.resource_realm, self.resource_id, name, value_item, idx)  
     302                                for idx, value_item in enumerate(value)]) 
     303        if handle_ta: 
     304            db.commit() 
     305             
     306class ResourcePropertyModelProvider(Component): 
    264307    implements(IEnvironmentSetupParticipant) 
    265308 
    266309    SCHEMA = [ 
    267         Table('metadata', key='id')[ 
     310        Table('resource_custom', key='id')[ 
    268311              Column('id', auto_increment=True), 
    269               Column('parent_realm'), 
    270               Column('parent_id'), 
    271               Column('parent_version'), 
     312              Column('resource_realm'), 
     313              Column('resource_id'), 
    272314              Column('name'), 
    273               Column('index_', type='int'), 
    274315              Column('value'), 
    275               Index(['parent_realm', 'parent_id', 'parent_version', 'name']) 
     316              Column('property_index', type='int'), 
     317              Index(['resource_realm', 'resource_id', 'name']) 
    276318              ] 
    277319        ] 
     
    284326        cursor = db.cursor() 
    285327        try: 
    286             cursor.execute("select count(*) from metadata") 
     328            cursor.execute("select count(*) from resource_custom") 
    287329            cursor.fetchone() 
    288330            return False 
     
    308350                    cursor.execute(stmt) 
    309351            db.commit() 
    310  
    311352        except: 
    312353            db.rollback() 
  • trunk/plugins/resourcetoolsplugin/tracresourcetools/tests/api.py

    r447 r467  
    1313from trac.test import EnvironmentStub 
    1414 
    15 from tracresourcetools.api import Metadata 
    16 from tracresourcetools.model import MetadataModelProvider 
     15from tracresourcetools.api import ResourcePropertySystem, ResourcePropertiesCache, ResourceProperty 
     16from tracresourcetools.model import ResourcePropertyModelProvider 
    1717 
    18 class MetadataSystemTestCase(unittest.TestCase): 
     18class ResourcePropertySystemTestCase(unittest.TestCase): 
    1919    def setUp(self): 
    2020        self.basedir = os.path.realpath(tempfile.mkdtemp()) 
    2121        self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 
    2222         
    23         MetadataModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
     23        ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
    2424        self.env.path = os.path.join(self.basedir, 'trac-tempenv') 
    2525        os.mkdir(self.env.path)     
     
    2828        shutil.rmtree(self.basedir) 
    2929         
    30     def test_set_get_metadata(self): 
     30    def test_resource_property_registration(self):  
     31        resource = Resource('wiki', 'WikiStart') 
     32        props_in = [('test_meta_%s'%(i), 'some_value_%s'%(i)) for i in range(10)] 
     33         
     34        class InnerComponent(Component): 
     35            def __init__(self):                 
     36                for prop_name, prop_value  in props_in: 
     37                    ResourceProperty(prop_name, default_value=prop_value, env=self.env) 
     38         
     39        InnerComponent(self.env) 
     40         
     41        self.assertEqual(len(ResourceProperty.registry), len(props_in)) 
     42     
     43    def test_set_get_property(self): 
    3144        resource = Resource('wiki', 'WikiStart', version=1) 
    32         meta_name, meta_value = 'test_meta', 'some_value' 
     45        prop_name, prop_value = 'test_meta', 'some_value' 
    3346         
    34         Metadata(self.env, resource, meta_name).set(meta_value) 
    35         self.assertEqual(Metadata(self.env, resource, meta_name).get(), meta_value) 
    36      
     47        class InnerComponent(Component): 
     48            prop = ResourceProperty(prop_name) 
     49             
     50        prop_model = ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource] 
     51        prop_model.value = prop_value 
     52        prop_model.update() 
     53         
     54        self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 
     55             
     56    def test_set_get_property_from_component(self): 
     57        resource = Resource('wiki', 'WikiStart', version=1) 
     58        prop_name, prop_value = 'test_meta', 'some_value' 
     59         
     60        class InnerComponent(Component): 
     61            prop = ResourceProperty(prop_name) 
     62            def get(self, resource): 
     63                return self.prop[resource].value 
     64            def set(self, resource, value): 
     65                prop_model = self.prop[resource] 
     66                prop_model.value = value 
     67                prop_model.update() 
     68 
     69        InnerComponent(self.env).set(resource, prop_value) 
     70        self.assertEqual(InnerComponent(self.env).get(resource), prop_value) 
     71 
     72class ResourcePropertyCacheTestCase(unittest.TestCase): 
     73    def setUp(self): 
     74        self.basedir = os.path.realpath(tempfile.mkdtemp()) 
     75        self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 
     76         
     77        ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
     78        self.env.path = os.path.join(self.basedir, 'trac-tempenv') 
     79        os.mkdir(self.env.path)     
     80   
     81    def tearDown(self): 
     82        shutil.rmtree(self.basedir) 
     83         
     84    def test_set_get_property(self): 
     85        resource = Resource('wiki', 'WikiStart') 
     86        prop_name, prop_value = 'test_meta', 'some_value' 
     87         
     88        class InnerComponent(Component): 
     89            prop = ResourceProperty(prop_name) 
     90         
     91        cache = ResourcePropertiesCache(self.env) 
     92        cache.get(InnerComponent(self.env).prop, resource).value = prop_value 
     93        cache.get(InnerComponent(self.env).prop, resource).update() 
     94         
     95        self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 
     96         
     97    def test_set_get_property(self): 
     98        resource = Resource('wiki', 'WikiStart') 
     99        prop_name, prop_value = 'test_meta', 'some_value' 
     100         
     101        class InnerComponent(Component): 
     102            prop = ResourceProperty(prop_name) 
     103         
     104        cache = ResourcePropertiesCache(self.env) 
     105        cache.get(InnerComponent(self.env).prop, resource).value = prop_value 
     106        cache.get(InnerComponent(self.env).prop, resource).update() 
     107         
     108        self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 
     109 
     110    def test_get_resource_properties(self): 
     111        resource = Resource('wiki', 'WikiStart') 
     112        props_in = [('test_meta_%s'%(i), 'some_value_%s'%(i)) for i in range(10)] 
     113         
     114        class InnerComponent(Component): 
     115            def __init__(self):                 
     116                for prop_name, prop_value  in props_in: 
     117                    ResourceProperty(prop_name, default_value=prop_value, env=self.env) 
     118         
     119        InnerComponent(self.env) 
     120         
     121        rp_cache = ResourcePropertiesCache(self.env) 
     122        self.assertEqual(rp_cache.get(ResourceProperty.registry[props_in[0][0]], resource).value, props_in[0][1]) 
     123        self.assertEqual(len(rp_cache.property_cache),1) 
     124         
     125        props_out = [(model.name, model.value) for model in rp_cache.get_resource_properties(resource)] 
     126         
     127        for prop_in in props_in: 
     128            self.assertTrue(prop_in in props_out)  
     129 
    37130def suite(): 
    38131    suite = unittest.TestSuite() 
    39     suite.addTest(unittest.makeSuite(MetadataSystemTestCase, 'test')) 
     132    suite.addTest(unittest.makeSuite(ResourcePropertySystemTestCase, 'test')) 
     133    suite.addTest(unittest.makeSuite(ResourcePropertyCacheTestCase, 'test')) 
    40134    return suite 
    41135 
  • trunk/plugins/resourcetoolsplugin/tracresourcetools/tests/model.py

    r447 r467  
    1212from trac.test import EnvironmentStub 
    1313 
    14 from tracresourcetools.api import MetadataSystem 
    15 from tracresourcetools.model import MetadataModelProvider, MetadataModel 
    16  
    17 class MetadataModelTestCase(unittest.TestCase): 
     14from tracresourcetools.api import ResourcePropertySystem 
     15from tracresourcetools.model import ResourcePropertyModelProvider, StrResourcePropertyModel, ListResourcePropertyModel, ResourcePropertyModel 
     16 
     17 
     18class ResourceModelModelTestCase(unittest.TestCase): 
    1819    def setUp(self): 
    1920        self.basedir = os.path.realpath(tempfile.mkdtemp()) 
    2021        self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 
    21         MetadataModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
     22        ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
    2223        self.env.path = os.path.join(self.basedir, 'trac-tempenv') 
    2324        os.mkdir(self.env.path)     
     
    2627        shutil.rmtree(self.basedir) 
    2728         
    28     def test_insert(self): 
    29         parent_realm, parent_id = "wiki", "WikiStart" 
    30         name, value = "meta_test", "some_value" 
    31         MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    32          
    33         db = self.env.get_db_cnx() 
    34         cursor = db.cursor() 
    35         cursor.execute("SELECT id, name, value, index_ " 
    36                        "FROM metadata " 
    37                        "WHERE parent_realm=%s " 
    38                            "AND parent_id=%s " 
    39                            "AND name=%s", 
    40                        (parent_realm, parent_id, name)) 
    41         row = cursor.fetchone() 
    42         cursor.close() 
    43          
    44         self.assertTrue(row) 
    45         self.assertEqual(name, row[1]) 
    46         self.assertEqual(value, row[2]) 
    47      
    48     def test_insert_multi(self): 
    49         parent_realm, parent_id = "wiki", "WikiStart" 
    50         name, value = "meta_test", ["some_value1", "some_value2"] 
    51         MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    52          
    53         db = self.env.get_db_cnx() 
    54         cursor = db.cursor() 
    55         cursor.execute("SELECT id, name, value, index_ " 
    56                        "FROM metadata " 
    57                        "WHERE parent_realm=%s " 
    58                            "AND parent_id=%s " 
    59                            "AND name=%s", 
    60                        (parent_realm, parent_id, name)) 
    61         rows = cursor.fetchall() 
    62         cursor.close() 
    63          
    64         self.assertEqual(len(rows),2) 
    65         self.assertEqual(name, rows[0][1]) 
    66         value_out = [row_out[2] for row_out in rows] 
    67         for value_item_in in value: 
    68             self.assertTrue(value_item_in in value_out) 
    69  
    70     def test_fetch_multi_one_item(self): 
    71         parent_realm, parent_id = "wiki", "WikiStart" 
    72         name, value = "meta_test", ["some_value"] 
    73         MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    74          
    75         self.assertEqual(value, MetadataModel(self.env, Resource(parent_realm, parent_id), name).value) 
    76          
    77     def test_update(self): 
    78         parent_realm, parent_id = "wiki", "WikiStart" 
    79         name, value, value_new = "meta_test", "some_value", "some_new_value" 
    80          
    81         # insert new value 
    82         MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    83                  
    84         # retrive inserted 
    85         metadata = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 
    86         metadata.value = value_new 
    87         metadata.update() 
    88                 
    89         db = self.env.get_db_cnx() 
    90         cursor = db.cursor() 
    91         cursor.execute("SELECT id, name, value, index_ " 
    92                        "FROM metadata " 
    93                        "WHERE parent_realm=%s " 
    94                            "AND parent_id=%s " 
    95                            "AND name=%s", 
    96                        (parent_realm, parent_id, name)) 
    97         row = cursor.fetchone() 
    98         cursor.close() 
    99          
    100         self.assertTrue(row) 
    101         self.assertEqual(name, row[1]) 
    102         self.assertEqual(value_new, row[2]) 
    103          
    104     def test_delete(self): 
    105         parent_realm, parent_id = "wiki", "WikiStart" 
    106         metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)]  
    107                  
    108         # insert new value 
    109         for name, value in metas_in:  
    110             MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    111                          
    112         # check if inserted 
    113         for name, value in metas_in:  
    114             meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 
    115             self.assertEqual(value, meta.value) 
    116          
    117         # delete one 
    118         meta_to_delete = metas_in.pop(3) 
    119         MetadataModel(self.env, Resource(parent_realm, parent_id), meta_to_delete[0]).delete() 
    120                          
    121         # select all contents 
    122         metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 
    123                  
    124         # check if it's deleted 
    125         self.assertEqual(len(metas_in), len(metas_out)) 
    126         for meta in metas_out: 
    127             self.assertEqual((meta.name, meta.value) in metas_in, True ) 
    128      
    129     def test_select(self): 
    130         parent_realm, parent_id = "wiki", "WikiStart" 
    131         metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)]  
    132                  
    133         # insert new value     
    134         for name, value in metas_in:  
    135             MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    136                          
    137         # check if inserted 
    138         for name, value in metas_in:  
    139             meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 
    140             self.assertEqual(value, meta.value) 
    141          
    142         # checking select 
    143         metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 
    144          
    145         self.assertEqual(len(metas_in), len(metas_out)) 
    146          
    147         for meta in metas_out: 
    148             self.assertEqual((meta.name, meta.value) in metas_in, True ) 
    149      
    150     def test_delete_all(self): 
    151         parent_realm, parent_id = "wiki", "WikiStart" 
    152         metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)]  
    153                  
    154         # insert new value 
    155         for name, value in metas_in:  
    156             MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 
    157                          
    158         # check if inserted 
    159         for name, value in metas_in:  
    160             meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 
    161             self.assertEqual(value, meta.value) 
    162          
    163         MetadataModel.delete_all(self.env, Resource(parent_realm, parent_id)) 
    164                          
    165         # select all contents 
    166         metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 
    167         self.assertEqual(0, len(metas_out)) 
    168          
    169     def test_get_resources_with_metadatas(self): 
    170         metas_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 
     29    def test_get_resources_with_properties(self): 
     30        props_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 
    17131        resources_in_filter = [('wiki','TestPage%s'%(i)) for i in range(10)] 
    17232         
    173         metas_in_other = {'m7':1, 'm4':2, 'm5':3} 
     33        props_in_other = {'m7':1, 'm4':2, 'm5':3} 
    17434        resources_in_other = [('wiki','TestOtherPage%s'%(i)) for i in range(10)] 
    17535         
    17636        # insert filter 
    17737        for realm, id in resources_in_filter: 
    178             for k,v in metas_in_filter.items(): 
    179                 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 
     38            for k,v in props_in_filter.items(): 
     39                StrResourcePropertyModel(self.env, Resource(realm, id)).insert(k, v) 
    18040         
    18141        # insert other  
    18242        for realm, id in resources_in_other: 
    183             for k,v in metas_in_other.items(): 
    184                 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 
    185                  
     43            for k,v in props_in_other.items(): 
     44                StrResourcePropertyModel(self.env, Resource(realm, id)).insert(k, v) 
     45 
    18646        # get resources 
    187         resources_out_filter = MetadataModel.get_resources_with_metadatas(self.env, metas_in_filter) 
    188         resources_out_other = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other) 
     47        resources_out_filter = list(ResourcePropertyModel.get_resources_with_properties(self.env, **props_in_filter)) 
     48        resources_out_other = list(ResourcePropertyModel.get_resources_with_properties(self.env, **props_in_other)) 
    18949         
    19050        # test 
     
    19656        for resource in resources_out_other: 
    19757            self.assertTrue((resource.realm, resource.id) in resources_in_other) 
     58         
     59class StrResourceModelModelTestCase(unittest.TestCase): 
     60    def setUp(self): 
     61        self.basedir = os.path.realpath(tempfile.mkdtemp()) 
     62        self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 
     63        ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
     64        self.env.path = os.path.join(self.basedir, 'trac-tempenv') 
     65        os.mkdir(self.env.path)     
     66   
     67    def tearDown(self): 
     68        shutil.rmtree(self.basedir) 
     69         
     70    def test_insert(self): 
     71        resource_realm, resource_id = "wiki", "WikiStart" 
     72        name, value_in = "test_prop", "some_value" 
     73        prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)) 
     74        prop.insert(name, value_in) 
     75         
     76        db = self.env.get_db_cnx() 
     77        cursor = db.cursor() 
     78        cursor.execute("SELECT id, value " 
     79                       "FROM resource_custom " 
     80                       "WHERE resource_realm=%s " 
     81                           "AND resource_id=%s " 
     82                           "AND name=%s", 
     83                       (resource_realm, resource_id, name)) 
     84        uniq_id_out, value_out = cursor.fetchone() 
     85        cursor.close() 
     86         
     87        self.assertEqual(value_in, value_out) 
     88        self.assertEqual(prop.uniq_id, uniq_id_out) 
     89         
     90    def test_update(self): 
     91        resource_realm, resource_id = "wiki", "WikiStart" 
     92        name_in, value_in, value_in_new = "testing_is_fun", "some_value", "some_new_value" 
     93         
     94        # insert new value 
     95        StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name_in, value_in) 
     96                 
     97        # retrive inserted 
     98        prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name_in) 
     99        prop.value = value_in_new 
     100        prop.update() 
     101                
     102        db = self.env.get_db_cnx() 
     103        cursor = db.cursor() 
     104        cursor.execute("SELECT id, name, value " 
     105                       "FROM resource_custom " 
     106                       "WHERE id=%s", 
     107                       (prop.uniq_id,)) 
     108        uniq_id_out, name_out, value_out = cursor.fetchone() 
     109        cursor.close() 
     110         
     111        self.assertEqual(name_in, name_out) 
     112        self.assertEqual(value_in_new, value_out) 
     113         
     114    def test_delete(self): 
     115        resource_realm, resource_id = "wiki", "WikiStart" 
     116        props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)]  
     117                 
     118        # insert new value 
     119        for name, value in props_in:  
     120            StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 
     121                         
     122        # check if inserted 
     123        for name, value in props_in:  
     124            prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 
     125            self.assertEqual(value, prop.value) 
     126         
     127        # delete one 
     128        prop_to_delete = props_in.pop(3) 
     129        StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), prop_to_delete[0]).delete() 
     130                         
     131        # select all contents 
     132        props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 
     133                 
     134        # check if it's deleted 
     135        self.assertEqual(len(props_in), len(props_out)) 
     136        for prop_out in props_out: 
     137            self.assertEqual((prop_out.name, prop_out.value) in props_in, True ) 
    198138     
    199     def test_get_resources_with_metadatas_limit(self): 
    200         metas_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 
    201         resources_in_filter = [('wiki','TestPage%s'%(i)) for i in range(10)] 
    202          
    203         metas_in_other = {'m7':1, 'm4':2, 'm5':3} 
    204         resources_in_other = [('wiki','TestOtherPage%s'%(i)) for i in range(10)] 
    205          
    206         # insert filter 
    207         for realm, id in resources_in_filter: 
    208             for k,v in metas_in_filter.items(): 
    209                 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 
    210          
    211         # insert other  
    212         for realm, id in resources_in_other: 
    213             for k,v in metas_in_other.items(): 
    214                 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 
    215                  
    216         # get resources 
    217         resources_out_filter = MetadataModel.get_resources_with_metadatas(self.env, metas_in_filter) 
    218         resources_out_other = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other) 
    219          
    220         resources_out_filter_limit = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other, limit=2) 
    221          
    222         # test 
    223         self.assertEqual(len(resources_in_filter), len(resources_out_filter)) 
    224         for resource in resources_out_filter: 
    225             self.assertTrue((resource.realm, resource.id) in resources_in_filter) 
    226          
    227         self.assertEqual(len(resources_in_other), len(resources_out_other)) 
    228         for resource in resources_out_other: 
    229             self.assertTrue((resource.realm, resource.id) in resources_in_other) 
    230          
    231         self.assertEqual(len(resources_out_filter_limit), 2) 
     139    def test_select(self): 
     140        resource_realm, resource_id = "wiki", "WikiStart" 
     141        props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)]  
     142                 
     143        # insert new value     
     144        for name, value in props_in:  
     145            StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 
     146                         
     147        # check if inserted 
     148        for name, value in props_in:  
     149            prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 
     150            self.assertEqual(value, prop.value) 
     151         
     152        # checking select 
     153        props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 
     154         
     155        self.assertEqual(len(props_in), len(props_out)) 
     156         
     157        for prop in props_out: 
     158            self.assertEqual((prop.name, prop.value) in props_in, True ) 
     159     
     160    def test_delete_all(self): 
     161        resource_realm, resource_id = "wiki", "WikiStart" 
     162        props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)]  
     163                 
     164        # insert new value 
     165        for name, value in props_in:  
     166            StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 
     167                         
     168        # check if inserted 
     169        for name, value in props_in:  
     170            prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 
     171            self.assertEqual(value, prop.value) 
     172         
     173        StrResourcePropertyModel.delete_all(self.env, Resource(resource_realm, resource_id)) 
     174                         
     175        # select all contents 
     176        props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 
     177        self.assertEqual(0, len(props_out)) 
     178 
     179class ListResourceModelModelTestCase(unittest.TestCase): 
     180    def setUp(self): 
     181        self.basedir = os.path.realpath(tempfile.mkdtemp()) 
     182        self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 
     183        ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 
     184        self.env.path = os.path.join(self.basedir, 'trac-tempenv') 
     185        os.mkdir(self.env.path)     
     186   
     187    def tearDown(self): 
     188        shutil.rmtree(self.basedir) 
     189         
     190    def test_insert(self): 
     191        resource_realm, resource_id = "wiki", "WikiStart" 
     192        name, value_in = 'prop_list', ['value%s'%(i) for i in range(10)] 
     193          
     194        prop = ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id)) 
     195        prop.insert(name, value_in) 
     196         
     197        db = self.env.get_db_cnx() 
     198        cursor = db.cursor() 
     199        cursor.execute("SELECT value, property_index " 
     200                       "FROM resource_custom " 
     201                       "WHERE resource_realm=%s " 
     202                           "AND resource_id=%s " 
     203                           "AND name=%s " 
     204                        "ORDER BY property_index", 
     205                       (resource_realm, resource_id, name)) 
     206        value_out=[value for value, idx in cursor] 
     207        cursor.close() 
     208         
     209        self.assertEqual(value_in, value_out) 
     210 
     211    def test_update(self): 
     212        resource_realm, resource_id = "wiki", "WikiStart" 
     213        name_in, value_in, value_in_new = "testing_is_fun", [u'value%s'%(i) for i in range(10)], [u'new value%s'%(i) for i in range(10)] 
     214         
     215        # insert new value 
     216        ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name_in, value_in) 
     217                 
     218        # retrive inserted 
     219        prop = ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name_in) 
     220        prop.value = value_in_new 
     221        prop.update() 
     222                
     223        db = self.env.get_db_cnx() 
     224        cursor = db.cursor() 
     225        cursor.execute("SELECT value, property_index " 
     226                       "FROM resource_custom " 
     227                       "WHERE resource_realm=%s " 
     228                           "AND resource_id=%s " 
     229                           "AND name=%s " 
     230                        "ORDER BY property_index ASC", 
     231                       (resource_realm, resource_id, name_in)) 
     232        value_out=[value for value, idx in cursor] 
     233        cursor.close() 
     234         
     235        self.assertEqual(value_in_new, value_out) 
    232236         
    233237def suite(): 
    234238    suite = unittest.TestSuite() 
    235     suite.addTest(unittest.makeSuite(MetadataModelTestCase, 'test')) 
     239    suite.addTest(unittest.makeSuite(StrResourceModelModelTestCase, 'test')) 
     240    suite.addTest(unittest.makeSuite(ListResourceModelModelTestCase, 'test')) 
     241    suite.addTest(unittest.makeSuite(ResourceModelModelTestCase, 'test')) 
    236242    return suite 
    237243 
  • trunk/plugins/wikitoolsplugin/setup.py

    r447 r467  
    88 
    99setup(name="TracWikiToolsPlugin", 
    10       version="0.2", 
     10      version="0.3", 
    1111      packages=['tracwikitools'], 
    1212      url="http://code.optaros.com/trac/oforge", 
     
    1414      license="BSD", 
    1515      entry_points={'trac.plugins': [ 
     16            'tracwikitools.api = tracwikitools.api', 
    1617            'tracwikitools.web_ui = tracwikitools.web_ui', 
    1718            'tracwikitools.wiki = tracwikitools.wiki']} 
  • trunk/plugins/wikitoolsplugin/tracwikitools/web_ui.py

    r447 r467  
    66from trac.core import * 
    77from trac.web.api import ITemplateStreamFilter 
    8 from trac.web.main import IRequestHandler 
    98from trac.wiki.api import IWikiPageManipulator 
    10 from trac.util.compat import groupby 
    11  
    129from genshi.filters.transform import Transformer 
    1310from genshi.builder import tag 
    14 from tracresourcetools.api import IMetadataHandler, MetadataSystem 
    1511 
    16 class DefaultWikiCustomFieldHandler(Component): 
    17     implements(IMetadataHandler) 
    18      
    19     def __init__(self): 
    20         self._type_fields_map = None 
    21         self._fields = None 
    22  
    23     # IMetadataHandler 
    24     def get_handled_metadata(self, resource, context_data={}): 
    25         page_type = MetadataSystem(self.env).get_metadata(resource, 'type',  
    26                                                           default_value=context_data.get('type'), 
    27                                                           lookup_handler=False) 
    28         for field_name in self._parse_config().get(page_type,[]): 
    29             yield field_name 
    30     def get_supported_operations(self, metadata): 
    31         yield 'init_metadata' 
    32         yield 'render_metadata' 
    33     def init_metadata(self, metadata): 
    34         metadata.data.update(self._fields[metadata.name]) 
    35  
    36     def render_metadata(self, req, metadata): 
    37         element_type = metadata.data.get('element_type', 'text') 
    38         try: 
    39             return dict(text = self._render_text,  
    40                         textarea = self._render_textarea, 
    41                         select = self._render_select 
    42                         )[element_type](metadata) 
    43         except KeyError, e:       
    44             return self._render_text(metadata)     
    45      
    46     # internals  
    47     def _render_text(self, metadata): 
    48         return tag.div( 
    49                 tag.label(metadata.data.get('label', metadata.name), 
    50                           tag.br(), 
    51                           tag.input(type="text", name=metadata.name, size=metadata.data.get('size'), value=metadata.get())),  
    52             class_="field") 
    53              
    54     def _render_textarea(self, metadata): 
    55         return tag.div( 
    56                 tag.label(metadata.data.get('label', metadata.name), 
    57                           tag.br(), 
    58                           tag.textarea(metadata.get(),  
    59                              name=metadata.name,  
    60                              rows=metadata.data.get('rows'), 
    61                              cols=metadata.data.get('cols'), 
    62                              style=metadata.data.get('style'))), 
    63             class_="field") 
    64     def _render_select(self, metadata): 
    65         return tag.div( 
    66                 tag.label(metadata.data.get('label', metadata.name), 
    67                           tag.br(), 
    68                           tag.select([tag.option(opt_item, selected=metadata.get()==opt_item and 'selected' or None)  
    69                                 for opt_item in metadata.data.get('options','').split("|")],  
    70                              name=metadata.name,  
    71                              rows=metadata.data.get('rows'), 
    72                              cols=metadata.data.get('cols'), 
    73                              style=metadata.data.get('style'))), 
    74             class_="field") 
    75      
    76     def _parse_config(self): 
    77         if self._type_fields_map is None: 
    78             self._type_fields_map={} 
    79             self._fields = {} 
    80              
    81             for option, value in list(self.config.options('wiki-custom')): 
    82                 parts = option.split('.') 
    83                 field = parts[0] 
    84                 if field not in self._fields: 
    85                     self._fields[field] = {} 
    86                 if len(parts) == 1: 
    87                     self._fields[field]['type']=value 
    88                     if not self._type_fields_map.has_key(value): 
    89                         self._type_fields_map[value]=[] 
    90                     self._type_fields_map[value].append(field) 
    91                 else: 
    92                     self._fields[field]['.'.join(parts[1:])] = value 
    93         return self._type_fields_map 
     12from tracresourcetools.api import ResourcePropertySystem, ResourceProperty 
     13from tracwikitools.api import WikiCustomFieldsSystem 
    9414       
    9515class WikiCustomFieldModule(Component): 
    9616    implements(IWikiPageManipulator, ITemplateStreamFilter) 
     17    page_type = ResourceProperty('wiki_type', default_value='wiki-page') 
    9718     
    9819    # IWikiPageManipulator methods 
     
    11132    # Internals    
    11233    def _set_custom_fields(self, req, page): 
    113         # save fields 
    114         meta = MetadataSystem(self.env) 
    115         page_type = meta.get_metadata(page.resource, 'type', req.args.get('page_type')) 
    116         for metadata in meta.get_metadatas(page.resource,  
    117                                            context_data=dict(type=page_type), 
    118                                            filter=lambda x: x.data.get('type') in [page_type, 'all']): 
    119             metadata.set(req.args.get(metadata.name).strip()) 
    120         # save type  
    121         meta.set_metadata(page.resource, 'type', req.args.get('page_type').strip()) 
     34        resource = page.resource 
     35        db = self.env.get_db_cnx() 
     36         
     37        page_type = self.page_type.get(resource, req=req, default_value=req.args.get('page_type')) 
     38        page_type.set(req.args.get('page_type', 'wiki-page'), db) 
     39         
     40        for prop, renderer in WikiCustomFieldsSystem(self.env).get_wiki_custom_fields(resource, req=req): 
     41            prop_model = prop.get(resource, req=req)  
     42            prop_model.set(req.args.get(prop.name, prop_model.value).strip(), db) 
     43 
     44        db.commit() 
    12245         
    12346    def _view_wiki_edit(self, req, method, filename, stream, data): 
    124  
    125         # collect custom wiki fields 
    126         meta = MetadataSystem(self.env) 
    127         page_type = meta.get_metadata(data['page'].resource, 'type', req.args.get('page_type')) 
     47        resource = data['page'].resource 
    12848         
    129         metadatas = meta.get_metadatas(data['page'].resource, 
    130                                        context_data=dict(type=page_type), 
    131                                        filter=lambda x: x.data.get('type') in [page_type, 'all']) 
    132  
    133         # insert custom fields 
    134         insert_before = tag.span() 
    135         insert_after = tag.span() 
    136           
    137         for position, metadatas_group in groupby(metadatas, lambda x:x.data.get('position', 'before-wikitext')): 
    138             tag_holder = position=='before-wikitext' and insert_before or insert_after 
    139             for metadata in sorted(metadatas_group, key=lambda x:x.data.get('order', 9999)): 
    140                 if metadata.is_render_supported(): 
    141                     tag_holder.append(metadata.render(req)) 
    142                 else: 
    143                     tag_holder.append(tag.input(type='text', name=metadata.name, id=metadata.name, value=metadata.get())) 
     49        page_type = self.page_type.get(resource, req=req, default_value=req.args.get('page_type')) 
     50        page_type_element = tag.input(type='hidden', name='page_type', id='page_type', value=page_type.value) 
     51        stream = stream | Transformer('//textarea[@name="text"]').before(page_type_element) 
    14452         
    145         page_type_element = tag.input(type='hidden', name='page_type', id='page_type', value=page_type) 
    146          
    147         return stream | Transformer('//textarea[@name="text"]').before(page_type_element).before(insert_before).after(insert_after) 
     53        for resource_property, renderer in WikiCustomFieldsSystem(self.env).get_wiki_custom_fields(resource, req=req): 
     54            stream = renderer.render_wiki_custom_fields(req, method, filename, stream, data, resource_property.get(resource, req=req)) 
     55        return stream  
  • trunk/plugins/wikitoolsplugin/tracwikitools/wiki.py

    r447 r467  
    1212from trac.util.translation import _ 
    1313 
    14 from tracwikitools.web_ui import DefaultWikiCustomFieldHandler 
    15 from tracresourcetools.api import IMetadataHandler, MetadataSystem, DontSaveMetadataException 
    16 from tracresourcetools.model import MetadataModel 
     14from tracresourcetools.api import ResourcePropertySystem, ResourceProperty, ListResourceProperty   
     15 
     16from tracwikitools.api import IWikiCustomFieldsProvider 
    1717    
    1818class ShowRelatedPagesMacro(WikiMacroBase): 
     
    2020    {{{ 
    2121        [[ShowRelatedPages]]  
    22     }}} 
    23      
     22    }}}     
    2423    """ 
     24    implements(IWikiCustomFieldsProvider) 
    2525     
    2626    max_levels_lookup = Option('wikitools', 'max_levels_lookup', 500, "Maximum number of tree levels lookup.") 
     27         
     28    parent_page = ResourceProperty('parent_page', label='Parent page', position='changeinfo1') 
     29    related_pages = ListResourceProperty('related_pages', label='Related Pages', position='changeinfo1') 
    2730     
    28     implements(IMetadataHandler) 
    29      
    30     # IMetadataHandler 
    31     def get_handled_metadata(self, resource, context_data={}): 
    32         if resource.realm == 'wiki': 
    33             yield 'related_pages' 
    34             yield 'parent_page' 
    35             # virtual field used only to update child page's parent_page with current resource's name 
    36             yield 'child_pages'  
    37     def get_supported_operations(self, metadata, context_data={}): 
    38         yield 'init_metadata' 
    39         yield 'render_metadata' 
    40         if metadata.name in ['related_pages','child_pages']: 
    41             yield 'get_metadata' 
    42             yield 'set_metadata' 
    43     def get_metadata(self, metadata, default_value, context_data={}): 
    44         if metadata.name == 'child_pages': 
    45             meta = MetadataSystem(self.env) 
    46             child_pages = [res.id for res in meta.get_resources_with_metadatas(dict(parent_page=metadata.resource.id))] 
    47             return ' '.join(child_pages) 
     31    # IWikiCustomFieldsProvider 
     32    def get_wiki_custom_fields(self, resource, resource_properties_cache): 
     33        yield self.parent_page 
     34        yield self.related_pages 
    4835 
    49         value = metadata.model.value 
    50         if value:  
    51             return ' '.join(value) 
    52         return default_value 
    53     def set_metadata(self, metadata, value, context_data={}): 
    54         if metadata.name=='child_pages': 
    55             # update child pages with parent_page 
    56             if value: 
    57                 meta = MetadataSystem(self.env) 
    58                 # delete old values 
    59                 for res in meta.get_resources_with_metadatas(dict(parent_page=metadata.resource.id)): 
    60                     meta.delete_metadata(res, 'parent_page') 
    61                 # update child list 
    62                 for child_page in value.split(' '): 
    63                     child_resource = Resource('wiki', child_page) 
    64                     meta.set_metadata(child_resource, 'parent_page', metadata.resource.id, lookup_handler=False) 
    65             raise DontSaveMetadataException('child_pages') 
    66         # set related pages  
    67         if value: 
    68             metadata.model.value = value.split(' ') 
    69         else: 
    70             metadata.model.value = None 
    71     def init_metadata(self, metadata, context_data={}): 
    72         metadata.data.update(type='all', 
    73                              element_type='text', 
    74                              size='50', 
    75                              position = 'after-wikitext') 
    76         if metadata.name=='related_pages': 
    77             metadata.data.update(label=_('Related pages')) 
    78         elif metadata.name=='parent_page': 
    79             metadata.data.update(label=_('Parent page')) 
    80         elif metadata.name=='child_pages': 
    81             metadata.data.update(label=_('Child pages')) 
    82             metadata._fetched=True # Avoid model call 
    83     def render_metadata(self, req, metadata, context_data={}): 
    84         return DefaultWikiCustomFieldHandler(self.env).render_metadata(req, metadata)     
    85      
    86     # WikiMacroBase methods   
     36    # WikiMacroBase methods 
    8737    def expand_macro(self, formatter, name, content): 
    88         meta = MetadataSystem(self.env) 
    89         related_resources = meta.get_resources_with_metadatas(dict(related_pages='%'+formatter.resource.id+'%')) 
     38        rp_system = ResourcePropertySystem(self.env) 
     39        related_resources = list(rp_system.get_resources_with_properties(related_pages=formatter.resource.id)) 
     40         
    9041        for resource in [Resource('wiki',related) for related in  
    91                          meta.get_metadata(formatter.resource,'related_pages','', handler=self).split(' ') ]: 
    92             if resource not in related_resources: 
     42                         self.related_pages.get(formatter.resource, req=formatter.req).value]: 
     43            if resource not in related_resources and resource.id: 
    9344                related_resources.append(resource) 
    9445                  
     
    9950            related_links.append(html.li(html.a(link, href=href))) 
    10051         
    101          
    10252        _get_href = lambda res: get_resource_url(self.env, res, formatter.context.href) 
    10353        _get_link = lambda res: html.a(res.id, href=_get_href(res))  
    10454         
    105          
    106         child_pages = meta.get_metadata(formatter.resource, 'child_pages', handler=self).split(' ') 
    10755        ancestors = html.li(html.a(html.b(formatter.resource.id), href=_get_href(formatter.resource))) 
    10856        ancestors.append(html.ul( 
    10957            [ html.li( _get_link(child_page))  
    110                 for child_page in meta.get_resources_with_metadatas(dict(parent_page=formatter.resource.id)) ])) 
     58                for child_page in reversed(list(rp_system.get_resources_with_properties(parent_page=formatter.resource.id))) ])) 
    11159         
    11260        parent = formatter.resource 
     61        visited_parent_ids = [parent.id] 
    11362        for counter in range(self.max_levels_lookup): 
    114             parent_id = meta.get_metadata(parent, 'parent_page', lookup_handler=False) 
    115             if parent_id is None: 
     63            parent_id = self.parent_page.get(parent, req=formatter.req).value 
     64            if parent_id is None or parent_id in visited_parent_ids: 
    11665                break 
    11766            parent = Resource('wiki', parent_id) 
     
    11968         
    12069        return html.span( html.ul(ancestors), 
    121                 html.li(_('Related'), related_links), class_="wiki-toc") 
    122          
     70                len(related_links.children)>0 and html.ul(html.b(_('Related')), related_links) or None, class_="wiki-toc") 
Note: See TracChangeset for help on using the changeset viewer.