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
=== modified file 'lib/lp/bugs/model/personsubscriptioninfo.py'
--- lib/lp/bugs/model/personsubscriptioninfo.py 2011-04-13 10:01:27 +0000
+++ lib/lp/bugs/model/personsubscriptioninfo.py 2011-04-15 10:49:53 +0000
@@ -116,7 +116,8 @@
116 person, administrated_teams)116 person, administrated_teams)
117 self._principal_bug_to_infos = {}117 self._principal_bug_to_infos = {}
118118
119 def _add_item_to_collection(self, collection, principal, bug, subscription):119 def _add_item_to_collection(self, collection, principal,
120 bug, subscription):
120 info = RealSubscriptionInfo(principal, bug, subscription)121 info = RealSubscriptionInfo(principal, bug, subscription)
121 key = (principal, bug)122 key = (principal, bug)
122 infos = self._principal_bug_to_infos.get(key)123 infos = self._principal_bug_to_infos.get(key)
@@ -180,8 +181,6 @@
180 # membership) in a single query.181 # membership) in a single query.
181 store = Store.of(person)182 store = Store.of(person)
182 bug_id_options = [Bug.id == bug.id, Bug.duplicateofID == bug.id]183 bug_id_options = [Bug.id == bug.id, Bug.duplicateofID == bug.id]
183 if bug.duplicateof is not None:
184 bug_id_options.append(Bug.id == bug.duplicateof.id)
185 info = store.find(184 info = store.find(
186 (BugSubscription, Bug, Person),185 (BugSubscription, Bug, Person),
187 BugSubscription.bug == Bug.id,186 BugSubscription.bug == Bug.id,
@@ -256,6 +255,7 @@
256 def getDataForClient(self):255 def getDataForClient(self):
257 reference_map = {}256 reference_map = {}
258 dest = {}257 dest = {}
258
259 def get_id(obj):259 def get_id(obj):
260 "Get an id for the object so it can be shared."260 "Get an id for the object so it can be shared."
261 # We could leverage .id for most objects, but not pillars.261 # We could leverage .id for most objects, but not pillars.
@@ -266,6 +266,7 @@
266 reference_map[obj] = identifier266 reference_map[obj] = identifier
267 dest[identifier] = obj267 dest[identifier] = obj
268 return identifier268 return identifier
269
269 def virtual_sub_data(info):270 def virtual_sub_data(info):
270 return {271 return {
271 'principal': get_id(info.principal),272 'principal': get_id(info.principal),
@@ -273,6 +274,7 @@
273 'pillar': get_id(info.pillar),274 'pillar': get_id(info.pillar),
274 # We won't add bugtasks yet unless we need them.275 # We won't add bugtasks yet unless we need them.
275 }276 }
277
276 def real_sub_data(info):278 def real_sub_data(info):
277 return {279 return {
278 'principal': get_id(info.principal),280 'principal': get_id(info.principal),
@@ -302,18 +304,18 @@
302 }304 }
303 for category, collection in ((as_owner, self.as_owner),305 for category, collection in ((as_owner, self.as_owner),
304 (as_assignee, self.as_assignee)):306 (as_assignee, self.as_assignee)):
305 for name, inner in (('personal', collection.personal),307 for name, inner in (
306 ('as_team_admin', collection.as_team_admin),308 ('personal', collection.personal),
307 ('as_team_member', collection.as_team_member)309 ('as_team_admin', collection.as_team_admin),
308 ):310 ('as_team_member', collection.as_team_member)):
309 category[name] = [virtual_sub_data(info) for info in inner]311 category[name] = [virtual_sub_data(info) for info in inner]
310 category['count'] = collection.count312 category['count'] = collection.count
311 for category, collection in ((direct, self.direct),313 for category, collection in ((direct, self.direct),
312 (from_duplicate, self.from_duplicate)):314 (from_duplicate, self.from_duplicate)):
313 for name, inner in (('personal', collection.personal),315 for name, inner in (
314 ('as_team_admin', collection.as_team_admin),316 ('personal', collection.personal),
315 ('as_team_member', collection.as_team_member)317 ('as_team_admin', collection.as_team_admin),
316 ):318 ('as_team_member', collection.as_team_member)):
317 category[name] = [real_sub_data(info) for info in inner]319 category[name] = [real_sub_data(info) for info in inner]
318 category['count'] = collection.count320 category['count'] = collection.count
319 return subscription_data, dest321 return subscription_data, dest
320322
=== modified file 'lib/lp/bugs/model/tests/test_personsubscriptioninfo.py'
--- lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-04-13 10:01:27 +0000
+++ lib/lp/bugs/model/tests/test_personsubscriptioninfo.py 2011-04-15 10:49:53 +0000
@@ -5,35 +5,25 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from storm.store import Store8from zope.security.proxy import removeSecurityProxy
9from zope.security.interfaces import Unauthorized
10from zope.security.proxy import ProxyFactory, removeSecurityProxy
119
12from canonical.launchpad import searchbuilder
13from canonical.launchpad.interfaces.lpstorm import IStore
14from canonical.testing import DatabaseFunctionalLayer10from canonical.testing import DatabaseFunctionalLayer
15from lp.bugs.enum import BugNotificationLevel11from lp.bugs.enum import BugNotificationLevel
16from lp.bugs.interfaces.bugtask import (
17 BugTaskImportance,
18 BugTaskStatus,
19 )
20from lp.bugs.interfaces.personsubscriptioninfo import (12from lp.bugs.interfaces.personsubscriptioninfo import (
21 IRealSubscriptionInfo,13 IRealSubscriptionInfo,
22 IRealSubscriptionInfoCollection,14 IRealSubscriptionInfoCollection,
23 IVirtualSubscriptionInfo,15 IVirtualSubscriptionInfo,
24 IVirtualSubscriptionInfoCollection,16 IVirtualSubscriptionInfoCollection,
25 )17 )
26from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
27from lp.bugs.model.personsubscriptioninfo import PersonSubscriptions18from lp.bugs.model.personsubscriptioninfo import PersonSubscriptions
28from lp.registry.interfaces.teammembership import TeamMembershipStatus19from lp.registry.interfaces.teammembership import TeamMembershipStatus
29from lp.testing import (20from lp.testing import (
30 anonymous_logged_in,
31 login_person,
32 person_logged_in,21 person_logged_in,
33 TestCaseWithFactory,22 TestCaseWithFactory,
34 )23 )
35from lp.testing.matchers import Provides24from lp.testing.matchers import Provides
3625
26
37class TestPersonSubscriptionInfo(TestCaseWithFactory):27class TestPersonSubscriptionInfo(TestCaseWithFactory):
3828
39 layer = DatabaseFunctionalLayer29 layer = DatabaseFunctionalLayer
@@ -312,20 +302,18 @@
312302
313 def test_duplicate_direct_reverse(self):303 def test_duplicate_direct_reverse(self):
314 # Subscribed directly to the primary bug, and a duplicate bug changes.304 # Subscribed directly to the primary bug, and a duplicate bug changes.
315 duplicate = self.factory.makeBug()305 primary = self.factory.makeBug()
316 with person_logged_in(self.subscriber):306 with person_logged_in(self.subscriber):
317 self.bug.markAsDuplicate(duplicate)307 self.bug.markAsDuplicate(primary)
318 duplicate.subscribe(self.subscriber, self.subscriber)308 primary.subscribe(self.subscriber, self.subscriber)
319 # Load a `PersonSubscriptionInfo`s for subscriber and a bug.309 # Load a `PersonSubscriptionInfo`s for subscriber and a bug.
320 self.subscriptions.reload()310 self.subscriptions.reload()
321311
322 self.assertCollectionsAreEmpty(except_='from_duplicate')312 # This means no subscriptions on the duplicate bug.
313 self.assertCollectionsAreEmpty()
323 self.failIf(self.subscriptions.muted)314 self.failIf(self.subscriptions.muted)
324 self.assertCollectionContents(315 self.assertCollectionContents(
325 self.subscriptions.from_duplicate, personal=1)316 self.subscriptions.from_duplicate, personal=0)
326 self.assertRealSubscriptionInfoMatches(
327 self.subscriptions.from_duplicate.personal[0],
328 duplicate, self.subscriber, False, [], [])
329317
330 def test_duplicate_multiple(self):318 def test_duplicate_multiple(self):
331 # Subscribed directly to more than one duplicate bug.319 # Subscribed directly to more than one duplicate bug.