Merge lp:~linaro-infrastructure/launchpad/upcoming-work-progress-bars into lp:launchpad

Proposed by Guilherme Salgado on 2012-04-04
Status: Merged
Approved by: Aaron Bentley on 2012-04-04
Approved revision: no longer in the source branch.
Merged at revision: 15070
Proposed branch: lp:~linaro-infrastructure/launchpad/upcoming-work-progress-bars
Merge into: lp:launchpad
Prerequisite: lp:~linaro-infrastructure/launchpad/team-engineering-view-ui
Diff against target: 188 lines (+99/-6)
3 files modified
lib/lp/registry/browser/team.py (+14/-3)
lib/lp/registry/browser/tests/test_team_upcomingwork.py (+59/-2)
lib/lp/registry/templates/team-upcomingwork.pt (+26/-1)
To merge this branch: bzr merge lp:~linaro-infrastructure/launchpad/upcoming-work-progress-bars
Reviewer Review Type Date Requested Status
Aaron Bentley (community) 2012-04-04 Approve on 2012-04-04
Review via email: mp+100808@code.launchpad.net

Commit Message

Add progress bars to the new +upcomingwork page.

Description of the Change

This branch adds progress bars to the new +upcomingwork page, as shown on https://dev.launchpad.net/Projects/WorkItems

To post a comment you must log in.
Aaron Bentley (abentley) wrote :

Please add docs to the tests. Otherwise, looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/team.py'
2--- lib/lp/registry/browser/team.py 2012-04-05 13:53:41 +0000
3+++ lib/lp/registry/browser/team.py 2012-04-05 13:53:42 +0000
4@@ -2172,11 +2172,16 @@
5 self.workitem_counts = {}
6 self.bugtask_counts = {}
7 self.milestones_per_date = {}
8+ self.progress_per_date = {}
9 for date, containers in self.work_item_containers:
10+ total_items = 0
11+ total_done = 0
12 milestones = set()
13 self.bugtask_counts[date] = 0
14 self.workitem_counts[date] = 0
15 for container in containers:
16+ total_items += len(container.items)
17+ total_done += len(container.done_items)
18 if isinstance(container, AggregatedBugsContainer):
19 self.bugtask_counts[date] += len(container.items)
20 else:
21@@ -2185,6 +2190,8 @@
22 milestones.add(item.milestone)
23 self.milestones_per_date[date] = sorted(
24 milestones, key=attrgetter('displayname'))
25+ self.progress_per_date[date] = '{0:.0f}'.format(
26+ 100.0 * total_done / float(total_items))
27
28 @property
29 def label(self):
30@@ -2230,9 +2237,13 @@
31 raise NotImplementedError("Must be implemented in subclasses")
32
33 @property
34- def progress_text(self):
35- done_items = [item for item in self._items if item.is_complete]
36- return '{0:.0f}%'.format(100.0 * len(done_items) / len(self._items))
37+ def done_items(self):
38+ return [item for item in self._items if item.is_complete]
39+
40+ @property
41+ def percent_done(self):
42+ return '{0:.0f}'.format(
43+ 100.0 * len(self.done_items) / len(self._items))
44
45 def append(self, item):
46 self._items.append(item)
47
48=== modified file 'lib/lp/registry/browser/tests/test_team_upcomingwork.py'
49--- lib/lp/registry/browser/tests/test_team_upcomingwork.py 2012-04-05 13:53:41 +0000
50+++ lib/lp/registry/browser/tests/test_team_upcomingwork.py 2012-04-05 13:53:42 +0000
51@@ -11,6 +11,10 @@
52
53 from zope.security.proxy import removeSecurityProxy
54
55+from lp.blueprints.enums import (
56+ SpecificationPriority,
57+ SpecificationWorkItemStatus,
58+ )
59 from lp.registry.browser.team import (
60 GenericWorkItem,
61 getWorkItemsDueBefore,
62@@ -25,6 +29,7 @@
63 from lp.testing.layers import DatabaseFunctionalLayer
64 from lp.testing.pages import (
65 extract_text,
66+ find_tag_by_id,
67 find_tags_by_class,
68 )
69 from lp.testing.views import create_initialized_view
70@@ -161,12 +166,12 @@
71 def __init__(self, is_complete):
72 self.is_complete = is_complete
73
74- def test_progress_text(self):
75+ def test_percent_done(self):
76 container = WorkItemContainer()
77 container.append(self.MockWorkItem(True))
78 container.append(self.MockWorkItem(False))
79 container.append(self.MockWorkItem(True))
80- self.assertEqual('67%', container.progress_text)
81+ self.assertEqual('67', container.percent_done)
82
83
84 class TestTeamUpcomingWork(BrowserTestCase):
85@@ -230,6 +235,58 @@
86 "<td>\n<span>&lt;script&gt;window.alert('XSS')&lt;/script&gt;"
87 "</span>\n</td>", str(title_td))
88
89+ def test_overall_progressbar(self):
90+ """Check that the per-date progress bar is present."""
91+ # Create two work items on separate specs. One of them is done and the
92+ # other is in progress.
93+ self.factory.makeSpecificationWorkItem(
94+ assignee=self.team.teamowner, milestone=self.today_milestone,
95+ status=SpecificationWorkItemStatus.DONE)
96+ self.factory.makeSpecificationWorkItem(
97+ assignee=self.team.teamowner, milestone=self.today_milestone,
98+ status=SpecificationWorkItemStatus.INPROGRESS)
99+
100+ browser = self.getViewBrowser(
101+ self.team, view_name='+upcomingwork', no_login=True)
102+
103+ # The progress bar for the due date of today_milestone will show that
104+ # 50% of the work is done (1 out of 2 work items).
105+ progressbar = find_tag_by_id(browser.contents, 'progressbar_0')
106+ self.assertEqual('50%', progressbar.get('width'))
107+
108+ def test_container_progressbar(self):
109+ """Check that the per-blueprint progress bar is present."""
110+ # Create two work items on separate specs. One of them is done and the
111+ # other is in progress. Here we create the specs explicitly, using
112+ # different priorities to force spec1 to show up first on the page.
113+ spec1 = self.factory.makeSpecification(
114+ product=self.today_milestone.product,
115+ priority=SpecificationPriority.HIGH)
116+ spec2 = self.factory.makeSpecification(
117+ product=self.today_milestone.product,
118+ priority=SpecificationPriority.LOW)
119+ self.factory.makeSpecificationWorkItem(
120+ specification=spec1, assignee=self.team.teamowner,
121+ milestone=self.today_milestone,
122+ status=SpecificationWorkItemStatus.DONE)
123+ self.factory.makeSpecificationWorkItem(
124+ specification=spec2, assignee=self.team.teamowner,
125+ milestone=self.today_milestone,
126+ status=SpecificationWorkItemStatus.INPROGRESS)
127+
128+ browser = self.getViewBrowser(
129+ self.team, view_name='+upcomingwork', no_login=True)
130+
131+ # The progress bar of the first blueprint will be complete as the sole
132+ # work item there is done, while the other is going to be empty as the
133+ # sole work item is still in progress.
134+ container1_progressbar = find_tag_by_id(
135+ browser.contents, 'container_progressbar_0')
136+ container2_progressbar = find_tag_by_id(
137+ browser.contents, 'container_progressbar_1')
138+ self.assertEqual('100%', container1_progressbar.get('width'))
139+ self.assertEqual('0%', container2_progressbar.get('width'))
140+
141
142 class TestTeamUpcomingWorkView(TestCaseWithFactory):
143
144
145=== modified file 'lib/lp/registry/templates/team-upcomingwork.pt'
146--- lib/lp/registry/templates/team-upcomingwork.pt 2012-04-05 13:53:41 +0000
147+++ lib/lp/registry/templates/team-upcomingwork.pt 2012-04-05 13:53:42 +0000
148@@ -41,6 +41,21 @@
149 <div tal:repeat="pair view/work_item_containers" class="workitems-group">
150 <div tal:define="date python: pair[0]; containers python: pair[1]">
151 <h2>Work items due in <span tal:replace="date/fmt:date" /></h2>
152+
153+ <div>
154+ <div style="float: left">Overall completion: &nbsp;</div>
155+ <div tal:define="percent_done python: view.progress_per_date[date]"
156+ tal:attributes="title string:${percent_done}% of items completed;"
157+ style="border: 1px solid gray; width: 300px; float:left">
158+ <img tal:attributes="id string:progressbar_${repeat/pair/index};
159+ width string:${percent_done}%"
160+ style="display: block"
161+ src="/@@/green-bar"
162+ height="15"/>
163+ </div>
164+ <div style="clear: both" />
165+ </div>
166+
167 <p>
168 From
169 <tal:milestones repeat="milestone python: view.milestones_per_date[date]">
170@@ -77,7 +92,17 @@
171 <td tal:content="structure container/target_link" />
172 <td tal:content="structure container/assignee_link" />
173 <td tal:content="container/priority_title" />
174- <td><span tal:replace="container/progress_text" /> done</td>
175+ <td>
176+ <div tal:attributes="title string:${container/percent_done}% of items completed;"
177+ style="border: 1px solid gray; width: 60px">
178+ <img tal:attributes="
179+ id string:container_progressbar_${repeat/container/index};
180+ width string:${container/percent_done}%"
181+ style="display: block"
182+ src="/@@/green-bar"
183+ height="10"/>
184+ </div>
185+ </td>
186 </tr>
187 </tbody>
188 <tbody class="collapsible-body">