Changeset 602


Ignore:
Timestamp:
10/17/08 14:24:34 (5 years ago)
Author:
aculapov
Message:
  • redesign the system to add the relations fields to the ticket object
Location:
trunk/plugins/tickettypedsystem/ticketsystem
Files:
3 deleted
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/plugins/tickettypedsystem/ticketsystem/api.py

    r495 r602  
    2121    interface changes that have to be done for specific ticket types.  
    2222    """ 
    23     def match_for_render(self, req, data): 
    24         """ 
    25         Returns True if the implementation can handle this request. 
     23    def match_for_render(self, type): 
     24        """ 
     25        Returns True if the implementation can handle this ticket type. 
    2626        """ 
    2727        pass 
     
    7575class ITicketFieldProvider(Interface): 
    7676     
    77     def get_ticket_type(self): 
    78         """ 
    79         Returns the ticket type for which a implementation was built. 
    80         """ 
    81         pass 
    82      
    83     def get_fields(self): 
     77    def get_ticket_types(self): 
     78        """ 
     79        Returns the ticket types for which a implementation was built. 
     80        """ 
     81        pass 
     82     
     83    def get_fields(self, type): 
    8484        """ 
    8585        Returns a list with the permitted fields.  
     86        """ 
     87        pass 
     88     
     89    def get_value(self, ticket, field_name): 
     90        """ 
     91        Returns the value for this field. 
    8692        """ 
    8793        pass 
     
    148154        fields = list() 
    149155        for filter in self.ticket_filters : 
    150             if ticket_type == filter.get_ticket_type(): 
    151                 fields.extend(filter.get_fields()) 
     156            if ticket_type in filter.get_ticket_types(): 
     157                fields.extend(filter.get_fields(ticket_type)) 
    152158        # return the filtered fields  
    153159        return [field for field in original_fields if field['name'] in fields] 
     
    160166        filtered = False 
    161167        for filter in self.ticket_filters : 
    162             if ticket_type == filter.get_ticket_type(): 
     168            if ticket_type in filter.get_ticket_types(): 
    163169                filtered = True 
    164                 fields.extend(filter.get_fields()) 
     170                fields.extend(filter.get_fields(ticket_type)) 
    165171        # return the filtered fields  
    166172        if filtered : 
     
    179185        fields = list() 
    180186        for filter in self.ticket_filters : 
    181             if ticket_type == filter.get_ticket_type(): 
    182                 fields.extend(filter.get_fields()) 
     187            if ticket_type in filter.get_ticket_types(): 
     188                fields.extend(filter.get_fields(ticket_type)) 
    183189        # return the filtered fields names  
    184190        return [field['name'] for field in original_fields if field['name'] not in fields] 
     
    193199        fields = list() 
    194200        for filter in self.ticket_filters : 
    195             if ticket_type == filter.get_ticket_type(): 
    196                 fields.extend(filter.get_fields()) 
     201            if ticket_type in filter.get_ticket_types(): 
     202                fields.extend(filter.get_fields(ticket_type)) 
    197203        # return the filtered fields  
    198204        return [field for field in original_fields if field['name'] in fields] 
     
    238244    the Ticket object. 
    239245    """ 
     246     
    240247    def __init__(self, env, tkt_id=None, db=None, version=None): 
    241248        if type(tkt_id) is Ticket : 
    242             #self.resource = Resource('ticket', tkt_id.id, version) 
    243             self.parent = None 
     249            self.parent = tkt_id 
    244250            super(TypedTicket, self).__init__(env, tkt_id.id, db, version) 
    245251        else : 
    246             self.parent = None 
     252            self.parent = Ticket(env, tkt_id) 
    247253            super(TypedTicket, self).__init__(env, tkt_id, db, version) 
    248254        self.fields = TypedTicketSystem(self.env).get_ticket_fields(self['type']) 
    249255        self._old = {} 
    250         #self.realm= self.resource.realm 
    251256     
    252257    def _get_typed_values(self): 
    253258        # TODO: add values for the ticket relations  
    254         pass 
     259        for provider in TypedTicketSystem(self.env).ticket_filters : 
     260            tt = self.parent['type'] 
     261            for field in provider.get_fields(tt or None) : 
     262                self.values[field] = None 
    255263 
    256264    def _fetch_ticket(self, tkt_id, tkt_type=None, db=None): 
    257         if self.parent is None : 
    258             Ticket._fetch_ticket(self, tkt_id, db) 
     265        Ticket._fetch_ticket(self, tkt_id, db) 
    259266        if type(self.parent) is Ticket : 
    260             self.values = self.parent.values 
    261267            self.time_created = self.parent.time_created 
    262268            self.time_changed = self.parent.time_changed 
    263269        self._get_typed_values() 
    264270     
     271    def __getattr__(self, name) : 
     272        return getattr(self.parent, name) 
     273     
    265274    def __getitem__(self, name): 
    266         return super(TypedTicket, self).__getitem__(name) 
     275        if name not in self.parent.values.keys() : 
     276            for provider in TypedTicketSystem(self.env).ticket_filters : 
     277                value = provider.get_value(self, name) 
     278                if value : 
     279                    return value  
     280        return self.parent[name] 
    267281     
    268282    def __setitem__(self, name, value): 
    269283        super(TypedTicket, self).__setitem__(name, value) 
     284 
  • trunk/plugins/tickettypedsystem/ticketsystem/templates/view_ticket.html

    r534 r602  
    33      py:strip=""> 
    44 
     5    <py:def function="render_ticket_list(tk_list, label)"> 
     6      <tr py:if="tk_list and len(tk_list) > 0" id=""> 
     7        <td style="vertical-align:top;"> 
     8        <label>${label}:</label></td> 
     9        <td> 
     10          <div py:for="ticket in tk_list" id="view-relations"> 
     11              <div id="relation${ticket.id}"> 
     12                <a href="${href.ticket(ticket.id)}">${system.get_resource_description(ticket, 'compact')}</a> -${system.get_resource_description(ticket, 'summary')} 
     13              </div> 
     14            <br /> 
     15          </div> 
     16        </td> 
     17      </tr> 
     18    </py:def> 
     19 
    520    <py:match path="//div[@id='checklist-spot']" once="true"> 
    6             <tr><td colspan="4">&nbsp;</td></tr> 
    7             <tr py:if="len(subtickets) > 0" id="view-subrelations-container"> 
    8                 <th style="vertical-align:top" ><label>Sub tickets:</label></th> 
    9                 <td colspan="3"> 
    10                     <div py:for="ticket in subtickets" id="view-relations"> 
    11                         <div id="relation${ticket.id}"> 
    12                            <a href="${href.ticket(ticket.id)}">${system.get_resource_description(ticket, 'compact')}</a> -${system.get_resource_description(ticket, 'summary')} 
    13                         </div> 
    14                         <br /> 
    15                     </div> 
    16                 </td> 
    17             </tr> 
    18             <tr py:if="len(parents) > 0" id="view-relations-container"> 
    19                 <th style="vertical-align:top"><label>Parent ticket:</label></th> 
    20                 <td colspan="3"> 
    21                     <div py:for="ticket in parents" id="view-relations"> 
    22                         <div id="relation${ticket.id}"> 
    23                            <a href="${href.ticket(ticket.id)}">${system.get_resource_description(ticket, 'compact')}</a> -${system.get_resource_description(ticket, 'summary')} 
    24                         </div> 
    25                         <br /> 
    26                     </div> 
    27                 </td> 
    28             </tr> 
     21            <tr><td colspan="2">&nbsp;</td></tr> 
     22            ${render_ticket_list(ticket['sub_tickets'], 'Sub Tickets')} 
     23            ${render_ticket_list(ticket['parent_tickets'], 'Parent Tickets')} 
    2924    </py:match> 
    3025</html> 
  • trunk/plugins/tickettypedsystem/ticketsystem/tests/api.py

    r400 r602  
    2929        os.mkdir(self.env.path)     
    3030         
    31         prop_in={'ticket.type': 'checklist, dependency', 
    32 'ticket.fields': 'owner, status, summary', 
    33 'ticket.checklist.fields': 'description, cc, keywords, estimate_hours', 
    34 'ticket.checklist.relations': 'child, father', 
    35 'ticket.checklist.child.resource': 'ticket', 
    36 'ticket.checklist.child': 'one-many', 
    37 'ticket.checklist.child.synchronize' : 'milestone, version, cc, keywords', 
    38 'ticket.dependency.fields': 'estimate_hours', 
    39 'ticket.dependency.relations': 'child', 
    40 'ticket.dependency.child.resources': 'ticket', 
    41 'ticket.dependency.child': 'one-many', 
    42 'ticket.dependency.child.sum' : 'estimate_hours', 
    43 'ticket.dependency.child.synchronize' : 'milestone, version, cc, keywords', 
     31        prop_in={'ticket.type': 'defect, issue', 
     32'ticket.defect.fields': 'estimate_hours, actual_hours, testcase', 
     33'ticket.issue.fields': 'issuetype', 
     34'ticket.fields': 'id, owner, type, status, summary, description, keywords, cc, estimate_date', 
    4435                 } 
    4536         
     
    4940        prop_in={'estimate_hours': 'text', 
    5041'estimate_hours.label': 'Estimate Hours', 
    51 'estimate_hours.order': '4'} 
     42'estimate_hours.order': '4', 
     43'estimate_date': 'text', 
     44'estimate_date.label': 'Estimate Date', 
     45'estimate_date.order': '1', 
     46'actual_hours': 'text', 
     47'actual_hours.label': 'Actual Hours', 
     48'actual_hours.order': '2', 
     49'testcase': 'text', 
     50'testcase.label': 'Test Case', 
     51'testcase.order': '3'} 
    5252        # save in ini 
    5353        for prop, value in prop_in.items(): 
     
    7070                           {'type': 'text', 'name': 'keywords', 'label': 'Keywords'}, 
    7171                           {'type': 'text', 'name': 'cc', 'label': 'Cc'}, 
    72                            {'name': 'estimate_hours', 'value': u'', 'custom': True, 'label': u'Estimate Hours', 'type': u'text', 'order': 4}] 
     72                           {'name': 'estimate_hours', 'value': u'', 'custom': True, 'label': u'Estimate Hours', 'type': u'text', 'order': 4}, 
     73                           {'name': 'estimate_date', 'value': u'', 'custom': True, 'label': u'Estimate Date', 'type': u'text', 'order': 1}, 
     74                           {'name': 'actual_hours', 'value': u'', 'custom': True, 'label': u'Actual Hours', 'type': u'text', 'order': 2}, 
     75                           {'name': 'testcase', 'value': u'', 'custom': True, 'label': u'Test Case', 'type': u'text', 'order': 3},] 
    7376 
    74         self.assertEquals(self.otsys.get_ticket_fields('checklist'), expected_result) 
     77        self.assertEquals(self.otsys.get_ticket_fields('defect'), expected_result) 
    7578     
    7679    def test_get_custom_fields(self): 
    7780        self.assertEquals(self.otsys.get_custom_fields(), self.tsys.get_custom_fields()) 
    78         expected_result = [{'name': 'estimate_hours', 'value': u'', 'label': u'Estimate Hours', 'type': u'text', 'order': 4}] 
    79         self.assertEquals(self.otsys.get_custom_fields('checklist'), expected_result) 
     81        expected_result = [{'name': 'estimate_hours', 'value': u'', 'label': u'Estimate Hours', 'type': u'text', 'order': 4}, 
     82                           {'name': 'estimate_date', 'value': u'', 'custom': True, 'label': u'Estimate Date', 'type': u'text', 'order': 1}, 
     83                           {'name': 'actual_hours', 'value': u'', 'custom': True, 'label': u'Actual Hours', 'type': u'text', 'order': 2}, 
     84                           {'name': 'testcase', 'value': u'', 'custom': True, 'label': u'Test Case', 'type': u'text', 'order': 3},] 
     85        self.assertEquals(self.otsys.get_custom_fields('defect'), expected_result) 
    8086     
    8187    def test_get_ticket(self): 
    8288        t = Ticket(self.env) 
    83         t['type'] = 'checklist' 
     89        t['type'] = 'defect' 
    8490        t['status'] = 'new' 
    8591        id = t.insert() 
    86         self.assertEquals(self.otsys.get_ticket(id)['status'], t['status']) 
     92        new_tk = self.otsys.get_ticket(id) 
     93        self.assertEquals(new_tk['status'], t['status']) 
    8794     
    8895    def test_get_ticket_types(self): 
    89         self.assertEquals(self.otsys.get_ticket_types(), ['checklist', 'dependency']) 
     96        self.assertEquals(self.otsys.get_ticket_types(), [u'defect', u'issue']) 
    9097 
    9198 
  • trunk/plugins/tickettypedsystem/ticketsystem/tickettypes/__init__.py

    r520 r602  
    22# 
    33# Copyright 2008 Optaros, Inc 
    4 import parentticket, util, uxhandler, oforgetickets  
     4import parentticket, util, uxhandler 
  • trunk/plugins/tickettypedsystem/ticketsystem/tickettypes/parentticket.py

    r536 r602  
    1313class ParentTicketRelation(Component) : 
    1414     
    15     abstract  = True 
    16      
    17     implements(ITicketFieldProvider,  
    18                ITicketRelationshipProvider, ITicketUIWrapper) 
    19  
     15    implements(ITicketRelationshipProvider, ITicketUIWrapper, ITicketFieldProvider) 
    2016 
    2117    # ITicketUIWrapper 
    22      
    2318    def get_content_to_inject(self, req, data): 
    2419        """ 
     
    4843                        'parents': list()}) 
    4944             
    50             if data['ticket']['type'] == self.get_ticket_type() : 
     45            if data['ticket']['type'] in self.get_ticket_types() : 
    5146                new_data['style'] = False 
    5247             
     
    7974            if ticket : 
    8075                # add the values to the ticket object to survive the redirect 
    81                 setattr(ticket, self.get_ticket_type(), tck_list) 
     76                setattr(ticket, self.get_relation_name(), tck_list) 
    8277            else : 
    8378                # add a description to be sure that a field has changed and the  
     
    8883        else : 
    8984            # add the relation 
    90             if not hasattr(ticket, self.get_ticket_type()) : 
     85            if not hasattr(ticket, self.get_relation_name()) : 
    9186                return 
    92             for tck_id in getattr(ticket, self.get_ticket_type()) : 
     87            for tck_id in getattr(ticket, self.get_relation_name()) : 
    9388                parent = TypedTicket(self.env, ticket) 
    9489                self.add_relation(parent, TypedTicket(self.env, tck_id)) 
     
    9792        return None 
    9893     
     94    def match_for_render(self, type): 
     95        if type in self.get_ticket_types() : 
     96            return True 
     97         
     98        return False 
     99     
    99100    # ITicketRelationshipProvider methods 
    100  
    101101    def match(self, ticket): 
    102102        if ticket.id is None : 
    103103            return True 
    104         if ticket['type'] == self.get_ticket_type() : 
     104        if ticket['type'] in self.get_ticket_types() : 
    105105            return True 
    106106         
     
    109109    def get_relation_name(self): 
    110110        """It returns the relation name.""" 
    111         return self.get_ticket_type() 
     111        return 'parent_child' 
    112112 
    113113    def get_supported_operations(self): 
     
    116116        relationship_filters = TicketMetadataProvider(self.env) \ 
    117117            .get_relationship_filters(Resource('ticket'),  
    118                                       self.get_ticket_type()) 
     118                                      self.get_relation_name()) 
    119119        if relationship_filters is not None : 
    120120            for name in relationship_filters['ticket'].get_keys() : 
     
    128128    def add_relation(self, ticket, to_ticket): 
    129129        return TicketRelationshipSystem(self.env).create_relationship(ticket, \ 
    130                                         self.get_ticket_type(), to_ticket) 
     130                                        self.get_relation_name(), to_ticket) 
    131131     
    132132    def delete_relation(self, ticket, to_ticket): 
    133133        return TicketRelationshipSystem(self.env).delete_relationship(ticket, \ 
    134                                         self.get_ticket_type(), to_ticket) 
    135  
    136     # ITicketFieldProvider methods 
     134                                        self.get_relation_name(), to_ticket) 
    137135     
    138     def get_ticket_type(self): 
    139         """It returns the ticket type name. """ 
    140         return '' 
     136    def get_ticket_types(self): 
     137        return self.env.config.getlist('ticket-system', 'ticket.type', []) 
    141138     
    142     def get_fields(self): 
    143         # get the relationships described in the ini file 
    144         resource_filters = TicketMetadataProvider(self.env) \ 
    145             .get_field_filters(Resource('ticket'), self.get_ticket_type()) 
    146         if resource_filters is not None : 
    147             return resource_filters['fields'] 
     139    def get_fields(self, type): 
     140        if type not in self.get_ticket_types() : 
     141            if type != None : 
     142                return [] 
     143         
     144        return ['sub_tickets', 'parent_tickets'] 
     145     
     146    def get_value(self, ticket, name): 
     147        if ticket['type'] not in self.get_ticket_types() : 
     148            return None 
     149         
     150        if name == 'sub_tickets' : 
     151            return self.get_related_tickets(ticket) 
     152         
     153        if name == 'parent_tickets' : 
     154            return filter(lambda x: x != None,  
     155                          TicketRelationship.get_parent(self.env, ticket.id)) 
    148156         
    149157        return None 
    150158     
    151  
    152 class CheckListImplementation(ParentTicketRelation) : 
    153     """Dummy implementation""" 
    154     # ITicketUIWrapper 
    155  
    156     def match_for_render(self, req, data):         
    157         return False 
    158      
    159     def handle_request(self, req, data, content_type): 
    160         return None 
    161  
    162     # ITicketFieldProvider methods 
    163      
    164     def get_ticket_type(self): 
    165         """It returns the ticket type name.""" 
    166         return 'checklist' 
  • trunk/plugins/tickettypedsystem/ticketsystem/tickettypes/uxhandler.py

    r574 r602  
    33# Copyright 2008 Optaros, Inc 
    44 
     5from trac.core import implements, Component 
     6from trac.resource import Resource 
     7 
    58from genshi.core import QName 
    69from genshi.builder import Element, tag 
    710 
    8 from ticketsystem.tickettypes.parentticket import ParentTicketRelation 
     11from ticketsystem.api import ITicketFieldProvider, \ 
     12                            ITicketUIWrapper, TypedTicket, TypedTicketSystem 
     13from ticketsystem.tickettypes.util import TicketMetadataProvider 
    914 
    10 class UXTicketHandler(ParentTicketRelation) : 
    11      
     15class OForgeTicketHandler(Component) : 
    1216    """ 
    13     This class adds logic for making the ticket submit buttons work.  
     17    This class adds logic for making the ticket submit buttons work and to  
     18    implement oforge ticket types.  
    1419    """ 
    1520     
    16     abstract = True 
     21    implements(ITicketFieldProvider, ITicketUIWrapper) 
    1722     
    1823    # ITicketUIWrapper 
    19     def match_for_render(self, req, data): 
    20         if not data.has_key('ticket') : 
    21             return False 
    22         if data['ticket']['type'] == self.get_ticket_type() or  \ 
    23             ('type' in req.args and req.args['type'] == self.get_ticket_type()): 
     24    def match_for_render(self, type): 
     25        if type in self.get_ticket_types() : 
    2426            return True 
    25          
    2627        return False 
    27  
     28     
     29    def get_content_to_inject(self, req, data): 
     30        return dict() 
     31     
    2832    def process_changes(self, req, ticket): 
    2933        # add in the session the redirect to new 
     
    3842                    new_data[field.lstrip('field')[1:]] = req.args[field] 
    3943            req.redirect(req.href.newticket(**new_data)) 
    40         super(UXTicketHandler, self).process_changes(req, ticket) 
     44         
    4145     
    4246    def handle_request(self, req, data, content_type): 
     
    6872 
    6973    # ITicketFieldProvider methods 
    70     def get_ticket_type(self): 
    71         return '' 
     74 
     75    def get_fields(self, type): 
     76        # get the relationships described in the ini file 
     77        resource_filters = TicketMetadataProvider(self.env) \ 
     78            .get_field_filters(Resource('ticket'), type) 
     79        if resource_filters is not None : 
     80            return resource_filters['fields'] 
     81         
     82        return None 
     83 
     84    def get_ticket_types(self): 
     85        return self.env.config.getlist('ticket-system', 'ticket.type', []) 
     86     
     87    def get_value(self, ticket, field_name): 
     88        return None 
     89     
  • trunk/plugins/tickettypedsystem/ticketsystem/web_ui.py

    r535 r602  
    1212from trac.web.chrome import ITemplateProvider 
    1313 
    14 from ticketsystem.api import IOperationsProvider, ITicketRelationshipProvider, ITicketUIWrapper, TypedTicketSystem 
     14from ticketsystem.api import IOperationsProvider, ITicketRelationshipProvider, ITicketUIWrapper, TypedTicketSystem, TypedTicket 
    1515 
    1616class TypedTicketModule(Component): 
     
    4545    # ITicketChangeListener methods 
    4646    def ticket_changed(self, ticket, comment, author, old_values) : 
    47         for ticket_renderer in [ r for r in self.renderers if r.match(ticket)]: 
     47        print 'ticket changed' 
     48        for ticket_renderer in [ r for r in self.renderers if r.match_for_render(ticket['type'])]: 
    4849            ticket_renderer.process_changes(None, ticket) 
    4950     
    5051    def ticket_created(self, ticket): 
    51         for ticket_renderer in [ r for r in self.renderers if r.match(ticket)]: 
     52        for ticket_renderer in [ r for r in self.renderers if r.match_for_render(ticket['type'])]: 
    5253            ticket_renderer.process_changes(None, ticket) 
    5354     
     
    5758    # ITicketManipulator methods 
    5859    def validate_ticket(self, req, ticket): 
    59         for ticket_renderer in [ r for r in self.renderers if r.match(ticket)]: 
     60        for ticket_renderer in [ r for r in self.renderers if r.match_for_render(ticket['type'])]: 
    6061                ticket_renderer.process_changes(req, ticket) 
    6162         
     
    7374                data['fields_dict'][field]['datefield'] = \ 
    7475                    self.config.getbool('ticket-custom',field+'.date') 
    75  
    76             for render_handle in [ rh for rh in self.renderers if rh.match_for_render(req, data)] : 
     76             
     77            type = None 
     78            if data.has_key('ticket') : 
     79                data['ticket'] = TypedTicket(self.env, data['ticket']) 
     80                type = data['ticket']['type'] 
     81            if 'type' in req.args and req.args['type'] is not None: 
     82                type = req.args['type'] 
     83            for render_handle in [ rh for rh in self.renderers if rh.match_for_render(type)] : 
    7784                data.update(render_handle.get_content_to_inject(req, data)) 
    7885                     
    7986            for render_handle in self.renderers : 
    80                 if render_handle.match_for_render(req, data) and 'oldpage' not in req.args: 
     87                if render_handle.match_for_render(type) and 'oldpage' not in req.args: 
    8188                    page = render_handle.handle_request(req, data, content_type) 
    8289                    if page : 
     
    8693    def pre_process_request(self, req, handler) : 
    8794        match = self.typed_ticket_re.match( req.path_info ) 
     95         
    8896        if match and req.method=='POST': 
     97            type = None 
     98            if 'id' in req.args and req.args['id'] is not None: 
     99                type = TypedTicket(self.env, int(req.args['id']))['type'] 
     100            if 'field_type' in req.args and req.args['field_type'] is not None: 
     101                type = req.args['field_type'] 
    89102            try : 
    90                 for ticket_renderer in self.renderers : 
     103                for ticket_renderer in [ rh for rh in self.renderers if rh.match_for_render(type)] : 
    91104                    ticket_renderer.process_changes(req, None) 
    92105            except Exception, e: 
     106                print e 
    93107                self.log.error(e) 
    94108        return handler 
Note: See TracChangeset for help on using the changeset viewer.