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):
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp