Merge lp://staging/~allenap/launchpad/bugnomination-timeout-bug-874250-preload-email into lp://staging/launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 14638
Proposed branch: lp://staging/~allenap/launchpad/bugnomination-timeout-bug-874250-preload-email
Merge into: lp://staging/launchpad
Diff against target: 1541 lines (+706/-257)
9 files modified
lib/lp/bugs/configure.zcml (+13/-0)
lib/lp/bugs/doc/bugsubscription.txt (+4/-4)
lib/lp/bugs/interfaces/bug.py (+3/-2)
lib/lp/bugs/interfaces/bugtask.py (+1/-1)
lib/lp/bugs/model/bug.py (+180/-100)
lib/lp/bugs/model/structuralsubscription.py (+84/-41)
lib/lp/bugs/model/tests/test_bug.py (+82/-8)
lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py (+239/-99)
lib/lp/bugs/tests/test_structuralsubscription.py (+100/-2)
To merge this branch: bzr merge lp://staging/~allenap/launchpad/bugnomination-timeout-bug-874250-preload-email
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) Approve
Review via email: mp+87397@code.staging.launchpad.net

Commit message

Load preferredemail when calculating bug subscribers of any kind via BugSubscriptionInfo.

Description of the change

This branch is long, but is actually quite readable, I think :)

One big problem when updating bugs is sending notifications to
subscribers. It looks like preferred email addresses are being loaded
one by one (though I started this branch long enough ago that the
details have faded).

In getting email addresses preloaded I updated BugSubscriptionInfo,
fixed some issues with it, and gotten get_also_notified_subscribers()
using it.

I think BSI is now a fairly good and comprehensive foundation for any
bug subscription calculation. It may not do everything in the minimum
number of queries possible, but it's does everything in a constant
number of queries, and makes it easy for code that uses it to also do
so.

There are still methods in IBug (and probably elsewhere) that could be
refactored to use BSI more directly, but I'll leave that for another
time.

The changes:

- Updates BugSubscriptionInfo:

  - Adds all_direct_subscriptions, all_direct_subscribers,
    direct_subscribers, muted_subscribers, and structural_subscribers
    properties.

  - Adds support for returning subscription info for only a single
    bugtask as well as all bugtasks of the given bug.

  - Adds forLevel() and forTask() methods.

  - When loading Person records, load preferred email information too.

- Updates get_also_notified_subscribers():

  - Use BugSubscriptionInfo more heavily.

  - Adds tests around performance of this function when passed a
    recipients argument. This was previously poor (potato potato).

- Updates structural subscriptions:

  Split get_structural_subscribers() into two functions -
  get_structural_subscribers() and get_structural_subscriptions() -
  which both back onto query_structural_subscriptions(), a new
  function. This supports the changes to BugSubscriptionInfo and also
  makes the implementation cleaner.

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Fwiw, passes in devel:

Tests started at approximately 2012-01-03 16:07:06.335715
Source: bzr+ssh://bazaar.launchpad.net/~allenap/launchpad/bugnomination-timeout-bug-874250-preload-email r14523
Target: bzr+ssh://bazaar.launchpad.net/~launchpad-pqm/launchpad/devel/ r14623

17326 tests run in 4:21:55.682084, 0 failures, 0 errors

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :
Download full text (22.9 KiB)

Hi Gavin,

This took me a while to review. By and large it looks nice, but I have a few cosmetic issues to rant on. Not all of them are vital, and some will seem more trouble than they're worth — unless they can become habits. It may seem petty of me; but I could give a more effective review without these things in the way.

=== modified file 'lib/lp/bugs/doc/bugsubscription.txt'
--- lib/lp/bugs/doc/bugsubscription.txt 2011-12-24 17:49:30 +0000
+++ lib/lp/bugs/doc/bugsubscription.txt 2012-01-03 17:52:00 +0000
@@ -99,7 +99,7 @@
> getAlsoNotifiedSubscribers() and getSubscribersFromDuplicates().
>
> >>> linux_source_bug.getAlsoNotifiedSubscribers()
> - [<Person at ...>]
> + (<Person at ...>,)
> >>> linux_source_bug.getSubscribersFromDuplicates()
> ()
>
@@ -109,7 +109,7 @@
> >>> from lp.bugs.model.bug import get_also_notified_subscribers
> >>> res = get_also_notified_subscribers(linux_source_bug.bugtasks[0])
> >>> res
> - [<Person at ...>]
> + (<Person at ...>,)

These two original tests were brittle and a bit obscure. They seemed to establish that (1) a list is returned and (2) the list contains one or more Persons. I suspect (1) is of no significance.

Your new tests establish that (1) a tuple is returned and (2) the tuple contains exactly one Person, as implied by the trailing comma. So you test more, but it's still brittle and still a bit obscure.

It's not worth spending too much time on any given instance of these problems, but it's good to have fixes ready when you come across them: listify so that it won't matter what kind of iterable you get, or print just the length, or use a “[foo] = give_me_a_list_or_tuple()” assignment. The big question of course is what the test means to prove and what is incidental. Stay close to the former and away from the latter.

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-12-30 06:14:56 +0000
+++ lib/lp/bugs/model/bug.py 2012-01-03 17:52:00 +0000

@@ -948,9 +949,10 @@
> BugSubscription.bug_id == self.id).order_by(BugSubscription.id)
> return DecoratedResultSet(results, operator.itemgetter(1))
>
> - def getSubscriptionInfo(self, level=BugNotificationLevel.LIFECYCLE):
> + def getSubscriptionInfo(self, level=None):
> """See `IBug`."""
> - return BugSubscriptionInfo(self, level)
> + return BugSubscriptionInfo(
> + self, BugNotificationLevel.LIFECYCLE if level is None else level)

Found the "if/else" slightly hard to read here. Maybe I'm just not used to the syntax yet, but it made me double-check that nothing else goes on in this line. Please consider putting the if/else on a separate line.

@@ -1002,6 +1004,8 @@
> # the regular proxied object.
 > return sorted(
 > indirect_subscribers,
> + # XXX: GavinPanella 2011-12-12 bug=???: Should probably use
> + # person_sort_key.
> key=lambda x: removeSecurityProxy(x).displayname)

Don't forget to file that bug!

@@ -2389,48 +2392,44 @@

> - # Direct subscriptions always take precedence over indirect
> - # subscriptions.
> - direct_subscriber...

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (28.3 KiB)

> Hi Gavin,
>
> This took me a while to review. By and large it looks nice, but I
> have a few cosmetic issues to rant on. Not all of them are vital,
> and some will seem more trouble than they're worth — unless they can
> become habits. It may seem petty of me; but I could give a more
> effective review without these things in the way.

Thanks for going through this branch!

>
>
> === modified file 'lib/lp/bugs/doc/bugsubscription.txt'
> --- lib/lp/bugs/doc/bugsubscription.txt 2011-12-24 17:49:30 +0000
> +++ lib/lp/bugs/doc/bugsubscription.txt 2012-01-03 17:52:00 +0000
> @@ -99,7 +99,7 @@
> > getAlsoNotifiedSubscribers() and getSubscribersFromDuplicates().
> >
> > >>> linux_source_bug.getAlsoNotifiedSubscribers()
> > - [<Person at ...>]
> > + (<Person at ...>,)
> > >>> linux_source_bug.getSubscribersFromDuplicates()
> > ()
> >
> @@ -109,7 +109,7 @@
> > >>> from lp.bugs.model.bug import get_also_notified_subscribers
> > >>> res = get_also_notified_subscribers(linux_source_bug.bugtasks[0])
> > >>> res
> > - [<Person at ...>]
> > + (<Person at ...>,)
>
> These two original tests were brittle and a bit obscure. They
> seemed to establish that (1) a list is returned and (2) the list
> contains one or more Persons. I suspect (1) is of no significance.
>
> Your new tests establish that (1) a tuple is returned and (2) the
> tuple contains exactly one Person, as implied by the trailing comma.
> So you test more, but it's still brittle and still a bit obscure.
>
> It's not worth spending too much time on any given instance of these
> problems, but it's good to have fixes ready when you come across
> them: listify so that it won't matter what kind of iterable you get,
> or print just the length, or use a “[foo] =
> give_me_a_list_or_tuple()” assignment. The big question of course
> is what the test means to prove and what is incidental. Stay close
> to the former and away from the latter.

Good points, well put. I've amended those.

>
>
> === modified file 'lib/lp/bugs/model/bug.py'
> --- lib/lp/bugs/model/bug.py 2011-12-30 06:14:56 +0000
> +++ lib/lp/bugs/model/bug.py 2012-01-03 17:52:00 +0000
>
> @@ -948,9 +949,10 @@
> > BugSubscription.bug_id == self.id).order_by(BugSubscription.id)
> > return DecoratedResultSet(results, operator.itemgetter(1))
> >
> > - def getSubscriptionInfo(self, level=BugNotificationLevel.LIFECYCLE):
> > + def getSubscriptionInfo(self, level=None):
> > """See `IBug`."""
> > - return BugSubscriptionInfo(self, level)
> > + return BugSubscriptionInfo(
> > + self, BugNotificationLevel.LIFECYCLE if level is None else
> level)
>
> Found the "if/else" slightly hard to read here. Maybe I'm just not
> used to the syntax yet, but it made me double-check that nothing
> else goes on in this line. Please consider putting the if/else on a
> separate line.

Done.

>
>
> @@ -1002,6 +1004,8 @@
> > # the regular proxied object.
> > return sorted(
> > indirect_subscribers,
> > + # XXX: GavinPanella 2011-12-12 bug=???: Should probably use
> > + # person_sort_key.
> ...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
The diff is not available at this time. You can reload the page or download it.