Merge lp:~clint-fewbar/launchpad-work-items-tracker/server-team-mods into lp:launchpad-work-items-tracker
- server-team-mods
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Developers of work-items-tracker | Pending | ||
Review via email: mp+31336@code.launchpad.net |
Commit message
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 |