← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~danilo/launchpad/bug-690568 into lp:launchpad

 

Данило Шеган has proposed merging lp:~danilo/launchpad/bug-690568 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #690568 in Launchpad itself: "Timeout when adding ~canonical (400 people) to private PPA"
  https://bugs.launchpad.net/launchpad/+bug/690568

For more details, see:
https://code.launchpad.net/~danilo/launchpad/bug-690568/+merge/71394

= Bug 690568 =

Subscribing a big team to a private PPA results in a timeout because we fetch preferred email address one by one.  Fetch them all in one go.

Archive.getNonActiveSubscribers is used solely for this purpose so we can simply add in the EmailAddress into it.  Make it slightly prettier at that.

== Implementation details ==

I haven't fixed lint in the doctest, nor have I bothered with moving the test to a proper unit test.

== Tests ==

bin/test -vvt archivesubscriber.txt -t archive_sub

== Demo and Q/A ==


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/doc/archivesubscriber.txt
  lib/lp/soyuz/model/archivesubscriber.py
  lib/canonical/launchpad/mailnotification.py
  lib/lp/soyuz/interfaces/archivesubscriber.py

./lib/lp/soyuz/doc/archivesubscriber.txt
       1: narrative uses a moin header.
      35: narrative uses a moin header.
     232: narrative uses a moin header.
     326: source exceeds 78 characters.
     372: narrative uses a moin header.
     411: narrative uses a moin header.
     445: narrative uses a moin header.
-- 
https://code.launchpad.net/~danilo/launchpad/bug-690568/+merge/71394
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~danilo/launchpad/bug-690568 into lp:launchpad.
=== modified file 'lib/canonical/launchpad/mailnotification.py'
--- lib/canonical/launchpad/mailnotification.py	2011-07-01 09:54:54 +0000
+++ lib/canonical/launchpad/mailnotification.py	2011-08-12 16:09:24 +0000
@@ -285,13 +285,8 @@
 
     template = get_email_template('ppa-subscription-new.txt')
 
-    for person in non_active_subscribers:
-
-        if person.preferredemail is None:
-            # Don't send to people without a preferred email.
-            continue
-
-        to_address = [person.preferredemail.email]
+    for person, preferred_email in non_active_subscribers:
+        to_address = [preferred_email.email]
         root = getUtility(ILaunchpadRoot)
         recipient_subscriptions_url = "%s~/+archivesubscriptions" % (
             canonical_url(root))

=== modified file 'lib/lp/soyuz/doc/archivesubscriber.txt'
--- lib/lp/soyuz/doc/archivesubscriber.txt	2011-07-26 21:10:24 +0000
+++ lib/lp/soyuz/doc/archivesubscriber.txt	2011-08-12 16:09:24 +0000
@@ -461,18 +461,18 @@
 So the getNonActiveSubscribers() method for this team subscription will
 currently include Joe:
 
-    >>> for person in subscription.getNonActiveSubscribers():
-    ...     print person.displayname
-    Celso Providelo
-    Joe Smith
-    John Smith
+    >>> for person, email in subscription.getNonActiveSubscribers():
+    ...     print person.displayname, email.email
+    Celso Providelo   celso.providelo@xxxxxxxxxxxxx
+    Joe Smith         joe@xxxxxxxxxxx
+    John Smith        john@xxxxxxxxxxx
 
 But if we create an auth token for joe to the archive (this could be via
 a separate subscription), then he will no longer be listed as a non-active
 subscriber for this subscription:
 
     >>> joesmith_token = mark_private_ppa.newAuthToken(joesmith)
-    >>> for person in subscription.getNonActiveSubscribers():
+    >>> for person, email in subscription.getNonActiveSubscribers():
     ...     print person.displayname
     Celso Providelo
     John Smith
@@ -487,7 +487,7 @@
     ...                                 email="harry@xxxxxxxxxxx")
     >>> subscription = mark_private_ppa.newSubscription(
     ...     harrysmith, mark, description=u"subscription for joesmith")
-    >>> for person in subscription.getNonActiveSubscribers():
+    >>> for person, email in subscription.getNonActiveSubscribers():
     ...     print person.displayname
     Harry Smith
 
@@ -507,7 +507,7 @@
     ...     team_cprov, mark, force_team_add=True)
     >>> subscription = mark_private_ppa.newSubscription(
     ...     launchpad_devs, mark, description=u"LP team too")
-    >>> for person in subscription.getNonActiveSubscribers():
+    >>> for person, email in subscription.getNonActiveSubscribers():
     ...     print person.displayname
     Celso Providelo
     John Smith

=== modified file 'lib/lp/soyuz/interfaces/archivesubscriber.py'
--- lib/lp/soyuz/interfaces/archivesubscriber.py	2011-05-06 14:12:11 +0000
+++ lib/lp/soyuz/interfaces/archivesubscriber.py	2011-08-12 16:09:24 +0000
@@ -93,9 +93,11 @@
     def getNonActiveSubscribers():
         """Return the people included in this subscription.
 
-        :return: a storm `ResultSet` of all the people who are included in
-            this subscription who do not yet have an active token for the
-            corresponding archive.
+        :return: a storm `ResultSet` of all the people and their preferred
+            email address who are included in this subscription who do not
+            yet have an active token for the corresponding archive.
+
+            Persons with no preferred email addresses are not included.
         :rtype: `storm.store.ResultSet`
         """
 

=== modified file 'lib/lp/soyuz/model/archivesubscriber.py'
--- lib/lp/soyuz/model/archivesubscriber.py	2011-05-06 14:12:11 +0000
+++ lib/lp/soyuz/model/archivesubscriber.py	2011-08-12 16:09:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Database class for table ArchiveSubscriber."""
@@ -13,8 +13,8 @@
 from storm.expr import (
     And,
     Desc,
+    Join,
     LeftJoin,
-    Join,
     )
 from storm.locals import (
     DateTime,
@@ -30,6 +30,8 @@
 
 from canonical.database.constants import UTC_NOW
 from canonical.database.enumcol import DBEnum
+from canonical.launchpad.database.emailaddress import EmailAddress
+from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
 from lp.registry.interfaces.person import validate_person
 from lp.registry.model.teammembership import TeamParticipation
 from lp.soyuz.interfaces.archiveauthtoken import IArchiveAuthTokenSet
@@ -96,26 +98,32 @@
         store = Store.of(self)
         if self.subscriber.is_team:
 
+            # We get all the people who already have active tokens for
+            # this archive (for example, through separate subscriptions).
+            auth_token = LeftJoin(
+                ArchiveAuthToken,
+                And(ArchiveAuthToken.person_id == Person.id,
+                    ArchiveAuthToken.archive_id == self.archive_id,
+                    ArchiveAuthToken.date_deactivated == None))
+
+            team_participation = Join(
+                TeamParticipation,
+                TeamParticipation.personID == Person.id)
+
+            # Only return people with preferred email address set.
+            preferred_email = Join(
+                EmailAddress, EmailAddress.personID == Person.id)
+
             # We want to get all participants who are themselves
             # individuals, not teams:
-            all_subscribers = store.find(
-                Person,
+            non_active_subscribers = store.using(
+                Person, team_participation, preferred_email, auth_token).find(
+                (Person, EmailAddress),
+                EmailAddress.status == EmailAddressStatus.PREFERRED,
                 TeamParticipation.teamID == self.subscriber_id,
-                TeamParticipation.personID == Person.id,
-                Person.teamowner == None)
-
-            # Then we get all the people who already have active
-            # tokens for this archive (for example, through separate
-            # subscriptions).
-            active_subscribers = store.find(
-                Person,
-                Person.id == ArchiveAuthToken.person_id,
-                ArchiveAuthToken.archive_id == self.archive_id,
-                ArchiveAuthToken.date_deactivated == None)
-
-            # And return just the non active subscribers:
-            non_active_subscribers = all_subscribers.difference(
-                active_subscribers)
+                Person.teamowner == None,
+                # There is no existing archive auth token.
+                ArchiveAuthToken.person_id == None)
             non_active_subscribers.order_by(Person.name)
             return non_active_subscribers
         else:
@@ -128,8 +136,12 @@
                 return EmptyResultSet()
 
             # Otherwise return a result set containing only the
-            # subscriber.
-            return store.find(Person, Person.id == self.subscriber_id)
+            # subscriber and their preferred email address.
+            return store.find(
+                (Person, EmailAddress),
+                Person.id == self.subscriber_id,
+                EmailAddress.personID == Person.id,
+                EmailAddress.status == EmailAddressStatus.PREFERRED)
 
 
 class ArchiveSubscriberSet: