Edgewall Software

Ticket #226: ticket_dependency.patch

File ticket_dependency.patch, 17.2 kB (added by trac@…, 4 years ago)

Ticket Dependecy - First patch

  • trac/db_default.py

     
    2121 
    2222 
    2323# Database version identifier. Used for automatic upgrades. 
    24 db_version = 7 
     24db_version = 8 
    2525 
    2626def __mkreports(reps): 
    2727    """Utility function used to create report data in same syntax as the 
     
    8888        reporter        text, 
    8989        cc              text,           -- email addresses to notify 
    9090        url             text,           -- url related to this ticket 
    91         version         text,           --  
    92         milestone       text,           --  
     91        version         text,           -- 
     92        milestone       text,           -- 
    9393        status          text, 
    9494        resolution      text, 
    9595        summary         text,           -- one-line summary 
     
    119119        description     text 
    120120); 
    121121CREATE TABLE permission ( 
    122         username        text,           --  
     122        username        text,           -- 
    123123        action          text,           -- allowable activity 
    124124        UNIQUE(username,action) 
    125125); 
     
    188188""", 
    189189""" 
    190190SELECT p.value AS __color__, 
    191    id AS ticket, summary, component, version, milestone, severity,  
     191   id AS ticket, summary, component, version, milestone, severity, 
    192192   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    193193   time AS created, 
    194194   changetime AS _changetime, description AS _description, 
    195195   reporter AS _reporter 
    196196  FROM ticket t, enum p 
    197   WHERE status IN ('new', 'assigned', 'reopened')  
     197  WHERE status IN ('new', 'assigned', 'reopened') 
    198198AND p.name = t.priority AND p.type = 'priority' 
    199199  ORDER BY p.value, milestone, severity, time 
    200200"""), 
     
    210210""" 
    211211SELECT p.value AS __color__, 
    212212   version AS __group__, 
    213    id AS ticket, summary, component, version, severity,  
     213   id AS ticket, summary, component, version, severity, 
    214214   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    215215   time AS created, 
    216216   changetime AS _changetime, description AS _description, 
    217217   reporter AS _reporter 
    218218  FROM ticket t, enum p 
    219   WHERE status IN ('new', 'assigned', 'reopened')  
     219  WHERE status IN ('new', 'assigned', 'reopened') 
    220220AND p.name = t.priority AND p.type = 'priority' 
    221221  ORDER BY (version IS NULL),version, p.value, severity, time 
    222222"""), 
     
    232232""" 
    233233SELECT p.value AS __color__, 
    234234   milestone||' Release' AS __group__, 
    235    id AS ticket, summary, component, version, severity,  
     235   id AS ticket, summary, component, version, severity, 
    236236   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    237237   time AS created, 
    238238   changetime AS _changetime, description AS _description, 
    239239   reporter AS _reporter 
    240240  FROM ticket t, enum p 
    241   WHERE status IN ('new', 'assigned', 'reopened')  
     241  WHERE status IN ('new', 'assigned', 'reopened') 
    242242AND p.name = t.priority AND p.type = 'priority' 
    243243  ORDER BY (milestone IS NULL),milestone, p.value, severity, time 
    244244"""), 
     
    284284""" 
    285285SELECT p.value AS __color__, 
    286286   t.milestone AS __group__, 
    287    (CASE status  
     287   (CASE status 
    288288      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;' 
    289       ELSE  
     289      ELSE 
    290290        (CASE owner WHEN '$USER' THEN 'font-weight: bold' END) 
    291291    END) AS __style__, 
    292    id AS ticket, summary, component, status,  
     292   id AS ticket, summary, component, status, 
    293293   resolution,version, severity, priority, owner, 
    294294   changetime AS modified, 
    295295   time AS _time,reporter AS _reporter 
    296296  FROM ticket t,enum p 
    297297  WHERE p.name=t.priority AND p.type='priority' 
    298   ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'),  
     298  ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'), 
    299299        (CASE status WHEN 'closed' THEN modified ELSE -p.value END) DESC 
    300300"""), 
    301301#---------------------------------------------------------------------------- 
    302302('My Tickets', 
    303303""" 
    304 This report demonstrates the use of the automatically set  
     304This report demonstrates the use of the automatically set 
    305305$USER dynamic variable, replaced with the username of the 
    306306logged in user when executed. 
    307307""", 
     
    313313   changetime AS _changetime, description AS _description, 
    314314   reporter AS _reporter 
    315315  FROM ticket t, enum p 
    316   WHERE t.status IN ('new', 'assigned', 'reopened')  
     316  WHERE t.status IN ('new', 'assigned', 'reopened') 
    317317AND p.name = t.priority AND p.type = 'priority' AND owner = '$USER' 
    318318  ORDER BY (status = 'assigned') DESC, p.value, milestone, severity, time 
    319319"""), 
     
    325325""", 
    326326""" 
    327327SELECT p.value AS __color__, 
    328    (CASE owner  
    329      WHEN '$USER' THEN 'My Tickets'  
    330      ELSE 'Active Tickets'  
     328   (CASE owner 
     329     WHEN '$USER' THEN 'My Tickets' 
     330     ELSE 'Active Tickets' 
    331331    END) AS __group__, 
    332    id AS ticket, summary, component, version, milestone, severity,  
     332   id AS ticket, summary, component, version, milestone, severity, 
    333333   (CASE status WHEN 'assigned' THEN owner||' *' ELSE owner END) AS owner, 
    334334   time AS created, 
    335335   changetime AS _changetime, description AS _description, 
    336336   reporter AS _reporter 
    337337  FROM ticket t, enum p 
    338   WHERE status IN ('new', 'assigned', 'reopened')  
     338  WHERE status IN ('new', 'assigned', 'reopened') 
    339339AND p.name = t.priority AND p.type = 'priority' 
    340340  ORDER BY (owner = '$USER') DESC, p.value, milestone, severity, time 
    341341""")) 
     
    352352                ('component2', 'somebody'))), 
    353353           ('milestone', 
    354354             ('name', 'time'), 
    355                (('', 0),  
     355               (('', 0), 
    356356                ('milestone1', 0), 
    357357                ('milestone2', 0), 
    358358                ('milestone3', 0), 
     
    446446  ('notification', 'smtp_replyto', 'trac@localhost'), 
    447447  ('timeline', 'changeset_show_files', 'false'), 
    448448  ('timeline', 'changeset_files_count', 3)) 
    449     
     449 
  • trac/Ticket.py

     
    2929import util 
    3030from Module import Module 
    3131from WikiFormatter import wiki_to_html 
     32from Wiki import wiki_to_oneliner 
    3233from Notify import TicketNotifyEmail 
    3334 
    3435__all__ = ['Ticket', 'NewticketModule', 'TicketModule'] 
     
    3738class Ticket(UserDict): 
    3839    std_fields = ['time', 'component', 'severity', 'priority', 'milestone', 
    3940                  'reporter', 'owner', 'cc', 'url', 'version', 'status', 'resolution', 
    40                   'keywords', 'summary', 'description', 'reporter'] 
     41                  'keywords', 'summary', 'description'] 
    4142 
    4243    def __init__(self, *args): 
    4344        UserDict.__init__(self) 
     
    7879        if rows: 
    7980            for r in rows: 
    8081                self['custom_' + r[0]] = r[1] 
     82 
     83        self['dependson'] = self.get_dependson_list(db, id) 
     84        self['blocks'] = self.get_blocks_list(db, id) 
     85 
     86 
    8187        self._forget_changes() 
    8288 
    8389    def populate(self, dict): 
     
    8793        for name in names: 
    8894            self[name] = dict.get(name, '') 
    8995 
     96        self['blocks'] = dict.get('blocks',''); 
     97        self['dependson'] = dict.get('dependson',''); 
     98 
    9099        # We have to do an extra trick to catch unchecked checkboxes 
    91100        checkboxes = filter(lambda n: n[:9] == 'checkbox_', dict.keys()) 
    92101        for name in ['custom_' + n[9:] for n in checkboxes]: 
     
    103112        self['time'] = now 
    104113        self['changetime'] = now 
    105114 
    106         std_fields = filter(lambda n: n[:7] != 'custom_', self.keys()) 
     115        std_fields = filter(lambda n: n[:7] != 'custom_' and n != 'dependson' and n != 'blocks' , self.keys()) 
    107116        custom_fields = filter(lambda n: n[:7] == 'custom_', self.keys()) 
    108117        std_values = map(lambda n, self=self: self[n], std_fields) 
    109118        nstr = string.join(std_fields, ',') 
     
    114123        for name in custom_fields: 
    115124            cursor.execute('INSERT INTO ticket_custom(ticket,name,value)' 
    116125                           ' VALUES(%d, %s, %s)', id, name[7:], self[name]) 
     126        # Dependencies 
     127        cursor.execute('DELETE FROM dependencies WHERE dependson=%s',id) 
     128        cursor.executemany ('INSERT INTO dependencies (dependson,blocks) ' 
     129                           "VALUES (" + str(id) + ",%s)", 
     130                           self['blocks']) 
     131        cursor.execute('DELETE FROM dependencies WHERE blocks=%s',id) 
     132        cursor.executemany ('INSERT INTO dependencies (blocks,dependson) ' 
     133                           "VALUES (" + str(id) + ",%s)", 
     134                           self['dependson']) 
    117135        db.commit() 
    118136        self['id'] = id 
    119137        self._forget_changes() 
     
    141159                cursor.execute('SELECT owner FROM component ' 
    142160                               'WHERE name=%s', self['component']) 
    143161                self['owner'] = cursor.fetchone()[0] 
    144             
    145162 
     163 
    146164        for name in self._old.keys(): 
    147165            if name[:7] == 'custom_': 
    148166                fname = name[7:] 
    149167                cursor.execute('REPLACE INTO ticket_custom(ticket,name,value)' 
    150168                               ' VALUES(%s, %s, %s)', id, fname, self[name]) 
     169            elif name == 'dependson': 
     170                fname = name 
     171                cursor.execute('DELETE FROM dependencies WHERE blocks=%s',id) 
     172                if len(self[name]) > 0: 
     173                    cursor.executemany ('INSERT INTO dependencies (blocks,dependson) ' 
     174                                        "VALUES (" + str(id) + ",%s)", 
     175                                        string.split(self[name],',')) 
     176            elif name == 'blocks': 
     177                fname = name 
     178                cursor.execute('DELETE FROM dependencies WHERE dependson=%s',id) 
     179                if len(self[name]) > 0: 
     180                    cursor.executemany ('INSERT INTO dependencies (dependson,blocks) ' 
     181                                    "VALUES (" + str(id) + ",%s)", 
     182                                    string.split(self[name],',')) 
    151183            else: 
    152184                fname = name 
    153185                cursor.execute ('UPDATE ticket SET %s=%s WHERE id=%s', 
    154186                                fname, self[name], id) 
    155187 
    156             cursor.execute ('INSERT INTO ticket_change ' 
    157                             '(ticket, time, author, field, oldvalue, newvalue) ' 
    158                             'VALUES (%s, %s, %s, %s, %s, %s)', 
    159                             id, when, author, fname, self._old[name], self[name]) 
     188            # Weasle out on this one. 
     189            if fname != 'dependson' and fname != 'blocks': 
     190                cursor.execute ('INSERT INTO ticket_change ' 
     191                                '(ticket, time, author, field, oldvalue, newvalue) ' 
     192                                'VALUES (%s, %s, %s, %s, %s, %s)', 
     193                                id, when, author, fname, self._old[name], self[name]) 
    160194        if comment: 
    161195            cursor.execute ('INSERT INTO ticket_change ' 
    162196                            '(ticket,time,author,field,oldvalue,newvalue) ' 
     
    195229            log.append((int(row[0]), row[1], row[2], row[3] or '', row[4] or '')) 
    196230        return log 
    197231 
     232    def get_blocks_list(self, db, id): 
     233        # Determine Tickets this Ticket blocks 
     234        cursor = db.cursor () 
     235        cursor.execute( 'SELECT blocks FROM dependencies WHERE dependson=%s', id ) 
    198236 
     237        blocks = [] # List of Tickets the current Ticket blocks 
     238        while 1: 
     239            row = cursor.fetchone() 
     240            if not row: 
     241                break 
     242            blocks.append(row['blocks']); 
     243 
     244        cursor.close () 
     245 
     246        return blocks 
     247 
     248    def get_dependson_list(self, db, id): 
     249        # Determine Tickets this Ticket depends on 
     250        cursor = db.cursor () 
     251        cursor.execute( 'SELECT dependson FROM dependencies WHERE blocks=%s', id ) 
     252 
     253        dependson = [] # List of Tickets the current Ticket deepnds on 
     254        while 1: 
     255            row = cursor.fetchone() 
     256            if not row: 
     257                break 
     258            dependson.append(row['dependson']); 
     259 
     260        cursor.close () 
     261 
     262        return dependson 
     263 
    199264def get_custom_fields(env): 
    200265    cfg = env.get_config_items('ticket-custom') 
    201266    if not cfg: 
     
    387452                                           self.env, self.db)) 
    388453        self.req.hdf.setValue('ticket.opened', time.strftime('%c', time.localtime(int(ticket['time'])))) 
    389454 
     455        # Dependencies 
     456        dependson = [] 
     457        dependson_displ = [] 
     458        for i in range(len(ticket['dependson'])): 
     459            dependson_displ.append( '#' + str(ticket['dependson'][i]) ) 
     460            dependson.append( str(ticket['dependson'][i]) ) 
     461        self.req.hdf.setValue('ticket.dependson_displ', 
     462                              wiki_to_oneliner(string.join(dependson_displ, ', '), 
     463                                            self.req.hdf, self.env, self.db)) 
     464        self.req.hdf.setValue('ticket.dependson', 
     465                              string.join(dependson, ',')) 
     466        blocks = [] 
     467        blocks_displ = [] 
     468        for i in range(len(ticket['blocks'])): 
     469            blocks_displ.append( '#' + str(ticket['blocks'][i]) ) 
     470            blocks.append( str(ticket['blocks'][i]) ) 
     471        self.req.hdf.setValue('ticket.blocks_displ', 
     472                              wiki_to_oneliner(string.join(blocks_displ, ', '), 
     473                                            self.req.hdf, self.env, self.db)) 
     474        self.req.hdf.setValue('ticket.blocks', 
     475                              string.join(blocks, ',')) 
     476 
    390477        changelog = ticket.get_changelog(self.db) 
    391478        curr_author = None 
    392479        curr_date   = 0 
     
    437524            self.req.hdf.setValue('ticket.action', action) 
    438525            reporter_id = self.args.get('author') 
    439526            comment = self.args.get('comment') 
     527            if self.args.has_key('dependson'): 
     528                ticket['dependson'] = string.split(self.args.get('dependson'),',') 
     529            else: 
     530                ticket['dependson'] = [] 
     531 
     532            if self.args.has_key('blocks'): 
     533                ticket['blocks'] = string.split(self.args.get('blocks'),',') 
     534            else: 
     535                ticket['blocks'] = [] 
     536 
    440537            if comment: 
    441538                self.req.hdf.setValue('ticket.comment', comment) 
    442539                # Wiki format a preview of comment 
  • templates/ticket.cs

     
    4141  call:ticketprop("Version", "version", ticket.version, 0) ?><?cs 
    4242  call:ticketprop("Resolution", "resolution", ticket.resolution, 0) ?><?cs 
    4343  call:ticketprop("Milestone", "milestone", ticket.milestone, 0) ?><?cs 
     44  call:ticketprop("Keywords", "keywords", ticket.keywords, 0) ?><?cs 
     45  call:ticketprop("Depends On", "dependson", ticket.dependson_displ, 0) ?><?cs 
    4446  set:last_prop = #1 ?><?cs 
    45   call:ticketprop("Keywords", "keywords", ticket.keywords, 0) ?><?cs 
     47  call:ticketprop("Blocks", "blocks", ticket.blocks_displ, 0) ?><?cs 
    4648  set:last_prop = #0 ?> 
    4749 </tr></table><?cs if ticket.custom.0.name ?> 
    4850 <hr /> 
     
    178180   <label for="keywords">Keywords:</label> 
    179181   <input type="text" id="keywords" name="keywords" size="20" 
    180182       value="<?cs var:ticket.keywords ?>" /> 
     183   <br /> 
     184   <label for="dependson">Depends On:</label> 
     185   <input type="text" id="dependson" name="dependson" size="20" 
     186       value="<?cs var:ticket.dependson ?>" /> 
    181187  </div> 
    182188  <div class="col2"> 
    183189   <label for="priority">Priority:</label><?cs 
     
    189195     var:ticket.owner ?>" disabled="disabled" /><br /> 
    190196   <label for="cc">Cc:</label> 
    191197   <input type="text" id="cc" name="cc" size="30" value="<?cs var:ticket.cc ?>" /> 
     198   <br /> 
     199   <label for="blocks">Blocks:</label> 
     200   <input type="text" id="blocks" name="blocks" size="20" 
     201       value="<?cs var:ticket.blocks ?>" /> 
    192202  </div> 
    193203  <div class="custom"> 
    194204   <?cs call:ticket_custom_props(ticket) ?> 
     
    233243 <div class="buttons"> 
    234244  <input type="reset" value="Reset" />&nbsp; 
    235245  <input type="submit" name="preview" value="Preview" />&nbsp; 
    236   <input type="submit" value="Submit changes" />  
     246  <input type="submit" value="Submit changes" /> 
    237247 </div> 
    238248</form> 
    239249<?cs /if ?> 
  • templates/newticket.cs

     
    22<?cs include "header.cs" ?> 
    33<?cs include "macros.cs" ?> 
    44<script type="text/javascript"> 
    5 addEvent(window, 'load', function() { document.getElementById('summary').focus()});  
     5addEvent(window, 'load', function() { document.getElementById('summary').focus()}); 
    66</script> 
    77 
    88<div id="ctxtnav" class="nav"></div> 
     
    5252   <label for="keywords">Keywords:</label> 
    5353   <input type="text" id="keywords" name="keywords" size="20" 
    5454       value="<?cs var:ticket.keywords ?>" /> 
     55   <br /> 
     56   <label for="dependson">Depends On:</label> 
     57   <input type="text" id="dependson" name="dependson" size="20" 
     58       value="<?cs var:ticket.dependson ?>" /> 
    5559  </div> 
    5660  <div class="col2"> 
    5761   <label for="priority">Priority:</label><?cs 
     
    6367     var:newticket.owner ?>" /><br /> 
    6468   <label for="cc">Cc:</label> 
    6569   <input type="text" id="cc" name="cc" size="30" value="<?cs var:newticket.cc ?>" /> 
     70   <br /> 
     71   <label for="blocks">Blocks:</label> 
     72   <input type="text" id="blocks" name="blocks" size="20" 
     73       value="<?cs var:ticket.blocks ?>" /> 
    6674  </div> 
    6775  <div class="custom"> 
    6876   <?cs call:ticket_custom_props(ticket) ?>