launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #01044
[Merge] lp:~allenap/launchpad/bugsubscription-to-storm into lp:launchpad/devel
Gavin Panella has proposed merging lp:~allenap/launchpad/bugsubscription-to-storm into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers): code
This converts BugSubscription to inherit from Storm instead of SQLObject.
--
https://code.launchpad.net/~allenap/launchpad/bugsubscription-to-storm/+merge/35537
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/bugsubscription-to-storm into lp:launchpad/devel.
=== modified file 'lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt'
--- lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt 2009-11-06 17:15:31 +0000
+++ lib/lp/bugs/browser/tests/bug-portlet-subscribers-content.txt 2010-09-15 14:19:49 +0000
@@ -12,9 +12,9 @@
>>> bug = factory.makeBug(owner=reporter)
>>> view = BugPortletSubcribersContents(bug, None)
>>> bug.subscribe(view.user, view.user)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> bug.subscribe(view.user.teams_participated_in[0], view.user)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for subscription in view.sorted_direct_subscriptions:
... print '%s %s' % (
... subscription.person.displayname,
=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
--- lib/lp/bugs/browser/tests/bug-views.txt 2010-08-07 14:54:40 +0000
+++ lib/lp/bugs/browser/tests/bug-views.txt 2010-09-15 14:19:49 +0000
@@ -325,7 +325,7 @@
If we subscribe Foo Bar, 'Unsubscribe' is shown.
>>> bug_one.subscribe(foo_bar, foo_bar)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> bug_menu = BugContextMenu(bug_one_bugtask)
>>> bug_menu.subscription().text
'Unsubscribe'
@@ -340,7 +340,7 @@
>>> foo_bar.inTeam(launchpad_team)
True
>>> bug_one.subscribe(launchpad_team, launchpad_team)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> bug_menu = BugContextMenu(bug_one_bugtask)
>>> bug_menu.subscription().text
'Unsubscribe'
@@ -416,7 +416,7 @@
>>> ubuntu_team = getUtility(IPersonSet).getByName("ubuntu-team")
>>> bug_two.subscribe(ubuntu_team, ubuntu_team)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> bug_two.markAsDuplicate(bug_three)
>>> syncUpdate(bug_two)
=== modified file 'lib/lp/bugs/browser/tests/bugs-views.txt'
--- lib/lp/bugs/browser/tests/bugs-views.txt 2009-06-12 16:36:02 +0000
+++ lib/lp/bugs/browser/tests/bugs-views.txt 2010-09-15 14:19:49 +0000
@@ -98,7 +98,7 @@
>>> bug_two.subscribe(
... person_set.getByEmail('test@xxxxxxxxxxxxx'),
... person_set.getByEmail('foo.bar@xxxxxxxxxxxxx'))
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> login('test@xxxxxxxxxxxxx')
>>> bugs_view = MaloneView(MaloneApplication(), LaunchpadTestRequest())
>>> bugs_view.initialize()
=== modified file 'lib/lp/bugs/browser/tests/person-bug-views.txt'
--- lib/lp/bugs/browser/tests/person-bug-views.txt 2010-06-16 16:46:06 +0000
+++ lib/lp/bugs/browser/tests/person-bug-views.txt 2010-09-15 14:19:49 +0000
@@ -621,7 +621,7 @@
Once new_user has subscribed, the related milestones appear.
>>> bug.subscribe(new_user, new_user)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> print pretty(subscribed_bugs_view.getMilestoneWidgetValues())
[{'checked': False,
=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt 2010-09-02 16:33:43 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt 2010-09-15 14:19:49 +0000
@@ -858,13 +858,13 @@
... displayname='Verbose Person', email='verbose@xxxxxxxxxxx')
>>> verbose_person.verbose_bugnotifications = True
>>> bug.subscribe(verbose_person, verbose_person)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> concise_person = factory.makePerson(
... displayname='Concise Person', email='concise@xxxxxxxxxxx')
>>> concise_person.verbose_bugnotifications = False
>>> bug.subscribe(concise_person, concise_person)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
Concise Team doesn't want verbose notifications, while Concise Team
@@ -880,7 +880,7 @@
>>> ignored = concise_team.addMember(
... concise_team_person, concise_team_person)
>>> bug.subscribe(concise_team, concise_team_person)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
Verbose Team wants verbose notifications, while Verbose Team Person, a
member, does not.
@@ -895,7 +895,7 @@
>>> ignored = verbose_team.addMember(
... verbose_team_person, verbose_team_person)
>>> bug.subscribe(verbose_team, verbose_team_person)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
We'll expire all existing notifications since we're not interested in
them:
=== modified file 'lib/lp/bugs/doc/bugsubscription.txt'
--- lib/lp/bugs/doc/bugsubscription.txt 2010-09-09 21:00:54 +0000
+++ lib/lp/bugs/doc/bugsubscription.txt 2010-09-15 14:19:49 +0000
@@ -49,7 +49,7 @@
>>> personset = getUtility(IPersonSet)
>>> linux_source = ubuntu.getSourcePackage("linux-source-2.6.15")
- >>> linux_source.bug_subscriptions
+ >>> list(linux_source.bug_subscriptions)
[]
>>> print linux_source.distribution.bug_supervisor
None
@@ -78,7 +78,7 @@
>>> mark = personset.getByName("mark")
>>> linux_source_bug.subscribe(mark, mark)
- <BugSubscription ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> print_displayname(linux_source_bug.getDirectSubscribers())
Foo Bar
@@ -527,7 +527,7 @@
False
>>> bug_six.subscribe(sample_person, sample_person)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription...>
>>> bug_five.isSubscribedToDupes(sample_person)
True
@@ -781,7 +781,7 @@
contacts:
>>> evolution = ubuntu.getSourcePackage("evolution")
- >>> evolution.bug_subscriptions
+ >>> list(evolution.bug_subscriptions)
[]
>>> params = CreateBugParams(
@@ -858,7 +858,7 @@
... owner=mark)
>>> ff_bug = firefox.createBug(params)
>>> ff_bug.subscribe(lifeless, mark)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for subscription in ff_bug.getDirectSubscriptions():
... print '%s (%s)' % (
... subscription.person.displayname,
@@ -873,7 +873,7 @@
>>> dupe_ff_bug.markAsDuplicate(ff_bug)
>>> dupe_ff_bug.syncUpdate()
>>> dupe_ff_bug.subscribe(foobar, lifeless)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for subscription in ff_bug.getSubscriptionsFromDuplicates():
... print '%s (%s)' % (
... subscription.person.displayname,
=== modified file 'lib/lp/bugs/doc/bugtask-expiration.txt'
--- lib/lp/bugs/doc/bugtask-expiration.txt 2010-06-29 15:48:57 +0000
+++ lib/lp/bugs/doc/bugtask-expiration.txt 2010-09-15 14:19:49 +0000
@@ -398,7 +398,7 @@
... unless he's subscribed to the bug.
>>> private_bug.subscribe(no_priv, sample_person)
- <BugSubscription at ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> expirable_bugtasks = bugtaskset.findExpirableBugTasks(
... 0, user=no_priv, target=ubuntu)
>>> visible_bugs = set(bugtask.bug for bugtask in expirable_bugtasks)
=== modified file 'lib/lp/bugs/doc/milestones-from-bugtask-search.txt'
--- lib/lp/bugs/doc/milestones-from-bugtask-search.txt 2010-06-16 16:46:06 +0000
+++ lib/lp/bugs/doc/milestones-from-bugtask-search.txt 2010-09-15 14:19:49 +0000
@@ -17,7 +17,7 @@
>>> bugtask = factory.makeBugTask(target=milestone.product)
>>> bugtask.milestone = milestone
>>> bugtask.subscribe(person, person)
- <BugSubscription ...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> transaction.commit()
The results of a search for all bug tasks related to a person can be
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-09-09 17:02:33 +0000
+++ lib/lp/bugs/model/bug.py 2010-09-15 14:19:49 +0000
@@ -25,6 +25,7 @@
timedelta,
)
from email.Utils import make_msgid
+from itertools import groupby
import operator
import re
@@ -324,10 +325,6 @@
cve_links = SQLMultipleJoin('BugCve', joinColumn='bug', orderBy='id')
mentoring_offers = SQLMultipleJoin(
'MentoringOffer', joinColumn='bug', orderBy='id')
- # XXX: kiko 2006-09-23: Why is subscriptions ordered by ID?
- subscriptions = SQLMultipleJoin(
- 'BugSubscription', joinColumn='bug', orderBy='id',
- prejoins=["person"])
duplicates = SQLMultipleJoin(
'Bug', joinColumn='duplicateof', orderBy='id')
specifications = SQLRelatedJoin('Specification', joinColumn='bug',
@@ -658,19 +655,29 @@
"""See `IBug`."""
if person is None:
return False
-
- bs = BugSubscription.selectBy(bug=self, person=person)
- return bool(bs)
+ return not IStore(BugSubscription).find(
+ BugSubscription, bug=self, person=person).is_empty()
def isSubscribedToDupes(self, person):
"""See `IBug`."""
if person is None:
return False
+ duplicates = Select(
+ columns=[Bug.id], where=(Bug.duplicateof == self.id),
+ tables=[Bug])
+ return not IStore(BugSubscription).find(
+ BugSubscription, In(BugSubscription.bug_id, duplicates),
+ BugSubscription.person == person).is_empty()
- return bool(
- BugSubscription.select("""
- bug IN (SELECT id FROM Bug WHERE duplicateof = %d) AND
- person = %d""" % (self.id, person.id)))
+ @property
+ def subscriptions(self):
+ """The set of `BugSubscriptions` for this bug."""
+ # XXX: kiko 2006-09-23: Why is subscriptions ordered by ID?
+ results = Store.of(self).find(
+ (Person, BugSubscription),
+ BugSubscription.person_id == Person.id,
+ BugSubscription.bug_id == self.id).order_by(BugSubscription.id)
+ return DecoratedResultSet(results, operator.itemgetter(1))
def getDirectSubscriptions(self):
"""See `IBug`."""
@@ -682,14 +689,14 @@
valid_persons = Store.of(self).find(
(Person, ValidPersonCache),
Person.id == ValidPersonCache.id,
- ValidPersonCache.id == BugSubscription.personID,
+ ValidPersonCache.id == BugSubscription.person_id,
BugSubscription.bug == self)
# Suck in all the records so that they're actually cached.
list(valid_persons)
# Do the main query.
return Store.of(self).find(
BugSubscription,
- BugSubscription.personID == Person.id,
+ BugSubscription.person_id == Person.id,
BugSubscription.bug == self).order_by(
Func('person_sort_key', Person.displayname, Person.name))
@@ -730,27 +737,28 @@
"""See `IBug`."""
if self.private:
return []
-
- duplicate_subscriptions = set(
- BugSubscription.select("""
- BugSubscription.bug = Bug.id AND
- Bug.duplicateof = %d""" % self.id,
- prejoins=["person"], clauseTables=["Bug"]))
-
- # Only add a subscriber once to the list.
- duplicate_subscribers = set(
- sub.person for sub in duplicate_subscriptions)
- subscriptions = []
- for duplicate_subscriber in duplicate_subscribers:
- for duplicate_subscription in duplicate_subscriptions:
- if duplicate_subscription.person == duplicate_subscriber:
- subscriptions.append(duplicate_subscription)
- break
-
- def get_person_displayname(subscription):
- return subscription.person.displayname
-
- return sorted(subscriptions, key=get_person_displayname)
+ # Get bug subscribers and subscriptions for duplicates.
+ subscribers_and_subscriptions = (
+ IStore(BugSubscription).find(
+ (Person, BugSubscription),
+ BugSubscription.person_id == Person.id,
+ BugSubscription.bug_id == Bug.id,
+ Bug.duplicateof == self).order_by(
+ Person.id, BugSubscription.id))
+ # Generator for the subscription objects alone. The subscribers
+ # (Person objects) are loaded to warm up the cache only.
+ subscriptions = (
+ subscription for subscriber, subscription in (
+ subscribers_and_subscriptions))
+ # Group by the subscriber. The results are ordered by subscriber and
+ # subscription id, so we can grab the earliest subscription for a
+ # subscriber (and that the results of this method are stable).
+ subscriptions = (
+ list(subscriptions)[0] for person_id, subscriptions in groupby(
+ subscriptions, key=operator.attrgetter("person_id")))
+ # Sort by the subscriber's display name.
+ return sorted(
+ subscriptions, key=operator.attrgetter("person.displayname"))
def getSubscribersFromDuplicates(self, recipients=None, level=None):
"""See `IBug`.
@@ -765,10 +773,10 @@
Store.of(self).find(
(Person, Bug),
BugSubscription.person == Person.id,
- BugSubscription.bug == Bug.id,
+ BugSubscription.bug_id == Bug.id,
Bug.duplicateof == self.id))
- dupe_subscribers = set([person for person in dupe_details.keys()])
+ dupe_subscribers = set(dupe_details)
# Direct and "also notified" subscribers take precedence over
# subscribers from dupes. Note that we don't supply recipients
@@ -795,7 +803,7 @@
Bug.id == self.id,
Bug.duplicateof == self.id),
# Get subscriptions for these bugs
- BugSubscription.bug == Bug.id,
+ BugSubscription.bug_id == Bug.id,
# Filter by subscriptions to any team person is in.
# Note that teamparticipation includes self-participation entries
# (person X is in the team X)
@@ -1726,7 +1734,7 @@
subscriptions_from_dupes = store.find(
BugSubscription,
Bug.duplicateof == self,
- BugSubscription.bugID == Bug.id,
+ BugSubscription.bug_id == Bug.id,
BugSubscription.person == person)
return not subscriptions_from_dupes.is_empty()
=== modified file 'lib/lp/bugs/model/bugsubscription.py'
--- lib/lp/bugs/model/bugsubscription.py 2010-09-09 21:00:54 +0000
+++ lib/lp/bugs/model/bugsubscription.py 2010-09-15 14:19:49 +0000
@@ -6,41 +6,55 @@
__metaclass__ = type
__all__ = ['BugSubscription']
-from sqlobject import ForeignKey
+import pytz
+from storm.base import Storm
+from storm.locals import (
+ DateTime,
+ Int,
+ Reference,
+ )
from zope.interface import implements
from canonical.database.constants import UTC_NOW
-from canonical.database.datetimecol import UtcDateTimeCol
-from canonical.database.enumcol import EnumCol
-from canonical.database.sqlbase import SQLBase
+from canonical.database.enumcol import DBEnum
from lp.bugs.interfaces.bugsubscription import IBugSubscription
from lp.registry.enum import BugNotificationLevel
from lp.registry.interfaces.person import validate_person
-class BugSubscription(SQLBase):
+class BugSubscription(Storm):
"""A relationship between a person and a bug."""
implements(IBugSubscription)
- _table = 'BugSubscription'
-
- person = ForeignKey(
- dbName='person', foreignKey='Person',
- storm_validator=validate_person,
- notNull=True
- )
- bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True)
- bug_notification_level = EnumCol(
+ __storm_table__ = 'BugSubscription'
+
+ id = Int(primary=True)
+
+ person_id = Int(
+ "person", allow_none=False, validator=validate_person)
+ person = Reference(person_id, "Person.id")
+
+ bug_id = Int("bug", allow_none=False)
+ bug = Reference(bug_id, "Bug.id")
+
+ bug_notification_level = DBEnum(
enum=BugNotificationLevel,
default=BugNotificationLevel.COMMENTS,
- notNull=True)
- date_created = UtcDateTimeCol(notNull=True, default=UTC_NOW)
- subscribed_by = ForeignKey(
- dbName='subscribed_by', foreignKey='Person',
- storm_validator=validate_person,
- notNull=True
- )
+ allow_none=False)
+
+ date_created = DateTime(
+ allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
+
+ subscribed_by_id = Int(
+ "subscribed_by", allow_none=False, validator=validate_person)
+ subscribed_by = Reference(subscribed_by_id, "Person.id")
+
+ def __init__(self, bug=None, person=None, subscribed_by=None):
+ super(BugSubscription, self).__init__()
+ self.bug = bug
+ self.person = person
+ self.subscribed_by = subscribed_by
@property
def display_subscribed_by(self):
@@ -54,9 +68,9 @@
def display_duplicate_subscribed_by(self):
"""See `IBugSubscription`."""
if self.person == self.subscribed_by:
- return u'Self-subscribed to bug %s' % (self.bugID)
+ return u'Self-subscribed to bug %s' % (self.bug_id)
else:
- return u'Subscribed to bug %s by %s' % (self.bugID,
+ return u'Subscribed to bug %s by %s' % (self.bug_id,
self.subscribed_by.displayname)
def canBeUnsubscribedByUser(self, user):
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2010-09-09 17:02:33 +0000
+++ lib/lp/bugs/model/bugtask.py 2010-09-15 14:19:49 +0000
@@ -2155,8 +2155,8 @@
tables.append(BugAffectsPerson)
if params.hardware_owner_is_subscribed_to_bug:
bug_link_clauses.append(
- And(BugSubscription.personID == HWSubmission.ownerID,
- BugSubscription.bugID == Bug.id))
+ And(BugSubscription.person_id == HWSubmission.ownerID,
+ BugSubscription.bug_id == Bug.id))
tables.append(BugSubscription)
if params.hardware_is_linked_to_bug:
bug_link_clauses.append(
=== modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt'
--- lib/lp/bugs/tests/bugs-emailinterface.txt 2010-08-27 13:15:43 +0000
+++ lib/lp/bugs/tests/bugs-emailinterface.txt 2010-09-15 14:19:49 +0000
@@ -772,7 +772,7 @@
[u'Sample Person', u'Ubuntu Team']
>>> bug_six.subscribe(no_priv, no_priv)
- <BugSubscription...>
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> sorted([subscriber.displayname
... for subscriber in bug_five.getIndirectSubscribers()])
=== modified file 'lib/lp/hardwaredb/doc/hwdb-device-tables.txt'
--- lib/lp/hardwaredb/doc/hwdb-device-tables.txt 2010-02-02 17:12:29 +0000
+++ lib/lp/hardwaredb/doc/hwdb-device-tables.txt 2010-09-15 14:19:49 +0000
@@ -2310,7 +2310,7 @@
for bug subscribers who own a given device.
>>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
- <BugSubscription at ...
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
... bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS ',
... bug_ids=[1], subscribed_to_bug=True):
@@ -2420,7 +2420,7 @@
>>> LaunchpadZopelessLayer.switchDbUser('launchpad')
>>> no_priv = getUtility(IPersonSet).getByEmail('no-priv@xxxxxxxxxxxxx')
>>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
- <BugSubscription at ...
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
... bus=HWBus.IDE, vendor_id='SEAGATE', product_id='ST3250820NS ',
... bug_ids=[1], subscribed_to_bug=True, user=no_priv):
@@ -2474,7 +2474,7 @@
>>> bug_one.markUserAffected(foo_bar, False)
>>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
- <BugSubscription at ...
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for person in submission_set.deviceDriverOwnersAffectedByBugs(
... driver_name='sd', bug_ids=[1], subscribed_to_bug=True):
... print person.displayname
@@ -2664,7 +2664,7 @@
We can also check for when the user is subscribed to the bug.
>>> bug_one.subscribe(foo_bar, subscribed_by=foo_bar)
- <BugSubscription ...
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for entry in submission_set.hwInfoByBugRelatedUsers(
... bug_ids=[1], subscribed_to_bug=True):
@@ -2688,7 +2688,7 @@
The owner of a private submission can see his or her submission.
>>> bug_one.subscribe(no_priv, subscribed_by=no_priv)
- <BugSubscription ...
+ <lp.bugs.model.bugsubscription.BugSubscription ...>
>>> for entry in submission_set.hwInfoByBugRelatedUsers(
... bug_ids=[1], subscribed_to_bug=True, user=no_priv):
=== modified file 'lib/lp/hardwaredb/model/hwdb.py'
--- lib/lp/hardwaredb/model/hwdb.py 2010-08-23 09:10:10 +0000
+++ lib/lp/hardwaredb/model/hwdb.py 2010-09-15 14:19:49 +0000
@@ -467,7 +467,7 @@
if subscribed_to_bug:
subscriber_clauses = [
- BugSubscription.personID == HWSubmission.ownerID,
+ BugSubscription.person_id == HWSubmission.ownerID,
BugSubscription.bug == Bug.id,
]
subscriber_query = Select(
@@ -531,7 +531,7 @@
person_clauses = [Bug.ownerID == HWSubmission.ownerID]
if subscribed_to_bug:
person_clauses.append(
- And(BugSubscription.personID == HWSubmission.ownerID,
+ And(BugSubscription.person_id == HWSubmission.ownerID,
BugSubscription.bug == Bug.id))
tables.append(BugSubscription)
if affected_by_bug:
Follow ups