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

Proposed by Clint Byrum
Status: Superseded
Proposed branch: lp:~clint-fewbar/launchpad-work-items-tracker/server-team-mods
Merge into: lp:launchpad-work-items-tracker
Diff against target: 587 lines (+207/-101)
6 files modified
burndown-chart (+38/-16)
collect (+14/-1)
generate-all (+11/-4)
html-report (+22/-10)
json-report (+11/-0)
report_tools.py (+111/-70)
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+32613@code.launchpad.net

This proposal has been superseded by a proposal from 2010-08-18.

Description of the change

- reordering of statuses so inprogress appears between todo and done in html report
- only generate milestones due after today, unless told otherwise.

To post a comment you must log in.
212. By Clint Byrum

generate milestones due today or later, rather than only after today

213. By Clint Byrum

only show user direct assigned items on personal page

214. By Clint Byrum

- adding blocked status
- where possible, reducing number of queries run by using group by instead
- adding index for assignee+milestone (used for several of the user mode queries)

215. By Clint Byrum

need to create indexes on db initialization as well

216. By Clint Byrum

merging with trunk

217. By Clint Byrum

fixing user page svg generation

218. By Clint Byrum

merging with trunk

219. By Clint Byrum

Fixing failure during milestone-only html-report (forgot to remove w.status clause and make args an explicit tuple)

220. By Clint Byrum

Re-showing data points accidentally hidden in burndown chart

221. By Clint Byrum

fixing broken trend lines

222. By Clint Byrum

- changing link for users to their wi tracker page on team pages
- fixing all teams burndown charts to look nicer (no more foreign/team distinction)

223. By Clint Byrum

- fixing burnup chart trend line
- adding all users in server team to burnup_chart_teams, and enabling adding users to burnup

