Merge lp:~danilo/launchpad/bug-1000787 into lp:launchpad

Proposed by Данило Шеган
Status: Merged
Approved by: Francesco Banconi
Approved revision: no longer in the source branch.
Merged at revision: 15300
Proposed branch: lp:~danilo/launchpad/bug-1000787
Merge into: lp:launchpad
Diff against target: 68 lines (+44/-2)
2 files modified
lib/lp/registry/browser/person.py (+2/-2)
lib/lp/registry/browser/tests/test_person_upcomingwork.py (+42/-0)
To merge this branch: bzr merge lp:~danilo/launchpad/bug-1000787
Reviewer Review Type Date Requested Status
Raphaël Badin (community) Approve
Francesco Banconi (community) code* Approve
Review via email: mp+107041@code.launchpad.net

Commit message

Upcoming work view should split out blueprint work items into appropriate milestone blocks.

Description of the change

= Bug 1000787: incorrect grouping of workitems in different milestones =

Initially, this bug was about a ZeroDivisionError, where we were dividing by zero as a total number of work items in a single "date grouping". That should have been impossible, so James Tunnicliffe just guarded against division-by-zero in one of his other branches, and that helped uncover where the bug was.

A single blueprint can have workitems targetted to multiple milestones. However, due to code structure, all of them ended up in the same blueprint-container that could only go under one single date.

== Proposed fix ==

The change is to make by-blueprint (specification) grouping be also by milestone first, so we would create a spec-container in each of the milestones that require it.

== LOC rationale ==

All branches so far:
  https://docs.google.com/a/linaro.org/spreadsheet/ccc?key=0AvOsYPy8e7yUdGkyRmx2WGFwT3NnSjdHVW04Q1pvSmc

== Tests ==

bin/test -cvvt person_upcoming

== Demo and Q/A ==

 1. Create a BP, target it to a certain milestone (with the date set)
 2. Add two workitems, one in default milestone; another in a different milestone with a different date (use "Work items for milestone-name:" in the work-items edit box to do that)
 3. Assign the BP to a person/team
 4. Go to that person's/team's upcomingwork page
    This should show up work-items/blueprints appropriately split over two milestones

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/browser/tests/test_person_upcomingwork.py
  lib/lp/registry/browser/person.py

To post a comment you must log in.
Revision history for this message
Francesco Banconi (frankban) wrote :

The code looks good, tests pass, thank you.
Just a suggestion: maybe the code in lines 9..11 could be simplified, e.g.::

    container = containers_by_spec.setdefault(milestone, {}).get(spec)

review: Approve (code*)
Revision history for this message
Raphaël Badin (rvb) wrote :

Looks good.

[0]

53 + self.assertContentEqual([spec],
54 + [container.spec

56 + self.assertContentEqual([current_workitem],
57 + [item.actual_workitem

63 + self.assertContentEqual([spec],
64 + [container.spec

66 + self.assertContentEqual([future_workitem],
67 + [item.actual_workitem

Weird line wrapping here.

review: Approve
Revision history for this message
Данило Шеган (danilo) wrote :

Francesco, thanks for the review, and indeed, that's good suggestion, I'll use that.

Raphael (thanks as well), what line wrapping would you suggest instead? I've used something that should be a bit more conservative now :)

Also, if the changes make you happy, can you please submit this for landing?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2012-05-22 15:04:48 +0000
+++ lib/lp/registry/browser/person.py 2012-05-23 16:44:19 +0000
@@ -4701,10 +4701,10 @@
4701 milestone = spec.milestone4701 milestone = spec.milestone
4702 if milestone.dateexpected not in containers_by_date:4702 if milestone.dateexpected not in containers_by_date:
4703 containers_by_date[milestone.dateexpected] = []4703 containers_by_date[milestone.dateexpected] = []
4704 container = containers_by_spec.get(spec)4704 container = containers_by_spec.setdefault(milestone, {}).get(spec)
4705 if container is None:4705 if container is None:
4706 container = SpecWorkItemContainer(spec)4706 container = SpecWorkItemContainer(spec)
4707 containers_by_spec[spec] = container4707 containers_by_spec[milestone][spec] = container
4708 containers_by_date[milestone.dateexpected].append(container)4708 containers_by_date[milestone.dateexpected].append(container)
4709 container.append(GenericWorkItem.from_workitem(workitem))4709 container.append(GenericWorkItem.from_workitem(workitem))
47104710
47114711
=== modified file 'lib/lp/registry/browser/tests/test_person_upcomingwork.py'
--- lib/lp/registry/browser/tests/test_person_upcomingwork.py 2012-05-22 15:23:25 +0000
+++ lib/lp/registry/browser/tests/test_person_upcomingwork.py 2012-05-23 16:44:19 +0000
@@ -127,6 +127,48 @@
127 self.assertEqual(1, len(container.items))127 self.assertEqual(1, len(container.items))
128 self.assertEqual(current_wi, container.items[0].actual_workitem)128 self.assertEqual(current_wi, container.items[0].actual_workitem)
129129
130 def test_multiple_milestone_separation(self):
131 # A single blueprint with workitems targetted to multiple
132 # milestones is processed so that the same blueprint appears
133 # in both with only the relevant work items.
134 spec = self.factory.makeSpecification(
135 product=self.current_milestone.product,
136 assignee=self.team.teamowner)
137 current_workitem = self.factory.makeSpecificationWorkItem(
138 title=u'workitem 1', specification=spec,
139 milestone=self.current_milestone)
140 future_workitem = self.factory.makeSpecificationWorkItem(
141 title=u'workitem 2', specification=spec,
142 milestone=self.future_milestone)
143
144 workitems = getWorkItemsDueBefore(
145 self.team, self.future_milestone.dateexpected, user=None)
146
147 # Both milestone dates are present in the returned results.
148 self.assertContentEqual(
149 [self.current_milestone.dateexpected,
150 self.future_milestone.dateexpected],
151 workitems.keys())
152
153 # Current milestone date has a single specification
154 # with only the matching work item.
155 containers_current = workitems[self.current_milestone.dateexpected]
156 self.assertContentEqual(
157 [spec], [container.spec for container in containers_current])
158 self.assertContentEqual(
159 [current_workitem],
160 [item.actual_workitem for item in containers_current[0].items])
161
162 # Future milestone date has the same specification
163 # containing only the work item targetted to future.
164 containers_future = workitems[self.future_milestone.dateexpected]
165 self.assertContentEqual(
166 [spec],
167 [container.spec for container in containers_future])
168 self.assertContentEqual(
169 [future_workitem],
170 [item.actual_workitem for item in containers_future[0].items])
171
130172
131class TestGenericWorkItem(TestCaseWithFactory):173class TestGenericWorkItem(TestCaseWithFactory):
132174