Roundup Tracker

This mod is an addition to the TimeLog mod.

The TimelogAuditor automatically parses messages which are added to an issue. It creates and links a timelog-entry for each line that starts with a description and ends with a roundup Interval (see roundup.date). If you add a property **title** to the TimeLog class, the descriptions for each timelog will be saved.

Sample message::

    Re: Fix Flicker Bug

    Hello Jack
    I fixed the bug that bugged you.
 
    Bug-Fixing    2:30
    Testing       0:15
    Relaxing      2w 3d 2:20:05

detectors/timelogauditor.py::

   1     #
   2     # timelogAuditor scans messages for timelogs.
   3     # 
   4     # $Id$
   5     
   6     import re
   7     from roundup.date import Interval
   8     
   9     def timelogAuditor(db, cl, nodeid, newvalues):
  10         ''' Check if a new messages where added to the issue and scan them
  11             for timelogs in the following format
  12             
  13               [title] [interval] <endofline>
  14               
  15               Programming              2h
  16               Consulting            2h30m
  17               Implementing Bug-Fix    30m
  18             
  19             if newvalues has an attribute 'messages', then we either are dealing
  20             with a new issue (create-auditor) or the attribute 'messages' was 
  21             changed for an existing issue (set-auditor).
  22         '''
  23     
  24         # return quickly if there is no new message
  25         if not newvalues.has_key('messages'):
  26             return
  27     
  28         # parse the new messages for timelogs
  29         timelogs = []
  30         for msgid in determineNewMessages(cl, nodeid, newvalues):
  31             timelogs += parseMessage(db, msgid)
  32         
  33         # add links for timelogs to the current issue
  34         if timelogs:
  35             if newvalues.has_key('times'):
  36                 times = newvalues['times']
  37             elif nodeid:
  38                 times = db.issue.get(nodeid, 'times')
  39             else:
  40                 times = []
  41             
  42             times += timelogs
  43             newvalues['times'] = times
  44         
  45     def parseMessage(db, msgid, time_re = re.compile(r'''
  46                 \s*(?P<title>(.*?))                   # title
  47                 \s*(?P<time>[-+]?(?:\d+\s*[ymwd]\s*)* # [+-] [#y] [#m] [#w] [#d]
  48                   [012]?\d:\d+(?:\:\d+)?)\s*$         # [[[H]H:MM]:SS]
  49             ''', re.IGNORECASE|re.VERBOSE)):
  50         ''' parse the messages content field and return an array of timelogs
  51         '''
  52         msg_content = db.msg.get(msgid, 'content')
  53         if not msg_content:
  54             raise KeyException("no content found for message: %s\n" %(msgid))
  55             
  56         timelogs = []
  57         for line in msg_content.split("\n"):
  58             m = time_re.match(line)
  59             if m:
  60                 title = m.group("title")
  61                 time_str = m.group("time")
  62                 interval  = Interval(time_str)
  63                 
  64                 # if there is a property title=String() in timelog, then fill it
  65                 if db.timelog.getprops().has_key("title"):
  66                     timelogs += db.timelog.create(period=interval, title=title),
  67                 else:
  68                     timelogs += db.timelog.create(period=interval),
  69     
  70         return timelogs
  71     
  72     def determineNewMessages(cl, nodeid, newvalues):
  73         if not newvalues.has_key('messages'):
  74             return []
  75         
  76         # return all messages for a new node
  77         if nodeid is None:
  78             return newvalues['messages']
  79         
  80         # return only new messages for an existing node
  81         messages = []
  82         old_messages = cl.get(nodeid, 'messages')
  83         return [msgid for msgid in newvalues['messages'] if msgid not in old_messages]
  84       
  85     def init(db):
  86         ''' we need to audit issue-changes to catch new messages instead
  87             of attaching an auditor directly to the message class.
  88             this is the only way to have the issue ready when linking the
  89             new timelogs.
  90         '''
  91         db.issue.audit('set', timelogAuditor)
  92         db.issue.audit('create', timelogAuditor)
  93     
  94     # vim: set filetype=python ts=4 sw=4 et si


CategoryDetectors