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
=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py 2010-03-19 05:19:20 +0000
+++ lib/canonical/launchpad/security.py 2010-03-22 07:42:21 +0000
@@ -71,6 +71,8 @@
71from lp.soyuz.interfaces.packageset import IPackageset, IPackagesetSet71from lp.soyuz.interfaces.packageset import IPackageset, IPackagesetSet
72from lp.translations.interfaces.pofile import IPOFile72from lp.translations.interfaces.pofile import IPOFile
73from lp.translations.interfaces.potemplate import IPOTemplate73from lp.translations.interfaces.potemplate import IPOTemplate
74from lp.soyuz.interfaces.binarypackagerelease import (
75 IBinaryPackageReleaseDownloadCount)
74from lp.soyuz.interfaces.build import IBuild76from lp.soyuz.interfaces.build import IBuild
75from lp.soyuz.interfaces.buildfarmbuildjob import IBuildFarmBuildJob77from lp.soyuz.interfaces.buildfarmbuildjob import IBuildFarmBuildJob
76from lp.soyuz.interfaces.publishing import (78from lp.soyuz.interfaces.publishing import (
@@ -2173,6 +2175,11 @@
2173 usedfor = IBinaryPackagePublishingHistory2175 usedfor = IBinaryPackagePublishingHistory
21742176
21752177
2178class ViewBinaryPackageReleaseDownloadCount(ViewSourcePackagePublishingHistory):
2179 """Restrict viewing of binary package download counts."""
2180 usedfor = IBinaryPackageReleaseDownloadCount
2181
2182
2176class ViewSourcePackageRelease(AuthorizationBase):2183class ViewSourcePackageRelease(AuthorizationBase):
2177 """Restrict viewing of source packages.2184 """Restrict viewing of source packages.
21782185
21792186
=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py 2010-03-10 12:50:18 +0000
+++ lib/lp/soyuz/browser/archive.py 2010-03-22 07:42:21 +0000
@@ -48,6 +48,7 @@
48from canonical.lazr.utils import smartquote48from canonical.lazr.utils import smartquote
49from lp.buildmaster.interfaces.buildbase import BuildStatus49from lp.buildmaster.interfaces.buildbase import BuildStatus
50from lp.services.browser_helpers import get_user_agent_distroseries50from lp.services.browser_helpers import get_user_agent_distroseries
51from lp.services.worlddata.interfaces.country import ICountrySet
51from lp.soyuz.browser.build import BuildRecordsView52from lp.soyuz.browser.build import BuildRecordsView
52from lp.soyuz.browser.sourceslist import (53from lp.soyuz.browser.sourceslist import (
53 SourcesListEntries, SourcesListEntriesView)54 SourcesListEntries, SourcesListEntriesView)
@@ -62,6 +63,7 @@
62from lp.soyuz.interfaces.archivepermission import (63from lp.soyuz.interfaces.archivepermission import (
63 ArchivePermissionType, IArchivePermissionSet)64 ArchivePermissionType, IArchivePermissionSet)
64from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet65from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet
66from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
65from lp.soyuz.interfaces.build import BuildSetStatus, IBuildSet67from lp.soyuz.interfaces.build import BuildSetStatus, IBuildSet
66from lp.soyuz.interfaces.buildrecords import IHasBuildRecords68from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
67from lp.soyuz.interfaces.component import IComponentSet69from lp.soyuz.interfaces.component import IComponentSet
@@ -217,6 +219,56 @@
217219
218 return None220 return None
219221
222 @stepthrough('+binaryhits')
223 def traverse_binaryhits(self, name_str):
224 """Traverse to an `IBinaryPackageReleaseDownloadCount`.
225
226 A matching path is something like this:
227
228 +binaryhits/foopkg/1.0/i386/2010-03-11/AU
229
230 To reach one where the country is None, use:
231
232 +binaryhits/foopkg/1.0/i386/2010-03-11/unknown
233 """
234
235 if len(self.request.stepstogo) < 4:
236 return None
237
238 version = self.request.stepstogo.consume()
239 archtag = self.request.stepstogo.consume()
240 date_str = self.request.stepstogo.consume()
241 country_str = self.request.stepstogo.consume()
242
243 try:
244 name = getUtility(IBinaryPackageNameSet)[name_str]
245 except NotFoundError:
246 return None
247
248 # This will return None if there are multiple BPRs with the same
249 # name in the archive's history, but in that case downloads
250 # won't be counted either.
251 bpr = self.context.getBinaryPackageRelease(name, version, archtag)
252 if bpr is None:
253 return None
254
255 try:
256 date = datetime.strptime(date_str, '%Y-%m-%d').date()
257 except ValueError:
258 return None
259
260 # 'unknown' should always be safe, since the key is the two letter
261 # ISO code, and 'unknown' has more than two letters.
262 if country_str == 'unknown':
263 country = None
264 else:
265 try:
266 country = getUtility(ICountrySet)[country_str]
267 except NotFoundError:
268 return None
269
270 return self.context.getPackageDownloadCount(bpr, date, country)
271
220 @stepthrough('+subscriptions')272 @stepthrough('+subscriptions')
221 def traverse_subscription(self, person_name):273 def traverse_subscription(self, person_name):
222 try:274 try:
223275
=== modified file 'lib/lp/soyuz/browser/configure.zcml'
--- lib/lp/soyuz/browser/configure.zcml 2010-03-16 07:31:35 +0000
+++ lib/lp/soyuz/browser/configure.zcml 2010-03-22 07:42:21 +0000
@@ -798,6 +798,11 @@
798 path_expression="string:+dependency/${dependency/id}"798 path_expression="string:+dependency/${dependency/id}"
799 attribute_to_parent="archive"799 attribute_to_parent="archive"
800 />800 />
801 <browser:url
802 for="lp.soyuz.interfaces.binarypackagerelease.IBinaryPackageReleaseDownloadCount"
803 path_expression="string:+binaryhits/${binary_package_release/name}/${binary_package_release/version}/${binary_package_release/build/distroarchseries/architecturetag}/${day}/${country/iso3166code2|string:unknown}"
804 attribute_to_parent="archive"
805 />
801806
802 <!-- XXX JeroenVermeulen 2010-03-13 bug=539395: This is buildmaster, not soyuz -->807 <!-- XXX JeroenVermeulen 2010-03-13 bug=539395: This is buildmaster, not soyuz -->
803 <browser:page808 <browser:page
804809
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2010-03-18 17:14:50 +0000
+++ lib/lp/soyuz/configure.zcml 2010-03-22 07:42:21 +0000
@@ -864,4 +864,12 @@
864 factory="lp.soyuz.model.binarypackagebuildbehavior.BinaryPackageBuildBehavior"864 factory="lp.soyuz.model.binarypackagebuildbehavior.BinaryPackageBuildBehavior"
865 permission="zope.Public" />865 permission="zope.Public" />
866866
867 <!-- BinaryPackageReleaseDownloadCount -->
868 <class
869 class="lp.soyuz.model.binarypackagerelease.BinaryPackageReleaseDownloadCount">
870 <require
871 permission="launchpad.View"
872 interface="lp.soyuz.interfaces.binarypackagerelease.IBinaryPackageReleaseDownloadCount"/>
873 </class>
874
867</configure>875</configure>
868876
=== modified file 'lib/lp/soyuz/doc/publishing.txt'
--- lib/lp/soyuz/doc/publishing.txt 2010-03-19 06:53:28 +0000
+++ lib/lp/soyuz/doc/publishing.txt 2010-03-22 07:42:21 +0000
@@ -956,11 +956,46 @@
956 0956 0
957957
958 >>> from datetime import date958 >>> from datetime import date
959 >>> bpph.archive.updatePackageDownloadCount(959 >>> from lp.services.worlddata.interfaces.country import ICountrySet
960 ... bpph.binarypackagerelease, date(2010, 2, 21), None, 10)960 >>> australia = getUtility(ICountrySet)['AU']
961 >>> uk = getUtility(ICountrySet)['GB']
962
963 >>> bpph.archive.updatePackageDownloadCount(
964 ... bpph.binarypackagerelease, date(2010, 2, 19), None, 2)
965 >>> bpph.archive.updatePackageDownloadCount(
966 ... bpph.binarypackagerelease, date(2010, 2, 21), australia, 10)
967 >>> bpph.archive.updatePackageDownloadCount(
968 ... bpph.binarypackagerelease, date(2010, 2, 21), uk, 4)
961969
962 >>> print bpph.getDownloadCount()970 >>> print bpph.getDownloadCount()
963 10971 16
972
973We can also use getDownloadCounts to find the raw download counts per
974day and country.
975
976 >>> [(b.day, b.country.name if b.country is not None else None)
977 ... for b in bpph.getDownloadCounts()]
978 [(datetime.date(2010, 2, 21), u'Australia'),
979 (datetime.date(2010, 2, 21), u'United Kingdom'),
980 (datetime.date(2010, 2, 19), None)]
981
982getDownloadCounts lets us filter by date.
983
984 >>> [b.day for b in bpph.getDownloadCounts(start_date=date(2010, 2, 21))]
985 [datetime.date(2010, 2, 21), datetime.date(2010, 2, 21)]
986 >>> [b.day for b in bpph.getDownloadCounts(end_date=date(2010, 2, 20))]
987 [datetime.date(2010, 2, 19)]
988 >>> [b.day for b in bpph.getDownloadCounts(
989 ... start_date=date(2010, 2, 20), end_date=date(2010, 2, 20))]
990 []
991
992We can also get a dict of totals for each day. The keys are strings to
993work around lazr.restful's dict limitations. This too has a date filter.
994
995 >>> bpph.getDailyDownloadTotals()
996 {'2010-02-21': 14L, '2010-02-19': 2L}
997 >>> bpph.getDailyDownloadTotals(start_date=date(2010, 2, 20))
998 {'2010-02-21': 14L}
964999
9651000
966IPublishingSet1001IPublishingSet
9671002
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2010-03-19 11:29:22 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2010-03-22 07:42:21 +0000
@@ -425,6 +425,18 @@
425 :return the corresponding `ILibraryFileAlias` is the file was found.425 :return the corresponding `ILibraryFileAlias` is the file was found.
426 """426 """
427427
428 def getBinaryPackageRelease(name, version, archtag):
429 """Find the specified `IBinaryPackageRelease` in the archive.
430
431 :param name: The `IBinaryPackageName` of the package.
432 :param version: The version of the package.
433 :param archtag: The architecture tag of the package's build. 'all'
434 will not work here -- 'i386' (the build DAS) must be used instead.
435
436 :return The binary package release with the given name and version,
437 or None if one does not exist or there is more than one.
438 """
439
428 def getBinaryPackageReleaseByFileName(filename):440 def getBinaryPackageReleaseByFileName(filename):
429 """Return the corresponding `IBinaryPackageRelease` in this context.441 """Return the corresponding `IBinaryPackageRelease` in this context.
430442
@@ -919,6 +931,9 @@
919 :return: A list of `IArchivePermission` records.931 :return: A list of `IArchivePermission` records.
920 """932 """
921933
934 def getPackageDownloadCount(bpr, day, country):
935 """Get the `IBinaryPackageDownloadCount` with the given key."""
936
922937
923class IArchiveAppend(Interface):938class IArchiveAppend(Interface):
924 """Archive interface for operations restricted by append privilege."""939 """Archive interface for operations restricted by append privilege."""
925940
=== modified file 'lib/lp/soyuz/interfaces/binarypackagerelease.py'
--- lib/lp/soyuz/interfaces/binarypackagerelease.py 2010-03-15 23:01:48 +0000
+++ lib/lp/soyuz/interfaces/binarypackagerelease.py 2010-03-22 07:42:21 +0000
@@ -16,13 +16,15 @@
16 ]16 ]
1717
18from lazr.enum import DBEnumeratedType, DBItem18from lazr.enum import DBEnumeratedType, DBItem
19from lazr.restful.fields import Reference19from lazr.restful.declarations import exported, export_as_webservice_entry
20from zope.schema import Bool, Choice, Date, Int, Text, TextLine, Datetime20from lazr.restful.fields import Reference, ReferenceChoice
21from zope.schema import Bool, Date, Int, Text, TextLine, Datetime
21from zope.interface import Interface, Attribute22from zope.interface import Interface, Attribute
2223
23from canonical.launchpad import _24from canonical.launchpad import _
24from canonical.launchpad.validators.version import valid_debian_version25from canonical.launchpad.validators.version import valid_debian_version
25from lp.soyuz.interfaces.archive import IArchive26from lp.soyuz.interfaces.archive import IArchive
27from lp.services.worlddata.interfaces.country import ICountry
2628
2729
28class IBinaryPackageRelease(Interface):30class IBinaryPackageRelease(Interface):
@@ -95,19 +97,31 @@
9597
96class IBinaryPackageReleaseDownloadCount(Interface):98class IBinaryPackageReleaseDownloadCount(Interface):
97 """Daily download count of a binary package release in an archive."""99 """Daily download count of a binary package release in an archive."""
100 export_as_webservice_entry()
98101
99 archive = Reference(102 id = Int(title=_('ID'), required=True, readonly=True)
100 title=_('The archive'), schema=IArchive, required=True,103 archive = exported(Reference(
101 readonly=True)104 title=_('Archive'), schema=IArchive, required=True,
105 readonly=True))
102 binary_package_release = Reference(106 binary_package_release = Reference(
103 title=_('The binary package release'), schema=IBinaryPackageRelease,107 title=_('The binary package release'), schema=IBinaryPackageRelease,
104 required=True, readonly=True)108 required=True, readonly=True)
105 day = Date(109 binary_package_name = exported(
106 title=_('The day of the downloads'), required=True, readonly=True)110 TextLine(
107 count = Int(111 title=_("Binary package name"),
108 title=_('The number of downloads'), required=True, readonly=False)112 required=False, readonly=True))
109 country = Choice(113 binary_package_version = exported(
110 title=_('Country'), required=False, vocabulary='CountryName')114 TextLine(
115 title=_("Binary package version"),
116 required=False, readonly=True))
117 day = exported(
118 Date(title=_('Day of the downloads'), required=True, readonly=True))
119 count = exported(
120 Int(title=_('Number of downloads'), required=True, readonly=True))
121 country = exported(
122 ReferenceChoice(
123 title=_('Country'), required=False, readonly=True,
124 vocabulary='CountryName', schema=ICountry))
111125
112126
113class BinaryPackageFileType(DBEnumeratedType):127class BinaryPackageFileType(DBEnumeratedType):
114128
=== modified file 'lib/lp/soyuz/interfaces/publishing.py'
--- lib/lp/soyuz/interfaces/publishing.py 2010-03-11 02:26:37 +0000
+++ lib/lp/soyuz/interfaces/publishing.py 2010-03-22 07:42:21 +0000
@@ -29,7 +29,7 @@
29 'name_priority_map',29 'name_priority_map',
30 ]30 ]
3131
32from zope.schema import Choice, Datetime, Int, TextLine, Text32from zope.schema import Choice, Date, Datetime, Int, TextLine, Text
33from zope.interface import Interface, Attribute33from zope.interface import Interface, Attribute
34from lazr.enum import DBEnumeratedType, DBItem34from lazr.enum import DBEnumeratedType, DBItem
3535
@@ -37,6 +37,8 @@
37from lp.registry.interfaces.distroseries import IDistroSeries37from lp.registry.interfaces.distroseries import IDistroSeries
38from lp.registry.interfaces.person import IPerson38from lp.registry.interfaces.person import IPerson
39from lp.registry.interfaces.pocket import PackagePublishingPocket39from lp.registry.interfaces.pocket import PackagePublishingPocket
40from lp.soyuz.interfaces.binarypackagerelease import (
41 IBinaryPackageReleaseDownloadCount)
4042
41from lazr.restful.fields import Reference43from lazr.restful.fields import Reference
42from lazr.restful.declarations import (44from lazr.restful.declarations import (
@@ -862,6 +864,29 @@
862864
863 This is currently only meaningful for PPAs."""865 This is currently only meaningful for PPAs."""
864866
867 @operation_parameters(
868 start_date=Date(title=_("Start date"), required=False),
869 end_date=Date(title=_("End date"), required=False))
870 @operation_returns_collection_of(IBinaryPackageReleaseDownloadCount)
871 @export_read_operation()
872 def getDownloadCounts(start_date=None, end_date=None):
873 """Get detailed download counts for this binary.
874
875 :param start_date: The optional first date to return.
876 :param end_date: The optional last date to return.
877 """
878
879 @operation_parameters(
880 start_date=Date(title=_("Start date"), required=False),
881 end_date=Date(title=_("End date"), required=False))
882 @export_read_operation()
883 def getDailyDownloadTotals(start_date=None, end_date=None):
884 """Get the daily download counts for this binary.
885
886 :param start_date: The optional first date to return.
887 :param end_date: The optional last date to return.
888 """
889
865890
866class IBinaryPackagePublishingHistory(IBinaryPackagePublishingHistoryPublic,891class IBinaryPackagePublishingHistory(IBinaryPackagePublishingHistoryPublic,
867 IPublishingEdit):892 IPublishingEdit):
868893
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2010-03-19 11:29:22 +0000
+++ lib/lp/soyuz/model/archive.py 2010-03-22 07:42:21 +0000
@@ -1133,6 +1133,25 @@
11331133
1134 return archive_file1134 return archive_file
11351135
1136 def getBinaryPackageRelease(self, name, version, archtag):
1137 """See `IArchive`."""
1138 from lp.soyuz.model.distroarchseries import DistroArchSeries
1139
1140 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1141 results = store.find(
1142 BinaryPackageRelease,
1143 BinaryPackageRelease.binarypackagename == name,
1144 BinaryPackageRelease.version == version,
1145 Build.id == BinaryPackageRelease.buildID,
1146 DistroArchSeries.id == Build.distroarchseriesID,
1147 DistroArchSeries.architecturetag == archtag,
1148 BinaryPackagePublishingHistory.archive == self,
1149 BinaryPackagePublishingHistory.binarypackagereleaseID ==
1150 BinaryPackageRelease.id).config(distinct=True)
1151 if results.count() > 1:
1152 return None
1153 return results.one()
1154
1136 def getBinaryPackageReleaseByFileName(self, filename):1155 def getBinaryPackageReleaseByFileName(self, filename):
1137 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)1156 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
1138 results = store.find(1157 results = store.find(
@@ -1355,6 +1374,12 @@
1355 ).one()1374 ).one()
1356 return count or 01375 return count or 0
13571376
1377 def getPackageDownloadCount(self, bpr, day, country):
1378 """See `IArchive`."""
1379 return Store.of(self).find(
1380 BinaryPackageReleaseDownloadCount, archive=self,
1381 binary_package_release=bpr, day=day, country=country).one()
1382
1358 def _setBuildStatuses(self, status):1383 def _setBuildStatuses(self, status):
1359 """Update the pending Build Jobs' status for this archive."""1384 """Update the pending Build Jobs' status for this archive."""
13601385
13611386
=== modified file 'lib/lp/soyuz/model/binarypackagerelease.py'
--- lib/lp/soyuz/model/binarypackagerelease.py 2010-03-15 23:01:48 +0000
+++ lib/lp/soyuz/model/binarypackagerelease.py 2010-03-22 07:42:21 +0000
@@ -230,3 +230,14 @@
230 self.day = day230 self.day = day
231 self.country = country231 self.country = country
232 self.count = count232 self.count = count
233
234 @property
235 def binary_package_name(self):
236 """See `IBinaryPackageReleaseDownloadCount`."""
237 return self.binary_package_release.name
238
239 @property
240 def binary_package_version(self):
241 """See `IBinaryPackageReleaseDownloadCount`."""
242 return self.binary_package_release.version
243
233244
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2010-03-17 09:23:40 +0000
+++ lib/lp/soyuz/model/publishing.py 2010-03-22 07:42:21 +0000
@@ -28,7 +28,7 @@
2828
29from sqlobject import ForeignKey, StringCol29from sqlobject import ForeignKey, StringCol
3030
31from storm.expr import Desc, In, LeftJoin31from storm.expr import Desc, In, LeftJoin, Sum
32from storm.store import Store32from storm.store import Store
3333
34from canonical.database.sqlbase import SQLBase, sqlvalues34from canonical.database.sqlbase import SQLBase, sqlvalues
@@ -43,8 +43,10 @@
43from lp.buildmaster.interfaces.buildbase import BuildStatus43from lp.buildmaster.interfaces.buildbase import BuildStatus
44from lp.registry.interfaces.person import validate_public_person44from lp.registry.interfaces.person import validate_public_person
45from lp.registry.interfaces.pocket import PackagePublishingPocket45from lp.registry.interfaces.pocket import PackagePublishingPocket
46from lp.services.worlddata.model.country import Country
46from lp.soyuz.model.binarypackagename import BinaryPackageName47from lp.soyuz.model.binarypackagename import BinaryPackageName
47from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease48from lp.soyuz.model.binarypackagerelease import (BinaryPackageRelease,
49 BinaryPackageReleaseDownloadCount)
48from lp.soyuz.model.files import (50from lp.soyuz.model.files import (
49 BinaryPackageFile, SourcePackageReleaseFile)51 BinaryPackageFile, SourcePackageReleaseFile)
50from canonical.launchpad.database.librarian import (52from canonical.launchpad.database.librarian import (
@@ -1010,6 +1012,50 @@
10101012
1011 self.component = component1013 self.component = component
10121014
1015 def _getDownloadCountClauses(self, start_date=None, end_date=None):
1016 clauses = [
1017 BinaryPackageReleaseDownloadCount.archive == self.archive,
1018 BinaryPackageReleaseDownloadCount.binary_package_release ==
1019 self.binarypackagerelease,
1020 ]
1021
1022 if start_date is not None:
1023 clauses.append(
1024 BinaryPackageReleaseDownloadCount.day >= start_date)
1025 if end_date is not None:
1026 clauses.append(
1027 BinaryPackageReleaseDownloadCount.day <= end_date)
1028
1029 return clauses
1030
1031 def getDownloadCounts(self, start_date=None, end_date=None):
1032 """See `IBinaryPackagePublishingHistory`."""
1033 clauses = self._getDownloadCountClauses(start_date, end_date)
1034
1035 return Store.of(self).using(
1036 BinaryPackageReleaseDownloadCount,
1037 LeftJoin(
1038 Country,
1039 BinaryPackageReleaseDownloadCount.country_id ==
1040 Country.id)).find(
1041 BinaryPackageReleaseDownloadCount, *clauses).order_by(
1042 Desc(BinaryPackageReleaseDownloadCount.day), Country.name)
1043
1044 def getDailyDownloadTotals(self, start_date=None, end_date=None):
1045 """See `IBinaryPackagePublishingHistory`."""
1046 clauses = self._getDownloadCountClauses(start_date, end_date)
1047
1048 results = Store.of(self).find(
1049 (BinaryPackageReleaseDownloadCount.day,
1050 Sum(BinaryPackageReleaseDownloadCount.count)),
1051 *clauses).group_by(
1052 BinaryPackageReleaseDownloadCount.day)
1053
1054 def date_to_string(result):
1055 return (result[0].strftime('%Y-%m-%d'), result[1])
1056
1057 return dict(date_to_string(result) for result in results)
1058
10131059
1014class PublishingSet:1060class PublishingSet:
1015 """Utilities for manipulating publications in batches."""1061 """Utilities for manipulating publications in batches."""
10161062
=== modified file 'lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt'
--- lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2010-03-18 22:47:27 +0000
+++ lib/lp/soyuz/stories/webservice/xx-binary-package-publishing.txt 2010-03-22 07:42:21 +0000
@@ -86,24 +86,6 @@
86 self_link: u'http://.../~cprov/+archive/ppa/+binarypub/30'86 self_link: u'http://.../~cprov/+archive/ppa/+binarypub/30'
87 status: u'Published'87 status: u'Published'
8888
89We can also retrieve the total download count for the binary in this archive.
90
91 >>> webservice.named_get(
92 ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
93 0
94
95 >>> login("foo.bar@canonical.com")
96 >>> from datetime import date
97 >>> from lp.soyuz.model.publishing import BinaryPackagePublishingHistory
98 >>> bpph = BinaryPackagePublishingHistory.get(30)
99 >>> bpph.archive.updatePackageDownloadCount(
100 ... bpph.binarypackagerelease, date(2010, 2, 21), None, 10)
101 >>> logout()
102
103 >>> webservice.named_get(
104 ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
105 10
106
10789
108Security90Security
109========91========
@@ -158,3 +140,111 @@
158 >>> print response140 >>> print response
159 HTTP/1.1 401 Unauthorized141 HTTP/1.1 401 Unauthorized
160 ...142 ...
143
144
145Download counts
146===============
147
148We can retrieve the total download count for a binary in this archive.
149
150 >>> webservice.named_get(
151 ... pubs['entries'][0]['self_link'], 'getDownloadCount').jsonBody()
152 0
153
154 >>> login("foo.bar@canonical.com")
155
156 >>> from datetime import date
157 >>> from lp.services.worlddata.interfaces.country import ICountrySet
158 >>> australia = getUtility(ICountrySet)['AU']
159
160 >>> firefox_db = cprov_db.archive.getAllPublishedBinaries(
161 ... name='mozilla-firefox')[0]
162 >>> firefox_db.archive.updatePackageDownloadCount(
163 ... firefox_db.binarypackagerelease, date(2010, 2, 21), australia, 10)
164 >>> firefox_db.archive.updatePackageDownloadCount(
165 ... firefox_db.binarypackagerelease, date(2010, 2, 23), None, 8)
166
167 >>> logout()
168
169 >>> firefox = webservice.named_get(
170 ... cprov_archive['self_link'], 'getPublishedBinaries',
171 ... binary_name='mozilla-firefox').jsonBody()['entries'][0]
172 >>> webservice.named_get(
173 ... firefox['self_link'], 'getDownloadCount').jsonBody()
174 18
175
176Detailed download counts are also available from the getDownloadCounts method.
177
178 >>> counts = webservice.named_get(
179 ... firefox['self_link'], 'getDownloadCounts').jsonBody()['entries']
180 >>> len(counts)
181 2
182
183A detailed count object can be retrieved by its URL.
184
185 >>> pprint_entry(webservice.get(counts[1]['self_link']).jsonBody())
186 archive_link: u'http://.../~cprov/+archive/ppa'
187 binary_package_name: u'mozilla-firefox'
188 binary_package_version: u'1.0'
189 count: 10
190 country_link: u'http://.../+countries/AU'
191 day: u'2010-02-21'
192 resource_type_link: u'http://.../#binary_package_release_download_count'
193 self_link: u'http://.../~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-21/AU'
194
195We can also filter by date.
196
197 >>> counts = webservice.named_get(
198 ... firefox['self_link'], 'getDownloadCounts',
199 ... start_date='2010-02-22').jsonBody()['entries']
200 >>> len(counts)
201 1
202
203 >>> pprint_entry(webservice.get(counts[0]['self_link']).jsonBody())
204 archive_link: u'http://.../~cprov/+archive/ppa'
205 binary_package_name: u'mozilla-firefox'
206 binary_package_version: u'1.0'
207 count: 8
208 country_link: None
209 day: u'2010-02-23'
210 resource_type_link: u'http://.../#binary_package_release_download_count'
211 self_link: u'http://.../~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/unknown'
212
213But other URLs result in a 404.
214
215 >>> print webservice.get(
216 ... '/~cprov/+archive/ppa/+binaryhits/moz')
217 HTTP/1.1 404 Not Found
218 ...
219
220 >>> print webservice.get(
221 ... '/~cprov/+archive/ppa/+binaryhits/phoenix/1.0/hppa/2010-02-23/unknown')
222 HTTP/1.1 404 Not Found
223 ...
224
225 >>> print webservice.get(
226 ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.1/hppa/2010-02-23/unknown')
227 HTTP/1.1 404 Not Found
228 ...
229
230 >>> print webservice.get(
231 ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/foo/2010-02-23/unknown')
232 HTTP/1.1 404 Not Found
233 ...
234
235 >>> print webservice.get(
236 ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-25/unknown')
237 HTTP/1.1 404 Not Found
238 ...
239
240 >>> print webservice.get(
241 ... '/~cprov/+archive/ppa/+binaryhits/mozilla-firefox/1.0/hppa/2010-02-23/XX')
242 HTTP/1.1 404 Not Found
243 ...
244
245getDailyDownloadTotals returns a dict mapping dates to total counts.
246
247 >>> webservice.named_get(
248 ... firefox['self_link'], 'getDailyDownloadTotals').jsonBody()
249 {u'2010-02-21': 10, u'2010-02-23': 8}
250
161251
=== modified file 'lib/lp/soyuz/tests/test_archive.py'
--- lib/lp/soyuz/tests/test_archive.py 2010-03-16 08:27:03 +0000
+++ lib/lp/soyuz/tests/test_archive.py 2010-03-22 07:42:21 +0000
@@ -23,6 +23,7 @@
23from lp.soyuz.interfaces.archive import (23from lp.soyuz.interfaces.archive import (
24 IArchiveSet, ArchivePurpose, CannotSwitchPrivacy)24 IArchiveSet, ArchivePurpose, CannotSwitchPrivacy)
25from lp.soyuz.interfaces.archivearch import IArchiveArchSet25from lp.soyuz.interfaces.archivearch import IArchiveArchSet
26from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
26from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat27from lp.soyuz.interfaces.binarypackagerelease import BinaryPackageFormat
27from lp.soyuz.interfaces.processor import IProcessorFamilySet28from lp.soyuz.interfaces.processor import IProcessorFamilySet
28from lp.soyuz.interfaces.publishing import PackagePublishingStatus29from lp.soyuz.interfaces.publishing import PackagePublishingStatus
@@ -760,6 +761,87 @@
760 CannotSwitchPrivacy, self.make_ppa_public, self.private_ppa)761 CannotSwitchPrivacy, self.make_ppa_public, self.private_ppa)
761762
762763
764class TestGetBinaryPackageRelease(TestCaseWithFactory):
765 """Ensure that getBinaryPackageRelease works as expected."""
766
767 layer = LaunchpadZopelessLayer
768
769 def setUp(self):
770 """Setup an archive with relevant publications."""
771 super(TestGetBinaryPackageRelease, self).setUp()
772 self.publisher = SoyuzTestPublisher()
773 self.publisher.prepareBreezyAutotest()
774
775 self.archive = self.factory.makeArchive()
776 self.archive.require_virtualized = False
777
778 self.i386_pub, self.hppa_pub = self.publisher.getPubBinaries(
779 version="1.2.3-4", archive=self.archive, binaryname="foo-bin",
780 status=PackagePublishingStatus.PUBLISHED,
781 architecturespecific=True)
782
783 self.i386_indep_pub, self.hppa_indep_pub = (
784 self.publisher.getPubBinaries(
785 version="1.2.3-4", archive=self.archive, binaryname="bar-bin",
786 status=PackagePublishingStatus.PUBLISHED))
787
788 self.bpns = getUtility(IBinaryPackageNameSet)
789
790 def test_returns_matching_binarypackagerelease(self):
791 # The BPR with a file by the given name should be returned.
792 self.assertEqual(
793 self.i386_pub.binarypackagerelease,
794 self.archive.getBinaryPackageRelease(
795 self.bpns['foo-bin'], '1.2.3-4', 'i386'))
796
797 def test_returns_correct_architecture(self):
798 # The architecture is taken into account correctly.
799 self.assertEqual(
800 self.hppa_pub.binarypackagerelease,
801 self.archive.getBinaryPackageRelease(
802 self.bpns['foo-bin'], '1.2.3-4', 'hppa'))
803
804 def test_works_with_architecture_independent_binaries(self):
805 # Architecture independent binaries with multiple publishings
806 # are found properly.
807 # We use 'i386' as the arch tag here, since what we have in the DB
808 # is the *build* arch tag, not the one in the filename ('all').
809 self.assertEqual(
810 self.i386_indep_pub.binarypackagerelease,
811 self.archive.getBinaryPackageRelease(
812 self.bpns['bar-bin'], '1.2.3-4', 'i386'))
813
814 def test_returns_none_for_nonexistent_binary(self):
815 # Non-existent files return None.
816 self.assertIs(
817 None,
818 self.archive.getBinaryPackageRelease(
819 self.bpns['cdrkit'], '1.2.3-4', 'i386'))
820
821 def test_returns_none_for_duplicate_file(self):
822 # In the unlikely case of multiple BPRs in this archive with the same
823 # name (hopefully impossible, but it still happens occasionally due
824 # to bugs), None is returned.
825
826 # Publish the same binaries again. Evil.
827 self.publisher.getPubBinaries(
828 version="1.2.3-4", archive=self.archive, binaryname="foo-bin",
829 status=PackagePublishingStatus.PUBLISHED,
830 architecturespecific=True)
831
832 self.assertIs(
833 None,
834 self.archive.getBinaryPackageRelease(
835 self.bpns['foo-bin'], '1.2.3-4', 'i386'))
836
837 def test_returns_none_from_another_archive(self):
838 # Cross-archive searches are not performed.
839 self.assertIs(
840 None,
841 self.factory.makeArchive().getBinaryPackageRelease(
842 self.bpns['foo-bin'], '1.2.3-4', 'i386'))
843
844
763class TestGetBinaryPackageReleaseByFileName(TestCaseWithFactory):845class TestGetBinaryPackageReleaseByFileName(TestCaseWithFactory):
764 """Ensure that getBinaryPackageReleaseByFileName works as expected."""846 """Ensure that getBinaryPackageReleaseByFileName works as expected."""
765847

Subscribers

People subscribed via source and target branches

to status/vote changes: