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

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: e9187faa8aa2f8e143a015886033fb975ff46f5e
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:stormify-launchpadstatistic
Merge into: launchpad:master
Diff against target: 315 lines (+89/-109)
2 files modified
lib/lp/services/statistics/model/statistics.py (+31/-21)
lib/lp/services/statistics/tests/test_update_stats.py (+58/-88)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+424218@code.launchpad.net

Commit message

Convert LaunchpadStatistic to Storm

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py
2index 6170e56..2662af3 100644
3--- a/lib/lp/services/statistics/model/statistics.py
4+++ b/lib/lp/services/statistics/model/statistics.py
5@@ -8,6 +8,12 @@ __all__ = [
6 'LaunchpadStatisticSet',
7 ]
8
9+import pytz
10+from storm.locals import (
11+ DateTime,
12+ Int,
13+ Unicode,
14+ )
15 from zope.component import getUtility
16 from zope.interface import implementer
17
18@@ -22,16 +28,9 @@ from lp.code.interfaces.gitcollection import IAllGitRepositories
19 from lp.registry.interfaces.person import IPersonSet
20 from lp.registry.model.product import Product
21 from lp.services.database.constants import UTC_NOW
22-from lp.services.database.datetimecol import UtcDateTimeCol
23 from lp.services.database.interfaces import IStore
24-from lp.services.database.sqlbase import (
25- cursor,
26- SQLBase,
27- )
28-from lp.services.database.sqlobject import (
29- IntCol,
30- StringCol,
31- )
32+from lp.services.database.sqlbase import cursor
33+from lp.services.database.stormbase import StormBase
34 from lp.services.statistics.interfaces.statistic import (
35 ILaunchpadStatistic,
36 ILaunchpadStatisticSet,
37@@ -43,45 +42,56 @@ from lp.translations.model.potemplate import POTemplate
38
39
40 @implementer(ILaunchpadStatistic)
41-class LaunchpadStatistic(SQLBase):
42+class LaunchpadStatistic(StormBase):
43 """A table of Launchpad Statistics."""
44
45- _table = 'LaunchpadStatistic'
46- _defaultOrder = 'name'
47+ __storm_table__ = "LaunchpadStatistic"
48+ __storm_order__ = "name"
49+
50+ id = Int(primary=True)
51+
52+ name = Unicode(allow_none=False)
53+ value = Int(allow_none=False)
54+ dateupdated = DateTime(allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
55
56- # db field names
57- name = StringCol(notNull=True, alternateID=True, unique=True)
58- value = IntCol(notNull=True)
59- dateupdated = UtcDateTimeCol(notNull=True, default=UTC_NOW)
60+ def __init__(self, name, value):
61+ super().__init__()
62+ self.name = name
63+ self.value = value
64
65
66 @implementer(ILaunchpadStatisticSet)
67 class LaunchpadStatisticSet:
68- """See`ILaunchpadStatisticSet`."""
69+ """See `ILaunchpadStatisticSet`."""
70
71 def __iter__(self):
72 """See ILaunchpadStatisticSet."""
73- return iter(LaunchpadStatistic.select(orderBy='name'))
74+ store = IStore(LaunchpadStatistic)
75+ return iter(store.find(LaunchpadStatistic).order_by("name"))
76
77 def update(self, name, value):
78 """See ILaunchpadStatisticSet."""
79- stat = LaunchpadStatistic.selectOneBy(name=name)
80+ store = IStore(LaunchpadStatistic)
81+ stat = store.find(LaunchpadStatistic, name=name).one()
82 if stat is None:
83 stat = LaunchpadStatistic(name=name, value=value)
84+ store.add(stat)
85 else:
86 stat.value = value
87 stat.dateupdated = UTC_NOW
88
89 def dateupdated(self, name):
90 """See ILaunchpadStatisticSet."""
91- stat = LaunchpadStatistic.selectOneBy(name=name)
92+ store = IStore(LaunchpadStatistic)
93+ stat = store.find(LaunchpadStatistic, name=name).one()
94 if stat is None:
95 return None
96 return stat.dateupdated
97
98 def value(self, name):
99 """See ILaunchpadStatisticSet."""
100- stat = LaunchpadStatistic.selectOneBy(name=name)
101+ store = IStore(LaunchpadStatistic)
102+ stat = store.find(LaunchpadStatistic, name=name).one()
103 if stat is None:
104 return None
105 return stat.value
106diff --git a/lib/lp/services/statistics/tests/test_update_stats.py b/lib/lp/services/statistics/tests/test_update_stats.py
107index b027149..25a2462 100644
108--- a/lib/lp/services/statistics/tests/test_update_stats.py
109+++ b/lib/lp/services/statistics/tests/test_update_stats.py
110@@ -3,20 +3,32 @@
111
112 """Test updates to Distroseries stats."""
113
114+from datetime import timedelta
115 import os
116 import subprocess
117 import unittest
118
119+from storm.expr import (
120+ Cast,
121+ Max,
122+ Select,
123+ )
124 from zope.component import getUtility
125
126 from lp.registry.interfaces.distribution import IDistributionSet
127 from lp.registry.interfaces.distroseries import IDistroSeriesSet
128+from lp.registry.model.distroseries import DistroSeries
129 from lp.services.config import config
130-from lp.services.database.sqlbase import cursor
131+from lp.services.database.constants import UTC_NOW
132+from lp.services.database.interfaces import IStore
133+from lp.services.database.stormexpr import IsTrue
134+from lp.services.statistics.model.statistics import LaunchpadStatistic
135 from lp.services.worlddata.interfaces.language import ILanguageSet
136+from lp.services.worlddata.model.language import Language
137 from lp.testing.dbuser import switch_dbuser
138 from lp.testing.layers import LaunchpadZopelessLayer
139 from lp.translations.interfaces.potemplate import IPOTemplateSet
140+from lp.translations.model.distroserieslanguage import DistroSeriesLanguage
141
142
143 def get_script():
144@@ -40,40 +52,33 @@ class UpdateStatsTest(unittest.TestCase):
145 def test_basic(self):
146 """Test insert and update operations to LaunchpadStatistic."""
147 # Nuke some stats so we know that they are updated
148- cur = cursor()
149+ store = IStore(LaunchpadStatistic)
150
151 # Destroy the LaunchpadStatistic entries so we can confirm they are
152 # updated.
153- cur.execute(
154- "DELETE FROM LaunchpadStatistic WHERE name='pofile_count'")
155- cur.execute("""
156- UPDATE LaunchpadStatistic
157- SET value=-1, dateupdated=now()-'10 weeks'::interval
158- """)
159+ ten_weeks_ago = UTC_NOW - Cast(timedelta(weeks=10), "interval")
160+ store.find(LaunchpadStatistic, name="pofile_count").remove()
161+ store.find(LaunchpadStatistic).set(value=-1, dateupdated=ten_weeks_ago)
162
163 # Destroy the messagecount caches on distroseries so we can confirm
164 # they are all updated.
165- cur.execute("UPDATE DistroSeries SET messagecount=-1")
166+ store.find(DistroSeries).set(messagecount=-1)
167
168 # Delete half the entries in the DistroSeriesLanguage cache so we
169 # can confirm they are created as required, and set the remainders
170 # to invalid values so we can confirm they are updated.
171- cur.execute("""
172- DELETE FROM DistroSeriesLanguage
173- WHERE id > (SELECT max(id) FROM DistroSeriesLanguage)/2
174- """)
175- cur.execute("""
176- UPDATE DistroSeriesLanguage
177- SET
178- currentcount=-1, updatescount=-1, rosettacount=-1,
179- unreviewed_count=-1,contributorcount=-1,
180- dateupdated=now()-'10 weeks'::interval
181- """)
182+ store.find(
183+ DistroSeriesLanguage,
184+ DistroSeriesLanguage.id > Select(
185+ Max(DistroSeriesLanguage.id) / 2)).remove()
186+ store.find(DistroSeriesLanguage).set(
187+ currentcount=-1, updatescount=-1, rosettacount=-1,
188+ unreviewed_count=-1, contributorcount=-1,
189+ dateupdated=ten_weeks_ago)
190
191 # Update stats should create missing distroserieslanguage,
192 # so remember how many there are before the run.
193- cur.execute("SELECT COUNT(*) FROM DistroSeriesLanguage")
194- num_distroserieslanguage = cur.fetchone()[0]
195+ num_distroserieslanguage = store.find(DistroSeriesLanguage).count()
196
197 # Commit our changes so the subprocess can see them
198 self.layer.txn.commit()
199@@ -98,72 +103,39 @@ class UpdateStatsTest(unittest.TestCase):
200
201 # Now confirm it did stuff it is supposed to
202 self.layer.txn.abort()
203- cur = cursor()
204
205 # Make sure all DistroSeries.messagecount entries are updated
206- cur.execute(
207- "SELECT COUNT(*) FROM DistroSeries WHERE messagecount=-1")
208- self.assertEqual(cur.fetchone()[0], 0)
209+ self.assertEqual(0, store.find(DistroSeries, messagecount=-1).count())
210
211 # Make sure we have created missing DistroSeriesLanguage entries
212- cur.execute("SELECT COUNT(*) FROM DistroSeriesLanguage")
213- self.assertTrue(cur.fetchone()[0] > num_distroserieslanguage)
214-
215- # Make sure existing DistroSeriesLangauge entries have been updated.
216- cur.execute("""
217- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
218- WHERE DistroSeriesLanguage.language = Language.id AND
219- Language.visible = TRUE AND currentcount = -1
220- """)
221- self.assertEqual(cur.fetchone()[0], 0)
222-
223- cur.execute("""
224- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
225- WHERE DistroSeriesLanguage.language = Language.id AND
226- Language.visible = TRUE AND updatescount = -1
227- """)
228- self.assertEqual(cur.fetchone()[0], 0)
229-
230- cur.execute("""
231- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
232- WHERE DistroSeriesLanguage.language = Language.id AND
233- Language.visible = TRUE AND rosettacount = -1
234- """)
235- self.assertEqual(cur.fetchone()[0], 0)
236-
237- cur.execute("""
238- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
239- WHERE DistroSeriesLanguage.language = Language.id AND
240- Language.visible = TRUE AND unreviewed_count = -1
241- """)
242- self.assertEqual(cur.fetchone()[0], 0)
243-
244- cur.execute("""
245- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
246- WHERE DistroSeriesLanguage.language = Language.id AND
247- Language.visible = TRUE AND contributorcount = -1
248- """)
249- self.assertEqual(cur.fetchone()[0], 0)
250-
251- cur.execute("""
252- SELECT COUNT(*) FROM DistroSeriesLanguage, Language
253- WHERE DistroSeriesLanguage.language = Language.id AND
254- Language.visible = TRUE AND
255- dateupdated < now() - '2 days'::interval
256- """)
257- self.assertEqual(cur.fetchone()[0], 0)
258+ self.assertGreater(
259+ store.find(DistroSeriesLanguage).count(), num_distroserieslanguage)
260+
261+ # Make sure existing DistroSeriesLanguage entries have been updated.
262+ two_days_ago = UTC_NOW - Cast(timedelta(days=2), "interval")
263+ for term in (
264+ DistroSeriesLanguage.currentcount == -1,
265+ DistroSeriesLanguage.updatescount == -1,
266+ DistroSeriesLanguage.rosettacount == -1,
267+ DistroSeriesLanguage.unreviewed_count == -1,
268+ DistroSeriesLanguage.contributorcount == -1,
269+ DistroSeriesLanguage.dateupdated < two_days_ago,
270+ ):
271+ self.assertEqual(
272+ 0,
273+ store.find(
274+ DistroSeriesLanguage,
275+ DistroSeriesLanguage.language == Language.id,
276+ IsTrue(Language.visible),
277+ term).count())
278
279 # All LaunchpadStatistic rows should have been updated
280- cur.execute("""
281- SELECT COUNT(*) FROM LaunchpadStatistic
282- WHERE value=-1
283- """)
284- self.assertEqual(cur.fetchone()[0], 0)
285- cur.execute("""
286- SELECT COUNT(*) FROM LaunchpadStatistic
287- WHERE dateupdated < now() - '2 days'::interval
288- """)
289- self.assertEqual(cur.fetchone()[0], 0)
290+ self.assertEqual(0, store.find(LaunchpadStatistic, value=-1).count())
291+ self.assertEqual(
292+ 0,
293+ store.find(
294+ LaunchpadStatistic,
295+ LaunchpadStatistic.dateupdated < two_days_ago).count())
296
297 keys = [
298 'potemplate_count', 'pofile_count', 'pomsgid_count',
299@@ -177,12 +149,10 @@ class UpdateStatsTest(unittest.TestCase):
300 ]
301
302 for key in keys:
303- cur.execute("""
304- SELECT value from LaunchpadStatistic WHERE name=%(key)s
305- """, dict(key=key))
306- row = cur.fetchone()
307- self.assertIsNotNone(row, '%s not updated' % key)
308- self.assertTrue(row[0] >= 0, '%s is invalid' % key)
309+ value = store.find(
310+ LaunchpadStatistic.value, LaunchpadStatistic.name == key).one()
311+ self.assertIsNotNone(value, "%s not updated" % key)
312+ self.assertGreaterEqual(value, 0, "%s is invalid" % key)
313
314
315 class UpdateTranslationStatsTest(unittest.TestCase):

Subscribers

People subscribed via source and target branches

to status/vote changes: