launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #29558
[Merge] ~cjwatson/launchpad:stormify-bugtracker into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-bugtracker into launchpad:master.
Commit message:
Convert BugTracker to Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/435977
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-bugtracker into launchpad:master.
diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
index 88f793d..18d6d7d 100644
--- a/lib/lp/bugs/model/bug.py
+++ b/lib/lp/bugs/model/bug.py
@@ -402,7 +402,7 @@ class Bug(SQLBase, InformationTypeMixin):
"id", BugMessage.bug_id, order_by=BugMessage.index
)
watches = SQLMultipleJoin(
- "BugWatch", joinColumn="bug", orderBy=["bugtracker", "remotebug"]
+ "BugWatch", joinColumn="bug", orderBy=["bugtracker_id", "remotebug"]
)
duplicates = SQLMultipleJoin("Bug", joinColumn="duplicateof", orderBy="id")
linked_bugbranches = ReferenceSet(
diff --git a/lib/lp/bugs/model/bugtracker.py b/lib/lp/bugs/model/bugtracker.py
index 8a8a94e..f589dc9 100644
--- a/lib/lp/bugs/model/bugtracker.py
+++ b/lib/lp/bugs/model/bugtracker.py
@@ -12,12 +12,12 @@ __all__ = [
from datetime import datetime
from itertools import chain
+from operator import itemgetter
from urllib.parse import quote, urlsplit, urlunsplit
-import six
from lazr.uri import URI
from pytz import timezone
-from storm.expr import Count, Desc, Not
+from storm.expr import Count, Desc, Not, Or
from storm.locals import SQL, Bool, Int, Reference, ReferenceSet, Unicode
from storm.store import Store
from zope.component import getUtility
@@ -45,17 +45,10 @@ from lp.bugs.model.bugwatch import BugWatch
from lp.registry.interfaces.person import IPersonSet, validate_public_person
from lp.registry.model.product import Product, ProductSet
from lp.registry.model.projectgroup import ProjectGroup
+from lp.services.database.decoratedresultset import DecoratedResultSet
from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import SQLBase, flush_database_updates
-from lp.services.database.sqlobject import (
- OR,
- BoolCol,
- ForeignKey,
- SQLMultipleJoin,
- SQLObjectNotFound,
- StringCol,
-)
+from lp.services.database.sqlbase import flush_database_updates
from lp.services.database.stormbase import StormBase
from lp.services.helpers import shortlist
@@ -286,7 +279,7 @@ class BugTrackerComponentGroup(StormBase):
@implementer(IBugTracker)
-class BugTracker(SQLBase):
+class BugTracker(StormBase):
"""A class to access the BugTracker table in the database.
Each BugTracker is a distinct instance of that bug tracking
@@ -295,35 +288,60 @@ class BugTracker(SQLBase):
distinct BugTrackers.
"""
- _table = "BugTracker"
+ __storm_table__ = "BugTracker"
+ id = Int(primary=True)
bugtrackertype = DBEnum(
name="bugtrackertype", enum=BugTrackerType, allow_none=False
)
- name = StringCol(notNull=True, unique=True)
- title = StringCol(notNull=True)
- summary = StringCol(notNull=False)
- baseurl = StringCol(notNull=True)
+ name = Unicode(name="name", allow_none=False)
+ title = Unicode(name="title", allow_none=False)
+ summary = Unicode(name="summary", allow_none=True)
+ baseurl = Unicode(name="baseurl", allow_none=False)
active = Bool(name="active", allow_none=False, default=True)
- owner = ForeignKey(
- dbName="owner",
- foreignKey="Person",
- storm_validator=validate_public_person,
- notNull=True,
- )
- contactdetails = StringCol(notNull=False)
- has_lp_plugin = BoolCol(notNull=False, default=False)
- products = SQLMultipleJoin(
- "Product", joinColumn="bugtracker", orderBy="name"
+ owner_id = Int(
+ name="owner", validator=validate_public_person, allow_none=False
)
- watches = SQLMultipleJoin(
- "BugWatch",
- joinColumn="bugtracker",
- orderBy="-datecreated",
- prejoins=["bug"],
+ owner = Reference(owner_id, "Person.id")
+ contactdetails = Unicode(name="contactdetails", allow_none=True)
+ has_lp_plugin = Bool(name="has_lp_plugin", allow_none=True, default=False)
+ products = ReferenceSet(
+ "id", "Product.bugtracker_id", order_by="Product.name"
)
+ @property
+ def watches(self):
+ return DecoratedResultSet(
+ IStore(BugWatch)
+ .find(
+ (BugWatch, Bug),
+ BugWatch.bugtracker == self,
+ BugWatch.bugID == Bug.id,
+ )
+ .order_by(Desc(BugWatch.datecreated)),
+ result_decorator=itemgetter(0),
+ )
+
+ def __init__(
+ self,
+ bugtrackertype,
+ name,
+ title,
+ baseurl,
+ owner,
+ summary=None,
+ contactdetails=None,
+ ):
+ super().__init__()
+ self.bugtrackertype = bugtrackertype
+ self.name = name
+ self.title = title
+ self.baseurl = baseurl
+ self.owner = owner
+ self.summary = summary
+ self.contactdetails = contactdetails
+
_filing_url_patterns = {
BugTrackerType.BUGZILLA: (
"%(base_url)s/enter_bug.cgi?product=%(remote_product)s"
@@ -548,7 +566,7 @@ class BugTracker(SQLBase):
.find(
Bug,
BugWatch.bugID == Bug.id,
- BugWatch.bugtrackerID == self.id,
+ BugWatch.bugtracker == self,
BugWatch.remotebug == remotebug,
)
.config(distinct=True)
@@ -590,9 +608,7 @@ class BugTracker(SQLBase):
# Join to return a list of BugTrackerAliases relating to this
# BugTracker.
- _bugtracker_aliases = ReferenceSet(
- "<primary key>", "BugTrackerAlias.bugtracker_id"
- )
+ _bugtracker_aliases = ReferenceSet("id", "BugTrackerAlias.bugtracker_id")
def _get_aliases(self):
"""See `IBugTracker.aliases`."""
@@ -646,7 +662,7 @@ class BugTracker(SQLBase):
.find(
BugMessage,
BugMessage.bugwatch_id == BugWatch.id,
- BugWatch.bugtrackerID == self.id,
+ BugWatch.bugtracker == self,
)
.order_by(BugMessage.id)
)
@@ -807,7 +823,7 @@ class BugTracker(SQLBase):
IStore(Product)
.find(
Product,
- Product.bugtrackerID == self.id,
+ Product.bugtracker == self,
Product.active == True,
ProductSet.getProductPrivacyFilter(user),
)
@@ -817,13 +833,16 @@ class BugTracker(SQLBase):
IStore(ProjectGroup)
.find(
ProjectGroup,
- ProjectGroup.bugtrackerID == self.id,
+ ProjectGroup.bugtracker == self,
ProjectGroup.active == True,
)
.order_by(ProjectGroup.name)
)
return groups, products
+ def destroySelf(self):
+ IStore(self).remove(self)
+
@implementer(IBugTrackerSet)
class BugTrackerSet:
@@ -831,53 +850,52 @@ class BugTrackerSet:
either the full set in the db, or a subset.
"""
- table = BugTracker
-
def __init__(self):
self.title = "Bug trackers registered in Launchpad"
def get(self, bugtracker_id, default=None):
"""See `IBugTrackerSet`."""
- try:
- return BugTracker.get(bugtracker_id)
- except SQLObjectNotFound:
+ bugtracker = IStore(BugTracker).get(BugTracker, bugtracker_id)
+ if bugtracker is None:
return default
+ return bugtracker
def getByName(self, name, default=None):
"""See `IBugTrackerSet`."""
- return self.table.selectOne(self.table.q.name == name)
+ return IStore(BugTracker).find(BugTracker, name=name).one()
def __getitem__(self, name):
- item = self.table.selectOne(self.table.q.name == name)
+ item = IStore(BugTracker).find(BugTracker, name=name).one()
if item is None:
raise NotFoundError(name)
else:
return item
def __iter__(self):
- yield from self.table.select(orderBy="title")
+ yield from IStore(BugTracker).find(BugTracker).order_by("title")
def queryByBaseURL(self, baseurl):
"""See `IBugTrackerSet`."""
# All permutations we'll search for.
permutations = base_url_permutations(baseurl)
# Construct the search. All the important parts in the next
- # expression are lazily evaluated. SQLObject queries do not
+ # expression are lazily evaluated. Storm queries do not
# execute any SQL until results are pulled, so the first query
# to return a match will be the last query executed.
matching_bugtrackers = chain(
# Search for any permutation in BugTracker.
- BugTracker.select(
- OR(*(BugTracker.q.baseurl == url for url in permutations))
+ IStore(BugTracker).find(
+ BugTracker,
+ Or(*(BugTracker.baseurl == url for url in permutations)),
),
# Search for any permutation in BugTrackerAlias.
(
alias.bugtracker
for alias in IStore(BugTrackerAlias).find(
BugTrackerAlias,
- OR(
+ Or(
*(
- BugTrackerAlias.base_url == six.ensure_text(url)
+ BugTrackerAlias.base_url == url
for url in permutations
)
),
@@ -891,7 +909,7 @@ class BugTrackerSet:
def search(self):
"""See `IBugTrackerSet`."""
- return BugTracker.select()
+ return IStore(BugTracker).find(BugTracker)
def getAllTrackers(self, active=None):
if active is not None:
@@ -945,17 +963,17 @@ class BugTrackerSet:
@property
def count(self):
- return IStore(self.table).find(self.table).count()
+ return IStore(BugTracker).find(BugTracker).count()
@property
def names(self):
- return IStore(self.table).find(self.table).values(self.table.name)
+ return IStore(BugTracker).find(BugTracker).values(BugTracker.name)
def getMostActiveBugTrackers(self, limit=None):
"""See `IBugTrackerSet`."""
return (
IStore(BugTracker)
- .find(BugTracker, BugTracker.id == BugWatch.bugtrackerID)
+ .find(BugTracker, BugTracker.id == BugWatch.bugtracker_id)
.group_by(BugTracker)
.order_by(Desc(Count(BugWatch)))
.config(limit=limit)
@@ -968,7 +986,7 @@ class BugTrackerSet:
IStore(Product)
.find(
Product,
- Product.bugtrackerID.is_in(ids),
+ Product.bugtracker_id.is_in(ids),
Product.active == True,
ProductSet.getProductPrivacyFilter(user),
)
@@ -978,7 +996,7 @@ class BugTrackerSet:
IStore(ProjectGroup)
.find(
ProjectGroup,
- ProjectGroup.bugtrackerID.is_in(ids),
+ ProjectGroup.bugtracker_id.is_in(ids),
ProjectGroup.active == True,
)
.order_by(ProjectGroup.name)
diff --git a/lib/lp/bugs/model/bugwatch.py b/lib/lp/bugs/model/bugwatch.py
index 2bbef55..c617a24 100644
--- a/lib/lp/bugs/model/bugwatch.py
+++ b/lib/lp/bugs/model/bugwatch.py
@@ -105,9 +105,8 @@ class BugWatch(SQLBase):
_table = "BugWatch"
bug = ForeignKey(dbName="bug", foreignKey="Bug", notNull=True)
- bugtracker = ForeignKey(
- dbName="bugtracker", foreignKey="BugTracker", notNull=True
- )
+ bugtracker_id = Int(name="bugtracker", allow_none=False)
+ bugtracker = Reference(bugtracker_id, "BugTracker.id")
remotebug = StringCol(notNull=True)
remotestatus = StringCol(notNull=False, default=None)
remote_importance = StringCol(notNull=False, default=None)
diff --git a/lib/lp/bugs/stories/webservice/xx-bug.rst b/lib/lp/bugs/stories/webservice/xx-bug.rst
index 5423f01..cbd273f 100644
--- a/lib/lp/bugs/stories/webservice/xx-bug.rst
+++ b/lib/lp/bugs/stories/webservice/xx-bug.rst
@@ -1510,7 +1510,7 @@ Non-admins can't disable a bugtracker through the API.
... )
HTTP/1.1 401 Unauthorized
...
- (<BugTracker at ...>, 'active', 'launchpad.Admin')
+ (<...BugTracker object at ...>, 'active', 'launchpad.Admin')
Admins can, however.
diff --git a/lib/lp/bugs/templates/bugtracker-index.pt b/lib/lp/bugs/templates/bugtracker-index.pt
index 5320747..b47fcfd 100644
--- a/lib/lp/bugs/templates/bugtracker-index.pt
+++ b/lib/lp/bugs/templates/bugtracker-index.pt
@@ -44,7 +44,7 @@
<div tal:replace="structure context/@@+portlet-components" />
</div>
<div class="yui-u"
- tal:condition="context/watches">
+ tal:condition="not: context/watches/is_empty">
<div tal:replace="structure context/@@+portlet-watches" />
</div>
</div>
diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py
index ed5f2f6..10a96f9 100644
--- a/lib/lp/bugs/vocabularies.py
+++ b/lib/lp/bugs/vocabularies.py
@@ -48,7 +48,7 @@ from lp.registry.model.milestone import milestone_sort_key
from lp.registry.model.productseries import ProductSeries
from lp.registry.vocabularies import DistributionVocabulary
from lp.services.database.interfaces import IStore
-from lp.services.database.sqlobject import CONTAINSSTRING, OR
+from lp.services.database.sqlobject import OR
from lp.services.helpers import shortlist
from lp.services.webapp.escaping import html_escape, structured
from lp.services.webapp.interfaces import ILaunchBag
@@ -57,6 +57,7 @@ from lp.services.webapp.vocabulary import (
IHugeVocabulary,
NamedSQLObjectVocabulary,
SQLObjectVocabularyBase,
+ StormVocabularyBase,
)
@@ -91,14 +92,13 @@ class BugVocabulary(SQLObjectVocabularyBase):
@implementer(IHugeVocabulary)
-class BugTrackerVocabulary(SQLObjectVocabularyBase):
+class BugTrackerVocabulary(StormVocabularyBase):
"""All web and email based external bug trackers."""
displayname = "Select a bug tracker"
step_title = "Search"
_table = BugTracker
_filter = True
- _orderBy = "title"
_order_by = [BugTracker.title]
def toTerm(self, obj):
@@ -125,10 +125,10 @@ class BugTrackerVocabulary(SQLObjectVocabularyBase):
self._filter,
BugTracker.active == True,
Or(
- CONTAINSSTRING(BugTracker.name, query),
- CONTAINSSTRING(BugTracker.title, query),
- CONTAINSSTRING(BugTracker.summary, query),
- CONTAINSSTRING(BugTracker.baseurl, query),
+ BugTracker.name.contains_string(query),
+ BugTracker.title.contains_string(query),
+ BugTracker.summary.contains_string(query),
+ BugTracker.baseurl.contains_string(query),
),
),
)
diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py
index 8a941d8..12f1035 100644
--- a/lib/lp/registry/model/product.py
+++ b/lib/lp/registry/model/product.py
@@ -348,12 +348,8 @@ class Product(
notNull=False,
default=None,
)
- bugtracker = ForeignKey(
- foreignKey="BugTracker",
- dbName="bugtracker",
- notNull=False,
- default=None,
- )
+ bugtracker_id = Int(name="bugtracker", allow_none=True, default=None)
+ bugtracker = Reference(bugtracker_id, "BugTracker.id")
official_answers = BoolCol(
dbName="official_answers", notNull=True, default=False
)
diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py
index 135c433..46cd5cb 100644
--- a/lib/lp/registry/model/projectgroup.py
+++ b/lib/lp/registry/model/projectgroup.py
@@ -164,12 +164,8 @@ class ProjectGroup(
)
active = BoolCol(dbName="active", notNull=True, default=True)
reviewed = BoolCol(dbName="reviewed", notNull=True, default=False)
- bugtracker = ForeignKey(
- foreignKey="BugTracker",
- dbName="bugtracker",
- notNull=False,
- default=None,
- )
+ bugtracker_id = Int(name="bugtracker", allow_none=True, default=None)
+ bugtracker = Reference(bugtracker_id, "BugTracker.id")
bug_reporting_guidelines = StringCol(default=None)
bug_reported_acknowledgement = StringCol(default=None)