Merge lp:~wgrant/launchpad/export-detailed-binary-download-stats into lp:launchpad/db-devel

Proposed by William Grant
Status: Merged
Approved by: Eleanor Berger
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~wgrant/launchpad/export-detailed-binary-download-stats
Merge into: lp:launchpad/db-devel
Diff against target: 714 lines (+450/-35)
13 files modified
lib/canonical/launchpad/security.py (+7/-0)
lib/lp/soyuz/browser/archive.py (+52/-0)
lib/lp/soyuz/browser/configure.zcml (+5/-0)
lib/lp/soyuz/configure.zcml (+8/-0)
lib/lp/soyuz/doc/publishing.txt (+38/-3)
lib/lp/soyuz/interfaces/archive.py (+15/-0)
lib/lp/soyuz/interfaces/binarypackagerelease.py (+25/-11)
lib/lp/soyuz/interfaces/publishing.py (+26/-1)
lib/lp/soyuz/model/archive.py (+25/-0)
lib/lp/soyuz/model/binarypackagerelease.py (+11/-0)
lib/lp/soyuz/model/publishing.py (+48/-2)
lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt (+108/-18)
lib/lp/soyuz/tests/test_archive.py (+82/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/export-detailed-binary-download-stats
Reviewer Review Type Date Requested Status
Eleanor Berger (community) code Approve
Review via email: mp+21828@code.launchpad.net

Commit message

Export detailed PPA binary download counts.

Description of the change

This branch exports the most detailed PPA binary download counts that we collect, adding webservice methods to retrieve counts per (archive, binary, day, country) and (archive, binary, day).

The URL for IBPRDC is pretty foul, and by far the longest in Launchpad. But Julian advised me to do that rather than use BPRDC.id.

To post a comment you must log in.
Revision history for this message
Eleanor Berger (intellectronica) wrote :

r=me, great work!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/security.py'
2--- lib/canonical/launchpad/security.py 2010-03-19 05:19:20 +0000
3+++ lib/canonical/launchpad/security.py 2010-03-22 07:42:21 +0000
4@@ -71,6 +71,8 @@
5 from lp.soyuz.interfaces.packageset import IPackageset, IPackagesetSet
6 from lp.translations.interfaces.pofile import IPOFile
7 from lp.translations.interfaces.potemplate import IPOTemplate
8+from lp.soyuz.interfaces.binarypackagerelease import (
9+ IBinaryPackageReleaseDownloadCount)
10 from lp.soyuz.interfaces.build import IBuild
11 from lp.soyuz.interfaces.buildfarmbuildjob import IBuildFarmBuildJob
12 from lp.soyuz.interfaces.publishing import (
13@@ -2173,6 +2175,11 @@
14 usedfor = IBinaryPackagePublishingHistory
15
16
17+class ViewBinaryPackageReleaseDownloadCount(ViewSourcePackagePublishingHistory):
18+ """Restrict viewing of binary package download counts."""
19+ usedfor = IBinaryPackageReleaseDownloadCount
20+
21+
22 class ViewSourcePackageRelease(AuthorizationBase):
23 """Restrict viewing of source packages.
24
25
26=== modified file 'lib/lp/soyuz/browser/archive.py'
27--- lib/lp/soyuz/browser/archive.py 2010-03-10 12:50:18 +0000
28+++ lib/lp/soyuz/browser/archive.py 2010-03-22 07:42:21 +0000
29@@ -48,6 +48,7 @@
30 from canonical.lazr.utils import smartquote
31 from lp.buildmaster.interfaces.buildbase import BuildStatus
32 from lp.services.browser_helpers import get_user_agent_distroseries
33+from lp.services.worlddata.interfaces.country import ICountrySet
34 from lp.soyuz.browser.build import BuildRecordsView
35 from lp.soyuz.browser.sourceslist import (
36 SourcesListEntries, SourcesListEntriesView)
37@@ -62,6 +63,7 @@
38 from lp.soyuz.interfaces.archivepermission import (
39 ArchivePermissionType, IArchivePermissionSet)
40 from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet
41+from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
42 from lp.soyuz.interfaces.build import BuildSetStatus, IBuildSet
43 from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
44 from lp.soyuz.interfaces.component import IComponentSet
45@@ -217,6 +219,56 @@
46
47 return None
48
49+ @stepthrough('+binaryhits')
50+ def traverse_binaryhits(self, name_str):
51+ """Traverse to an `IBinaryPackageReleaseDownloadCount`.
52+
53+ A matching path is something like this:
54+
55+ +binaryhits/foopkg/1.0/i386/2010-03-11/AU
56+
57+ To reach one where the country is None, use:
58+
59+ +binaryhits/foopkg/1.0/i386/2010-03-11/unknown
60+ """
61+
62+ if len(self.request.stepstogo) < 4:
63+ return None
64+
65+ version = self.request.stepstogo.consume()
66+ archtag = self.request.stepstogo.consume()
67+ date_str = self.request.stepstogo.consume()
68+ country_str = self.request.stepstogo.consume()
69+
70+ try:
71+ name = getUtility(IBinaryPackageNameSet)[name_str]
72+ except NotFoundError:
73+ return None
74+
75+ # This will return None if there are multiple BPRs with the same
76+ # name in the archive's history, but in that case downloads
77+ # won't be counted either.
78+ bpr = self.context.getBinaryPackageRelease(name, version, archtag)
79+ if bpr is None:
80+ return None
81+
82+ try:
83+ date = datetime.strptime(date_str, '%Y-%m-%d').date()
84+ except ValueError:
85+ return None
86+
87+ # 'unknown' should always be safe, since the key is the two letter
88+ # ISO code, and 'unknown' has more than two letters.
89+ if country_str == 'unknown':
90+ country = None
91+ else:
92+ try:
93+ country = getUtility(ICountrySet)[country_str]
94+ except NotFoundError:
95+ return None
96+
97+ return self.context.getPackageDownloadCount(bpr, date, country)
98+
99 @stepthrough('+subscriptions')
100 def traverse_subscription(self, person_name):
101 try:
102
103=== modified file 'lib/lp/soyuz/browser/configure.zcml'
104--- lib/lp/soyuz/browser/configure.zcml 2010-03-16 07:31:35 +0000
105+++ lib/lp/soyuz/browser/configure.zcml 2010-03-22 07:42:21 +0000
106@@ -798,6 +798,11 @@
107 path_expression="string:+dependency/${dependency/id}"
108 attribute_to_parent="archive"
109 />
110+ <browser:url
111+ for="lp.soyuz.interfaces.binarypackagerelease.IBinaryPackageReleaseDownloadCount"
112+ path_expression="string:+binaryhits/${binary_package_release/name}/${binary_package_release/version}/${binary_package_release/build/distroarchseries/architecturetag}/${day}/${country/iso3166code2|string:unknown}"
113+ attribute_to_parent="archive"
114+ />
115
116 <!-- XXX JeroenVermeulen 2010-03-13 bug=539395: This is buildmaster, not soyuz -->
117 <browser:page
118
119=== modified file 'lib/lp/soyuz/configure.zcml'
120--- lib/lp/soyuz/configure.zcml 2010-03-18 17:14:50 +0000
121+++ lib/lp/soyuz/configure.zcml 2010-03-22 07:42:21 +0000
122@@ -864,4 +864,12 @@
123 factory="lp.soyuz.model.binarypackagebuildbehavior.BinaryPackageBuildBehavior"
124 permission="zope.Public" />
125
126+ <!-- BinaryPackageReleaseDownloadCount -->
127+ <class
128+ class="lp.soyuz.model.binarypackagerelease.BinaryPackageReleaseDownloadCount">
129+ <require
130+ permission="launchpad.View"
131+ interface="lp.soyuz.interfaces.binarypackagerelease.IBinaryPackageReleaseDownloadCount"/>
132+ </class>
133+
134 </configure>
135
136=== modified file 'lib/lp/soyuz/doc/publishing.txt'
137--- lib/lp/soyuz/doc/publishing.txt 2010-03-19 06:53:28 +0000
138+++ lib/lp/soyuz/doc/publishing.txt 2010-03-22 07:42:21 +0000
139@@ -956,11 +956,46 @@
140 0
141
142 >>> from datetime import date
143- >>> bpph.archive.updatePackageDownloadCount(
144- ... bpph.binarypackagerelease, date(2010, 2, 21), None, 10)
145+ >>> from lp.services.worlddata.interfaces.country import ICountrySet
146+ >>> australia = getUtility(ICountrySet)['AU']
147+ >>> uk = getUtility(ICountrySet)['GB']
148+
149+ >>> bpph.archive.updatePackageDownloadCount(
150+ ... bpph.binarypackagerelease, date(2010, 2, 19), None, 2)
151+ >>> bpph.archive.updatePackageDownloadCount(
152+ ... bpph.binarypackagerelease, date(2010, 2, 21), australia, 10)
153+ >>> bpph.archive.updatePackageDownloadCount(
154+ ... bpph.binarypackagerelease, date(2010, 2, 21), uk, 4)
155
156 >>> print bpph.getDownloadCount()
157- 10
158+ 16
159+
160+We can also use getDownloadCounts to find the raw download counts per
161+day and country.
162+
163+ >>> [(b.day, b.country.name if b.country is not None else None)
164+ ... for b in bpph.getDownloadCounts()]
165+ [(datetime.date(2010, 2, 21), u'Australia'),
166+ (datetime.date(2010, 2, 21), u'United Kingdom'),
167+ (datetime.date(2010, 2, 19), None)]
168+
169+getDownloadCounts lets us filter by date.
170+
171+ >>> [b.day for b in bpph.getDownloadCounts(start_date=date(2010, 2, 21))]
172+ [datetime.date(2010, 2, 21), datetime.date(2010, 2, 21)]
173+ >>> [b.day for b in bpph.getDownloadCounts(end_date=date(2010, 2, 20))]
174+ [datetime.date(2010, 2, 19)]
175+ >>> [b.day for b in bpph.getDownloadCounts(
176+ ... start_date=date(2010, 2, 20), end_date=date(2010, 2, 20))]
177+ []
178+
179+We can also get a dict of totals for each day. The keys are strings to
180+work around lazr.restful's dict limitations. This too has a date filter.
181+
182+ >>> bpph.getDailyDownloadTotals()
183+ {'2010-02-21': 14L, '2010-02-19': 2L}
184+ >>> bpph.getDailyDownloadTotals(start_date=date(2010, 2, 20))
185+ {'2010-02-21': 14L}
186
187
188 IPublishingSet
189
190=== modified file 'lib/lp/soyuz/interfaces/archive.py'
191--- lib/lp/soyuz/interfaces/archive.py 2010-03-19 11:29:22 +0000
192+++ lib/lp/soyuz/interfaces/archive.py 2010-03-22 07:42:21 +0000
193@@ -425,6 +425,18 @@
194 :return the corresponding `ILibraryFileAlias` is the file was found.
195 """
196
197+ def getBinaryPackageRelease(name, version, archtag):
198+ """Find the specified `IBinaryPackageRelease` in the archive.
199+
200+ :param name: The `IBinaryPackageName` of the package.
201+ :param version: The version of the package.
202+ :param archtag: The architecture tag of the package's build. 'all'
203+ will not work here -- 'i386' (the build DAS) must be used instead.
204+
205+ :return The binary package release with the given name and version,
206+ or None if one does not exist or there is more than one.
207+ """
208+
209 def getBinaryPackageReleaseByFileName(filename):
210 """Return the corresponding `IBinaryPackageRelease` in this context.
211
212@@ -919,6 +931,9 @@
213 :return: A list of `IArchivePermission` records.
214 """
215
216+ def getPackageDownloadCount(bpr, day, country):
217+ """Get the `IBinaryPackageDownloadCount` with the given key."""
218+
219
220 class IArchiveAppend(Interface):
221 """Archive interface for operations restricted by append privilege."""
222
223=== modified file 'lib/lp/soyuz/interfaces/binarypackagerelease.py'
224--- lib/lp/soyuz/interfaces/binarypackagerelease.py 2010-03-15 23:01:48 +0000
225+++ lib/lp/soyuz/interfaces/binarypackagerelease.py 2010-03-22 07:42:21 +0000
226@@ -16,13 +16,15 @@
227 ]
228
229 from lazr.enum import DBEnumeratedType, DBItem
230-from lazr.restful.fields import Reference
231-from zope.schema import Bool, Choice, Date, Int, Text, TextLine, Datetime
232+from lazr.restful.declarations import exported, export_as_webservice_entry
233+from lazr.restful.fields import Reference, ReferenceChoice
234+from zope.schema import Bool, Date, Int, Text, TextLine, Datetime
235 from zope.interface import Interface, Attribute
236
237 from canonical.launchpad import _
238 from canonical.launchpad.validators.version import valid_debian_version
239 from lp.soyuz.interfaces.archive import IArchive
240+from lp.services.worlddata.interfaces.country import ICountry
241
242
243 class IBinaryPackageRelease(Interface):
244@@ -95,19 +97,31 @@
245
246 class IBinaryPackageReleaseDownloadCount(Interface):
247 """Daily download count of a binary package release in an archive."""
248+ export_as_webservice_entry()
249
250- archive = Reference(
251- title=_('The archive'), schema=IArchive, required=True,
252- readonly=True)
253+ id = Int(title=_('ID'), required=True, readonly=True)
254+ archive = exported(Reference(
255+ title=_('Archive'), schema=IArchive, required=True,
256+ readonly=True))
257 binary_package_release = Reference(
258 title=_('The binary package release'), schema=IBinaryPackageRelease,
259 required=True, readonly=True)
260- day = Date(
261- title=_('The day of the downloads'), required=True, readonly=True)
262- count = Int(
263- title=_('The number of downloads'), required=True, readonly=False)
264- country = Choice(
265- title=_('Country'), required=False, vocabulary='CountryName')
266+ binary_package_name = exported(
267+ TextLine(
268+ title=_("Binary package name"),
269+ required=False, readonly=True))
270+ binary_package_version = exported(
271+ TextLine(
272+ title=_("Binary package version"),
273+ required=False, readonly=True))
274+ day = exported(
275+ Date(title=_('Day of the downloads'), required=True, readonly=True))
276+ count = exported(
277+ Int(title=_('Number of downloads'), required=True, readonly=True))
278+ country = exported(
279+ ReferenceChoice(
280+ title=_('Country'), required=False, readonly=True,
281+ vocabulary='CountryName', schema=ICountry))
282
283
284 class BinaryPackageFileType(DBEnumeratedType):
285
286=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
287--- lib/lp/soyuz/interfaces/publishing.py 2010-03-11 02:26:37 +0000
288+++ lib/lp/soyuz/interfaces/publishing.py 2010-03-22 07:42:21 +0000
289@@ -29,7 +29,7 @@
290 'name_priority_map',
291 ]
292
293-from zope.schema import Choice, Datetime, Int, TextLine, Text
294+from zope.schema import Choice, Date, Datetime, Int, TextLine, Text
295 from zope.interface import Interface, Attribute
296 from lazr.enum import DBEnumeratedType, DBItem
297
298@@ -37,6 +37,8 @@
299 from lp.registry.interfaces.distroseries import IDistroSeries
300 from lp.registry.interfaces.person import IPerson
301 from lp.registry.interfaces.pocket import PackagePublishingPocket
302+from lp.soyuz.interfaces.binarypackagerelease import (
303+ IBinaryPackageReleaseDownloadCount)
304
305 from lazr.restful.fields import Reference
306 from lazr.restful.declarations import (
307@@ -862,6 +864,29 @@
308
309 This is currently only meaningful for PPAs."""
310
311+ @operation_parameters(
312+ start_date=Date(title=_("Start date"), required=False),
313+ end_date=Date(title=_("End date"), required=False))
314+ @operation_returns_collection_of(IBinaryPackageReleaseDownloadCount)
315+ @export_read_operation()
316+ def getDownloadCounts(start_date=None, end_date=None):
317+ """Get detailed download counts for this binary.
318+
319+ :param start_date: The optional first date to return.
320+ :param end_date: The optional last date to return.
321+ """
322+
323+ @operation_parameters(
324+ start_date=Date(title=_("Start date"), required=False),
325+ end_date=Date(title=_("End date"), required=False))
326+ @export_read_operation()
327+ def getDailyDownloadTotals(start_date=None, end_date=None):
328+ """Get the daily download counts for this binary.
329+
330+ :param start_date: The optional first date to return.
331+ :param end_date: The optional last date to return.
332+ """
333+
334
335 class IBinaryPackagePublishingHistory(IBinaryPackagePublishingHistoryPublic,
336 IPublishingEdit):
337
338=== modified file 'lib/lp/soyuz/model/archive.py'
339--- lib/lp/soyuz/model/archive.py 2010-03-19 11:29:22 +0000
340+++ lib/lp/soyuz/model/archive.py 2010-03-22 07:42:21 +0000
341@@ -1133,6 +1133,25 @@
342
343 return archive_file
344
345+ def getBinaryPackageRelease(self, name, version, archtag):
346+ """See `IArchive`."""
347+ from lp.soyuz.model.distroarchseries import DistroArchSeries
348+
349+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
350+ results = store.find(
351+ BinaryPackageRelease,
352+ BinaryPackageRelease.binarypackagename == name,
353+ BinaryPackageRelease.version == version,
354+ Build.id == BinaryPackageRelease.buildID,
355+ DistroArchSeries.id == Build.distroarchseriesID,
356+ DistroArchSeries.architecturetag == archtag,
357+ BinaryPackagePublishingHistory.archive == self,
358+ BinaryPackagePublishingHistory.binarypackagereleaseID ==
359+ BinaryPackageRelease.id).config(distinct=True)
360+ if results.count() > 1:
361+ return None
362+ return results.one()
363+
364 def getBinaryPackageReleaseByFileName(self, filename):
365 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
366 results = store.find(
367@@ -1355,6 +1374,12 @@
368 ).one()
369 return count or 0
370
371+ def getPackageDownloadCount(self, bpr, day, country):
372+ """See `IArchive`."""
373+ return Store.of(self).find(
374+ BinaryPackageReleaseDownloadCount, archive=self,
375+ binary_package_release=bpr, day=day, country=country).one()
376+
377 def _setBuildStatuses(self, status):
378 """Update the pending Build Jobs' status for this archive."""
379
380
381=== modified file 'lib/lp/soyuz/model/binarypackagerelease.py'
382--- lib/lp/soyuz/model/binarypackagerelease.py 2010-03-15 23:01:48 +0000
383+++ lib/lp/soyuz/model/binarypackagerelease.py 2010-03-22 07:42:21 +0000
384@@ -230,3 +230,14 @@
385 self.day = day
386 self.country = country
387 self.count = count
388+
389+ @property
390+ def binary_package_name(self):
391+ """See `IBinaryPackageReleaseDownloadCount`."""
392+ return self.binary_package_release.name
393+
394+ @property
395+ def binary_package_version(self):
396+ """See `IBinaryPackageReleaseDownloadCount`."""
397+ return self.binary_package_release.version
398+
399
400=== modified file 'lib/lp/soyuz/model/publishing.py'
401--- lib/lp/soyuz/model/publishing.py 2010-03-17 09:23:40 +0000
402+++ lib/lp/soyuz/model/publishing.py 2010-03-22 07:42:21 +0000
403@@ -28,7 +28,7 @@
404
405 from sqlobject import ForeignKey, StringCol
406
407-from storm.expr import Desc, In, LeftJoin
408+from storm.expr import Desc, In, LeftJoin, Sum
409 from storm.store import Store
410
411 from canonical.database.sqlbase import SQLBase, sqlvalues
412@@ -43,8 +43,10 @@
413 from lp.buildmaster.interfaces.buildbase import BuildStatus
414 from lp.registry.interfaces.person import validate_public_person
415 from lp.registry.interfaces.pocket import PackagePublishingPocket
416+from lp.services.worlddata.model.country import Country
417 from lp.soyuz.model.binarypackagename import BinaryPackageName
418-from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
419+from lp.soyuz.model.binarypackagerelease import (BinaryPackageRelease,
420+ BinaryPackageReleaseDownloadCount)
421 from lp.soyuz.model.files import (
422 BinaryPackageFile, SourcePackageReleaseFile)
423 from canonical.launchpad.database.librarian import (
424@@ -1010,6 +1012,50 @@
425
426 self.component = component
427
428+ def _getDownloadCountClauses(self, start_date=None, end_date=None):
429+ clauses = [
430+ BinaryPackageReleaseDownloadCount.archive == self.archive,
431+ BinaryPackageReleaseDownloadCount.binary_package_release ==
432+ self.binarypackagerelease,
433+ ]
434+
435+ if start_date is not None:
436+ clauses.append(
437+ BinaryPackageReleaseDownloadCount.day >= start_date)
438+ if end_date is not None:
439+ clauses.append(
440+ BinaryPackageReleaseDownloadCount.day <= end_date)
441+
442+ return clauses
443+
444+ def getDownloadCounts(self, start_date=None, end_date=None):
445+ """See `IBinaryPackagePublishingHistory`."""
446+ clauses = self._getDownloadCountClauses(start_date, end_date)
447+
448+ return Store.of(self).using(
449+ BinaryPackageReleaseDownloadCount,
450+ LeftJoin(
451+ Country,
452+ BinaryPackageReleaseDownloadCount.country_id ==
453+ Country.id)).find(
454+ BinaryPackageReleaseDownloadCount, *clauses).order_by(
455+ Desc(BinaryPackageReleaseDownloadCount.day), Country.name)
456+
457+ def getDailyDownloadTotals(self, start_date=None, end_date=None):
458+ """See `IBinaryPackagePublishingHistory`."""
459+ clauses = self._getDownloadCountClauses(start_date, end_date)
460+
461+ results = Store.of(self).find(
462+ (BinaryPackageReleaseDownloadCount.day,
463+ Sum(BinaryPackageReleaseDownloadCount.count)),
464+ *clauses).group_by(
465+ BinaryPackageReleaseDownloadCount.day)
466+
467+ def date_to_string(result):
468+ return (result[0].strftime('%Y-%m-%d'), result[1])
469+
470+ return dict(date_to_string(result) for result in results)
471+
472
473 class PublishingSet:
474 """Utilities for manipulating publications in batches."""
475
476=== modified file 'lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt'
477--- lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2010-03-18 22:47:27 +0000
478+++ lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2010-03-22 07:42:21 +0000
479@@ -86,24 +86,6 @@
480 self_link: u'http://.../~cprov/+archive/ppa/+binarypub/30'
481 status: u'Published'
482
483-We can also retrieve the total download count for the binary in this archive.
484-
485- >>> webservice.named_get(
486- ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
487- 0
488-
489- >>> login("foo.bar@canonical.com")
490- >>> from datetime import date
491- >>> from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
492- >>> bpph = BinaryPackagePublishingHistory.get(30)
493- >>> bpph.archive.updatePackageDownloadCount(
494- ... bpph.binarypackagerelease, date(2010, 2, 21), None, 10)
495- >>> logout()
496-
497- >>> webservice.named_get(
498- ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
499- 10
500-
501
502 Security
503 ========
504@@ -158,3 +140,111 @@
505 >>> print response
506 HTTP/1.1 401 Unauthorized
507 ...
508+
509+
510+Download counts
511+===============
512+
513+We can retrieve the total download count for a binary in this archive.
514+
515+ >>> webservice.named_get(
516+ ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
517+ 0
518+
519+ >>> login("foo.bar@canonical.com")
520+
521+ >>> from datetime import date
522+ >>> from lp.services.worlddata.interfaces.country import ICountrySet
523+ >>> australia = getUtility(ICountrySet)['AU']
524+
525+ >>> firefox_db = cprov_db.archive.getAllPublishedBinaries(
526+ ... name='mozilla-firefox')[0]
527+ >>> firefox_db.archive.updatePackageDownloadCount(
528+ ... firefox_db.binarypackagerelease, date(2010, 2, 21), australia, 10)
529+ >>> firefox_db.archive.updatePackageDownloadCount(
530+ ... firefox_db.binarypackagerelease, date(2010, 2, 23), None, 8)
531+
532+ >>> logout()
533+
534+ >>> firefox = webservice.named_get(
535+ ... cprov_archive['self_link'], 'getPublishedBinaries',
536+ ... binary_name='mozilla-firefox').jsonBody()['entries'][0]
537+ >>> webservice.named_get(
538+ ... firefox['self_link'], 'getDownloadCount').jsonBody()
539+ 18
540+
541+Detailed download counts are also available from the getDownloadCounts method.
542+
543+ >>> counts = webservice.named_get(
544+ ... firefox['self_link'], 'getDownloadCounts').jsonBody()['entries']
545+ >>> len(counts)
546+ 2
547+
548+A detailed count object can be retrieved by its URL.
549+
550+ >>> pprint_entry(webservice.get(counts[1]['self_link']).jsonBody())
551+ archive_link: u'http://.../~cprov/+archive/ppa'
552+ binary_package_name: u'mozilla-firefox'
553+ binary_package_version: u'1.0'
554+ count: 10
555+ country_link: u'http://.../+countries/AU'
556+ day: u'2010-02-21'
557+ resource_type_link: u'http://.../#binary_package_release_download_count'
558+ self_link: u'http://.../~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-21/AU'
559+
560+We can also filter by date.
561+
562+ >>> counts = webservice.named_get(
563+ ... firefox['self_link'], 'getDownloadCounts',
564+ ... start_date='2010-02-22').jsonBody()['entries']
565+ >>> len(counts)
566+ 1
567+
568+ >>> pprint_entry(webservice.get(counts[0]['self_link']).jsonBody())
569+ archive_link: u'http://.../~cprov/+archive/ppa'
570+ binary_package_name: u'mozilla-firefox'
571+ binary_package_version: u'1.0'
572+ count: 8
573+ country_link: None
574+ day: u'2010-02-23'
575+ resource_type_link: u'http://.../#binary_package_release_download_count'
576+ self_link: u'http://.../~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/unknown'
577+
578+But other URLs result in a 404.
579+
580+ >>> print webservice.get(
581+ ... '/~cprov/+archive/ppa/+binaryhits/moz')
582+ HTTP/1.1 404 Not Found
583+ ...
584+
585+ >>> print webservice.get(
586+ ... '/~cprov/+archive/ppa/+binaryhits/phoenix/1.0/hppa/2010-02-23/unknown')
587+ HTTP/1.1 404 Not Found
588+ ...
589+
590+ >>> print webservice.get(
591+ ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.1/hppa/2010-02-23/unknown')
592+ HTTP/1.1 404 Not Found
593+ ...
594+
595+ >>> print webservice.get(
596+ ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/foo/2010-02-23/unknown')
597+ HTTP/1.1 404 Not Found
598+ ...
599+
600+ >>> print webservice.get(
601+ ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-25/unknown')
602+ HTTP/1.1 404 Not Found
603+ ...
604+
605+ >>> print webservice.get(
606+ ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/XX')
607+ HTTP/1.1 404 Not Found
608+ ...
609+
610+getDailyDownloadTotals returns a dict mapping dates to total counts.
611+
612+ >>> webservice.named_get(
613+ ... firefox['self_link'], 'getDailyDownloadTotals').jsonBody()
614+ {u'2010-02-21': 10, u'2010-02-23': 8}
615+
616
617=== modified file 'lib/lp/soyuz/tests/test_archive.py'
618--- lib/lp/soyuz/tests/test_archive.py 2010-03-16 08:27:03 +0000
619+++ lib/lp/soyuz/tests/test_archive.py 2010-03-22 07:42:21 +0000
620@@ -23,6 +23,7 @@
621 from lp.soyuz.interfaces.archive import (
622 IArchiveSet, ArchivePurpose, CannotSwitchPrivacy)
623 from lp.soyuz.interfaces.archivearch import IArchiveArchSet
624+from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
625 from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
626 from lp.soyuz.interfaces.processor import IProcessorFamilySet
627 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
628@@ -760,6 +761,87 @@
629 CannotSwitchPrivacy, self.make_ppa_public, self.private_ppa)
630
631
632+class TestGetBinaryPackageRelease(TestCaseWithFactory):
633+ """Ensure that getBinaryPackageRelease works as expected."""
634+
635+ layer = LaunchpadZopelessLayer
636+
637+ def setUp(self):
638+ """Setup an archive with relevant publications."""
639+ super(TestGetBinaryPackageRelease, self).setUp()
640+ self.publisher = SoyuzTestPublisher()
641+ self.publisher.prepareBreezyAutotest()
642+
643+ self.archive = self.factory.makeArchive()
644+ self.archive.require_virtualized = False
645+
646+ self.i386_pub, self.hppa_pub = self.publisher.getPubBinaries(
647+ version="1.2.3-4", archive=self.archive, binaryname="foo-bin",
648+ status=PackagePublishingStatus.PUBLISHED,
649+ architecturespecific=True)
650+
651+ self.i386_indep_pub, self.hppa_indep_pub = (
652+ self.publisher.getPubBinaries(
653+ version="1.2.3-4", archive=self.archive, binaryname="bar-bin",
654+ status=PackagePublishingStatus.PUBLISHED))
655+
656+ self.bpns = getUtility(IBinaryPackageNameSet)
657+
658+ def test_returns_matching_binarypackagerelease(self):
659+ # The BPR with a file by the given name should be returned.
660+ self.assertEqual(
661+ self.i386_pub.binarypackagerelease,
662+ self.archive.getBinaryPackageRelease(
663+ self.bpns['foo-bin'], '1.2.3-4', 'i386'))
664+
665+ def test_returns_correct_architecture(self):
666+ # The architecture is taken into account correctly.
667+ self.assertEqual(
668+ self.hppa_pub.binarypackagerelease,
669+ self.archive.getBinaryPackageRelease(
670+ self.bpns['foo-bin'], '1.2.3-4', 'hppa'))
671+
672+ def test_works_with_architecture_independent_binaries(self):
673+ # Architecture independent binaries with multiple publishings
674+ # are found properly.
675+ # We use 'i386' as the arch tag here, since what we have in the DB
676+ # is the *build* arch tag, not the one in the filename ('all').
677+ self.assertEqual(
678+ self.i386_indep_pub.binarypackagerelease,
679+ self.archive.getBinaryPackageRelease(
680+ self.bpns['bar-bin'], '1.2.3-4', 'i386'))
681+
682+ def test_returns_none_for_nonexistent_binary(self):
683+ # Non-existent files return None.
684+ self.assertIs(
685+ None,
686+ self.archive.getBinaryPackageRelease(
687+ self.bpns['cdrkit'], '1.2.3-4', 'i386'))
688+
689+ def test_returns_none_for_duplicate_file(self):
690+ # In the unlikely case of multiple BPRs in this archive with the same
691+ # name (hopefully impossible, but it still happens occasionally due
692+ # to bugs), None is returned.
693+
694+ # Publish the same binaries again. Evil.
695+ self.publisher.getPubBinaries(
696+ version="1.2.3-4", archive=self.archive, binaryname="foo-bin",
697+ status=PackagePublishingStatus.PUBLISHED,
698+ architecturespecific=True)
699+
700+ self.assertIs(
701+ None,
702+ self.archive.getBinaryPackageRelease(
703+ self.bpns['foo-bin'], '1.2.3-4', 'i386'))
704+
705+ def test_returns_none_from_another_archive(self):
706+ # Cross-archive searches are not performed.
707+ self.assertIs(
708+ None,
709+ self.factory.makeArchive().getBinaryPackageRelease(
710+ self.bpns['foo-bin'], '1.2.3-4', 'i386'))
711+
712+
713 class TestGetBinaryPackageReleaseByFileName(TestCaseWithFactory):
714 """Ensure that getBinaryPackageReleaseByFileName works as expected."""
715

Subscribers

People subscribed via source and target branches

to status/vote changes: