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