source: timingandestimationplugin/branches/trac0.11-Permissions/timingandestimationplugin/ticket_daemon.py

Last change on this file was 6902, checked in by Russ Tyndall, 14 years ago

fixes #6043 during ticket field validation dont throw exceptions if the field isnt there

File size: 7.7 KB
RevLine 
[5162]1from trac.ticket import ITicketChangeListener, Ticket, ITicketManipulator
[4271]2from trac.perm import PermissionCache
[1119]3from trac.core import *
[1710]4import datetime
[1119]5
[1710]6def identity(x):
7    return x;
8
[6268]9def convertfloat(x):
10    "some european countries use , as the decimal separator"
11    x = str(x).strip()
12    if len(x) > 0:
13        return float(x.replace(',','.'))
14    else: 
15        return 0.0
16
[2015]17try:
18    import trac.util.datefmt
19    to_timestamp = trac.util.datefmt.to_timestamp
20except Exception:
21    to_timestamp = identity
[1710]22
[2015]23
[1119]24def save_custom_field_value( db, ticket_id, field, value ):
25    cursor = db.cursor();
26    cursor.execute("SELECT * FROM ticket_custom " 
27                   "WHERE ticket=%s and name=%s", (ticket_id, field))
28    if cursor.fetchone():
29        cursor.execute("UPDATE ticket_custom SET value=%s "
30                       "WHERE ticket=%s AND name=%s",
31                       (value, ticket_id, field))
32    else:
[1578]33        cursor.execute("INSERT INTO ticket_custom (ticket,name, "
[1119]34                       "value) VALUES(%s,%s,%s)",
[1578]35                       (ticket_id, field, value))
[2893]36    db.commit()
37   
[4164]38DONTUPDATE = "DONTUPDATE"
39
[4166]40def save_ticket_change( db, ticket_id, author, change_time, field, oldvalue, newvalue, log, dontinsert=False):
41    """tries to save a ticket change,
42 
43       dontinsert means do not add the change if it didnt already exist
44    """
[1710]45    if type(change_time) == datetime.datetime:
46        change_time = to_timestamp(change_time)
[1119]47    cursor = db.cursor();
[1710]48    sql = """SELECT * FROM ticket_change 
49             WHERE ticket=%s and author=%s and time=%s and field=%s""" 
50                   
51    cursor.execute(sql, (ticket_id, author, change_time, field))
[1119]52    if cursor.fetchone():
[4164]53        if oldvalue == DONTUPDATE:
54            cursor.execute("""UPDATE ticket_change  SET  newvalue=%s 
[1710]55                       WHERE ticket=%s and author=%s and time=%s and field=%s""",
[4164]56                           ( newvalue, ticket_id, author, change_time, field))
57
58        else:
59            cursor.execute("""UPDATE ticket_change  SET oldvalue=%s, newvalue=%s 
60                       WHERE ticket=%s and author=%s and time=%s and field=%s""",
61                           (oldvalue, newvalue, ticket_id, author, change_time, field))
[1119]62    else:
[4164]63        if oldvalue == DONTUPDATE:
64            oldvalue = '0'
[4166]65        if not dontinsert:
66            cursor.execute("""INSERT INTO ticket_change  (ticket,time,author,field, oldvalue, newvalue)
[1710]67                        VALUES(%s, %s, %s, %s, %s, %s)""",
[4166]68                           (ticket_id, change_time, author, field, oldvalue, newvalue))
[2893]69    db.commit()
[1119]70
[4271]71def delete_ticket_change( comp, ticket_id, author, change_time, field):
72    """ removes a ticket change from the database """
73    db=comp.env.get_db_cnx()
74    cursor = db.cursor();
75    sql = """DELETE FROM ticket_change 
76             WHERE ticket=%s and author=%s and time=%s and field=%s""" 
77    cursor.execute(sql, (ticket_id, author, change_time, field))
78    db.commit()
79
80   
81   
82
[1119]83class TimeTrackingTicketObserver(Component):
[4270]84    implements(ITicketChangeListener)
[1119]85    def __init__(self):
86        pass
87
88    def watch_hours(self, ticket):
89        def readTicketValue(name, tipe, default=0):
90            if ticket.values.has_key(name):       
91                return tipe(ticket.values[name] or default)
92            else:
93                cursor = self.env.get_db_cnx().cursor()
[1840]94                cursor.execute("SELECT * FROM ticket_custom where ticket=%s and name=%s" , (ticket.id, name))
[1119]95                val = cursor.fetchone()
96                if val:
97                    return tipe(val[2] or default)
98                return default
[4233]99
[4157]100        hours = readTicketValue("hours", convertfloat)
101        totalHours = readTicketValue("totalhours", convertfloat)
[1119]102
[4164]103        db = self.env.get_db_cnx()
104        ticket_id = ticket.id
105        cl = ticket.get_changelog()
[4233]106       
107        self.log.debug("found hours: "+str(hours ));
[4164]108        #self.log.debug("Dir_ticket:"+str(dir(ticket)))
109        #self.log.debug("ticket.values:"+str(ticket.values))
[4271]110        #self.log.debug("changelog:"+str(cl))
[4233]111   
[4166]112        most_recent_change = None
[4164]113        if cl:
114            most_recent_change = cl[-1];
115            change_time = most_recent_change[0]
116            author = most_recent_change[1]
117        else:
118            change_time = ticket.time_created
119            author = ticket.values["reporter"]
[4233]120
[4271]121        self.log.debug("Checking permissions")
122        perm = PermissionCache(self.env, author)
123        if not perm or not perm.has_permission("TIME_RECORD"):
124            self.log.debug("Skipping recording because no permission to affect time")
125            if hours != 0:
126               
127                tup = (ticket_id, author, change_time, "hours")
128                self.log.debug("deleting ticket change %s %s %s %s" % tup)
129                try:
130                    delete_ticket_change(self, ticket_id, author, change_time, "hours")
131                except Exception, e:
132                    self.log.debug("FAIL: %s" % e)
133                self.log.debug("hours change deleted")
134            return
135        self.log.debug("passed permissions check")
136
[4233]137        ## SAVE estimated hour
138        estimatedhours = readTicketValue("estimatedhours", convertfloat)       
139        self.log.debug("found Estimated hours:"+str(estimatedhours))
[4164]140        db = self.env.get_db_cnx()
[4233]141        save_ticket_change( db, ticket_id, author, change_time, "estimatedhours", DONTUPDATE, str(estimatedhours), self.log, True)
[4164]142        save_custom_field_value( db, ticket.id, "estimatedhours", str(estimatedhours))
143        db.commit();
[4233]144        #######################
[4164]145
[4233]146
147        ## If our hours changed
[4166]148        if not hours == 0:               
[1119]149            newtotal = str(totalHours+hours)
[4233]150            save_ticket_change( db, ticket_id, author, change_time, "hours", '0.0', str(hours), self.log)
[1710]151            save_ticket_change( db, ticket_id, author, change_time, "totalhours", str(totalHours), str(newtotal), self.log)
[1119]152            save_custom_field_value( db, ticket_id, "hours", '0')
[1667]153            save_custom_field_value( db, ticket_id, "totalhours", str(newtotal) )           
[4233]154        ########################
[1119]155
[4233]156    # END of watch_hours
157
[1119]158    def ticket_created(self, ticket):
159        """Called when a ticket is created."""
160        self.watch_hours(ticket)
161                               
162
[1286]163    def ticket_changed(self, ticket, comment, author, old_values):
[1119]164        """Called when a ticket is modified.
165       
166        `old_values` is a dictionary containing the previous values of the
167        fields that have changed.
168        """
169        self.watch_hours(ticket)
170
171    def ticket_deleted(self, ticket):
172        """Called when a ticket is deleted."""
[5162]173
174class TimeTrackingTicketValidator(Component):
175    implements(ITicketManipulator)
176
177    def __init__(self):
178        pass
179
180    def prepare_ticket(req, ticket, fields, actions):
181        """not currently called"""
182
183    def validate_ticket(self, req, ticket):
184        """Validate a ticket after it's been populated from user input.
185
186        Must return a list of `(field, message)` tuples, one for each problem
187        detected. `field` can be `None` to indicate an overall problem with the
188        ticket. Therefore, a return value of `[]` means everything is OK."""
189        errors = []
190        try:
191            convertfloat(ticket.values['hours'])
[6902]192        except KeyError:
193            self.log.exception("The hours field was not submitted")
[5162]194        except ValueError:
195            errors.append(('Add Hours to Ticket', 'Value must be a number'))
196        try:
197            convertfloat(ticket.values['estimatedhours'])
[6902]198        except KeyError:
199            self.log.exception("The estimatedhours field was not submitted")
[5162]200        except ValueError:
201            errors.append(('Estimated Number of Hours', 'Value must be a number'))
202        return errors
Note: See TracBrowser for help on using the repository browser.