Merge lp:~stevenk/launchpad/populate-searchables-for-pu into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 16371
Proposed branch: lp:~stevenk/launchpad/populate-searchables-for-pu
Merge into: lp:launchpad
Diff against target: 619 lines (+294/-56)
9 files modified
database/schema/security.cfg (+1/-0)
lib/lp/registry/model/distroseries.py (+2/-3)
lib/lp/scripts/garbo.py (+128/-0)
lib/lp/scripts/tests/test_garbo.py (+49/-0)
lib/lp/soyuz/configure.zcml (+3/-1)
lib/lp/soyuz/interfaces/queue.py (+5/-2)
lib/lp/soyuz/model/queue.py (+66/-48)
lib/lp/soyuz/tests/test_packageupload.py (+39/-1)
lib/lp/testing/factory.py (+1/-1)
To merge this branch: bzr merge lp:~stevenk/launchpad/populate-searchables-for-pu
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+138906@code.launchpad.net

Commit message

Populate the new searchable_names and searchable_versions columns on PackageUpload.

Description of the change

Populate the new searchable_names and searchable_versions columns on PackageUpload. The queries the garbo job are running are pretty hideous, but they're temporary and actually quite performant -- dogfood is averaging 840 rows each loop through.

LoC justification is the garbo job is temporary and will be dropped when it's done, and after that we can delete most of IPackageUploadSet.get_all() and the horrible 8 table joins it does today -- that's roughly a 140 line drop. By my calculations, we end up at -50 lines, even including the +36 from three or four database patches.

This can not land until https://code.launchpad.net/~stevenk/launchpad/db-searchableversions-for-pu/+merge/136843 has landed and been deployed.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

20 + searchable_names = None
21 + searchable_versions = None
22 + if package_copy_job is not None:
23 + searchable_names = package_copy_job.package_name
24 + searchable_versions = [package_copy_job.package_version]

Could you make it clear that the non-PCJ case is handled when the PU[SBC] are created? Wouldn't it also make more sense to set searchable_names and searchable_versions to '' and [] respectively in the constructor, and then use the normal add mechanism with the PCJ data?

74 + return self.store.find(
75 + (PackageUpload.id,), PackageUpload.searchable_names == None,
76 + PackageUpload.searchable_versions == None,
77 + PackageUpload.id >= self.start_at).order_by(PackageUpload.id)

I generally prefer to the start the conditions on the line after the findspec, so it's easier to scan.

443 + def setSearchableNames(self, names):
444 + self.searchable_names = ' '.join(
445 + self._appendSearchables(self.searchable_names, names))
446 +
447 + def setSearchableVersions(self, versions):
448 + self.searchable_versions = self._appendSearchables(
449 + self.searchable_versions, versions)

These look like they actually add, not set. It would also be nice to sort the sets (probably in _appendSearchables), so that this is all deterministic.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2012-12-03 21:56:19 +0000
+++ database/schema/security.cfg 2012-12-14 00:18:23 +0000
@@ -2253,6 +2253,7 @@
2253public.oauthnonce = SELECT, DELETE2253public.oauthnonce = SELECT, DELETE
2254public.openidconsumerassociation = SELECT, DELETE2254public.openidconsumerassociation = SELECT, DELETE
2255public.openidconsumernonce = SELECT, DELETE2255public.openidconsumernonce = SELECT, DELETE
2256public.packageupload = SELECT, UPDATE
2256public.person = SELECT, DELETE2257public.person = SELECT, DELETE
2257public.product = SELECT, UPDATE2258public.product = SELECT, UPDATE
2258public.pofiletranslator = SELECT, INSERT, UPDATE, DELETE2259public.pofiletranslator = SELECT, INSERT, UPDATE, DELETE
22592260
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2012-12-06 13:33:46 +0000
+++ lib/lp/registry/model/distroseries.py 2012-12-14 00:18:23 +0000
@@ -1342,9 +1342,8 @@
13421342
1343 return PackageUpload(1343 return PackageUpload(
1344 distroseries=self, status=PackageUploadStatus.NEW,1344 distroseries=self, status=PackageUploadStatus.NEW,
1345 pocket=pocket, archive=archive,1345 pocket=pocket, archive=archive, changesfile=changes_file_alias,
1346 changesfile=changes_file_alias, signing_key=signing_key,1346 signing_key=signing_key, package_copy_job=package_copy_job)
1347 package_copy_job=package_copy_job)
13481347
1349 def getPackageUploadQueue(self, state):1348 def getPackageUploadQueue(self, state):
1350 """See `IDistroSeries`."""1349 """See `IDistroSeries`."""
13511350
=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py 2012-12-04 16:34:40 +0000
+++ lib/lp/scripts/garbo.py 2012-12-14 00:18:23 +0000
@@ -18,6 +18,7 @@
18 )18 )
19import logging19import logging
20import multiprocessing20import multiprocessing
21from operator import itemgetter
21import os22import os
22import threading23import threading
23import time24import time
@@ -122,6 +123,7 @@
122from lp.services.verification.model.logintoken import LoginToken123from lp.services.verification.model.logintoken import LoginToken
123from lp.soyuz.model.archive import Archive124from lp.soyuz.model.archive import Archive
124from lp.soyuz.model.publishing import SourcePackagePublishingHistory125from lp.soyuz.model.publishing import SourcePackagePublishingHistory
126from lp.soyuz.model.queue import PackageUpload
125from lp.soyuz.model.reporting import LatestPersonSourcePackageReleaseCache127from lp.soyuz.model.reporting import LatestPersonSourcePackageReleaseCache
126from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease128from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
127from lp.translations.interfaces.potemplate import IPOTemplateSet129from lp.translations.interfaces.potemplate import IPOTemplateSet
@@ -1337,6 +1339,131 @@
1337 transaction.commit()1339 transaction.commit()
13381340
13391341
1342class PopulatePackageUploadSearchables(TunableLoop):
1343 """Populates PackageUpload.searchable_names and
1344 PackageUpload.searchable_versions."""
1345
1346 maximum_chunk_size = 5000
1347
1348 def __init__(self, log, abort_time=None):
1349 super(PopulatePackageUploadSearchables, self).__init__(log, abort_time)
1350 self.start_at = 1
1351 self.store = IMasterStore(PackageUpload)
1352
1353 def findPackageUploadIDs(self):
1354 return self.store.find(
1355 (PackageUpload.id,),
1356 Or(PackageUpload.searchable_names == None,
1357 PackageUpload.searchable_versions == None),
1358 PackageUpload.id >= self.start_at).order_by(PackageUpload.id)
1359
1360 def isDone(self):
1361 return self.findPackageUploadIDs().is_empty()
1362
1363 def __call__(self, chunk_size):
1364 packageupload_ids = map(
1365 itemgetter(0), list(self.findPackageUploadIDs()[:chunk_size]))
1366 # The following SQL links from PU[SBC] to fetch all of the relevant
1367 # source names, binary names, libraryfile filenames and their versions.
1368 results = self.store.find(
1369 (PackageUpload.id, SQL("""
1370 (SELECT COALESCE(
1371 string_agg(DISTINCT name, ' ' ORDER BY name), '') FROM (
1372 (SELECT spn.name
1373 FROM
1374 packageuploadbuild
1375 JOIN binarypackagebuild AS bpb ON
1376 bpb.id = packageuploadbuild.build
1377 JOIN sourcepackagerelease AS spr ON
1378 spr.id = bpb.source_package_release
1379 JOIN sourcepackagename AS spn ON
1380 spn.id = spr.sourcepackagename
1381 WHERE packageuploadbuild.packageupload = packageupload.id
1382 )
1383 UNION
1384 (SELECT bpn.name
1385 FROM
1386 packageuploadbuild
1387 JOIN binarypackagerelease ON
1388 binarypackagerelease.build = packageuploadbuild.build
1389 JOIN binarypackagename AS bpn ON
1390 bpn.id = binarypackagerelease.binarypackagename
1391 WHERE packageuploadbuild.packageupload = packageupload.id
1392 )
1393 UNION
1394 (SELECT sourcepackagename.name
1395 FROM
1396 packageuploadsource
1397 JOIN sourcepackagerelease AS spr ON
1398 spr.id = packageuploadsource.sourcepackagerelease
1399 JOIN sourcepackagename ON
1400 sourcepackagename.id = spr.sourcepackagename
1401 WHERE packageuploadsource.packageupload = packageupload.id
1402 )
1403 UNION
1404 (SELECT lfa.filename
1405 FROM
1406 packageuploadcustom
1407 JOIN libraryfilealias AS lfa ON
1408 lfa.id = packageuploadcustom.libraryfilealias
1409 WHERE packageuploadcustom.packageupload = packageupload.id
1410 )
1411 UNION
1412 (SELECT package_name FROM packagecopyjob
1413 WHERE packageupload.package_copy_job = packagecopyjob.id
1414 )) AS names (name))
1415 """), SQL("""
1416 (SELECT COALESCE(array_agg(DISTINCT version ORDER BY version)::text[],
1417 ARRAY[]::text[]) FROM (
1418 (
1419 SELECT spr.version
1420 FROM packageuploadsource
1421 JOIN sourcepackagerelease AS spr ON
1422 spr.id = packageuploadsource.sourcepackagerelease
1423 WHERE packageuploadsource.packageupload = packageupload.id
1424 )
1425 UNION
1426 (
1427 SELECT binarypackagerelease.version
1428 FROM packageuploadbuild
1429 JOIN binarypackagerelease ON
1430 binarypackagerelease.build = packageuploadbuild.build
1431 WHERE packageuploadbuild.packageupload = packageupload.id
1432 )
1433 UNION
1434 (
1435 SELECT (regexp_matches(json_data,
1436 '"package_version": "([^"]+)"')::debversion[])[1]
1437 FROM packagecopyjob
1438 WHERE packageupload.package_copy_job = packagecopyjob.id
1439 )) AS versions (version))
1440 """)), PackageUpload.id.is_in(packageupload_ids))
1441 # Construct our cache data and populate our Values expression.
1442 cache_data = ClassAlias(PackageUpload, "cache_data")
1443 updated_columns = dict(
1444 [(PackageUpload.searchable_names, cache_data.searchable_names),
1445 (PackageUpload.searchable_versions,
1446 cache_data.searchable_versions)])
1447 values = [
1448 [dbify_value(col, val)[0]
1449 for (col, val) in zip(
1450 (PackageUpload.id, PackageUpload.searchable_names,
1451 PackageUpload.searchable_versions), data)]
1452 for data in results]
1453 cols = [
1454 ('id', 'integer'), ('searchable_names', 'text'),
1455 ('searchable_versions', 'text[]')]
1456 cache_data_expr = Values('cache_data', cols, values)
1457 # Using the PackageUpload table, and the pseudo-table Values, set
1458 # updated_columns for every row in this loop.
1459 self.store.execute(
1460 BulkUpdate(
1461 updated_columns, table=PackageUpload, values=cache_data_expr,
1462 where=PackageUpload.id == cache_data.id))
1463 self.start_at = packageupload_ids[-1] + 1
1464 transaction.commit()
1465
1466
1340class BaseDatabaseGarbageCollector(LaunchpadCronScript):1467class BaseDatabaseGarbageCollector(LaunchpadCronScript):
1341 """Abstract base class to run a collection of TunableLoops."""1468 """Abstract base class to run a collection of TunableLoops."""
1342 script_name = None # Script name for locking and database user. Override.1469 script_name = None # Script name for locking and database user. Override.
@@ -1592,6 +1719,7 @@
1592 UnusedSessionPruner,1719 UnusedSessionPruner,
1593 DuplicateSessionPruner,1720 DuplicateSessionPruner,
1594 BugHeatUpdater,1721 BugHeatUpdater,
1722 PopulatePackageUploadSearchables,
1595 ]1723 ]
1596 experimental_tunable_loops = []1724 experimental_tunable_loops = []
15971725
15981726
=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py 2012-12-04 16:34:40 +0000
+++ lib/lp/scripts/tests/test_garbo.py 2012-12-14 00:18:23 +0000
@@ -1275,6 +1275,55 @@
1275 'PopulateLatestPersonSourcePackageReleaseCache')1275 'PopulateLatestPersonSourcePackageReleaseCache')
1276 self.assertEqual(spph_2.id, job_data['last_spph_id'])1276 self.assertEqual(spph_2.id, job_data['last_spph_id'])
12771277
1278 def test_PopulatePackageUploadSearchables(self):
1279 # PopulatePackageUploadSearchables sets searchable_names and
1280 # searchable_versions for existing uploads correctly.
1281 switch_dbuser('testadmin')
1282 distroseries = self.factory.makeDistroSeries()
1283 source = self.factory.makeSourcePackageUpload(distroseries)
1284 binary = self.factory.makeBuildPackageUpload(distroseries)
1285 build = self.factory.makeBinaryPackageBuild()
1286 self.factory.makeBinaryPackageRelease(build=build)
1287 binary.addBuild(build)
1288 custom = self.factory.makeCustomPackageUpload(distroseries)
1289 # They are all have searchable_{names,versions} set, so unset them.
1290 for kind in (source, binary, custom):
1291 removeSecurityProxy(kind).searchable_names = None
1292 removeSecurityProxy(kind).searchable_versions = None
1293 transaction.commit()
1294 self.runHourly()
1295 source_name = source.sources[0].sourcepackagerelease.name
1296 binary_names = ' '.join(
1297 [build.build.binarypackages[0].name for build in binary.builds] + [
1298 build.build.source_package_release.name
1299 for build in binary.builds])
1300 filename = custom.customfiles[0].libraryfilealias.filename
1301 self.assertEqual(source.searchable_names, source_name)
1302 self.assertEqual(binary.searchable_names, binary_names)
1303 self.assertEqual(custom.searchable_names, filename)
1304 source_version = [source.sources[0].sourcepackagerelease.version]
1305 binary_versions = [
1306 build.build.binarypackages[0].version for build in binary.builds]
1307 self.assertContentEqual(source_version, source.searchable_versions)
1308 self.assertContentEqual(binary_versions, binary.searchable_versions)
1309 self.assertEqual([], custom.searchable_versions)
1310
1311 def test_PopulatePackageUploadSearchables_deduplication(self):
1312 # When the SPN and the BPN are the same for a build, the
1313 # searchable_names field is set to just one name.
1314 switch_dbuser('testadmin')
1315 distroseries = self.factory.makeDistroSeries()
1316 spr = self.factory.makeSourcePackageRelease()
1317 bpn = self.factory.makeBinaryPackageName(name=spr.name)
1318 binary = self.factory.makeBuildPackageUpload(
1319 distroseries=distroseries, binarypackagename=bpn,
1320 source_package_release=spr)
1321 removeSecurityProxy(binary).searchable_names = None
1322 removeSecurityProxy(binary).searchable_versions = None
1323 transaction.commit()
1324 self.runHourly()
1325 self.assertEqual(spr.name, binary.searchable_names)
1326
12781327
1279class TestGarboTasks(TestCaseWithFactory):1328class TestGarboTasks(TestCaseWithFactory):
1280 layer = LaunchpadZopelessLayer1329 layer = LaunchpadZopelessLayer
12811330
=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml 2012-11-15 01:41:14 +0000
+++ lib/lp/soyuz/configure.zcml 2012-12-14 00:18:23 +0000
@@ -190,7 +190,9 @@
190 package_name190 package_name
191 package_version191 package_version
192 section_name192 section_name
193 components"/>193 components
194 searchable_names
195 searchable_versions"/>
194 <require196 <require
195 permission="launchpad.Edit"197 permission="launchpad.Edit"
196 attributes="198 attributes="
197199
=== modified file 'lib/lp/soyuz/interfaces/queue.py'
--- lib/lp/soyuz/interfaces/queue.py 2012-11-15 01:41:14 +0000
+++ lib/lp/soyuz/interfaces/queue.py 2012-12-14 00:18:23 +0000
@@ -1,8 +1,6 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E0211,E0213
5
6"""Queue interfaces."""4"""Queue interfaces."""
75
8__metaclass__ = type6__metaclass__ = type
@@ -212,6 +210,11 @@
212 sourcepackagerelease = Attribute(210 sourcepackagerelease = Attribute(
213 "The source package release for this item")211 "The source package release for this item")
214212
213 searchable_names = TextLine(
214 title=_("Searchable names for this item"), readonly=True)
215 searchable_versions = List(
216 title=_("Searchable versions for this item"), readonly=True)
217
215 package_name = exported(218 package_name = exported(
216 TextLine(219 TextLine(
217 title=_("Name of the uploaded source package"), readonly=True),220 title=_("Name of the uploaded source package"), readonly=True),
218221
=== modified file 'lib/lp/soyuz/model/queue.py'
--- lib/lp/soyuz/model/queue.py 2012-11-15 02:45:58 +0000
+++ lib/lp/soyuz/model/queue.py 2012-12-14 00:18:23 +0000
@@ -1,8 +1,6 @@
1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the1# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=E0611,W0212
5
6__metaclass__ = type4__metaclass__ = type
7__all__ = [5__all__ = [
8 'PackageUploadQueue',6 'PackageUploadQueue',
@@ -23,6 +21,7 @@
23 ForeignKey,21 ForeignKey,
24 SQLMultipleJoin,22 SQLMultipleJoin,
25 SQLObjectNotFound,23 SQLObjectNotFound,
24 StringCol,
26 )25 )
27from storm.expr import LeftJoin26from storm.expr import LeftJoin
28from storm.locals import (27from storm.locals import (
@@ -30,8 +29,10 @@
30 Desc,29 Desc,
31 Int,30 Int,
32 Join,31 Join,
32 List,
33 Or,33 Or,
34 Reference,34 Reference,
35 Unicode,
35 )36 )
36from storm.store import (37from storm.store import (
37 EmptyResultSet,38 EmptyResultSet,
@@ -216,30 +217,33 @@
216217
217 _defaultOrder = ['id']218 _defaultOrder = ['id']
218219
219 status = EnumCol(dbName='status', unique=False, notNull=True,220 status = EnumCol(
220 default=PackageUploadStatus.NEW,221 dbName='status', unique=False, notNull=True,
221 schema=PackageUploadStatus,222 default=PackageUploadStatus.NEW, schema=PackageUploadStatus,
222 storm_validator=validate_status)223 storm_validator=validate_status)
223224
224 date_created = UtcDateTimeCol(notNull=False, default=UTC_NOW)225 date_created = UtcDateTimeCol(notNull=False, default=UTC_NOW)
225226
226 distroseries = ForeignKey(dbName="distroseries",227 distroseries = ForeignKey(dbName="distroseries", foreignKey='DistroSeries')
227 foreignKey='DistroSeries')
228228
229 pocket = EnumCol(dbName='pocket', unique=False, notNull=True,229 pocket = EnumCol(
230 schema=PackagePublishingPocket)230 dbName='pocket', unique=False, notNull=True,
231 schema=PackagePublishingPocket)
231232
232 changesfile = ForeignKey(233 changesfile = ForeignKey(
233 dbName='changesfile', foreignKey="LibraryFileAlias", notNull=False)234 dbName='changesfile', foreignKey="LibraryFileAlias", notNull=False)
234235
235 archive = ForeignKey(dbName="archive", foreignKey="Archive", notNull=True)236 archive = ForeignKey(dbName="archive", foreignKey="Archive", notNull=True)
236237
237 signing_key = ForeignKey(foreignKey='GPGKey', dbName='signing_key',238 signing_key = ForeignKey(
238 notNull=False)239 foreignKey='GPGKey', dbName='signing_key', notNull=False)
239240
240 package_copy_job_id = Int(name='package_copy_job', allow_none=True)241 package_copy_job_id = Int(name='package_copy_job', allow_none=True)
241 package_copy_job = Reference(package_copy_job_id, 'PackageCopyJob.id')242 package_copy_job = Reference(package_copy_job_id, 'PackageCopyJob.id')
242243
244 searchable_names = StringCol(name='searchable_names', default='')
245 searchable_versions = List(type=Unicode(), default_factory=list)
246
243 # XXX julian 2007-05-06:247 # XXX julian 2007-05-06:
244 # Sources should not be SQLMultipleJoin, there is only ever one248 # Sources should not be SQLMultipleJoin, there is only ever one
245 # of each at most.249 # of each at most.
@@ -252,6 +256,14 @@
252 _builds = SQLMultipleJoin('PackageUploadBuild',256 _builds = SQLMultipleJoin('PackageUploadBuild',
253 joinColumn='packageupload')257 joinColumn='packageupload')
254258
259 def __init__(self, *args, **kwargs):
260 super(PackageUpload, self).__init__(*args, **kwargs)
261 # searchable_{name,version}s are set for the other cases when
262 # add{Source,Build,Custom} are called.
263 if self.package_copy_job:
264 self.addSearchableNames([self.package_copy_job.package_name])
265 self.addSearchableVersions([self.package_copy_job.package_version])
266
255 @cachedproperty267 @cachedproperty
256 def sources(self):268 def sources(self):
257 return list(self._sources)269 return list(self._sources)
@@ -353,15 +365,13 @@
353 def setNew(self):365 def setNew(self):
354 """See `IPackageUpload`."""366 """See `IPackageUpload`."""
355 if self.status == PackageUploadStatus.NEW:367 if self.status == PackageUploadStatus.NEW:
356 raise QueueInconsistentStateError(368 raise QueueInconsistentStateError('Queue item already new')
357 'Queue item already new')
358 self.status = PassthroughStatusValue(PackageUploadStatus.NEW)369 self.status = PassthroughStatusValue(PackageUploadStatus.NEW)
359370
360 def setUnapproved(self):371 def setUnapproved(self):
361 """See `IPackageUpload`."""372 """See `IPackageUpload`."""
362 if self.status == PackageUploadStatus.UNAPPROVED:373 if self.status == PackageUploadStatus.UNAPPROVED:
363 raise QueueInconsistentStateError(374 raise QueueInconsistentStateError('Queue item already unapproved')
364 'Queue item already unapproved')
365 self.status = PassthroughStatusValue(PackageUploadStatus.UNAPPROVED)375 self.status = PassthroughStatusValue(PackageUploadStatus.UNAPPROVED)
366376
367 def setAccepted(self):377 def setAccepted(self):
@@ -374,8 +384,7 @@
374 self.pocket.name, self.distroseries.status.name))384 self.pocket.name, self.distroseries.status.name))
375385
376 if self.status == PackageUploadStatus.ACCEPTED:386 if self.status == PackageUploadStatus.ACCEPTED:
377 raise QueueInconsistentStateError(387 raise QueueInconsistentStateError('Queue item already accepted')
378 'Queue item already accepted')
379388
380 for source in self.sources:389 for source in self.sources:
381 source.verifyBeforeAccept()390 source.verifyBeforeAccept()
@@ -461,15 +470,13 @@
461 def setDone(self):470 def setDone(self):
462 """See `IPackageUpload`."""471 """See `IPackageUpload`."""
463 if self.status == PackageUploadStatus.DONE:472 if self.status == PackageUploadStatus.DONE:
464 raise QueueInconsistentStateError(473 raise QueueInconsistentStateError('Queue item already done')
465 'Queue item already done')
466 self.status = PassthroughStatusValue(PackageUploadStatus.DONE)474 self.status = PassthroughStatusValue(PackageUploadStatus.DONE)
467475
468 def setRejected(self):476 def setRejected(self):
469 """See `IPackageUpload`."""477 """See `IPackageUpload`."""
470 if self.status == PackageUploadStatus.REJECTED:478 if self.status == PackageUploadStatus.REJECTED:
471 raise QueueInconsistentStateError(479 raise QueueInconsistentStateError('Queue item already rejected')
472 'Queue item already rejected')
473 self.status = PassthroughStatusValue(PackageUploadStatus.REJECTED)480 self.status = PassthroughStatusValue(PackageUploadStatus.REJECTED)
474481
475 def _closeBugs(self, changesfile_path, logger=None):482 def _closeBugs(self, changesfile_path, logger=None):
@@ -709,20 +716,17 @@
709 @cachedproperty716 @cachedproperty
710 def contains_upgrader(self):717 def contains_upgrader(self):
711 """See `IPackageUpload`."""718 """See `IPackageUpload`."""
712 return (PackageUploadCustomFormat.DIST_UPGRADER719 return PackageUploadCustomFormat.DIST_UPGRADER in self._customFormats
713 in self._customFormats)
714720
715 @cachedproperty721 @cachedproperty
716 def contains_ddtp(self):722 def contains_ddtp(self):
717 """See `IPackageUpload`."""723 """See `IPackageUpload`."""
718 return (PackageUploadCustomFormat.DDTP_TARBALL724 return PackageUploadCustomFormat.DDTP_TARBALL in self._customFormats
719 in self._customFormats)
720725
721 @cachedproperty726 @cachedproperty
722 def contains_uefi(self):727 def contains_uefi(self):
723 """See `IPackageUpload`."""728 """See `IPackageUpload`."""
724 return (PackageUploadCustomFormat.UEFI729 return PackageUploadCustomFormat.UEFI in self._customFormats
725 in self._customFormats)
726730
727 @property731 @property
728 def package_name(self):732 def package_name(self):
@@ -853,26 +857,43 @@
853857
854 return publishing_records858 return publishing_records
855859
860 def _appendSearchables(self, existing, new):
861 return sorted(filter(None, set(existing) | set(new)))
862
863 def addSearchableNames(self, names):
864 self.searchable_names = ' '.join(
865 self._appendSearchables(self.searchable_names.split(' '), names))
866
867 def addSearchableVersions(self, versions):
868 self.searchable_versions = self._appendSearchables(
869 self.searchable_versions, versions)
870
856 def addSource(self, spr):871 def addSource(self, spr):
857 """See `IPackageUpload`."""872 """See `IPackageUpload`."""
858 del get_property_cache(self).sources873 del get_property_cache(self).sources
874 self.addSearchableNames([spr.name])
875 self.addSearchableVersions([spr.version])
859 return PackageUploadSource(876 return PackageUploadSource(
860 packageupload=self,877 packageupload=self, sourcepackagerelease=spr.id)
861 sourcepackagerelease=spr.id)
862878
863 def addBuild(self, build):879 def addBuild(self, build):
864 """See `IPackageUpload`."""880 """See `IPackageUpload`."""
865 del get_property_cache(self).builds881 del get_property_cache(self).builds
866 return PackageUploadBuild(882 names = [build.source_package_release.name]
867 packageupload=self,883 versions = []
868 build=build.id)884 for bpr in build.binarypackages:
885 names.append(bpr.name)
886 versions.append(bpr.version)
887 self.addSearchableNames(names)
888 self.addSearchableVersions(versions)
889 return PackageUploadBuild(packageupload=self, build=build.id)
869890
870 def addCustom(self, library_file, custom_type):891 def addCustom(self, library_file, custom_type):
871 """See `IPackageUpload`."""892 """See `IPackageUpload`."""
872 del get_property_cache(self).customfiles893 del get_property_cache(self).customfiles
894 self.addSearchableNames([library_file.filename])
873 return PackageUploadCustom(895 return PackageUploadCustom(
874 packageupload=self,896 packageupload=self, libraryfilealias=library_file.id,
875 libraryfilealias=library_file.id,
876 customformat=custom_type)897 customformat=custom_type)
877898
878 def isPPA(self):899 def isPPA(self):
@@ -1361,15 +1382,14 @@
1361 _defaultOrder = ['id']1382 _defaultOrder = ['id']
13621383
1363 packageupload = ForeignKey(1384 packageupload = ForeignKey(
1364 dbName='packageupload',1385 dbName='packageupload', foreignKey='PackageUpload')
1365 foreignKey='PackageUpload')1386
13661387 customformat = EnumCol(
1367 customformat = EnumCol(dbName='customformat', unique=False,1388 dbName='customformat', unique=False, notNull=True,
1368 notNull=True, schema=PackageUploadCustomFormat)1389 schema=PackageUploadCustomFormat)
13691390
1370 libraryfilealias = ForeignKey(dbName='libraryfilealias',1391 libraryfilealias = ForeignKey(
1371 foreignKey="LibraryFileAlias",1392 dbName='libraryfilealias', foreignKey="LibraryFileAlias", notNull=True)
1372 notNull=True)
13731393
1374 def publish(self, logger=None):1394 def publish(self, logger=None):
1375 """See `IPackageUploadCustom`."""1395 """See `IPackageUploadCustom`."""
@@ -1425,8 +1445,7 @@
1425 """See `IPackageUploadCustom`."""1445 """See `IPackageUploadCustom`."""
1426 # XXX cprov 2005-03-03: We need to use the Zope Component Lookup1446 # XXX cprov 2005-03-03: We need to use the Zope Component Lookup
1427 # to instantiate the object in question and avoid circular imports1447 # to instantiate the object in question and avoid circular imports
1428 from lp.archivepublisher.dist_upgrader import (1448 from lp.archivepublisher.dist_upgrader import process_dist_upgrader
1429 process_dist_upgrader)
14301449
1431 self._publishCustom(process_dist_upgrader, logger=logger)1450 self._publishCustom(process_dist_upgrader, logger=logger)
14321451
@@ -1434,8 +1453,7 @@
1434 """See `IPackageUploadCustom`."""1453 """See `IPackageUploadCustom`."""
1435 # XXX cprov 2005-03-03: We need to use the Zope Component Lookup1454 # XXX cprov 2005-03-03: We need to use the Zope Component Lookup
1436 # to instantiate the object in question and avoid circular imports1455 # to instantiate the object in question and avoid circular imports
1437 from lp.archivepublisher.ddtp_tarball import (1456 from lp.archivepublisher.ddtp_tarball import process_ddtp_tarball
1438 process_ddtp_tarball)
14391457
1440 self._publishCustom(process_ddtp_tarball, logger=logger)1458 self._publishCustom(process_ddtp_tarball, logger=logger)
14411459
14421460
=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py 2012-11-15 01:41:14 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py 2012-12-14 00:18:23 +0000
@@ -116,6 +116,8 @@
116 upload.addSource(spr)116 upload.addSource(spr)
117 self.assertEqual(spr.sourcepackagename.name, upload.package_name)117 self.assertEqual(spr.sourcepackagename.name, upload.package_name)
118 self.assertEqual(spr.version, upload.package_version)118 self.assertEqual(spr.version, upload.package_version)
119 self.assertEqual(spr.name, upload.searchable_names)
120 self.assertContentEqual([spr.version], upload.searchable_versions)
119121
120 def test_publish_sets_packageupload(self):122 def test_publish_sets_packageupload(self):
121 # Publishing a PackageUploadSource will pass itself to the source123 # Publishing a PackageUploadSource will pass itself to the source
@@ -459,9 +461,45 @@
459 upload, job = self.makeUploadWithPackageCopyJob()461 upload, job = self.makeUploadWithPackageCopyJob()
460 self.assertEqual(job.package_name, upload.package_name)462 self.assertEqual(job.package_name, upload.package_name)
461 self.assertEqual(job.package_version, upload.package_version)463 self.assertEqual(job.package_version, upload.package_version)
464 self.assertEqual(job.package_name, upload.searchable_names)
465 self.assertContentEqual(
466 [job.package_version], upload.searchable_versions)
467
468 def test_searchables_for_builds(self):
469 distroseries = self.factory.makeDistroSeries()
470 upload = self.factory.makeBuildPackageUpload(distroseries)
471 build = self.factory.makeBinaryPackageBuild()
472 self.factory.makeBinaryPackageRelease(build=build)
473 upload.addBuild(build)
474 name_array = [
475 build.build.binarypackages[0].name for build in upload.builds]
476 name_array.extend(
477 [b.build.source_package_release.name for b in upload.builds])
478 names = ' '.join(sorted(name_array))
479 self.assertEqual(names, upload.searchable_names)
480 self.assertContentEqual(
481 [build.build.binarypackages[0].version for build in upload.builds],
482 upload.searchable_versions)
483
484 def test_searchables_for_builds_duplication(self):
485 distroseries = self.factory.makeDistroSeries()
486 spr = self.factory.makeSourcePackageRelease()
487 bpn = self.factory.makeBinaryPackageName(name=spr.name)
488 binary = self.factory.makeBuildPackageUpload(
489 distroseries=distroseries, binarypackagename=bpn,
490 source_package_release=spr)
491 self.assertEqual(spr.name, binary.searchable_names)
492
493 def test_searchables_for_custom(self):
494 distroseries = self.factory.makeDistroSeries()
495 upload = self.factory.makeCustomPackageUpload(distroseries)
496 self.assertEqual(
497 upload.searchable_names,
498 upload.customfiles[0].libraryfilealias.filename)
499 self.assertEqual([], upload.searchable_versions)
462500
463 def test_displayarchs_for_copy_job_is_sync(self):501 def test_displayarchs_for_copy_job_is_sync(self):
464 # For copy jobs, displayarchs is "source."502 # For copy jobs, displayarchs is "sync."
465 upload, job = self.makeUploadWithPackageCopyJob()503 upload, job = self.makeUploadWithPackageCopyJob()
466 self.assertEqual('sync', upload.displayarchs)504 self.assertEqual('sync', upload.displayarchs)
467505
468506
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-12-06 02:03:50 +0000
+++ lib/lp/testing/factory.py 2012-12-14 00:18:23 +0000
@@ -3437,10 +3437,10 @@
3437 pocket=pocket)3437 pocket=pocket)
3438 build = self.makeBinaryPackageBuild(3438 build = self.makeBinaryPackageBuild(
3439 source_package_release=source_package_release, pocket=pocket)3439 source_package_release=source_package_release, pocket=pocket)
3440 upload.addBuild(build)
3441 self.makeBinaryPackageRelease(3440 self.makeBinaryPackageRelease(
3442 binarypackagename=binarypackagename, build=build,3441 binarypackagename=binarypackagename, build=build,
3443 component=component)3442 component=component)
3443 upload.addBuild(build)
3444 return upload3444 return upload
34453445
3446 def makeCustomPackageUpload(self, distroseries=None, archive=None,3446 def makeCustomPackageUpload(self, distroseries=None, archive=None,