Unmerged revisions

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-29 22:11:16 +0000
3+++ burndown-chart 2010-08-18 21:15:52 +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('inprogress', 0) + i.get('postponed', 0)
9+ count = i.get('done', 0) + i.get('todo', 0) + i.get('blocked', 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('blocked_teamonly', 0), i.get('blocked', 0),
14 i.get('inprogress_teamonly', 0), i.get('inprogress', 0),
15 i.get('done_teamonly', 0), i.get('done', 0),
16 i.get('postponed_teamonly', 0), i.get('postponed', 0), count))
17@@ -71,6 +72,9 @@
18 # create the chart area
19 # tell it to start at coords 0,0
20 # tell it the labels, and the tics, etc..
21+ # HACK: to prevent 0 div
22+ if max_items == 0:
23+ max_items = 1
24 ar = area.T(legend=legend.T(loc=(legend_x,legend_y)), loc=(0,0),
25 x_axis=axis.X(label='Date', tic_interval=x_interval,format=format_date),
26 y_axis=axis.Y(label='Work Items', tic_interval=y_interval),
27@@ -91,30 +95,37 @@
28 plot2.fill_style = fill_style.Plain(bgcolor=color.coral1)
29 plot2.line_style = None
30
31- plot3 = bar_plot.T(label='inprogress (team)', hcol=3, stack_on = plot2)
32- plot3.fill_style = fill_style.Plain(bgcolor=color.orange2)
33+ plot3 = bar_plot.T(label='blocked (team)', hcol=3, stack_on = plot2)
34+ plot3.fill_style = fill_style.Plain(bgcolor=color.darkred)
35 plot3.line_style = None
36- plot4 = bar_plot.T(label='inprogress (foreign)', hcol=4, stack_on = plot2)
37- plot4.fill_style = fill_style.Plain(bgcolor=color.orange1)
38+ plot4 = bar_plot.T(label='blocked (foreign)', hcol=4, stack_on = plot2)
39+ plot4.fill_style = fill_style.Plain(bgcolor=color.indianred)
40 plot4.line_style = None
41
42- plot5 = bar_plot.T(label='done (team)', hcol=5, stack_on = plot4)
43- plot5.fill_style = fill_style.green
44+ plot5 = bar_plot.T(label='inprogress (team)', hcol=5, stack_on = plot4)
45+ plot5.fill_style = fill_style.Plain(bgcolor=color.orange2)
46 plot5.line_style = None
47- plot6 = bar_plot.T(label='done (foreign)', hcol=6, stack_on = plot4)
48- plot6.fill_style = fill_style.Plain(bgcolor=color.olivedrab1)
49+ plot6 = bar_plot.T(label='inprogress (foreign)', hcol=6, stack_on = plot4)
50+ plot6.fill_style = fill_style.Plain(bgcolor=color.orange1)
51 plot6.line_style = None
52
53- plot7 = bar_plot.T(label="postponed (team)", hcol=7, stack_on = plot6)
54- plot7.fill_style = fill_style.Plain(bgcolor=color.yellow2)
55+ plot7 = bar_plot.T(label='done (team)', hcol=7, stack_on = plot6)
56+ plot7.fill_style = fill_style.green
57 plot7.line_style = None
58- plot8 = bar_plot.T(label="postponed (foreign) ", hcol=8, stack_on = plot6)
59- plot8.fill_style = fill_style.Plain(bgcolor=color.yellow1)
60+ plot8 = bar_plot.T(label='done (foreign)', hcol=8, stack_on = plot6)
61+ plot8.fill_style = fill_style.Plain(bgcolor=color.olivedrab1)
62 plot8.line_style = None
63
64- plot9 = bar_plot.T(label='total', hcol=9)
65- plot9.fill_style = None
66- plot9.line_style = line_style.gray30
67+ plot9 = bar_plot.T(label="postponed (team)", hcol=9, stack_on = plot8)
68+ plot9.fill_style = fill_style.Plain(bgcolor=color.yellow2)
69+ plot9.line_style = None
70+ plot10 = bar_plot.T(label="postponed (foreign) ", hcol=10, stack_on = plot8)
71+ plot10.fill_style = fill_style.Plain(bgcolor=color.yellow1)
72+ plot10.line_style = None
73+
74+ plot11 = bar_plot.T(label='total', hcol=9)
75+ plot10.fill_style = None
76+ plot10.line_style = line_style.gray30
77
78 # create the canvas with the specified filename and file format
79 can = canvas.init(filename,format)
80@@ -156,6 +167,8 @@
81 help='Output file', dest='output')
82 optparser.add_option('--trend-start', type='int',
83 help='Explicitly set start of trend line', dest='trendstart')
84+optparser.add_option('-u', '--user',
85+ help='Run for this user', dest='user')
86
87 (opts, args) = optparser.parse_args()
88 if not opts.database:
89@@ -163,6 +176,15 @@
90 if not opts.output:
91 optparser.error('No output file given')
92
93+if opts.user and opts.team:
94+ optparser.error('team and user options are mutually exclusive')
95+
96+# The typing allows polymorphic behavior
97+if opts.user:
98+ opts.team = report_tools.user_string(opts.user)
99+elif opts.team:
100+ opts.team = report_tools.team_string(opts.team)
101+
102 # get date -> state -> count mapping
103 db = report_tools.get_db(opts.database)
104 data = report_tools.workitems_over_time(db, opts.team, opts.milestone)
105
106=== modified file 'collect'
107--- collect 2010-07-29 22:11:16 +0000
108+++ collect 2010-08-18 21:15:52 +0000
109@@ -618,7 +618,7 @@
110 else:
111 status_search = fields[2:]
112 for f in status_search:
113- if 'DONE' in f or 'POSTPONED' in f or 'TODO' in f or 'INPROGRESS' in f:
114+ if 'DONE' in f or 'POSTPONED' in f or 'TODO' in f or 'INPROGRESS' in f or 'BLOCKED' in f:
115 ff = f.split()
116 if len(ff) == 2:
117 assignee = ff[1]
118@@ -628,6 +628,9 @@
119 elif 'POSTPONED' in f:
120 istatus = 'postponed'
121 break
122+ elif 'BLOCKED' in f:
123+ istatus = 'blocked'
124+ break
125 elif 'INPROGRESS' in f and allow_inprogress:
126 istatus = 'inprogress'
127 break
128@@ -807,6 +810,13 @@
129 db.commit()
130 ver = 5
131
132+ if ver == 5:
133+ dbg('Upgrading DB to layout version 6')
134+ create_v6_indexes(cur)
135+ cur.execute('UPDATE version SET db_layout_ref = 6')
136+ db.commit()
137+ ver = 6
138+
139 return db
140
141 def create_v5_indexes(cur):
142@@ -814,6 +824,9 @@
143 cur.execute('''CREATE INDEX work_items_date_idx ON work_items (date)''')
144 cur.execute('''CREATE INDEX work_items_status_idx ON work_items (status)''')
145
146+def create_v6_indexes(cur):
147+ cur.execute('''CREATE INDEX work_items_assignee_milestone_idx on work_items(assignee,milestone)''')
148+
149 ########################################################################
150 #
151 # Program operations and main
152
153=== modified file 'generate-all'
154--- generate-all 2010-07-28 16:21:04 +0000
155+++ generate-all 2010-08-18 21:15:52 +0000
156@@ -20,6 +20,9 @@
157 dest='config', metavar='PATH')
158 optparser.add_option('-o', '--output', metavar='DIRECTORY',
159 help='Output directory', dest='output_dir')
160+optparser.add_option('-a', '--all-milestones',
161+ help='Regnerate all milestones, even those that were due before today',
162+ dest='all_milestones', default=False, action='store_true')
163
164 (opts, args) = optparser.parse_args()
165 if not opts.database:
166@@ -38,8 +41,12 @@
167
168 # get milestones and teams
169 cur = db.cursor()
170-cur.execute('SELECT name FROM milestones')
171+if opts.all_milestones:
172+ cur.execute('SELECT name FROM milestones')
173+else:
174+ cur.execute("SELECT name FROM milestones WHERE due_date >= date('now')")
175 milestones = [i[0] for i in cur]
176+print 'regenerating milestones: %s' % ','.join(milestones)
177 cur.execute('SELECT DISTINCT team FROM teams')
178 teams = [i[0] for i in cur]
179 cur.execute('SELECT DISTINCT name FROM teams')
180@@ -61,17 +68,17 @@
181 print basename + '.html'
182 f = open(basename + '.html', 'w')
183 subprocess.call([os.path.join(my_path, 'html-report'), '-d', opts.database,
184- '-t', u, '-m', m, '--chart', '%s.svg' % target], stdout=f)
185+ '-u', u, '-m', m, '--chart', '%s.svg' % target], stdout=f)
186 f.close()
187
188 print basename + '.json'
189 f = open(basename + '.json', 'w')
190 subprocess.call([os.path.join(my_path, 'json-report'), '-d', opts.database,
191- '-t', u, '-m', m, '-c', opts.config], stdout=f)
192+ '-u', u, '-m', m, '-c', opts.config or ''], stdout=f)
193 f.close()
194
195 print basename + '.svg'
196- argv = [os.path.join(my_path, 'burndown-chart'), '-d', opts.database, '-t',
197+ argv = [os.path.join(my_path, 'burndown-chart'), '-d', opts.database, '-u',
198 u, '-m', m, '-o', basename + '.svg']
199 if u in trend_starts:
200 argv += ['--trend-start', str(trend_starts[t])]
201
202=== modified file 'html-report'
203--- html-report 2010-07-29 22:11:16 +0000
204+++ html-report 2010-08-18 21:15:52 +0000
205@@ -27,7 +27,7 @@
206 <p>(Click header to sort)</p>
207 <table id="byspecification">
208 <thead>
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+ <tr><th>Specification</th><th>Complexity</th> <th>todo</th><th>blocked</th><th>inprogress</th><th>postponed</th><th>done</th> <th>Completion</th> <th>Priority</th> <th>Status</th></tr>
211 </thead>
212 '''
213 data = report_tools.blueprint_completion(db, team, milestone)
214@@ -35,16 +35,16 @@
215 completion = []
216 for (bp, i) in data.iteritems():
217 completion.append((bp,
218- int(float(i['postponed']+i['done'])/(i['todo']+i['done']+i['postponed']+i['inprogress'])*100 + 0.5)))
219+ int(float(i['postponed']+i['done'])/(i['todo']+i['blocked']+i['done']+i['postponed']+i['inprogress'])*100 + 0.5)))
220
221 completion.sort(key=lambda k: k[1]*100+report_tools.priority_value(data[k[0]]['priority']), reverse=True)
222
223 for (bp, percent) in completion:
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+ print ' <tr><td>%s</td> <td>%s</td><td>%i</td><td>%i</td><td>%i</td><td>%i</td><td>%i</td> <td>%i%%<br/>' \
226 '<span style="font-size: 70%%">%s</span></td> <td>%s</td> <td>%s</td></tr>' % (
227 '<a href="%s">%s</a>' % (data[bp]['url'], escape_html(bp)),
228 data[bp]['complexity'] or '',
229- data[bp]['todo'], data[bp]['inprogress'], data[bp]['postponed'], data[bp]['done'],
230+ data[bp]['todo'], data[bp]['blocked'], data[bp]['inprogress'], data[bp]['postponed'], data[bp]['done'],
231 percent,
232 data[bp]['implementation'],
233 format_priority(data[bp]['priority']),
234@@ -58,24 +58,24 @@
235 <p>(Click header to sort)</p>
236 <table id="byassignee">
237 <thead>
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+ <tr><th>Assignee</th> <th>Complexity</th><th>todo</th><th>blocked</th><th>inprogress</th><th>postponed</th><th>done</th> <th>Completion</th></tr>
240 </thead>
241 '''
242 data = report_tools.assignee_completion(db, team, milestone)
243
244 completion = []
245 for a, i in data.iteritems():
246- (todo, inprogress, postponed, done) = (len(i['todo']), len(i['inprogress']), len(i['postponed']), len(i['done']))
247+ (todo, blocked, inprogress, postponed, done) = (len(i['todo']), len(i['blocked']), len(i['inprogress']), len(i['postponed']), len(i['done']))
248 completion.append((a,
249- int(float(postponed+done) / (todo+inprogress+done+postponed)*100 + 0.5)))
250+ int(float(postponed+done) / (todo+blocked+inprogress+done+postponed)*100 + 0.5)))
251
252 completion.sort(key=lambda k: k[0], reverse=False)
253
254 for (a, percent) in completion:
255 a_html = escape_url(a or team or 'nobody')
256 url = '%s/~%s/+specs?role=assignee' % (report_tools.blueprints_base_url, a_html)
257- 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>' % (
258- url, a_html, data[a]['complexity'] or '', len(data[a]['todo']), len(data[a]['inprogress']), len(data[a]['postponed']),
259+ 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> <td>%i%%</td></tr>' % (
260+ url, a_html, data[a]['complexity'] or '', len(data[a]['todo']), len(data[a]['blocked']), len(data[a]['inprogress']), len(data[a]['postponed']),
261 len(data[a]['done']), percent)
262 print '</table>'
263
264@@ -101,12 +101,14 @@
265
266 for a, i in sorted(data.iteritems(), key=lambda k: k[0]):
267 todo_len = len(i['todo'])
268+ blocked_len = len(i['blocked'])
269 postponed_len = len(i['postponed'])
270 done_len = len(i['done'])
271 inprogress_len = len(i['inprogress'])
272 a_html = escape_url(a or team or 'nobody')
273 a_url = '%s/~%s/+specs?role=assignee' % (report_tools.blueprints_base_url, a_html)
274 rows = {'todo': '<td rowspan="%s">todo</td>' % todo_len,
275+ 'blocked': '<td rowspan="%s">blocked</td>' % blocked_len,
276 'postponed': '<td rowspan="%s">postponed</td>' % postponed_len,
277 'done': '<td rowspan="%s">done</td>' % done_len,
278 'inprogress': '<td rowspan="%s">inprogress</td>' % inprogress_len}
279@@ -118,7 +120,7 @@
280 print ' <tr>',
281 if not printed_assignee:
282 print '<td rowspan="%s"><a href="%s" name="%s">%s</a></td> ' % (
283- todo_len+postponed_len+done_len+inprogress_len, a_url, escape_url(a or team or 'nobody'),
284+ todo_len+blocked_len+postponed_len+done_len+inprogress_len, a_url, escape_url(a or team or 'nobody'),
285 a_html)
286 printed_assignee = True
287 if not printed_status:
288@@ -260,10 +262,20 @@
289 help='Restrict report to a particular milestone', dest='milestone')
290 optparser.add_option('--chart', default='burndown.svg',
291 help='(Relative) URL to burndown chart', dest='chart_url')
292+ optparser.add_option('-u', '--user',
293+ help='Run for this user', dest='user')
294
295 (opts, args) = optparser.parse_args()
296 if not opts.database:
297 optparser.error('No database given')
298+ if opts.user and opts.team:
299+ optparser.error('team and user options are mutually exclusive')
300+
301+ # The typing allows polymorphic behavior
302+ if opts.user:
303+ opts.team = report_tools.user_string(opts.user)
304+ elif opts.team:
305+ opts.team = report_tools.team_string(opts.team)
306
307 db = report_tools.get_db(opts.database)
308
309
310=== modified file 'json-report'
311--- json-report 2010-04-06 13:48:48 +0000
312+++ json-report 2010-08-18 21:15:52 +0000
313@@ -25,12 +25,23 @@
314 optparser.add_option('-c', '--config',
315 help='Path to configuration file (if given, this can read adjusted trend lines start values)',
316 dest='config', metavar='PATH')
317+ optparser.add_option('-u', '--user',
318+ help='Run for this user', dest='user')
319
320 (opts, args) = optparser.parse_args()
321 if not opts.database:
322 optparser.error('No database given')
323 db = report_tools.get_db(opts.database)
324
325+ if opts.user and opts.team:
326+ optparser.error('team and user options are mutually exclusive')
327+
328+ # The typing allows polymorphic behavior
329+ if opts.user:
330+ opts.team = report_tools.user_string(opts.user)
331+ elif opts.team:
332+ opts.team = report_tools.team_string(opts.team)
333+
334 if opts.config:
335 config = report_tools.load_config(opts.config)
336 else:
337
338=== modified file 'report_tools.py'
339--- report_tools.py 2010-07-29 22:11:16 +0000
340+++ report_tools.py 2010-08-18 21:15:52 +0000
341@@ -6,9 +6,15 @@
342 import sqlite3 as dbapi2
343 from cgi import escape
344
345-valid_states = ['todo', 'done', 'postponed', 'inprogress']
346+valid_states = ['inprogress','blocked','todo','done', 'postponed']
347 blueprints_base_url = 'https://blueprints.launchpad.net'
348
349+class user_string(str):
350+ pass
351+
352+class team_string(str):
353+ pass
354+
355 def escape_sql(text):
356 return text.replace("'", "''")
357
358@@ -84,35 +90,49 @@
359
360 # include both direct team assignments, as well as assignmets to
361 # members of that team
362- for s in valid_states:
363- cur = db.cursor()
364- if team:
365- # WIs which are assigned to team members
366- cur.execute('SELECT date, COUNT(DISTINCT w.description) '
367+ cur = db.cursor()
368+ if team:
369+ # WIs which are assigned to team members
370+ if isinstance(team, user_string):
371+ cur.execute('SELECT status, date, COUNT(DISTINCT w.description) '
372+ 'FROM work_items w '
373+ 'WHERE assignee = ? %s '
374+ 'GROUP BY status, date' % ms_sql, [team])
375+ else:
376+ cur.execute('SELECT status, date, COUNT(DISTINCT w.description) '
377 'FROM work_items w LEFT JOIN teams '
378 'ON w.assignee = teams.name '
379- 'WHERE status = ? AND (team = ? OR assignee = ?) %s'
380- 'GROUP BY date' % ms_sql, (s, team, team))
381-
382- for (date, num) in cur:
383- data.setdefault(date, {})[s + '_teamonly'] = num
384-
385- # all WIs which belong to that team's specs
386- cur.execute('SELECT date, COUNT(DISTINCT w.description) '
387+ 'WHERE (team = ? OR assignee = ?) %s'
388+ 'GROUP BY status, date' % ms_sql, (team, team))
389+
390+ for (s, date, num) in cur:
391+ data.setdefault(date, {})[s + '_teamonly'] = num
392+
393+ # all WIs which belong to that team's specs
394+ # Only do this for teams, users only want their
395+ # assigned work items
396+ if isinstance(team, user_string):
397+ cur.execute('SELECT w.status, date, COUNT(DISTINCT w.description) '
398+ 'FROM work_items w, specs s ON w.spec = s.name '
399+ 'WHERE (w.assignee = ? OR (s.assignee = ? and w.assignee is null)) %s'
400+ 'GROUP BY w.status, date' % ms_sql, (team, team))
401+ else:
402+ cur.execute('SELECT w.status, date, COUNT(DISTINCT w.description) '
403 'FROM work_items w, specs s ON w.spec = s.name '
404 ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name) '
405- 'WHERE w.status = ? AND (teams.team = ? OR w.assignee = ? OR s.assignee = ?) %s'
406- 'GROUP BY date' % ms_sql, (s, team, team, team))
407-
408- for (date, num) in cur:
409- data.setdefault(date, {})[s] = num
410- else:
411- # all WIs for this milestone
412- cur.execute('SELECT date, count(*) FROM work_items w '
413- 'WHERE status=? %s GROUP BY date' % ms_sql, (s,))
414-
415- for (date, num) in cur:
416- data.setdefault(date, {})[s] = num
417+ 'WHERE (teams.team = ? OR w.assignee = ? OR s.assignee = ?) %s'
418+ 'GROUP BY w.status, date' % ms_sql, (team, team, team))
419+
420+ for (s, date, num) in cur:
421+ data.setdefault(date, {})[s] = num
422+
423+ else:
424+ # all WIs for this milestone
425+ cur.execute('SELECT status, date, count(*) FROM work_items w '
426+ 'WHERE 1=1 %s GROUP BY status, date' % ms_sql)
427+
428+ for (date, num) in cur:
429+ data.setdefault(date, {})[s] = num
430 return data
431
432 def blueprint_completion(db, team=None, milestone=None):
433@@ -134,39 +154,46 @@
434 cur.execute('SELECT max(date) FROM work_items')
435 (last_date,) = cur.fetchone()
436
437- for s in valid_states:
438- cur = db.cursor()
439- if team:
440- # include both direct team assignments, as well as assignmnets to
441- # members of that team
442- cur.execute('SELECT s.name, COUNT(DISTINCT w.description), s.status, s.priority, s.implementation, s.url, s.roadmap_notes '
443+ cur = db.cursor()
444+ if team:
445+ # include both direct team assignments, as well as assignmnets to
446+ # members of that team
447+ if isinstance(team, user_string):
448+ cur.execute('SELECT w.status, s.name, COUNT(DISTINCT w.description), s.status, s.priority, s.implementation, s.url, s.roadmap_notes '
449+ 'FROM work_items w, specs s ON w.spec = s.name '
450+ 'WHERE w.date = ? AND'
451+ ' (w.assignee = ? or (s.assignee = ? and w.assignee is null)) %s '
452+ 'GROUP BY w.status,w.spec' % ms_sql,
453+ (last_date, team, team))
454+ else:
455+ cur.execute('SELECT w.status, s.name, COUNT(DISTINCT w.description), s.status, s.priority, s.implementation, s.url, s.roadmap_notes '
456 'FROM work_items w, specs s ON w.spec = s.name '
457 ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name)'
458- 'WHERE w.status = ? AND w.date = ? AND'
459+ 'WHERE w.date = ? AND'
460 ' (teams.team = ? OR w.assignee = ? or s.assignee = ?) %s '
461- 'GROUP BY w.spec' % ms_sql,
462- (s, last_date, team, team, team))
463- else:
464- cur.execute('SELECT s.name, COUNT(DISTINCT w.description), s.status, s.priority, s.implementation, s.url, s.roadmap_notes '
465- 'FROM work_items w, specs s '
466- 'ON w.spec = s.name '
467- 'WHERE w.status = ? AND w.date = ? %s '
468- 'GROUP BY w.spec' % ms_sql,
469- (s, last_date))
470+ 'GROUP BY w.status, w.spec' % ms_sql,
471+ (last_date, team, team, team))
472+ else:
473+ cur.execute('SELECT w.status, s.name, COUNT(DISTINCT w.description), s.status, s.priority, s.implementation, s.url, s.roadmap_notes '
474+ 'FROM work_items w, specs s '
475+ 'ON w.spec = s.name '
476+ 'WHERE w.status = ? AND w.date = ? %s '
477+ 'GROUP BY w.status, w.spec' % ms_sql,
478+ (last_date))
479
480- for (bp, num, status, priority, impl, url, roadmap_notes) in cur:
481- info = data.setdefault(bp, {'todo': 0, 'done': 0, 'postponed': 0, 'inprogress': 0})
482- info[s] = num
483- info['status'] = status or ''
484- info['priority'] = priority
485- info['implementation'] = impl
486- info['url'] = url
487- info['roadmap_notes'] = roadmap_notes
488-
489- cur2 = db.cursor();
490- cur2.execute('SELECT SUM(points) FROM complexity as w WHERE spec = ? AND date = ? %s' % ms_sql, (bp, last_date))
491- for points in cur2:
492- info['complexity'] = points[0]
493+ for (s, bp, num, status, priority, impl, url, roadmap_notes) in cur:
494+ info = data.setdefault(bp, {'todo': 0, 'blocked': 0, 'done': 0, 'postponed': 0, 'inprogress': 0})
495+ info[s] = num
496+ info['status'] = status or ''
497+ info['priority'] = priority
498+ info['implementation'] = impl
499+ info['url'] = url
500+ info['roadmap_notes'] = roadmap_notes
501+
502+ cur2 = db.cursor();
503+ cur2.execute('SELECT SUM(points) FROM complexity as w WHERE spec = ? AND date = ? %s' % ms_sql, (bp, last_date))
504+ for points in cur2:
505+ info['complexity'] = points[0]
506
507 return data
508
509@@ -175,7 +202,7 @@
510 '''Determine current blueprint tasks completion.
511
512 Return blueprint -> info mapping, with info being a map with these
513- keys: todo, inprogress, done, postponed, status, priority, implementation, url, tasks.
514+ keys: todo, blocked, inprogress, done, postponed, status, priority, implementation, url, tasks.
515 '''
516 default = blueprint_completion(db, team=team)
517 data = {}
518@@ -196,12 +223,19 @@
519 if team:
520 # include both direct team assignments, as well as assignmnets to
521 # members of that team
522- cur.execute('SELECT DISTINCT w.spec, w.description, w.assignee, w.milestone, w.status '
523- 'FROM work_items w, specs s ON w.spec = s.name '
524- ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name) '
525- 'WHERE w.status = ? AND w.date = ? AND'
526- ' (teams.team = ? OR w.assignee = ? or s.assignee = ?) %s ' % ms_sql,
527- (s, last_date, team, team, team))
528+ if isinstance(team, user_string):
529+ cur.execute('SELECT DISTINCT w.spec, w.description, w.assignee, w.milestone, w.status '
530+ 'FROM work_items w, specs s ON w.spec = s.name '
531+ 'WHERE w.status = ? AND w.date = ? AND'
532+ ' (w.assignee = ? or (s.assignee = ? and w.assignee is null)) %s ' % ms_sql,
533+ (s, last_date, team, team))
534+ else:
535+ cur.execute('SELECT DISTINCT w.spec, w.description, w.assignee, w.milestone, w.status '
536+ 'FROM work_items w, specs s ON w.spec = s.name '
537+ ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name) '
538+ 'WHERE w.status = ? AND w.date = ? AND'
539+ ' (teams.team = ? OR w.assignee = ? or s.assignee = ?) %s ' % ms_sql,
540+ (s, last_date, team, team, team))
541 else:
542 cur.execute('SELECT DISTINCT w.spec, w.description, w.assignee, w.milestone, w.status '
543 'FROM work_items w, specs s '
544@@ -227,7 +261,7 @@
545 '''Determine current by-assignee completion.
546
547 Return assignee -> info mapping with info being a map with these
548- keys: todo, inprogress, done, postponed. Each of those values is a list of [blueprint,
549+ keys: todo, blocked, inprogress, done, postponed. Each of those values is a list of [blueprint,
550 workitem, priority, spec_url].
551 '''
552 data = {}
553@@ -248,12 +282,19 @@
554 if team:
555 # include both direct team assignments, as well as assignmnets to
556 # members of that team
557- cur.execute('SELECT DISTINCT w.assignee, w.spec, w.description, s.priority, s.url '
558- 'FROM work_items w, specs s ON w.spec = s.name '
559- ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name) '
560- 'WHERE w.status = ? AND w.date = ? AND'
561- ' (teams.team = ? OR w.assignee = ? or s.assignee = ?) %s ' % ms_sql,
562- (s, last_date, team, team, team))
563+ if isinstance(team, user_string):
564+ cur.execute('SELECT DISTINCT w.assignee, w.spec, w.description, s.priority, s.url '
565+ 'FROM work_items w, specs s ON w.spec = s.name '
566+ 'WHERE w.status = ? AND w.date = ? AND'
567+ ' (w.assignee = ? or (s.assignee = ? and w.assignee is null)) %s ' % ms_sql,
568+ (s, last_date, team, team))
569+ else:
570+ cur.execute('SELECT DISTINCT w.assignee, w.spec, w.description, s.priority, s.url '
571+ 'FROM work_items w, specs s ON w.spec = s.name '
572+ ' LEFT JOIN teams ON (s.assignee = teams.name OR w.assignee = teams.name) '
573+ 'WHERE w.status = ? AND w.date = ? AND'
574+ ' (teams.team = ? OR w.assignee = ? or s.assignee = ?) %s ' % ms_sql,
575+ (s, last_date, team, team, team))
576 else:
577 cur.execute('SELECT DISTINCT w.assignee, w.spec, w.description, s.priority, s.url '
578 'FROM work_items w, specs s '
579@@ -261,7 +302,7 @@
580 'WHERE w.status = ? AND w.date = ? %s ' % ms_sql,
581 (s, last_date))
582 for (a, bp, description, priority, url) in cur:
583- info = data.setdefault(a, {'todo': [], 'done': [], 'postponed': [], 'inprogress': []})
584+ info = data.setdefault(a, {'todo': [], 'blocked': [], 'done': [], 'postponed': [], 'inprogress': []})
585 info[s].append([bp, description, priority, url])
586
587 cur2 = db.cursor();

Subscribers

People subscribed via source and target branches

to all changes: