Merge lp:~james-w/launchpad-work-items-tracker/workitem-list-pages into lp:~linaro-automation/launchpad-work-items-tracker/linaro

Proposed by James Westby
Status: Merged
Merged at revision: 276
Proposed branch: lp:~james-w/launchpad-work-items-tracker/workitem-list-pages
Merge into: lp:~linaro-automation/launchpad-work-items-tracker/linaro
Diff against target: 232 lines (+113/-25)
6 files modified
generate-all (+5/-0)
html-report (+42/-0)
report_tools.py (+19/-0)
templates/base.html (+12/-5)
templates/overview.html (+6/-20)
templates/workitem_list.html (+29/-0)
To merge this branch: bzr merge lp:~james-w/launchpad-work-items-tracker/workitem-list-pages
Reviewer Review Type Date Requested Status
Guilherme Salgado (community) Approve
Review via email: mp+51393@code.launchpad.net

Description of the change

Hi,

Here's a branch that goes part way to addressing bug 720346.

I haven't yet worked out how to do the per-team lists nicely, so let's start
with everything in one table.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (8.5 KiB)

Hi James,

I think this looks good but the new functions/methods would benefit from
some docstrings. I also have a few comments below.

 review approve

On Sat, 2011-02-26 at 01:02 +0000, James Westby wrote:
[...]
> === modified file 'generate-all'
> --- generate-all 2011-02-06 20:00:12 +0000
> +++ generate-all 2011-02-26 01:01:57 +0000
> @@ -133,6 +133,11 @@
> basename = os.path.join(opts.output_dir, "about")
> report_tools.about_page(my_path, opts.database, basename, opts.config, root=opts.root)
>
> +# workitems in status pages
> +for status in report_tools.valid_states:
> + basename = os.path.join(opts.output_dir, status)
> + report_tools.workitem_list(my_path, opts.database, basename, opts.config, status, root=opts.root)
> +
> # front page
> basename = os.path.join(opts.output_dir, 'index')
> report_tools.status_overview(my_path, opts.database, basename, opts.config, root=opts.root)
>
> === modified file 'html-report'
> --- html-report 2011-02-25 02:56:48 +0000
> +++ html-report 2011-02-26 01:01:57 +0000
> @@ -47,6 +47,7 @@
> self.priority = priority
> self.status = status
>
> +
> class Assignee(WorkitemTarget):
>
> def __init__(self, name, url, complexity=0, todo_wis=[],
> @@ -90,6 +91,19 @@
> return any([g.priority for g in self.groups])
>
>
> +class Workitem(object):
> +
> + def __init__(self, description, status, blueprint, assignee=None):
> + self.description = description
> + self.status = status
> + self.blueprint = blueprint
> + self._assignee = assignee
> +
> + @property
> + def assignee(self):
> + return self._assignee or self.blueprint.assignee
> +
> +
> def spec_group_completion(db, team, milestone):
> data = report_tools.spec_group_completion(db, team, milestone)
> if not data:
> @@ -286,6 +300,21 @@
> return group
>
>
> +def workitems_in_status(db, status, team=None, milestone=None):
> + data = report_tools.assignee_completion(
> + db, team=team, milestone=milestone)
> + workitems = []
> + for assignee in data:
> + if status in data[assignee]:
> + for wi_info in data[assignee][status]:
> + bp_name, description, priority, url = wi_info
> + blueprint = Blueprint(bp_name, url, priority=priority)
> + workitems.append(
> + Workitem(description, status, blueprint,
> + assignee=assignee))
> + return workitems
> +
> +
> def list_people(db, team=None):
> team_info = report_tools.team_information(db, team=team)
> if team is None:
> @@ -398,6 +427,17 @@
> data.update(dict(page_type="about"))
> print report_tools.fill_template("about.html", data)
>
> + def workitem_list(self, db, opts):
> + assert opts.status is not None, (
> + "Must pass --status for workitem_list report-type")
> + workitems = workitems_in_status(
> + db, opts.status, team=opts.team, milestone=opts.milestone)
> + data = self.template_data(db, opts)
> + data.update(dict(status=opts.status))
> + data.update(dict(workitems=workitems))
> + ...

Read more...

review: Approve
Revision history for this message
James Westby (james-w) wrote :

> Hi James,
>
> I think this looks good but the new functions/methods would benefit from
> some docstrings.

I will add some, thanks.

> Why 3 update() calls instead of just one passing a dict combining
> everything?

No reason really, I'll change it.

> > + $(".workitems_in_status").tablesorter({
> > + sortList: [[1,1],[0,0],[2,0]],
>
> Maybe a comment explaining these magic numbers?

Good idea.

> To make this even nicer you could add something like a
> valid_states_with_labels to report_tools (by zip()ping a list containing
> the lables with the existing valid_states there). :)

I'll do that. I need to see if we can import directly, because
passing it in the data dict would be a bit of a hassle.

Thanks,

James

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'generate-all'
--- generate-all 2011-02-06 20:00:12 +0000
+++ generate-all 2011-02-26 01:02:30 +0000
@@ -133,6 +133,11 @@
133basename = os.path.join(opts.output_dir, "about")133basename = os.path.join(opts.output_dir, "about")
134report_tools.about_page(my_path, opts.database, basename, opts.config, root=opts.root)134report_tools.about_page(my_path, opts.database, basename, opts.config, root=opts.root)
135135
136# workitems in status pages
137for status in report_tools.valid_states:
138 basename = os.path.join(opts.output_dir, status)
139 report_tools.workitem_list(my_path, opts.database, basename, opts.config, status, root=opts.root)
140
136# front page141# front page
137basename = os.path.join(opts.output_dir, 'index')142basename = os.path.join(opts.output_dir, 'index')
138report_tools.status_overview(my_path, opts.database, basename, opts.config, root=opts.root)143report_tools.status_overview(my_path, opts.database, basename, opts.config, root=opts.root)
139144
=== modified file 'html-report'
--- html-report 2011-02-25 02:56:48 +0000
+++ html-report 2011-02-26 01:02:30 +0000
@@ -47,6 +47,7 @@
47 self.priority = priority47 self.priority = priority
48 self.status = status48 self.status = status
4949
50
50class Assignee(WorkitemTarget):51class Assignee(WorkitemTarget):
5152
52 def __init__(self, name, url, complexity=0, todo_wis=[],53 def __init__(self, name, url, complexity=0, todo_wis=[],
@@ -90,6 +91,19 @@
90 return any([g.priority for g in self.groups])91 return any([g.priority for g in self.groups])
9192
9293
94class Workitem(object):
95
96 def __init__(self, description, status, blueprint, assignee=None):
97 self.description = description
98 self.status = status
99 self.blueprint = blueprint
100 self._assignee = assignee
101
102 @property
103 def assignee(self):
104 return self._assignee or self.blueprint.assignee
105
106
93def spec_group_completion(db, team, milestone):107def spec_group_completion(db, team, milestone):
94 data = report_tools.spec_group_completion(db, team, milestone)108 data = report_tools.spec_group_completion(db, team, milestone)
95 if not data:109 if not data:
@@ -286,6 +300,21 @@
286 return group300 return group
287301
288302
303def workitems_in_status(db, status, team=None, milestone=None):
304 data = report_tools.assignee_completion(
305 db, team=team, milestone=milestone)
306 workitems = []
307 for assignee in data:
308 if status in data[assignee]:
309 for wi_info in data[assignee][status]:
310 bp_name, description, priority, url = wi_info
311 blueprint = Blueprint(bp_name, url, priority=priority)
312 workitems.append(
313 Workitem(description, status, blueprint,
314 assignee=assignee))
315 return workitems
316
317
289def list_people(db, team=None):318def list_people(db, team=None):
290 team_info = report_tools.team_information(db, team=team)319 team_info = report_tools.team_information(db, team=team)
291 if team is None:320 if team is None:
@@ -398,6 +427,17 @@
398 data.update(dict(page_type="about"))427 data.update(dict(page_type="about"))
399 print report_tools.fill_template("about.html", data)428 print report_tools.fill_template("about.html", data)
400429
430 def workitem_list(self, db, opts):
431 assert opts.status is not None, (
432 "Must pass --status for workitem_list report-type")
433 workitems = workitems_in_status(
434 db, opts.status, team=opts.team, milestone=opts.milestone)
435 data = self.template_data(db, opts)
436 data.update(dict(status=opts.status))
437 data.update(dict(workitems=workitems))
438 data.update(dict(page_type="overview"))
439 print report_tools.fill_template("workitem_list.html", data)
440
401441
402#442#
403# main443# main
@@ -426,6 +466,8 @@
426 help="Select the group for a group_overview report.")466 help="Select the group for a group_overview report.")
427 optparser.add_option('--root', dest="root",467 optparser.add_option('--root', dest="root",
428 help="Root URL for the charts")468 help="Root URL for the charts")
469 optparser.add_option('--status', dest="status",
470 help="Workitem status to consider for workitem_list report-type")
429471
430 (opts, args) = optparser.parse_args()472 (opts, args) = optparser.parse_args()
431 if not opts.database:473 if not opts.database:
432474
=== modified file 'report_tools.py'
--- report_tools.py 2011-02-24 01:33:40 +0000
+++ report_tools.py 2011-02-26 01:02:30 +0000
@@ -76,6 +76,25 @@
76 proc.wait()76 proc.wait()
7777
7878
79def workitem_list(my_path, database, basename, config, status, root=None):
80 cfg = load_config(config)
81 team = cfg.get("primary_team", None)
82 fh = open(basename + '.html', 'w')
83 chartname = os.path.basename(basename)
84 try:
85 args = [os.path.join(my_path, 'html-report'), '-d', database, '--chart', '%s.svg' % chartname]
86 args += ['--report-type', 'group_overview']
87 args += ['--status', status]
88 if root:
89 args += ['--root', root]
90 report_args(args, team=team)
91 proc = Popen(args, stdout=fh)
92 print basename + '.html'
93 proc.wait()
94 finally:
95 fh.close()
96
97
79def team_list_page(my_path, database, basename, config, root=None):98def team_list_page(my_path, database, basename, config, root=None):
80 cfg = load_config(config)99 cfg = load_config(config)
81 team = cfg.get("primary_team", None)100 team = cfg.get("primary_team", None)
82101
=== modified file 'templates/base.html'
--- templates/base.html 2011-02-25 23:03:45 +0000
+++ templates/base.html 2011-02-26 01:02:30 +0000
@@ -19,11 +19,11 @@
19 td.priority_Essential, th.priority_Essential { color: red; }19 td.priority_Essential, th.priority_Essential { color: red; }
20 td span.implementation_status { font-size: 70%; }20 td span.implementation_status { font-size: 70%; }
2121
22 .status-todo {color: orange;}22 .status-todo a {color: orange;}
23 .status-inprogress {color: gray;}23 .status-inprogress a {color: gray;}
24 .status-done {color: green;}24 .status-done a {color: green;}
25 .status-blocked {color: red;}25 .status-blocked a {color: red;}
26 .status-postponed {color: purple;}26 .status-postponed a {color: purple;}
2727
28 .interesting {28 .interesting {
29 font-weight: bold;29 font-weight: bold;
@@ -340,6 +340,13 @@
340 }340 }
341 });341 });
342342
343 $(".workitems_in_status").tablesorter({
344 sortList: [[1,1],[0,0],[2,0]],
345 headers: {
346 1: { sorter:'priority' }
347 }
348 });
349
343 // with the 'todo/done' and assignee not appearing on every350 // with the 'todo/done' and assignee not appearing on every
344 // row, this does not lend itself well to sorting351 // row, this does not lend itself well to sorting
345 //$("#byworkitem").tablesorter();352 //$("#byworkitem").tablesorter();
346353
=== modified file 'templates/overview.html'
--- templates/overview.html 2011-02-16 21:53:06 +0000
+++ templates/overview.html 2011-02-26 01:02:30 +0000
@@ -44,26 +44,12 @@
44 <th>Count</th>44 <th>Count</th>
45 </tr>45 </tr>
46</thead>46</thead>
47<tr class="status-todo">47% for status, label in [('todo', 'Todo'), ('blocked', 'Blocked'), ('inprogress', 'In Progress'), ('done', 'Done'), ('postponed', 'Postponed')]:
48 <td>Todo</td>48<tr class="status-${status}">
49 <td>${all_workitems.todo}</td>49 <td><a href="${base.url(status + '.html')}">${label}</a></td>
50</tr>50 <td><a href="${base.url(status + '.html')}">${getattr(all_workitems,status)}</a></td>
51<tr class="status-blocked">51</tr>
52 <td>Blocked</td>52% endfor
53 <td>${all_workitems.blocked}</td>
54</tr>
55<tr class="status-inprogress">
56 <td>In Progress</td>
57 <td>${all_workitems.inprogress}</td>
58</tr>
59<tr class="status-done">
60 <td>Done</td>
61 <td>${all_workitems.done}</td>
62</tr>
63<tr class="status-postponed">
64 <td>Postponed</td>
65 <td>${all_workitems.postponed}</td>
66</tr>
67</table>53</table>
68<%def name="workitems_per_day(workitems, days)">54<%def name="workitems_per_day(workitems, days)">
69% if days < 1:55% if days < 1:
7056
=== added file 'templates/workitem_list.html'
--- templates/workitem_list.html 1970-01-01 00:00:00 +0000
+++ templates/workitem_list.html 2011-02-26 01:02:30 +0000
@@ -0,0 +1,29 @@
1<%inherit file="base.html"/>
2<%namespace name="base" file="base.html"/>
3
4<%def name="title()">
5Work items in ${status} status
6</%def>
7
8<h1>Work items in ${status} status</h1>
9
10<p>This page shows the full list of workitems in a particular status. This can be useful to see a list of workitems that are blocked or postponed for instance.</p>
11
12<table class="workitems_in_status">
13 <thead>
14 <tr>
15 <th>Blueprint</th>
16 <th>Priority</th>
17 <th>Assignee</th>
18 <th>Description</th>
19 </tr>
20 </thead>
21% for workitem in workitems:
22 <tr>
23 <td><a href="{workitem.blueprint.url}">${workitem.blueprint.name}</a></td>
24 <td class="priority_${workitem.blueprint.priority}">${workitem.blueprint.priority}</td>
25 <td>${base.real_name(workitem.assignee)}</td>
26 <td>${workitem.description}</td>
27 </tr>
28% endfor
29</table>

Subscribers

People subscribed via source and target branches

to all changes: