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
Line 
1from trac.ticket import ITicketChangeListener, Ticket, ITicketManipulator
2from trac.perm import PermissionCache
3from trac.core import *
4import datetime
5
6def identity(x):
7    return x;
8
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
17try:
18    import trac.util.datefmt
19    to_timestamp = trac.util.datefmt.to_timestamp
20except Exception:
21    to_timestamp = identity
22
23
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:
33        cursor.execute("INSERT INTO ticket_custom (ticket,name, "
34                       "value) VALUES(%s,%s,%s)",
35                       (ticket_id, field, value))
36    db.commit()
37   
38DONTUPDATE = "DONTUPDATE"
39
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    """
45    if type(change_time) == datetime.datetime:
46        change_time = to_timestamp(change_time)
47    cursor = db.cursor();
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))
52    if cursor.fetchone():
53        if oldvalue == DONTUPDATE:
54            cursor.execute("""UPDATE ticket_change  SET  newvalue=%s 
55                       WHERE ticket=%s and author=%s and time=%s and field=%s""",
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))
62    else:
63        if oldvalue == DONTUPDATE:
64            oldvalue = '0'
65        if not dontinsert:
66            cursor.execute("""INSERT INTO ticket_change  (ticket,time,author,field, oldvalue, newvalue)
67                        VALUES(%s, %s, %s, %s, %s, %s)""",
68                           (ticket_id, change_time, author, field, oldvalue, newvalue))
69    db.commit()
70
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
83class TimeTrackingTicketObserver(Component):
84    implements(ITicketChangeListener)
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()
94                cursor.execute("SELECT * FROM ticket_custom where ticket=%s and name=%s" , (ticket.id, name))
95                val = cursor.fetchone()
96                if val:
97                    return tipe(val[2] or default)
98                return default
99
100        hours = readTicketValue("hours", convertfloat)
101        totalHours = readTicketValue("totalhours", convertfloat)
102
103        db = self.env.get_db_cnx()
104        ticket_id = ticket.id
105        cl = ticket.get_changelog()
106       
107        self.log.debug("found hours: "+str(hours ));
108        #self.log.debug("Dir_ticket:"+str(dir(ticket)))
109        #self.log.debug("ticket.values:"+str(ticket.values))
110        #self.log.debug("changelog:"+str(cl))
111   
112        most_recent_change = None
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"]
120
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
137        ## SAVE estimated hour
138        estimatedhours = readTicketValue("estimatedhours", convertfloat)       
139        self.log.debug("found Estimated hours:"+str(estimatedhours))
140        db = self.env.get_db_cnx()
141        save_ticket_change( db, ticket_id, author, change_time, "estimatedhours", DONTUPDATE, str(estimatedhours), self.log, True)
142        save_custom_field_value( db, ticket.id, "estimatedhours", str(estimatedhours))
143        db.commit();
144        #######################
145
146
147        ## If our hours changed
148        if not hours == 0:               
149            newtotal = str(totalHours+hours)
150            save_ticket_change( db, ticket_id, author, change_time, "hours", '0.0', str(hours), self.log)
151            save_ticket_change( db, ticket_id, author, change_time, "totalhours", str(totalHours), str(newtotal), self.log)
152            save_custom_field_value( db, ticket_id, "hours", '0')
153            save_custom_field_value( db, ticket_id, "totalhours", str(newtotal) )           
154        ########################
155
156    # END of watch_hours
157
158    def ticket_created(self, ticket):
159        """Called when a ticket is created."""
160        self.watch_hours(ticket)
161                               
162
163    def ticket_changed(self, ticket, comment, author, old_values):
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."""
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'])
192        except KeyError:
193            self.log.exception("The hours field was not submitted")
194        except ValueError:
195            errors.append(('Add Hours to Ticket', 'Value must be a number'))
196        try:
197            convertfloat(ticket.values['estimatedhours'])
198        except KeyError:
199            self.log.exception("The estimatedhours field was not submitted")
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.