← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-milestone into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-milestone into launchpad:master.

Commit message:
Convert Milestone to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/448474
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-milestone into launchpad:master.
diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
index 2606bed..c8cc080 100644
--- a/lib/lp/bugs/model/bugtask.py
+++ b/lib/lp/bugs/model/bugtask.py
@@ -2324,10 +2324,10 @@ class BugTaskSet:
             Milestone,
             Milestone.active == True,
             Or(
-                Milestone.distributionID.is_in(distro_ids),
-                Milestone.distroseriesID.is_in(distro_series_ids),
-                Milestone.productID.is_in(product_ids),
-                Milestone.productseriesID.is_in(product_series_ids),
+                Milestone.distribution_id.is_in(distro_ids),
+                Milestone.distroseries_id.is_in(distro_series_ids),
+                Milestone.product_id.is_in(product_ids),
+                Milestone.productseries_id.is_in(product_series_ids),
             ),
         )
 
diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py
index 9a49e53..f7a0fe1 100644
--- a/lib/lp/bugs/model/bugtasksearch.py
+++ b/lib/lp/bugs/model/bugtasksearch.py
@@ -379,7 +379,7 @@ def _build_query(params):
                         tables=[Milestone, Product],
                         where=And(
                             Product.projectgroup == params.milestone.target,
-                            Milestone.productID == Product.id,
+                            Milestone.product_id == Product.id,
                             Milestone.name == params.milestone.name,
                             ProductSet.getProductPrivacyFilter(params.user),
                         ),
@@ -406,7 +406,7 @@ def _build_query(params):
                     tables=[Milestone, Product, MilestoneTag],
                     where=And(
                         Product.projectgroup == params.milestone_tag.target,
-                        Milestone.productID == Product.id,
+                        Milestone.product_id == Product.id,
                         Milestone.id == MilestoneTag.milestone_id,
                         MilestoneTag.tag.is_in(params.milestone_tag.tags),
                     ),
diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py
index 5586ef3..ecf92ef 100644
--- a/lib/lp/bugs/vocabularies.py
+++ b/lib/lp/bugs/vocabularies.py
@@ -332,17 +332,17 @@ def milestone_matches_bugtask(milestone, bugtask):
     naked_milestone = removeSecurityProxy(milestone)
 
     if IProduct.providedBy(bug_target):
-        return bugtask.product.id == naked_milestone.productID
+        return bugtask.product == naked_milestone.product
     elif IProductSeries.providedBy(bug_target):
-        return bugtask.productseries.product.id == naked_milestone.productID
+        return bugtask.productseries.product == naked_milestone.product
     elif IDistribution.providedBy(
         bug_target
     ) or IDistributionSourcePackage.providedBy(bug_target):
-        return bugtask.distribution.id == naked_milestone.distributionID
+        return bugtask.distribution == naked_milestone.distribution
     elif IDistroSeries.providedBy(bug_target) or ISourcePackage.providedBy(
         bug_target
     ):
-        return bugtask.distroseries.id == naked_milestone.distroseriesID
+        return bugtask.distroseries == naked_milestone.distroseries
     return False
 
 
diff --git a/lib/lp/registry/model/milestone.py b/lib/lp/registry/model/milestone.py
index e86bb1c..55f396d 100644
--- a/lib/lp/registry/model/milestone.py
+++ b/lib/lp/registry/model/milestone.py
@@ -18,7 +18,7 @@ from operator import itemgetter
 
 from lazr.restful.declarations import error_status
 from storm.expr import And, Desc, LeftJoin, Select, Union
-from storm.locals import Store
+from storm.locals import Bool, Date, Int, Reference, Store, Unicode
 from storm.zope import IResultSet
 from zope.component import getUtility
 from zope.interface import implementer
