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
1=== modified file 'lib/lp/registry/browser/person.py'
2--- lib/lp/registry/browser/person.py 2012-05-22 15:04:48 +0000
3+++ lib/lp/registry/browser/person.py 2012-05-23 16:44:19 +0000
4@@ -4701,10 +4701,10 @@
5 milestone = spec.milestone
6 if milestone.dateexpected not in containers_by_date:
7 containers_by_date[milestone.dateexpected] = []
8- container = containers_by_spec.get(spec)
9+ container = containers_by_spec.setdefault(milestone, {}).get(spec)
10 if container is None:
11 container = SpecWorkItemContainer(spec)
12- containers_by_spec[spec] = container
13+ containers_by_spec[milestone][spec] = container
14 containers_by_date[milestone.dateexpected].append(container)
15 container.append(GenericWorkItem.from_workitem(workitem))
16
17
18=== modified file 'lib/lp/registry/browser/tests/test_person_upcomingwork.py'
19--- lib/lp/registry/browser/tests/test_person_upcomingwork.py 2012-05-22 15:23:25 +0000
20+++ lib/lp/registry/browser/tests/test_person_upcomingwork.py 2012-05-23 16:44:19 +0000
21@@ -127,6 +127,48 @@
22 self.assertEqual(1, len(container.items))
23 self.assertEqual(current_wi, container.items[0].actual_workitem)
24
25+ def test_multiple_milestone_separation(self):
26+ # A single blueprint with workitems targetted to multiple
27+ # milestones is processed so that the same blueprint appears
28+ # in both with only the relevant work items.
29+ spec = self.factory.makeSpecification(
30+ product=self.current_milestone.product,
31+ assignee=self.team.teamowner)
32+ current_workitem = self.factory.makeSpecificationWorkItem(
33+ title=u'workitem 1', specification=spec,
34+ milestone=self.current_milestone)
35+ future_workitem = self.factory.makeSpecificationWorkItem(
36+ title=u'workitem 2', specification=spec,
37+ milestone=self.future_milestone)
38+
39+ workitems = getWorkItemsDueBefore(
40+ self.team, self.future_milestone.dateexpected, user=None)
41+
42+ # Both milestone dates are present in the returned results.
43+ self.assertContentEqual(
44+ [self.current_milestone.dateexpected,
45+ self.future_milestone.dateexpected],
46+ workitems.keys())
47+
48+ # Current milestone date has a single specification
49+ # with only the matching work item.
50+ containers_current = workitems[self.current_milestone.dateexpected]
51+ self.assertContentEqual(
52+ [spec], [container.spec for container in containers_current])
53+ self.assertContentEqual(
54+ [current_workitem],
55+ [item.actual_workitem for item in containers_current[0].items])
56+
57+ # Future milestone date has the same specification
58+ # containing only the work item targetted to future.
59+ containers_future = workitems[self.future_milestone.dateexpected]
60+ self.assertContentEqual(
61+ [spec],
62+ [container.spec for container in containers_future])
63+ self.assertContentEqual(
64+ [future_workitem],
65+ [item.actual_workitem for item in containers_future[0].items])
66+
67
68 class TestGenericWorkItem(TestCaseWithFactory):
69