Merge lp:~danilo/launchpad/remove-primary-duplicate-reason into lp:launchpad

Proposed by Данило Шеган
Status: Merged
Approved by: Данило Шеган
Approved revision: no longer in the source branch.
Merged at revision: 12887
Proposed branch: lp:~danilo/launchpad/remove-primary-duplicate-reason
Merge into: lp:launchpad
Diff against target: 142 lines (+21/-31)
2 files modified
lib/lp/bugs/model/personsubscriptioninfo.py (+13/-11)
lib/lp/bugs/model/tests/test_personsubscriptioninfo.py (+8/-20)
To merge this branch: bzr merge lp:~danilo/launchpad/remove-primary-duplicate-reason
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+57839@code.launchpad.net

Commit message

[r=adeuring][bug=761596] Stop treating bug duplication as symmetric for the purposes of bug subscription descriptions. It's not.

Description of the change

= Remove symmetric handling of duplicates =

Atm, the code assumes that bug duplication is a symmetric process as far as email notifications go. Since that's not the case (I double-checked that), we need to stop treating it as such.

== Implementation details ==

Remove the bug join condition that fetches the primary bug for a duplicate. The condition list was Or()d together.

The test has been updated to confirm this is the case.

Lint fixes in a separate commit, getting pushed after review :)

== Tests ==

bin/test -cvvt PersonSubscriptionInfo.*duplicate

== Demo and Q/A ==

  1. Turn on 'malone.advanced-structural-subscriptions.enabled' feature flag
  2. Mark a bug https://bugs.launchpad.dev/firefox/+bug/6 as duplicate of https://bugs.launchpad.dev/firefox/+bug/5
  3. Subscribe to bug 5
  4. Make sure the https://bugs.launchpad.dev/firefox/+bug/6/+subscriptions doesn't say how bug 5 is a duplicate of bug 6

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/model/personsubscriptioninfo.py
  lib/lp/bugs/model/tests/test_personsubscriptioninfo.py

./lib/lp/bugs/model/personsubscriptioninfo.py
     119: E501 line too long (80 characters)
     257: E301 expected 1 blank line, found 0
     267: E301 expected 1 blank line, found 0
     274: E301 expected 1 blank line, found 0
     305: E202 whitespace before ')'
     313: E202 whitespace before ')'
     119: Line exceeds 78 characters.