@@ -43,14 +43,7 @@ from lp.registry.interfaces.milestone import (
 from lp.registry.model.productrelease import ProductRelease
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import SQLBase
-from lp.services.database.sqlobject import (
-    AND,
-    BoolCol,
-    DateCol,
-    ForeignKey,
-    StringCol,
-)
+from lp.services.database.stormbase import StormBase
 from lp.services.propertycache import get_property_cache
 from lp.services.webapp.sorting import expand_numbers
 
@@ -107,7 +100,8 @@ class HasMilestonesMixin:
         store = Store.of(self)
         result = store.find(
             Milestone,
-            And(self._getMilestoneCondition(), Milestone.active == True),
+            self._getMilestoneCondition(),
+            Milestone.active == True,
         )
         return result.order_by(self._milestone_order)
 
@@ -223,34 +217,59 @@ class MilestoneData:
 
 @implementer(IHasBugs, IMilestone, IBugSummaryDimension)
 class Milestone(
-    SQLBase, MilestoneData, StructuralSubscriptionTargetMixin, HasBugsBase
+    StormBase, MilestoneData, StructuralSubscriptionTargetMixin, HasBugsBase
 ):
-    active = BoolCol(notNull=True, default=True)
+    __storm_table__ = "Milestone"
+
+    id = Int(primary=True)
+
+    active = Bool(allow_none=False, default=True)
 
     # XXX: EdwinGrubbs 2009-02-06 bug=326384:
     # The Milestone.dateexpected should be changed into a date column,
     # since the class defines the field as a DateCol, so that a list of
     # milestones can't have some dateexpected attributes that are
     # datetimes and others that are dates, which can't be compared.
-    dateexpected = DateCol(notNull=False, default=None)
+    dateexpected = Date(allow_none=True, default=None)
 
     # XXX: Guilherme Salgado 2007-03-27 bug=40978:
     # Milestones should be associated with productseries/distroseries
     # so these columns are not needed.
-    product = ForeignKey(dbName="product", foreignKey="Product", default=None)
-    distribution = ForeignKey(
-        dbName="distribution", foreignKey="Distribution", default=None
-    )
-
-    productseries = ForeignKey(
-        dbName="productseries", foreignKey="ProductSeries", default=None
-    )
-    distroseries = ForeignKey(
-        dbName="distroseries", foreignKey="DistroSeries", default=None
-    )
-    name = StringCol(notNull=True)
-    summary = StringCol(notNull=False, default=None)
-    code_name = StringCol(dbName="codename", notNull=False, default=None)
+    product_id = Int(name="product", default=None)
+    product = Reference(product_id, "Product.id")
+    distribution_id = Int(name="distribution", default=None)
+    distribution = Reference(distribution_id, "Distribution.id")
+
+    productseries_id = Int(name="productseries", default=None)
+    productseries = Reference(productseries_id, "ProductSeries.id")
+    distroseries_id = Int(name="distroseries", default=None)
+    distroseries = Reference(distroseries_id, "DistroSeries.id")
+    name = Unicode(allow_none=False)
+    summary = Unicode(allow_none=True, default=None)
+    code_name = Unicode(name="codename", allow_none=True, default=None)
+
+    def __init__(
+        self,
+        name,
+        active=True,
+        dateexpected=None,
+        product=None,
+        distribution=None,
+        productseries=None,
+        distroseries=None,
+        summary=None,
+        code_name=None,
+    ):
+        super().__init__()
+        self.name = name
+        self.active = active
+        self.dateexpected = dateexpected
+        self.product = product
+        self.distribution = distribution
+        self.productseries = productseries
+        self.distroseries = distroseries
+        self.summary = summary
+        self.code_name = code_name
 
     def _milestone_ids_expr(self, user):
         return (self.id,)
@@ -346,7 +365,7 @@ class Milestone(
             "You cannot delete a milestone which has a product release "
             "associated with it."
         )
-        super().destroySelf()
+        Store.of(self).remove(self)
 
     def getBugSummaryContextWhereClause(self):
         """See BugTargetBase."""
@@ -415,7 +434,7 @@ class Milestone(
 class MilestoneSet:
     def __iter__(self):
         """See lp.registry.interfaces.milestone.IMilestoneSet."""
-        yield from Milestone.select()
+        yield from IStore(Milestone).find(Milestone)
 
     def get(self, milestoneid):
         """See lp.registry.interfaces.milestone.IMilestoneSet."""
@@ -434,28 +453,31 @@ class MilestoneSet:
 
     def getByNameAndProduct(self, name, product, default=None):
         """See lp.registry.interfaces.milestone.IMilestoneSet."""
-        query = AND(
-            Milestone.q.name == name, Milestone.q.productID == product.id
+        milestone = (
+            IStore(Milestone).find(Milestone, name=name, product=product).one()
         )
-        milestone = Milestone.selectOne(query)
         if milestone is None:
             return default
         return milestone
 
     def getByNameAndDistribution(self, name, distribution, default=None):
         """See lp.registry.interfaces.milestone.IMilestoneSet."""
-        query = AND(
-            Milestone.q.name == name,
-            Milestone.q.distributionID == distribution.id,
+        milestone = (
+            IStore(Milestone)
+            .find(Milestone, name=name, distribution=distribution)
+            .one()
         )
-        milestone = Milestone.selectOne(query)
         if milestone is None:
             return default
         return milestone
 
     def getVisibleMilestones(self):
         """See lp.registry.interfaces.milestone.IMilestoneSet."""
-        return Milestone.selectBy(active=True, orderBy="id")
+        return (
+            IStore(Milestone)
+            .find(Milestone, active=True)
+            .order_by(Milestone.id)
+        )
 
 
 @implementer(IProjectGroupMilestone)
@@ -498,7 +520,7 @@ class ProjectMilestone(MilestoneData, HasBugsBase):
             tables=[Milestone, Product],
             where=And(
                 Milestone.name == self.name,
-                Milestone.productID == Product.id,
+                Milestone.product_id == Product.id,
                 Product.projectgroup == self.target,
                 ProductSet.getProductPrivacyFilter(user),
             ),
diff --git a/lib/lp/registry/model/milestonetag.py b/lib/lp/registry/model/milestonetag.py
index bd23e71..984a5b4 100644
--- a/lib/lp/registry/model/milestonetag.py
+++ b/lib/lp/registry/model/milestonetag.py
@@ -85,7 +85,7 @@ class ProjectGroupMilestoneTag(MilestoneData):
             Milestone.id,
             tables=[Milestone, Product],
             where=And(
-                Milestone.productID == Product.id,
+                Milestone.product_id == Product.id,
                 Product.projectgroup == self.target,
                 tag_constraints,
             ),
diff --git a/lib/lp/registry/model/productrelease.py b/lib/lp/registry/model/productrelease.py
index a73d29a..8a87785 100644
--- a/lib/lp/registry/model/productrelease.py
+++ b/lib/lp/registry/model/productrelease.py
@@ -344,7 +344,7 @@ class ProductReleaseSet:
             .find(
                 ProductRelease,
                 And(ProductRelease.milestone == Milestone.id),
-                Milestone.productseriesID.is_in(series_ids),
+                Milestone.productseries_id.is_in(series_ids),
             )
             .order_by(Desc(ProductRelease.datereleased))
         )
diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py
index 428c718..dfc4609 100644
--- a/lib/lp/registry/model/projectgroup.py
+++ b/lib/lp/registry/model/projectgroup.py
@@ -10,7 +10,7 @@ __all__ = [
 ]
 
 import six
-from storm.expr import SQL, And, In, Join
+from storm.expr import And, Desc, Func, In, Is, Join, Min
 from storm.locals import Int, Reference
 from storm.store import Store
 from zope.component import getUtility
@@ -418,7 +418,7 @@ class ProjectGroup(
         user = getUtility(ILaunchBag).user
         privacy_filter = ProductSet.getProductPrivacyFilter(user)
         return And(
-            Milestone.productID == Product.id,
+            Milestone.product_id == Product.id,
             Product.projectgroupID == self.id,
             privacy_filter,
         )
@@ -436,8 +436,8 @@ class ProjectGroup(
 
         columns = (
             Milestone.name,
-            SQL("MIN(Milestone.dateexpected)"),
-            SQL("BOOL_OR(Milestone.active)"),
+            Min(Milestone.dateexpected),
+            Func("bool_or", Milestone.active),
         )
         privacy_filter = ProductSet.getProductPrivacyFilter(user)
         conditions = And(
@@ -449,12 +449,17 @@ class ProjectGroup(
         result = store.find(columns, conditions)
         result.group_by(Milestone.name)
         if only_active:
-            result.having("BOOL_OR(Milestone.active) = TRUE")
-        # MIN(Milestone.dateexpected) has to be used to match the
+            result.having(Is(Func("bool_or", Milestone.active), True))
+        # Min(Milestone.dateexpected) has to be used to match the
         # aggregate function in the `columns` variable.
         result.order_by(
-            "milestone_sort_key(MIN(Milestone.dateexpected), Milestone.name) "
-            "DESC"
+            Desc(
+                Func(
+                    "milestone_sort_key",
+                    Min(Milestone.dateexpected),
+                    Milestone.name,
+                )
+            )
         )
         # An extra query is required here in order to get the correct
         # products without affecting the group/order of the query above.
diff --git a/lib/lp/registry/scripts/productreleasefinder/finder.py b/lib/lp/registry/scripts/productreleasefinder/finder.py
index 107a570..2271655 100644
--- a/lib/lp/registry/scripts/productreleasefinder/finder.py
+++ b/lib/lp/registry/scripts/productreleasefinder/finder.py
@@ -171,7 +171,7 @@ class ProductReleaseFinder:
             LibraryFileAlias.filename,
             Product.name == product_name,
             Product.id == ProductSeries.productID,
-            Milestone.productseriesID == ProductSeries.id,
+            Milestone.productseries_id == ProductSeries.id,
             ProductRelease.milestone_id == Milestone.id,
             ProductReleaseFile.productrelease_id == ProductRelease.id,
             LibraryFileAlias.id == ProductReleaseFile.libraryfile_id,
diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py
index 9add187..566e9f7 100644
--- a/lib/lp/registry/vocabularies.py
+++ b/lib/lp/registry/vocabularies.py
@@ -1212,7 +1212,7 @@ class ProductReleaseVocabulary(StormVocabularyBase):
     _order_by = [Product.name, ProductSeries.name, Milestone.name]
     _clauses = [
         ProductRelease.milestone_id == Milestone.id,
-        Milestone.productseriesID == ProductSeries.id,
+        Milestone.productseries_id == ProductSeries.id,
         ProductSeries.productID == Product.id,
     ]
 
@@ -1248,7 +1248,7 @@ class ProductReleaseVocabulary(StormVocabularyBase):
             .find(
                 ProductRelease,
                 ProductRelease.milestone_id == Milestone.id,
-                Milestone.productseriesID == ProductSeries.id,
+                Milestone.productseries_id == ProductSeries.id,
                 ProductSeries.productID == Product.id,
                 Product.name == productname,
                 ProductSeries.name == productseriesname,
@@ -1271,7 +1271,7 @@ class ProductReleaseVocabulary(StormVocabularyBase):
             .find(
                 self._table,
                 ProductRelease.milestone_id == Milestone.id,
-                Milestone.productseriesID == ProductSeries.id,
+                Milestone.productseries_id == ProductSeries.id,
                 ProductSeries.productID == Product.id,
                 Or(
                     Product.name.contains_string(query),
@@ -1393,11 +1393,11 @@ class FilteredProductSeriesVocabulary(SQLObjectVocabularyBase):
                 yield self.toTerm(series)
 
 
-class MilestoneVocabulary(SQLObjectVocabularyBase):
+class MilestoneVocabulary(StormVocabularyBase):
     """The milestones for a target."""
 
     _table = Milestone
-    _orderBy = None
+    _order_by = None
 
     def toTerm(self, obj):
         """See `IVocabulary`."""
@@ -1475,19 +1475,23 @@ class MilestoneVocabulary(SQLObjectVocabularyBase):
         # Prefetch products and distributions for rendering
         # milestones: optimization to reduce the number of queries.
         product_ids = {
-            removeSecurityProxy(milestone).productID
+            removeSecurityProxy(milestone).product_id
             for milestone in milestones
         }
         product_ids.discard(None)
         distro_ids = {
-            removeSecurityProxy(milestone).distributionID
+            removeSecurityProxy(milestone).distribution_id
             for milestone in milestones
         }
         distro_ids.discard(None)
         if len(product_ids) > 0:
-            list(Product.select("id IN %s" % sqlvalues(product_ids)))
+            list(IStore(Product).find(Product, Product.id.is_in(product_ids)))
         if len(distro_ids) > 0:
-            list(Distribution.select("id IN %s" % sqlvalues(distro_ids)))
+            list(
+                IStore(Distribution).find(
+                    Distribution, Distribution.id.is_in(distro_ids)
+                )
+            )
 
         return sorted(milestones, key=attrgetter("displayname"))
 
@@ -1507,7 +1511,7 @@ class MilestoneVocabulary(SQLObjectVocabularyBase):
             # so we special-case them here just for that purpose.
             return obj.target.getMilestone(obj.name)
         else:
-            return SQLObjectVocabularyBase.__contains__(self, obj)
+            return super().__contains__(obj)
 
 
 class MilestoneWithDateExpectedVocabulary(MilestoneVocabulary):