Merge lp:~ursinha/launchpad/convert-translationuploads-to-job into lp:launchpad
- convert-translationuploads-to-job
- Merge into devel
Status: | Superseded |
---|---|
Proposed branch: | lp:~ursinha/launchpad/convert-translationuploads-to-job |
Merge into: | lp:launchpad |
Prerequisite: | lp:~stevenk/launchpad/packagediff-job |
Diff against target: |
613 lines (+314/-163) (has conflicts) 9 files modified
database/schema/security.cfg (+6/-2) lib/lp/services/config/schema-lazr.conf (+4/-0) lib/lp/services/job/interfaces/job.py (+7/-0) lib/lp/soyuz/configure.zcml (+12/-0) lib/lp/soyuz/doc/distroseriesqueue-translations.txt (+14/-149) lib/lp/soyuz/interfaces/packagetranslationsuploadjob.py (+25/-0) lib/lp/soyuz/model/packagetranslationsuploadjob.py (+94/-0) lib/lp/soyuz/model/queue.py (+5/-12) lib/lp/soyuz/tests/test_packagetranslationsuploadjob.py (+147/-0) Text conflict in lib/lp/services/config/schema-lazr.conf |
To merge this branch: | bzr merge lp:~ursinha/launchpad/convert-translationuploads-to-job |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ursula Junque (community) | Needs Resubmitting | ||
William Grant | code | Needs Fixing | |
Review via email: mp+176415@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-07-23.
Commit message
publishRosettaT
Description of the change
This branch creates a TranslationsUpl
Ursula Junque (ursinha) wrote : | # |
> There are a few bits of lint. I'd suggest running 'make lint' and utilities
> /format-
Fixed.
>
> 8 +[ITranslations
> 9 +module: lp.soyuz.
> 10 +dbuser: process_accepted
>
> This needs to use a separate DB user. As a quick fix you can add a new alias
> user in database/
I created an alias called upload_
>
> 23 + UPLOAD_
> 24 + Upload Translations Files
> 25 +
> 26 + Job to upload translations files and attach them to a
> 27 + SourcePackageRe
> 28 + """)
>
> I'd call this UPLOAD_
> have a job for non-package translation uploads.
Done.
>
> 277 +class ITranslationsUp
> 278 + """An interface for acquiring ITranslationsUp
>
> The classes and interfaces probably want to be renamed to include "Package"
> too.
I renamed everything to PackageTranslat
>
> 323 + return None
>
> I'm not quite sure why you change the 'return' to 'return None' here. It
> doesn't make any difference in terms of functionality, but you'd normally only
> 'return None' when the other returns return a different value.
Reverted (that was StevenK idea :))
>
> 393 + @classmethod
> 394 + def get(cls, sourcepackagere
> 395 + metadata = simplejson.dumps(
> 396 + {'sourcepackage
> 397 + 'libraryfilealias': libraryfilealia
> 398 + return cls(IStore(
> metadata).one())
>
> Dumping to JSON is non-deterministic (eg. key order can change, quotes can
> change, etc.) so comparing it isn't always going to match. We have no need to
> retrieve a job by SPR and LFA afterwards, so I wouldn't have added this
> method.
Removed.
>
> 562 + entries_in_queue = translation_
> 563 + target=
> 564 + self.assertEqual(2, entries_in_queue)
>
> Can you check that the filenames are correct?
Done.
Preview Diff
1 | === modified file 'database/schema/security.cfg' |
2 | --- database/schema/security.cfg 2013-05-24 04:48:09 +0000 |
3 | +++ database/schema/security.cfg 2013-07-23 18:52:29 +0000 |
4 | @@ -1500,8 +1500,6 @@ |
5 | public.structuralsubscription = SELECT |
6 | public.teammembership = SELECT |
7 | public.teamparticipation = SELECT, INSERT |
8 | -public.translationgroup = SELECT |
9 | -public.translationimportqueueentry = SELECT, INSERT, UPDATE |
10 | public.validpersoncache = SELECT |
11 | public.validpersonorteamcache = SELECT |
12 | type=user |
13 | @@ -1511,6 +1509,12 @@ |
14 | groups=queued |
15 | public.processacceptedbugsjob = SELECT, INSERT |
16 | |
17 | +[upload_package_translations_job] |
18 | +type=user |
19 | +groups=queued |
20 | +public.translationgroup = SELECT |
21 | +public.translationimportqueueentry = SELECT, INSERT, UPDATE |
22 | + |
23 | [session] |
24 | type=user |
25 | |
26 | |
27 | === modified file 'lib/lp/services/config/schema-lazr.conf' |
28 | --- lib/lp/services/config/schema-lazr.conf 2013-07-23 18:52:28 +0000 |
29 | +++ lib/lp/services/config/schema-lazr.conf 2013-07-23 18:52:29 +0000 |
30 | @@ -1749,6 +1749,10 @@ |
31 | module: lp.translations.interfaces.translationpackagingjob |
32 | dbuser: rosettaadmin |
33 | |
34 | +[IPackageTranslationsUploadJobSource] |
35 | +module: lp.soyuz.interfaces.packagetranslationsuploadjob |
36 | +dbuser: upload_package_translations_job |
37 | + |
38 | >>>>>>> MERGE-SOURCE |
39 | [IPersonMergeJobSource] |
40 | module: lp.registry.interfaces.persontransferjob |
41 | |
42 | === modified file 'lib/lp/services/job/interfaces/job.py' |
43 | --- lib/lp/services/job/interfaces/job.py 2013-07-23 18:52:28 +0000 |
44 | +++ lib/lp/services/job/interfaces/job.py 2013-07-23 18:52:29 +0000 |
45 | @@ -78,6 +78,13 @@ |
46 | Job to generate the diff between two SourcePackageReleases. |
47 | """) |
48 | |
49 | + UPLOAD_PACKAGE_TRANSLATIONS = DBItem(1, """ |
50 | + Upload Package Translations |
51 | + |
52 | + Job to upload package translations files and attach them to a |
53 | + SourcePackageRelease. |
54 | + """) |
55 | + |
56 | |
57 | class IJob(Interface): |
58 | """Basic attributes of a job.""" |
59 | |
60 | === modified file 'lib/lp/soyuz/configure.zcml' |
61 | --- lib/lp/soyuz/configure.zcml 2013-07-23 18:52:28 +0000 |
62 | +++ lib/lp/soyuz/configure.zcml 2013-07-23 18:52:29 +0000 |
63 | @@ -995,6 +995,18 @@ |
64 | <allow interface=".interfaces.packagediffjob.IPackageDiffJob" /> |
65 | </class> |
66 | |
67 | + <!-- PackageTranslationsUploadJobSource --> |
68 | + <securedutility |
69 | + component=".model.packagetranslationsuploadjob.PackageTranslationsUploadJob" |
70 | + provides=".interfaces.packagetranslationsuploadjob.IPackageTranslationsUploadJobSource"> |
71 | + <allow interface=".interfaces.packagetranslationsuploadjob.IPackageTranslationsUploadJobSource" /> |
72 | + </securedutility> |
73 | + |
74 | + <!-- PackageTranslationsUploadJob --> |
75 | + <class class=".model.packagetranslationsuploadjob.PackageTranslationsUploadJob"> |
76 | + <allow |
77 | + interface=".interfaces.packagetranslationsuploadjob.IPackageTranslationsUploadJob" /> |
78 | + </class> |
79 | <webservice:register module="lp.soyuz.interfaces.webservice" /> |
80 | |
81 | </configure> |
82 | |
83 | === modified file 'lib/lp/soyuz/doc/distroseriesqueue-translations.txt' |
84 | --- lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2011-12-30 06:14:56 +0000 |
85 | +++ lib/lp/soyuz/doc/distroseriesqueue-translations.txt 2013-07-23 18:52:29 +0000 |
86 | @@ -12,7 +12,6 @@ |
87 | ... SourcePackagePublishingHistory) |
88 | >>> from lp.registry.interfaces.distribution import IDistributionSet |
89 | >>> from lp.registry.interfaces.distroseries import ( |
90 | - ... IDistroSeries, |
91 | ... IDistroSeriesSet, |
92 | ... ) |
93 | >>> from lp.registry.interfaces.pocket import PackagePublishingPocket |
94 | @@ -31,6 +30,9 @@ |
95 | >>> from lp.services.database.constants import UTC_NOW |
96 | >>> from lp.registry.interfaces.sourcepackage import SourcePackageUrgency |
97 | |
98 | + >>> from lp.soyuz.model.packagetranslationsuploadjob import ( |
99 | + ... PackageTranslationsUploadJob) |
100 | + |
101 | # Login as an admin. |
102 | >>> login('foo.bar@canonical.com') |
103 | |
104 | @@ -157,6 +159,15 @@ |
105 | ... status=PackageUploadStatus.NEW)[0] |
106 | >>> queue_item.customfiles[0].publish() |
107 | |
108 | +When publish() runs, it creates a PackageTranslationsUploadJob that will |
109 | +process the package translation files. We need to find and run it to be |
110 | +able to verify the imported files. |
111 | + >>> def runPendingPackageTranslationsUploadJob(): |
112 | + ... job = list(PackageTranslationsUploadJob.iterReady())[0] |
113 | + ... job.run() |
114 | + |
115 | + >>> runPendingPackageTranslationsUploadJob() |
116 | + |
117 | As we can see from the translation import queue content. |
118 | |
119 | >>> for entry in translation_import_queue.getAllEntries(target=ubuntu): |
120 | @@ -206,6 +217,7 @@ |
121 | >>> queue_item = dapper.getPackageUploads(PackageUploadStatus.NEW)[0] |
122 | >>> queue_item.pocket = PackagePublishingPocket.UPDATES |
123 | >>> queue_item.customfiles[0].publish() |
124 | + >>> runPendingPackageTranslationsUploadJob() |
125 | |
126 | As we can see from the translation import queue content. |
127 | |
128 | @@ -239,6 +251,7 @@ |
129 | >>> queue_item.builds[0].build.source_package_release.override( |
130 | ... component=restricted_component) |
131 | >>> queue_item.customfiles[0].publish() |
132 | + >>> runPendingPackageTranslationsUploadJob() |
133 | |
134 | As we can see from the translation import queue content. |
135 | |
136 | @@ -340,154 +353,6 @@ |
137 | >>> translation_import_queue.getAllEntries(target=ubuntu).count() |
138 | 0 |
139 | |
140 | - |
141 | -Translations importer: publishRosettaTranslations |
142 | -------------------------------------------------- |
143 | - |
144 | -We create mock objects for SourcePackageRelease, PackageUpload and |
145 | -PackageUploadCustom: these will emulate everything we need to document |
146 | -different interpretations of "importer" in attachTranslationFiles. |
147 | - |
148 | - >>> from zope.interface import implements |
149 | - >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities |
150 | - >>> from lp.soyuz.model.queue import PackageUploadCustom |
151 | - >>> from lp.soyuz.interfaces.archive import ( |
152 | - ... IArchive, ArchivePurpose) |
153 | - >>> from lp.soyuz.interfaces.queue import ( |
154 | - ... IPackageUpload, IPackageUploadCustom) |
155 | - >>> from lp.registry.interfaces.person import IPerson |
156 | - >>> from lp.soyuz.enums import PackageUploadCustomFormat |
157 | - >>> from lp.soyuz.interfaces.component import IComponentSet |
158 | - >>> from lp.soyuz.interfaces.sourcepackagerelease import ( |
159 | - ... ISourcePackageRelease) |
160 | - >>> from lp.registry.interfaces.pocket import PackagePublishingPocket |
161 | - |
162 | - >>> class MockArchive: |
163 | - ... implements(IArchive) |
164 | - ... def __init__(self, purpose): |
165 | - ... self.purpose = purpose |
166 | - |
167 | - >>> class MockDistroSeries: |
168 | - ... implements(IDistroSeries) |
169 | - ... def __init__(self, version): |
170 | - ... self.version = version |
171 | - |
172 | - >>> class MockSourcePackageRelease: |
173 | - ... implements(ISourcePackageRelease) |
174 | - ... def __init__(self, component, creator, upload_distroseries): |
175 | - ... self.component = getUtility(IComponentSet)[component] |
176 | - ... self.upload_distroseries = upload_distroseries |
177 | - ... self.creator = creator |
178 | - ... self.packageupload = 1 |
179 | - ... |
180 | - ... def attachTranslationFiles(self, file, imported, importer): |
181 | - ... if (importer is not None and |
182 | - ... not IPerson.providedBy(importer)): |
183 | - ... print "`importer` not a person!" |
184 | - ... print "Imported by: %s" % ( |
185 | - ... getattr(importer, "name", "None")) |
186 | - |
187 | - >>> class MockPackageUpload: |
188 | - ... implements(IPackageUpload) |
189 | - ... def __init__(self, pocket, auto_sync, sourcepackagerelease, |
190 | - ... archive): |
191 | - ... self.id = 1 |
192 | - ... self.pocket = pocket |
193 | - ... self.auto_sync = auto_sync |
194 | - ... self.sourcepackagerelease = sourcepackagerelease |
195 | - ... self.archive = archive |
196 | - ... |
197 | - ... def isAutoSyncUpload(self, changed_by_email=None): |
198 | - ... return self.auto_sync |
199 | - |
200 | - >>> class MockPackageUploadCustom(PackageUploadCustom): |
201 | - ... implements(IPackageUploadCustom) |
202 | - ... packageupload = None |
203 | - ... |
204 | - ... def __init__(self): |
205 | - ... self.customformat = ( |
206 | - ... PackageUploadCustomFormat.ROSETTA_TRANSLATIONS) |
207 | - |
208 | -For translations from auto-synced packages we consider the importer to be |
209 | -'katie' (archive@ubuntu.com). |
210 | - |
211 | - >>> katie = getUtility(ILaunchpadCelebrities).katie |
212 | - >>> release_pocket = PackagePublishingPocket.RELEASE |
213 | - >>> archive = MockArchive(ArchivePurpose.PRIMARY) |
214 | - |
215 | - >>> distro_series = MockDistroSeries(u'9.04') |
216 | - >>> katie_sourcepackagerelease = MockSourcePackageRelease( |
217 | - ... 'main', katie, distro_series) |
218 | - >>> sync_package_upload = MockPackageUpload( |
219 | - ... release_pocket, True, katie_sourcepackagerelease, archive) |
220 | - >>> sync_package_upload.isAutoSyncUpload() |
221 | - True |
222 | - >>> translations_upload = MockPackageUploadCustom() |
223 | - >>> translations_upload.packageupload = sync_package_upload |
224 | - >>> translations_upload.publishRosettaTranslations() |
225 | - Imported by: katie |
226 | - |
227 | -Non-auto-sync uploads by 'katie' still indicate 'katie' as the uploader. |
228 | - |
229 | - >>> non_sync_package_upload = MockPackageUpload( |
230 | - ... release_pocket, False, katie_sourcepackagerelease, archive) |
231 | - >>> non_sync_package_upload.isAutoSyncUpload() |
232 | - False |
233 | - >>> translations_upload.packageupload = non_sync_package_upload |
234 | - >>> translations_upload.publishRosettaTranslations() |
235 | - Imported by: katie |
236 | - |
237 | -Uploads by anyone else are treated as if importer is the packager. |
238 | - |
239 | - >>> person_set = getUtility(IPersonSet) |
240 | - >>> carlos = person_set.getByName('carlos') |
241 | - >>> carlos_sourcepackagerelease = MockSourcePackageRelease( |
242 | - ... 'main', carlos, distro_series) |
243 | - >>> carlos_package_upload = MockPackageUpload( |
244 | - ... release_pocket, False, carlos_sourcepackagerelease, archive) |
245 | - >>> carlos_package_upload.isAutoSyncUpload() |
246 | - False |
247 | - >>> translations_upload.packageupload = carlos_package_upload |
248 | - >>> translations_upload.publishRosettaTranslations() |
249 | - Imported by: carlos |
250 | - |
251 | -Uploads for distroseries before Oneiric or later may not be targeted |
252 | -to any component but 'main' and 'restricted'. The upload attempt is ignored. |
253 | - |
254 | - >>> katie_sourcepackagerelease = MockSourcePackageRelease( |
255 | - ... 'universe', katie, distro_series) |
256 | - >>> sync_package_upload = MockPackageUpload( |
257 | - ... release_pocket, True, katie_sourcepackagerelease, archive) |
258 | - >>> translations_upload = MockPackageUploadCustom() |
259 | - >>> translations_upload.packageupload = sync_package_upload |
260 | - >>> translations_upload.publishRosettaTranslations() |
261 | - |
262 | -For Oneiric the import succeeds for 'universe'. |
263 | - |
264 | - >>> distro_series = MockDistroSeries(u'11.10') |
265 | - >>> katie_sourcepackagerelease = MockSourcePackageRelease( |
266 | - ... 'universe', katie, distro_series) |
267 | - >>> sync_package_upload = MockPackageUpload( |
268 | - ... release_pocket, True, katie_sourcepackagerelease, archive) |
269 | - >>> translations_upload = MockPackageUploadCustom() |
270 | - >>> translations_upload.packageupload = sync_package_upload |
271 | - >>> translations_upload.publishRosettaTranslations() |
272 | - Imported by: katie |
273 | - |
274 | -And for the 12.04 release the import succeeds for 'universe'. |
275 | - |
276 | - >>> distro_series = MockDistroSeries(u'12.04') |
277 | - >>> katie_sourcepackagerelease = MockSourcePackageRelease( |
278 | - ... 'universe', katie, distro_series) |
279 | - >>> sync_package_upload = MockPackageUpload( |
280 | - ... release_pocket, True, katie_sourcepackagerelease, archive) |
281 | - >>> translations_upload = MockPackageUploadCustom() |
282 | - >>> translations_upload.packageupload = sync_package_upload |
283 | - >>> translations_upload.publishRosettaTranslations() |
284 | - Imported by: katie |
285 | - |
286 | - |
287 | - |
288 | Translations tarball |
289 | ~~~~~~~~~~~~~~~~~~~~ |
290 | |
291 | |
292 | === added file 'lib/lp/soyuz/interfaces/packagetranslationsuploadjob.py' |
293 | --- lib/lp/soyuz/interfaces/packagetranslationsuploadjob.py 1970-01-01 00:00:00 +0000 |
294 | +++ lib/lp/soyuz/interfaces/packagetranslationsuploadjob.py 2013-07-23 18:52:29 +0000 |
295 | @@ -0,0 +1,25 @@ |
296 | +# Copyright 2013 Canonical Ltd. This software is licensed under the |
297 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
298 | + |
299 | +__metaclass__ = type |
300 | + |
301 | +__all__ = [ |
302 | + "IPackageTranslationsUploadJob", |
303 | + "IPackageTranslationsUploadJobSource", |
304 | + ] |
305 | + |
306 | +from lp.services.job.interfaces.job import ( |
307 | + IJobSource, |
308 | + IRunnableJob, |
309 | + ) |
310 | + |
311 | + |
312 | +class IPackageTranslationsUploadJobSource(IJobSource): |
313 | + """An interface for acquiring IPackageTranslationsUploadJob.""" |
314 | + |
315 | + def create(sourcepackagerelease, libraryfilealias): |
316 | + """Create new translations upload job for a source package release.""" |
317 | + |
318 | + |
319 | +class IPackageTranslationsUploadJob(IRunnableJob): |
320 | + """A `Job` that uploads and attaches files to a `ISourcePackageRelease`.""" |
321 | |
322 | === added file 'lib/lp/soyuz/model/packagetranslationsuploadjob.py' |
323 | --- lib/lp/soyuz/model/packagetranslationsuploadjob.py 1970-01-01 00:00:00 +0000 |
324 | +++ lib/lp/soyuz/model/packagetranslationsuploadjob.py 2013-07-23 18:52:29 +0000 |
325 | @@ -0,0 +1,94 @@ |
326 | +# Copyright 2013 Canonical Ltd. This software is licensed under the |
327 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
328 | + |
329 | +__metaclass__ = type |
330 | + |
331 | +__all__ = [ |
332 | + 'PackageTranslationsUploadJob', |
333 | + ] |
334 | + |
335 | +from lazr.delegates import delegates |
336 | +import simplejson |
337 | +from zope.component import getUtility |
338 | +from zope.interface import ( |
339 | + classProvides, |
340 | + implements, |
341 | + ) |
342 | + |
343 | +from lp.services.config import config |
344 | +from lp.services.database.interfaces import IStore |
345 | +from lp.services.job.interfaces.job import JobType |
346 | +from lp.services.job.model.job import ( |
347 | + EnumeratedSubclass, |
348 | + Job, |
349 | + ) |
350 | +from lp.services.job.runner import BaseRunnableJob |
351 | +from lp.services.librarian.interfaces import ILibraryFileAliasSet |
352 | +from lp.soyuz.interfaces.packagetranslationsuploadjob import ( |
353 | + IPackageTranslationsUploadJob, |
354 | + IPackageTranslationsUploadJobSource, |
355 | + ) |
356 | +from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease |
357 | + |
358 | + |
359 | +class PackageTranslationsUploadJobDerived(BaseRunnableJob): |
360 | + |
361 | + __metaclass__ = EnumeratedSubclass |
362 | + |
363 | + delegates(IPackageTranslationsUploadJob) |
364 | + classProvides(IPackageTranslationsUploadJobSource) |
365 | + config = config.IPackageTranslationsUploadJobSource |
366 | + |
367 | + def __init__(self, job): |
368 | + assert job.base_job_type == JobType.UPLOAD_PACKAGE_TRANSLATIONS |
369 | + self.job = job |
370 | + self.context = self |
371 | + |
372 | + @classmethod |
373 | + def create(cls, sourcepackagerelease, libraryfilealias): |
374 | + job = Job( |
375 | + base_job_type=JobType.UPLOAD_PACKAGE_TRANSLATIONS, |
376 | + requester=sourcepackagerelease.creator, |
377 | + base_json_data=simplejson.dumps( |
378 | + {'sourcepackagerelease': sourcepackagerelease.id, |
379 | + 'libraryfilealias': libraryfilealias.id})) |
380 | + derived = cls(job) |
381 | + derived.celeryRunOnCommit() |
382 | + return derived |
383 | + |
384 | + @classmethod |
385 | + def iterReady(cls): |
386 | + jobs = IStore(Job).find( |
387 | + Job, Job.id.is_in(Job.ready_jobs), |
388 | + Job.base_job_type == JobType.UPLOAD_PACKAGE_TRANSLATIONS) |
389 | + return [cls(job) for job in jobs] |
390 | + |
391 | + |
392 | +class PackageTranslationsUploadJob(PackageTranslationsUploadJobDerived): |
393 | + |
394 | + implements(IPackageTranslationsUploadJob) |
395 | + classProvides(IPackageTranslationsUploadJobSource) |
396 | + |
397 | + @property |
398 | + def sourcepackagerelease_id(self): |
399 | + return simplejson.loads(self.base_json_data)['sourcepackagerelease'] |
400 | + |
401 | + @property |
402 | + def libraryfilealias_id(self): |
403 | + return simplejson.loads(self.base_json_data)['libraryfilealias'] |
404 | + |
405 | + @property |
406 | + def sourcepackagerelease(self): |
407 | + return SourcePackageRelease.get(self.sourcepackagerelease_id) |
408 | + |
409 | + @property |
410 | + def libraryfilealias(self): |
411 | + return getUtility(ILibraryFileAliasSet)[self.libraryfilealias_id] |
412 | + |
413 | + def run(self): |
414 | + sourcepackagerelease = self.sourcepackagerelease |
415 | + if sourcepackagerelease is not None: |
416 | + libraryfilealias = self.libraryfilealias |
417 | + importer = sourcepackagerelease.creator |
418 | + sourcepackagerelease.attachTranslationFiles( |
419 | + libraryfilealias, True, importer=importer) |
420 | |
421 | === modified file 'lib/lp/soyuz/model/queue.py' |
422 | --- lib/lp/soyuz/model/queue.py 2013-07-16 08:10:32 +0000 |
423 | +++ lib/lp/soyuz/model/queue.py 2013-07-23 18:52:29 +0000 |
424 | @@ -73,7 +73,6 @@ |
425 | ) |
426 | from lp.services.features import getFeatureFlag |
427 | from lp.services.librarian.browser import ProxiedLibraryFileAlias |
428 | -from lp.services.librarian.interfaces.client import DownloadFailed |
429 | from lp.services.librarian.model import ( |
430 | LibraryFileAlias, |
431 | LibraryFileContent, |
432 | @@ -103,6 +102,9 @@ |
433 | IPublishingSet, |
434 | name_priority_map, |
435 | ) |
436 | +from lp.soyuz.interfaces.packagetranslationsuploadjob import ( |
437 | + IPackageTranslationsUploadJobSource, |
438 | + ) |
439 | from lp.soyuz.interfaces.queue import ( |
440 | IPackageUpload, |
441 | IPackageUploadBuild, |
442 | @@ -1453,17 +1455,8 @@ |
443 | # packages in main. |
444 | return |
445 | |
446 | - # Set the importer to package creator. |
447 | - importer = sourcepackagerelease.creator |
448 | - |
449 | - # Attach the translation tarball. It's always published. |
450 | - try: |
451 | - sourcepackagerelease.attachTranslationFiles( |
452 | - self.libraryfilealias, True, importer=importer) |
453 | - except DownloadFailed: |
454 | - if logger is not None: |
455 | - debug(logger, "Unable to fetch %s to import it into Rosetta" % |
456 | - self.libraryfilealias.http_url) |
457 | + getUtility(IPackageTranslationsUploadJobSource).create( |
458 | + sourcepackagerelease, self.libraryfilealias) |
459 | |
460 | def publishStaticTranslations(self, logger=None): |
461 | """See `IPackageUploadCustom`.""" |
462 | |
463 | === added file 'lib/lp/soyuz/tests/test_packagetranslationsuploadjob.py' |
464 | --- lib/lp/soyuz/tests/test_packagetranslationsuploadjob.py 1970-01-01 00:00:00 +0000 |
465 | +++ lib/lp/soyuz/tests/test_packagetranslationsuploadjob.py 2013-07-23 18:52:29 +0000 |
466 | @@ -0,0 +1,147 @@ |
467 | +# Copyright 2013 Canonical Ltd. This software is licensed under the |
468 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
469 | + |
470 | +__metaclass__ = type |
471 | + |
472 | +from testtools.content import text_content |
473 | +import transaction |
474 | +from zope.component import getUtility |
475 | +from zope.security.proxy import removeSecurityProxy |
476 | + |
477 | +from lp.soyuz.interfaces.packagetranslationsuploadjob import ( |
478 | + IPackageTranslationsUploadJob, |
479 | + IPackageTranslationsUploadJobSource, |
480 | + ) |
481 | +from lp.soyuz.model.packagetranslationsuploadjob import ( |
482 | + PackageTranslationsUploadJob, |
483 | + ) |
484 | +from lp.services.features.testing import FeatureFixture |
485 | +from lp.services.job.interfaces.job import JobStatus |
486 | +from lp.testing import ( |
487 | + run_script, |
488 | + TestCaseWithFactory, |
489 | + verifyObject, |
490 | + ) |
491 | +from lp.services.job.tests import block_on_job |
492 | +from lp.testing.fakemethod import FakeMethod |
493 | +from lp.testing.layers import ( |
494 | + CeleryJobLayer, |
495 | + LaunchpadZopelessLayer, |
496 | + ) |
497 | +from lp.services.tarfile_helpers import LaunchpadWriteTarFile |
498 | +from lp.translations.interfaces.translationimportqueue import ( |
499 | + ITranslationImportQueue, |
500 | + ) |
501 | + |
502 | + |
503 | +class LocalTestHelper(TestCaseWithFactory): |
504 | + |
505 | + def makeJob(self, spr_creator=None, archive=None, |
506 | + sourcepackagerelease=None, libraryfilealias=None, |
507 | + tar_content=None): |
508 | + if spr_creator is None: |
509 | + creator = self.factory.makePerson() |
510 | + else: |
511 | + creator = self.factory.makePerson(name=spr_creator) |
512 | + if archive is None: |
513 | + archive = self.factory.makeArchive() |
514 | + if sourcepackagerelease is None: |
515 | + sourcepackagerelease = self.factory.makeSourcePackageRelease( |
516 | + archive=archive, creator=creator) |
517 | + if libraryfilealias is None: |
518 | + libraryfilealias = self.makeTranslationsLFA(tar_content) |
519 | + return (sourcepackagerelease, |
520 | + getUtility(IPackageTranslationsUploadJobSource).create( |
521 | + sourcepackagerelease, libraryfilealias)) |
522 | + |
523 | + def makeTranslationsLFA(self, tar_content=None): |
524 | + """Create an LibraryFileAlias containing dummy translation data.""" |
525 | + if tar_content is None: |
526 | + tar_content = { |
527 | + 'source/po/foo.pot': 'Foo template', |
528 | + 'source/po/eo.po': 'Foo translation', |
529 | + } |
530 | + tarfile_content = LaunchpadWriteTarFile.files_to_string( |
531 | + tar_content) |
532 | + return self.factory.makeLibraryFileAlias(content=tarfile_content) |
533 | + |
534 | + |
535 | +class TestPackageTranslationsUploadJob(LocalTestHelper): |
536 | + |
537 | + layer = LaunchpadZopelessLayer |
538 | + |
539 | + def test_job_implements_IPackageTranslationsUploadJob(self): |
540 | + _, job = self.makeJob() |
541 | + self.assertTrue(verifyObject(IPackageTranslationsUploadJob, job)) |
542 | + |
543 | + def test_job_source_implements_IPackageTranslationsUploadJobSource(self): |
544 | + job_source = getUtility(IPackageTranslationsUploadJobSource) |
545 | + self.assertTrue(verifyObject(IPackageTranslationsUploadJobSource, |
546 | + job_source)) |
547 | + |
548 | + def test_iterReady(self): |
549 | + _, job1 = self.makeJob() |
550 | + removeSecurityProxy(job1).job._status = JobStatus.COMPLETED |
551 | + _, job2 = self.makeJob() |
552 | + jobs = list(PackageTranslationsUploadJob.iterReady()) |
553 | + self.assertEqual(1, len(jobs)) |
554 | + |
555 | + def test_importer_is_creator(self): |
556 | + spr, job = self.makeJob(spr_creator="foobar") |
557 | + transaction.commit() |
558 | + job.run() |
559 | + translation_import_queue = getUtility(ITranslationImportQueue) |
560 | + entries_in_queue = translation_import_queue.getAllEntries( |
561 | + target=spr.sourcepackage) |
562 | + self.assertEqual(entries_in_queue[0].importer.name, "foobar") |
563 | + |
564 | + def test_run(self): |
565 | + archive = self.factory.makeArchive() |
566 | + foo_pkg = self.factory.makeSourcePackageRelease(archive=archive) |
567 | + method = FakeMethod() |
568 | + removeSecurityProxy(foo_pkg).attachTranslationFiles = method |
569 | + spr, job = self.makeJob(archive=archive, sourcepackagerelease=foo_pkg) |
570 | + transaction.commit() |
571 | + job.run() |
572 | + self.assertEqual(method.call_count, 1) |
573 | + |
574 | + def test_smoke(self): |
575 | + tar_content = { |
576 | + 'source/po/foobar.pot': 'FooBar template', |
577 | + } |
578 | + spr, job = self.makeJob(tar_content=tar_content) |
579 | + transaction.commit() |
580 | + out, err, exit_code = run_script( |
581 | + "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % ( |
582 | + IPackageTranslationsUploadJobSource.getName())) |
583 | + |
584 | + self.addDetail("stdout", text_content(out)) |
585 | + self.addDetail("stderr", text_content(err)) |
586 | + |
587 | + self.assertEqual(0, exit_code) |
588 | + translation_import_queue = getUtility(ITranslationImportQueue) |
589 | + entries_in_queue = translation_import_queue.getAllEntries( |
590 | + target=spr.sourcepackage) |
591 | + |
592 | + self.assertEqual(1, entries_in_queue.count()) |
593 | + # Check if the file in tar_content is queued: |
594 | + self.assertTrue("po/foobar.pot", entries_in_queue[0].path) |
595 | + |
596 | + |
597 | +class TestViaCelery(LocalTestHelper): |
598 | + """PackageTranslationsUploadJob runs under Celery.""" |
599 | + |
600 | + layer = CeleryJobLayer |
601 | + |
602 | + def test_run(self): |
603 | + self.useFixture(FeatureFixture({ |
604 | + 'jobs.celery.enabled_classes': 'PackageTranslationsUploadJob', |
605 | + })) |
606 | + |
607 | + spr, job = self.makeJob() |
608 | + with block_on_job(self): |
609 | + transaction.commit() |
610 | + translation_import_queue = getUtility(ITranslationImportQueue) |
611 | + entries_in_queue = translation_import_queue.getAllEntries( |
612 | + target=spr.sourcepackage).count() |
613 | + self.assertEqual(2, entries_in_queue) |
There are a few bits of lint. I'd suggest running 'make lint' and utilities/ format- new-and- modified- imports.
8 +[ITranslations UploadJobSource ] interfaces. translationsupl oadjob
9 +module: lp.soyuz.
10 +dbuser: process_accepted
This needs to use a separate DB user. As a quick fix you can add a new alias user in database/ schema/ security. cfg and use that.
23 + UPLOAD_ TRANSLATIONS_ FILES = DBItem(1, """ lease.
24 + Upload Translations Files
25 +
26 + Job to upload translations files and attach them to a
27 + SourcePackageRe
28 + """)
I'd call this UPLOAD_ PACKAGE_ TRANSLATIONS, because we'll probably eventually have a job for non-package translation uploads.
277 +class ITranslationsUp loadJobSource( IJobSource) : loadJob. """
278 + """An interface for acquiring ITranslationsUp
The classes and interfaces probably want to be renamed to include "Package" too.
323 + return None
I'm not quite sure why you change the 'return' to 'return None' here. It doesn't make any difference in terms of functionality, but you'd normally only 'return None' when the other returns return a different value.
393 + @classmethod lease, libraryfilealias): release' : sourcepackagere lease.id, s.id}) Job).find( Job, Job.base_json_data == metadata).one())
394 + def get(cls, sourcepackagere
395 + metadata = simplejson.dumps(
396 + {'sourcepackage
397 + 'libraryfilealias': libraryfilealia
398 + return cls(IStore(
Dumping to JSON is non-deterministic (eg. key order can change, quotes can change, etc.) so comparing it isn't always going to match. We have no need to retrieve a job by SPR and LFA afterwards, so I wouldn't have added this method.
562 + entries_in_queue = translation_ import_ queue.getAllEnt ries( spr.sourcepacka ge).count( )
563 + target=
564 + self.assertEqual(2, entries_in_queue)
Can you check that the filenames are correct?