./lib/lp/bugs/model/tests/test_personsubscriptioninfo.py
      12: 'searchbuilder' imported but unused
      29: 'anonymous_logged_in' imported but unused
       8: 'Store' imported but unused
      16: 'BugTaskStatus' imported but unused
      10: 'ProxyFactory' imported but unused
       9: 'Unauthorized' imported but unused
      16: 'BugTaskImportance' imported but unused
      29: 'login_person' imported but unused
      26: 'BugSubscriptionFilter' imported but unused
      13: 'IStore' imported but unused
      37: E302 expected 2 blank lines, found 1

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/model/personsubscriptioninfo.py'
2--- lib/lp/bugs/model/personsubscriptioninfo.py 2011-04-13 10:01:27 +0000
3+++ lib/lp/bugs/model/personsubscriptioninfo.py 2011-04-15 10:49:53 +0000
4@@ -116,7 +116,8 @@
5 person, administrated_teams)
6 self._principal_bug_to_infos = {}
7
8- def _add_item_to_collection(self, collection, principal, bug, subscription):
9+ def _add_item_to_collection(self, collection, principal,
10+ bug, subscription):
11 info = RealSubscriptionInfo(principal, bug, subscription)
12 key = (principal, bug)
13 infos = self._principal_bug_to_infos.get(key)
14@@ -180,8 +181,6 @@
15 # membership) in a single query.
16 store = Store.of(person)
17 bug_id_options = [Bug.id == bug.id, Bug.duplicateofID == bug.id]
18- if bug.duplicateof is not None:
19- bug_id_options.append(Bug.id == bug.duplicateof.id)
20 info = store.find(
21 (BugSubscription, Bug, Person),
22 BugSubscription.bug == Bug.id,
23@@ -256,6 +255,7 @@
24 def getDataForClient(self):
25 reference_map = {}
26 dest = {}
27+
28 def get_id(obj):
29 "Get an id for the object so it can be shared."
30 # We could leverage .id for most objects, but not pillars.
31@@ -266,6 +266,7 @@
32 reference_map[obj] = identifier
33 dest[identifier] = obj
34 return identifier
35+
36 def virtual_sub_data(info):
37 return {
38 'principal': get_id(info.principal),
39@@ -273,6 +274,7 @@
40 'pillar': get_id(info.pillar),
41 # We won't add bugtasks yet unless we need them.
42 }
43+
44 def real_sub_data(info):
45 return {
46 'principal': get_id(info.principal),
47@@ -302,18 +304,18 @@
48 }
49 for category, collection in ((as_owner, self.as_owner),
50 (as_assignee, self.as_assignee)):
51- for name, inner in (('personal', collection.personal),
52- ('as_team_admin', collection.as_team_admin),
53- ('as_team_member', collection.as_team_member)
54- ):
55+ for name, inner in (
56+ ('personal', collection.personal),
57+ ('as_team_admin', collection.as_team_admin),
58+ ('as_team_member', collection.as_team_member)):
59 category[name] = [virtual_sub_data(info) for info in inner]
60 category['count'] = collection.count
61 for category, collection in ((direct, self.direct),
62 (from_duplicate, self.from_duplicate)):
63- for name, inner in (('personal', collection.personal),
64- ('as_team_admin', collection.as_team_admin),
65- ('as_team_member', collection.as_team_member)
66- ):
67+ for name, inner in (
68+ ('personal', collection.personal),
69+ ('as_team_admin', collection.as_team_admin),
70+ ('as_team_member', collection.as_team_member)):
71 category[name] = [real_sub_data(info) for info in inner]
72 category['count'] = collection.count
73 return subscription_data, dest
74
75=== modified file 'lib/lp/bugs/model/tests/test_personsubscriptioninfo.py'
76--- lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-04-13 10:01:27 +0000
77+++ lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-04-15 10:49:53 +0000
78@@ -5,35 +5,25 @@
79
80 __metaclass__ = type
81
82-from storm.store import Store
83-from zope.security.interfaces import Unauthorized
84-from zope.security.proxy import ProxyFactory, removeSecurityProxy
85+from zope.security.proxy import removeSecurityProxy
86
87-from canonical.launchpad import searchbuilder
88-from canonical.launchpad.interfaces.lpstorm import IStore
89 from canonical.testing import DatabaseFunctionalLayer
90 from lp.bugs.enum import BugNotificationLevel
91-from lp.bugs.interfaces.bugtask import (
92- BugTaskImportance,
93- BugTaskStatus,
94- )
95 from lp.bugs.interfaces.personsubscriptioninfo import (
96 IRealSubscriptionInfo,
97 IRealSubscriptionInfoCollection,
98 IVirtualSubscriptionInfo,
99 IVirtualSubscriptionInfoCollection,
100 )
101-from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
102 from lp.bugs.model.personsubscriptioninfo import PersonSubscriptions
103 from lp.registry.interfaces.teammembership import TeamMembershipStatus
104 from lp.testing import (
105- anonymous_logged_in,
106- login_person,
107 person_logged_in,
108 TestCaseWithFactory,
109 )
110 from lp.testing.matchers import Provides
111
112+
113 class TestPersonSubscriptionInfo(TestCaseWithFactory):
114
115 layer = DatabaseFunctionalLayer
116@@ -312,20 +302,18 @@
117
118 def test_duplicate_direct_reverse(self):
119 # Subscribed directly to the primary bug, and a duplicate bug changes.
120- duplicate = self.factory.makeBug()
121+ primary = self.factory.makeBug()
122 with person_logged_in(self.subscriber):
123- self.bug.markAsDuplicate(duplicate)
124- duplicate.subscribe(self.subscriber, self.subscriber)
125+ self.bug.markAsDuplicate(primary)
126+ primary.subscribe(self.subscriber, self.subscriber)
127 # Load a `PersonSubscriptionInfo`s for subscriber and a bug.
128 self.subscriptions.reload()
129
130- self.assertCollectionsAreEmpty(except_='from_duplicate')
131+ # This means no subscriptions on the duplicate bug.
132+ self.assertCollectionsAreEmpty()
133 self.failIf(self.subscriptions.muted)
134 self.assertCollectionContents(
135- self.subscriptions.from_duplicate, personal=1)
136- self.assertRealSubscriptionInfoMatches(
137- self.subscriptions.from_duplicate.personal[0],
138- duplicate, self.subscriber, False, [], [])
139+ self.subscriptions.from_duplicate, personal=0)
140
141 def test_duplicate_multiple(self):
142 # Subscribed directly to more than one duplicate bug.