← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
Convert LaunchpadStatistic to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/424218
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-launchpadstatistic into launchpad:master.
diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py
index 6170e56..2662af3 100644
--- a/lib/lp/services/statistics/model/statistics.py
+++ b/lib/lp/services/statistics/model/statistics.py
@@ -8,6 +8,12 @@ __all__ = [
     'LaunchpadStatisticSet',
     ]
 
+import pytz
+from storm.locals import (
+    DateTime,
+    Int,
+    Unicode,
+    )
 from zope.component import getUtility
 from zope.interface import implementer
 
@@ -22,16 +28,9 @@ from lp.code.interfaces.gitcollection import IAllGitRepositories
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.model.product import Product
 from lp.services.database.constants import UTC_NOW
-from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import (
-    cursor,
-    SQLBase,
-    )
-from lp.services.database.sqlobject import (
-    IntCol,
-    StringCol,
-    )
+from lp.services.database.sqlbase import cursor
+from lp.services.database.stormbase import StormBase
 from lp.services.statistics.interfaces.statistic import (
     ILaunchpadStatistic,
     ILaunchpadStatisticSet,
@@ -43,45 +42,56 @@ from lp.translations.model.potemplate import POTemplate
 
 
 @implementer(ILaunchpadStatistic)
-class LaunchpadStatistic(SQLBase):
+class LaunchpadStatistic(StormBase):
     """A table of Launchpad Statistics."""
 
-    _table = 'LaunchpadStatistic'
-    _defaultOrder = 'name'
+    __storm_table__ = "LaunchpadStatistic"
+    __storm_order__ = "name"
+
+    id = Int(primary=True)
+
+    name = Unicode(allow_none=False)
+    value = Int(allow_none=False)
+    dateupdated = DateTime(allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
 
-    # db field names
-    name = StringCol(notNull=True, alternateID=True, unique=True)
-    value = IntCol(notNull=True)
-    dateupdated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
+    def __init__(self, name, value):
+        super().__init__()
+        self.name = name
+        self.value = value
 
 
 @implementer(ILaunchpadStatisticSet)
 class LaunchpadStatisticSet:
-    """See`ILaunchpadStatisticSet`."""
+    """See `ILaunchpadStatisticSet`."""
 
     def __iter__(self):
         """See ILaunchpadStatisticSet."""
-        return iter(LaunchpadStatistic.select(orderBy='name'))
+        store = IStore(LaunchpadStatistic)
+        return iter(store.find(LaunchpadStatistic).order_by("name"))
 
     def update(self, name, value):
         """See ILaunchpadStatisticSet."""
-        stat = LaunchpadStatistic.selectOneBy(name=name)
+        store = IStore(LaunchpadStatistic)
+        stat = store.find(LaunchpadStatistic, name=name).one()
         if stat is None:
             stat = LaunchpadStatistic(name=name, value=value)
+            store.add(stat)
         else:
             stat.value = value
             stat.dateupdated = UTC_NOW
 
     def dateupdated(self, name):
         """See ILaunchpadStatisticSet."""
-        stat = LaunchpadStatistic.selectOneBy(name=name)
+        store = IStore(LaunchpadStatistic)
+        stat = store.find(LaunchpadStatistic, name=name).one()
         if stat is None:
             return None
         return stat.dateupdated
 
     def value(self, name):
         """See ILaunchpadStatisticSet."""
-        stat = LaunchpadStatistic.selectOneBy(name=name)
+        store = IStore(LaunchpadStatistic)
+        stat = store.find(LaunchpadStatistic, name=name).one()
         if stat is None:
             return None
         return stat.value
diff --git a/lib/lp/services/statistics/tests/test_update_stats.py b/lib/lp/services/statistics/tests/test_update_stats.py
index b027149..25a2462 100644
--- a/lib/lp/services/statistics/tests/test_update_stats.py
+++ b/lib/lp/services/statistics/tests/test_update_stats.py
@@ -3,20 +3,32 @@
 
 """Test updates to Distroseries stats."""
 
+from datetime import timedelta
 import os
 import subprocess
 import unittest
 
+from storm.expr import (
+    Cast,
+    Max,
+    Select,
+    )
 from zope.component import getUtility
 
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.distroseries import IDistroSeriesSet
+from lp.registry.model.distroseries import DistroSeries
 from lp.services.config import config
-from lp.services.database.sqlbase import cursor
+from lp.services.database.constants import UTC_NOW
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormexpr import IsTrue
+from lp.services.statistics.model.statistics import LaunchpadStatistic
 from lp.services.worlddata.interfaces.language import ILanguageSet
+from lp.services.worlddata.model.language import Language
 from lp.testing.dbuser import switch_dbuser
 from lp.testing.layers import LaunchpadZopelessLayer
 from lp.translations.interfaces.potemplate import IPOTemplateSet
+from lp.translations.model.distroserieslanguage import DistroSeriesLanguage
 
 
 def get_script():
@@ -40,40 +52,33 @@ class UpdateStatsTest(unittest.TestCase):
     def test_basic(self):
         """Test insert and update operations to LaunchpadStatistic."""
         # Nuke some stats so we know that they are updated
-        cur = cursor()
+        store = IStore(LaunchpadStatistic)
 
         # Destroy the LaunchpadStatistic entries so we can confirm they are
         # updated.
-        cur.execute(
-            "DELETE FROM LaunchpadStatistic WHERE name='pofile_count'")
-        cur.execute("""
-            UPDATE LaunchpadStatistic
-            SET value=-1, dateupdated=now()-'10 weeks'::interval
-            """)
+        ten_weeks_ago = UTC_NOW - Cast(timedelta(weeks=10), "interval")
+        store.find(LaunchpadStatistic, name="pofile_count").remove()
+        store.find(LaunchpadStatistic).set(value=-1, dateupdated=ten_weeks_ago)
 
         # Destroy the messagecount caches on distroseries so we can confirm
         # they are all updated.
-        cur.execute("UPDATE DistroSeries SET messagecount=-1")
+        store.find(DistroSeries).set(messagecount=-1)
 
         # Delete half the entries in the DistroSeriesLanguage cache so we
         # can confirm they are created as required, and set the remainders
         # to invalid values so we can confirm they are updated.
-        cur.execute("""
-            DELETE FROM DistroSeriesLanguage
-            WHERE id > (SELECT max(id) FROM DistroSeriesLanguage)/2
-            """)
-        cur.execute("""
-            UPDATE DistroSeriesLanguage
-            SET
-                currentcount=-1, updatescount=-1, rosettacount=-1,
-                unreviewed_count=-1,contributorcount=-1,
-                dateupdated=now()-'10 weeks'::interval
-            """)
+        store.find(
+            DistroSeriesLanguage,
+            DistroSeriesLanguage.id > Select(
+                Max(DistroSeriesLanguage.id) / 2)).remove()
+        store.find(DistroSeriesLanguage).set(
+            currentcount=-1, updatescount=-1, rosettacount=-1,
+            unreviewed_count=-1, contributorcount=-1,
+            dateupdated=ten_weeks_ago)
 
         # Update stats should create missing distroserieslanguage,
         # so remember how many there are before the run.
-        cur.execute("SELECT COUNT(*) FROM DistroSeriesLanguage")
-        num_distroserieslanguage = cur.fetchone()[0]
+        num_distroserieslanguage = store.find(DistroSeriesLanguage).count()
 
         # Commit our changes so the subprocess can see them
         self.layer.txn.commit()
@@ -98,72 +103,39 @@ class UpdateStatsTest(unittest.TestCase):
 
         # Now confirm it did stuff it is supposed to
         self.layer.txn.abort()
-        cur = cursor()
 
         # Make sure all DistroSeries.messagecount entries are updated
-        cur.execute(
-            "SELECT COUNT(*) FROM DistroSeries WHERE messagecount=-1")
-        self.assertEqual(cur.fetchone()[0], 0)
+        self.assertEqual(0, store.find(DistroSeries, messagecount=-1).count())
 
         # Make sure we have created missing DistroSeriesLanguage entries
-        cur.execute("SELECT COUNT(*) FROM DistroSeriesLanguage")
-        self.assertTrue(cur.fetchone()[0] > num_distroserieslanguage)
-
-        # Make sure existing DistroSeriesLangauge entries have been updated.
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND currentcount = -1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND updatescount = -1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND rosettacount = -1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND unreviewed_count = -1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND contributorcount = -1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-
-        cur.execute("""
-            SELECT COUNT(*) FROM DistroSeriesLanguage, Language
-            WHERE DistroSeriesLanguage.language = Language.id AND
-                  Language.visible = TRUE AND
-                  dateupdated < now() - '2 days'::interval
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
+        self.assertGreater(
+            store.find(DistroSeriesLanguage).count(), num_distroserieslanguage)
+
+        # Make sure existing DistroSeriesLanguage entries have been updated.
+        two_days_ago = UTC_NOW - Cast(timedelta(days=2), "interval")
+        for term in (
+            DistroSeriesLanguage.currentcount == -1,
+            DistroSeriesLanguage.updatescount == -1,
+            DistroSeriesLanguage.rosettacount == -1,
+            DistroSeriesLanguage.unreviewed_count == -1,
+            DistroSeriesLanguage.contributorcount == -1,
+            DistroSeriesLanguage.dateupdated < two_days_ago,
+        ):
+            self.assertEqual(
+                0,
+                store.find(
+                    DistroSeriesLanguage,
+                    DistroSeriesLanguage.language == Language.id,
+                    IsTrue(Language.visible),
+                    term).count())
 
         # All LaunchpadStatistic rows should have been updated
-        cur.execute("""
-            SELECT COUNT(*) FROM LaunchpadStatistic
-            WHERE value=-1
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
-        cur.execute("""
-            SELECT COUNT(*) FROM LaunchpadStatistic
-            WHERE dateupdated < now() - '2 days'::interval
-            """)
-        self.assertEqual(cur.fetchone()[0], 0)
+        self.assertEqual(0, store.find(LaunchpadStatistic, value=-1).count())
+        self.assertEqual(
+            0,
+            store.find(
+                LaunchpadStatistic,
+                LaunchpadStatistic.dateupdated < two_days_ago).count())
 
         keys = [
             'potemplate_count', 'pofile_count', 'pomsgid_count',
@@ -177,12 +149,10 @@ class UpdateStatsTest(unittest.TestCase):
             ]
 
         for key in keys:
-            cur.execute("""
-                SELECT value from LaunchpadStatistic WHERE name=%(key)s
-                """, dict(key=key))
-            row = cur.fetchone()
-            self.assertIsNotNone(row, '%s not updated' % key)
-            self.assertTrue(row[0] >= 0, '%s is invalid' % key)
+            value = store.find(
+                LaunchpadStatistic.value, LaunchpadStatistic.name == key).one()
+            self.assertIsNotNone(value, "%s not updated" % key)
+            self.assertGreaterEqual(value, 0, "%s is invalid" % key)
 
 
 class UpdateTranslationStatsTest(unittest.TestCase):