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

Last change on this file was 12829, checked in by Russ Tyndall, 11 years ago

use isinstance instead of checking types eql re #10978

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