Merge lp:~clint-fewbar/launchpad-work-items-tracker/server-team-mods into lp:launchpad-work-items-tracker

Proposed by Clint Byrum
Status: Merged
Merged at revision: 206
Proposed branch: lp:~clint-fewbar/launchpad-work-items-tracker/server-team-mods
Merge into: lp:launchpad-work-items-tracker
Diff against target: 374 lines (+107/-35)
6 files modified
burndown-chart (+20/-12)
collect (+26/-3)
config/maverick.cfg (+1/-0)
generate-all (+33/-0)
html-report (+13/-11)
report_tools.py (+14/-9)
To merge this branch: bzr merge lp:~clint-fewbar/launchpad-work-items-tracker/server-team-mods
Reviewer Review Type Date Requested Status
Developers of work-items-tracker Pending
Review via email: mp+31336@code.launchpad.net

Description of the change

Hello tracker hackers.. this branch adds these features:

* Adds indexes making report and chart generation 5 - 6 times faster
* generate-all produces a burndown and report for every single user in the u directory
* inprogress status is now recorded for teams marked as 'inprogress_teams' in config, which currently is only 'canonical-server'

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'burndown-chart'
2--- burndown-chart 2010-07-13 08:55:43 +0000
3+++ burndown-chart 2010-07-29 22:19:40 +0000
4@@ -43,10 +43,11 @@
5
6 for date in xrange(date_to_ordinal(start_date), date_to_ordinal(end_date)+1):
7 i = data.get(ordinal_to_date(date), {})
8- count = i.get('done', 0) + i.get('todo', 0) + i.get('postponed', 0)
9+ count = i.get('done', 0) + i.get('todo', 0) + i.get('inprogress', 0) + i.get('postponed', 0)
10 if max_items < count:
11 max_items = count
12 pcdata.append((date, i.get('todo_teamonly', 0), i.get('todo', 0),
13+ i.get('inprogress_teamonly', 0), i.get('inprogress', 0),
14 i.get('done_teamonly', 0), i.get('done', 0),
15 i.get('postponed_teamonly', 0), i.get('postponed', 0), count))
16
17@@ -90,23 +91,30 @@
18 plot2.fill_style = fill_style.Plain(bgcolor=color.coral1)
19 plot2.line_style = None
20
21- plot3 = bar_plot.T(label='done (team)', hcol=3, stack_on = plot2)
22- plot3.fill_style = fill_style.green
23+ plot3 = bar_plot.T(label='inprogress (team)', hcol=3, stack_on = plot2)
24+ plot3.fill_style = fill_style.Plain(bgcolor=color.orange2)
25 plot3.line_style = None
26- plot4 = bar_plot.T(label='done (foreign)', hcol=4, stack_on = plot2)
27- plot4.fill_style = fill_style.Plain(bgcolor=color.olivedrab1)
28+ plot4 = bar_plot.T(label='inprogress (foreign)', hcol=4, stack_on = plot2)
29+ plot4.fill_style = fill_style.Plain(bgcolor=color.orange1)
30 plot4.line_style = None
31
32- plot5 = bar_plot.T(label="postponed (team)", hcol=5, stack_on = plot4)
33- plot5.fill_style = fill_style.Plain(bgcolor=color.yellow2)
34+ plot5 = bar_plot.T(label='done (team)', hcol=5, stack_on = plot4)
35+ plot5.fill_style = fill_style.green
36 plot5.line_style = None
37- plot6 = bar_plot.T(label="postponed (foreign) ", hcol=6, stack_on = plot4)
38- plot6.fill_style = fill_style.Plain(bgcolor=color.yellow1)
39+ plot6 = bar_plot.T(label='done (foreign)', hcol=6, stack_on = plot4)
40+ plot6.fill_style = fill_style.Plain(bgcolor=color.olivedrab1)
41 plot6.line_style = None
42
43- plot7 = bar_plot.T(label='total', hcol=7)
44- plot7.fill_style = None
45- plot7.line_style = line_style.gray30
46+ plot7 = bar_plot.T(label="postponed (team)", hcol=7, stack_on = plot6)
47+ plot7.fill_style = fill_style.Plain(bgcolor=color.yellow2)
48+ plot7.line_style = None
49+ plot8 = bar_plot.T(label="postponed (foreign) ", hcol=8, stack_on = plot6)
50+ plot8.fill_style = fill_style.Plain(bgcolor=color.yellow1)
51+ plot8.line_style = None
52+
53+ plot9 = bar_plot.T(label='total', hcol=9)
54+ plot9.fill_style = None
55+ plot9.line_style = line_style.gray30
56
57 # create the canvas with the specified filename and file format
58 can = canvas.init(filename,format)
59
60=== modified file 'collect'
61--- collect 2010-07-22 12:08:04 +0000
62+++ collect 2010-07-29 22:19:40 +0000
63@@ -359,7 +359,7 @@
64 return None
65
66 def lp_import_blueprint_workitems(lp, db, bp_name, bp_url, contents, release,
67- allow_inprogress = False):
68+ inprogress_teams):
69 '''Collect work items from a Launchpad blueprint.
70
71 This includes work items from the whiteboard as well as linked bugs.
72@@ -386,6 +386,16 @@
73 dbg('lp_import_blueprint_workitems(): processing %s (spec milestone: %s, spec assignee: %s, spec implementation: %s)' % (
74 bp_name, spec_milestone, spec_assignee, spec_implementation))
75
76+ cur.execute('SELECT team FROM teams WHERE name = ?', (spec_assignee,))
77+ assignee_teams = [t[0] for t in cur]
78+
79+ allow_inprogress = False
80+ for t in assignee_teams:
81+ if t in inprogress_teams:
82+ dbg(' %s allows in progress due to membership in team %s' % (spec_assignee, t))
83+ allow_inprogress = True
84+ break
85+
86 cur.execute('SELECT name FROM milestones')
87 valid_milestones = [m[0] for m in cur]
88
89@@ -537,7 +547,7 @@
90 dbg('lp_import(): downloading %s from %s' % (bp, url))
91 contents = urllib.urlopen(url).read().decode('UTF-8')
92 if lp_import_blueprint(db, bp, url, contents):
93- lp_import_blueprint_workitems(lp, db, bp, url, contents, cfg.get('release'))
94+ lp_import_blueprint_workitems(lp, db, bp, url, contents, cfg.get('release'), cfg.get('inprogress_teams'))
95
96 ########################################################################
97 #
98@@ -690,7 +700,7 @@
99 cur.execute('''CREATE TABLE version (
100 db_layout_ref INT NOT NULL
101 )''')
102- cur.execute('''INSERT INTO version VALUES (4)''')
103+ cur.execute('''INSERT INTO version VALUES (5)''')
104
105 cur.execute('''CREATE TABLE specs (
106 name VARCHAR(255) PRIMARY KEY,
107@@ -742,6 +752,8 @@
108 date TIMESTAMP NOT NULL
109 )''')
110
111+ create_v5_indexes(cur)
112+
113 db.commit()
114 else:
115 # upgrade DB layout
116@@ -788,9 +800,20 @@
117 dbg('DB upgrade finished')
118 ver = 4
119
120+ if ver == 4:
121+ dbg('Upgrading DB to layout version 5')
122+ create_v5_indexes(cur)
123+ cur.execute('UPDATE version SET db_layout_ref = 5')
124+ db.commit()
125+ ver = 5
126
127 return db
128
129+def create_v5_indexes(cur):
130+ cur.execute('''CREATE INDEX teams_name_idx on teams(name)''')
131+ cur.execute('''CREATE INDEX work_items_date_idx ON work_items (date)''')
132+ cur.execute('''CREATE INDEX work_items_status_idx ON work_items (status)''')
133+
134 ########################################################################
135 #
136 # Program operations and main
137
138=== modified file 'config/maverick.cfg'
139--- config/maverick.cfg 2010-07-20 15:32:46 +0000
140+++ config/maverick.cfg 2010-07-29 22:19:40 +0000
141@@ -75,3 +75,4 @@
142
143 recursive_teams = ['arm-ubuntu']
144
145+inprogress_teams = ['canonical-server']
146
147=== modified file 'generate-all'
148--- generate-all 2010-04-06 13:48:48 +0000
149+++ generate-all 2010-07-29 22:19:40 +0000
150@@ -42,9 +42,41 @@
151 milestones = [i[0] for i in cur]
152 cur.execute('SELECT DISTINCT team FROM teams')
153 teams = [i[0] for i in cur]
154+cur.execute('SELECT DISTINCT name FROM teams')
155+users = [i[0] for i in cur]
156
157 my_path = os.path.dirname(sys.argv[0])
158
159+usersubdir = os.path.join(opts.output_dir, 'u')
160+try:
161+ os.mkdir(usersubdir)
162+except OSError:
163+ None
164+
165+for u in users:
166+ for m in milestones:
167+ # entire cycle report for user
168+ target = u + '-' + m
169+ basename = os.path.join(usersubdir, target)
170+ print basename + '.html'
171+ f = open(basename + '.html', 'w')
172+ subprocess.call([os.path.join(my_path, 'html-report'), '-d', opts.database,
173+ '-t', u, '-m', m, '--chart', '%s.svg' % target], stdout=f)
174+ f.close()
175+
176+ print basename + '.json'
177+ f = open(basename + '.json', 'w')
178+ subprocess.call([os.path.join(my_path, 'json-report'), '-d', opts.database,
179+ '-t', u, '-m', m, '-c', opts.config], stdout=f)
180+ f.close()
181+
182+ print basename + '.svg'
183+ argv = [os.path.join(my_path, 'burndown-chart'), '-d', opts.database, '-t',
184+ u, '-m', m, '-o', basename + '.svg']
185+ if u in trend_starts:
186+ argv += ['--trend-start', str(trend_starts[t])]
187+ subprocess.call(argv)
188+
189 # per team/milestone reports
190 for t in teams:
191 for m in milestones:
192@@ -68,6 +100,7 @@
193 argv += ['--trend-start', str(trend_starts[(t, m)])]
194 subprocess.call(argv)
195
196+
197 # entire cycle report for team
198 basename = os.path.join(opts.output_dir, t)
199 print basename + '.html'
200
201=== modified file 'html-report'
202--- html-report 2010-07-08 07:37:26 +0000
203+++ html-report 2010-07-29 22:19:40 +0000
204@@ -27,7 +27,7 @@
205 <p>(Click header to sort)</p>
206 <table id="byspecification">
207 <thead>
208- <tr><th>Specification</th><th>Complexity</th> <th>todo</th><th>postponed</th><th>done</th> <th>Completion</th> <th>Priority</th> <th>Status</th></tr>
209+ <tr><th>Specification</th><th>Complexity</th> <th>todo</th><th>inprogress</th><th>postponed</th><th>done</th> <th>Completion</th> <th>Priority</th> <th>Status</th></tr>
210 </thead>
211 '''
212 data = report_tools.blueprint_completion(db, team, milestone)
213@@ -35,16 +35,16 @@
214 completion = []
215 for (bp, i) in data.iteritems():
216 completion.append((bp,
217- int(float(i['postponed']+i['done'])/(i['todo']+i['done']+i['postponed'])*100 + 0.5)))
218+ int(float(i['postponed']+i['done'])/(i['todo']+i['done']+i['postponed']+i['inprogress'])*100 + 0.5)))
219
220 completion.sort(key=lambda k: k[1]*100+report_tools.priority_value(data[k[0]]['priority']), reverse=True)
221
222 for (bp, percent) in completion:
223- print ' <tr><td>%s</td> <td>%s</td><td>%i</td><td>%i</td><td>%i</td> <td>%i%%<br/>' \
224+ print ' <tr><td>%s</td> <td>%s</td><td>%i</td><td>%i</td><td>%i</td><td>%i</td> <td>%i%%<br/>' \
225 '<span style="font-size: 70%%">%s</span></td> <td>%s</td> <td>%s</td></tr>' % (
226 '<a href="%s">%s</a>' % (data[bp]['url'], escape_html(bp)),
227 data[bp]['complexity'] or '',
228- data[bp]['todo'], data[bp]['postponed'], data[bp]['done'],
229+ data[bp]['todo'], data[bp]['inprogress'], data[bp]['postponed'], data[bp]['done'],
230 percent,
231 data[bp]['implementation'],
232 format_priority(data[bp]['priority']),
233@@ -58,24 +58,24 @@
234 <p>(Click header to sort)</p>
235 <table id="byassignee">
236 <thead>
237- <tr><th>Assignee</th> <th>Complexity</th><th>todo</th><th>postponed</th><th>done</th> <th>Completion</th></tr>
238+ <tr><th>Assignee</th> <th>Complexity</th><th>todo</th><th>inprogress</th><th>postponed</th><th>done</th> <th>Completion</th></tr>
239 </thead>
240 '''
241 data = report_tools.assignee_completion(db, team, milestone)
242
243 completion = []
244 for a, i in data.iteritems():
245- (todo, postponed, done) = (len(i['todo']), len(i['postponed']), len(i['done']))
246+ (todo, inprogress, postponed, done) = (len(i['todo']), len(i['inprogress']), len(i['postponed']), len(i['done']))
247 completion.append((a,
248- int(float(postponed+done) / (todo+done+postponed)*100 + 0.5)))
249+ int(float(postponed+done) / (todo+inprogress+done+postponed)*100 + 0.5)))
250
251 completion.sort(key=lambda k: k[0], reverse=False)
252
253 for (a, percent) in completion:
254 a_html = escape_url(a or team or 'nobody')
255 url = '%s/~%s/+specs?role=assignee' % (report_tools.blueprints_base_url, a_html)
256- print ' <tr><td><a href="%s">%s</a></td> <td>%s</td><td>%i</td><td>%i</td><td>%i</td> <td>%i%%</td></tr>' % (
257- url, a_html, data[a]['complexity'] or '', len(data[a]['todo']), len(data[a]['postponed']),
258+ print ' <tr><td><a href="%s">%s</a></td> <td>%s</td><td>%i</td><td>%i</td><td>%i</td><td>%i</td> <td>%i%%</td></tr>' % (
259+ url, a_html, data[a]['complexity'] or '', len(data[a]['todo']), len(data[a]['inprogress']), len(data[a]['postponed']),
260 len(data[a]['done']), percent)
261 print '</table>'
262
263@@ -103,11 +103,13 @@
264 todo_len = len(i['todo'])
265 postponed_len = len(i['postponed'])
266 done_len = len(i['done'])
267+ inprogress_len = len(i['inprogress'])
268 a_html = escape_url(a or team or 'nobody')
269 a_url = '%s/~%s/+specs?role=assignee' % (report_tools.blueprints_base_url, a_html)
270 rows = {'todo': '<td rowspan="%s">todo</td>' % todo_len,
271 'postponed': '<td rowspan="%s">postponed</td>' % postponed_len,
272- 'done': '<td rowspan="%s">done</td>' % done_len}
273+ 'done': '<td rowspan="%s">done</td>' % done_len,
274+ 'inprogress': '<td rowspan="%s">inprogress</td>' % inprogress_len}
275 printed_assignee = False
276 for status in report_tools.valid_states:
277 printed_status = False
278@@ -116,7 +118,7 @@
279 print ' <tr>',
280 if not printed_assignee:
281 print '<td rowspan="%s"><a href="%s" name="%s">%s</a></td> ' % (
282- todo_len+postponed_len+done_len, a_url, escape_url(a or team or 'nobody'),
283+ todo_len+postponed_len+done_len+inprogress_len, a_url, escape_url(a or team or 'nobody'),
284 a_html)
285 printed_assignee = True
286 if not printed_status:
287
288=== modified file 'report_tools.py'
289--- report_tools.py 2010-07-08 00:38:33 +0000
290+++ report_tools.py 2010-07-29 22:19:40 +0000
291@@ -6,7 +6,7 @@
292 import sqlite3 as dbapi2
293 from cgi import escape
294
295-valid_states = ['todo', 'done', 'postponed']
296+valid_states = ['todo', 'done', 'postponed', 'inprogress']
297 blueprints_base_url = 'https://blueprints.launchpad.net'
298
299 def escape_sql(text):
300@@ -59,7 +59,7 @@
301 given team.
302
303 Return date -> state -> count mapping. states are
304- {todo,done,postponed}{,_teamonly}.
305+ {todo,inprogress,done,postponed}{,_teamonly}.
306 '''
307 data = {}
308 if milestone:
309@@ -119,7 +119,7 @@
310 '''Determine current blueprint completion.
311
312 Return blueprint -> info mapping, with info being a map with these
313- keys: todo, done, postponed, status, priority, implementation, url.
314+ keys: todo, inprogress, done, postponed, status, priority, implementation, url.
315 '''
316 data = {}
317 if milestone == '':
318@@ -155,7 +155,7 @@
319 (s, last_date))
320
321 for (bp, num, status, priority, impl, url, roadmap_notes) in cur:
322- info = data.setdefault(bp, {'todo': 0, 'done': 0, 'postponed': 0})
323+ info = data.setdefault(bp, {'todo': 0, 'done': 0, 'postponed': 0, 'inprogress': 0})
324 info[s] = num
325 info['status'] = status or ''
326 info['priority'] = priority
327@@ -175,7 +175,7 @@
328 '''Determine current blueprint tasks completion.
329
330 Return blueprint -> info mapping, with info being a map with these
331- keys: todo, done, postponed, status, priority, implementation, url, tasks.
332+ keys: todo, inprogress, done, postponed, status, priority, implementation, url, tasks.
333 '''
334 default = blueprint_completion(db, team=team)
335 data = {}
336@@ -227,7 +227,7 @@
337 '''Determine current by-assignee completion.
338
339 Return assignee -> info mapping with info being a map with these
340- keys: todo, done, postponed. Each of those values is a list of [blueprint,
341+ keys: todo, inprogress, done, postponed. Each of those values is a list of [blueprint,
342 workitem, priority, spec_url].
343 '''
344 data = {}
345@@ -261,7 +261,7 @@
346 'WHERE w.status = ? AND w.date = ? %s ' % ms_sql,
347 (s, last_date))
348 for (a, bp, description, priority, url) in cur:
349- info = data.setdefault(a, {'todo': [], 'done': [], 'postponed': []})
350+ info = data.setdefault(a, {'todo': [], 'done': [], 'postponed': [], 'inprogress': []})
351 info[s].append([bp, description, priority, url])
352
353 cur2 = db.cursor();
354@@ -288,7 +288,12 @@
355
356 ### limit by a team 'like'? (see config)
357 if team:
358- team_sql = "AND name like '%%%s%%' " % config.get('teams')[team]
359+ lookup = ''
360+ try:
361+ lookup = config.get('teams')[team]
362+ except KeyError:
363+ lookup = team
364+ team_sql = "AND name like '%%%s%%' " % lookup
365 else:
366 team_sql = ""
367
368@@ -345,7 +350,7 @@
369 ### use the todo/done/postponed numbers from blueprint_completion
370 csd = completion_dict.get( spec_row["name"], None )
371 if csd:
372- for k in ("todo","done","postponed"):
373+ for k in valid_states:
374 spec_dict["completion"][k] = csd[k]
375
376

Subscribers

People subscribed via source and target branches

to all changes: