Merge lp:~henninge/launchpad/devel-710591-importer into lp:launchpad

Proposed by Henning Eggers
Status: Superseded
Proposed branch: lp:~henninge/launchpad/devel-710591-importer
Merge into: lp:launchpad
Prerequisite: lp:~henninge/launchpad/devel-710591-setcurrenttranslation-extension
Diff against target: 860 lines (+489/-117)
10 files modified
lib/lp/registry/model/distribution.py (+9/-6)
lib/lp/translations/model/potmsgset.py (+6/-4)
lib/lp/translations/scripts/upload_translations.py (+96/-0)
lib/lp/translations/tests/test_translationmessage.py (+175/-75)
lib/lp/translations/tests/test_translationpolicy.py (+38/-8)
lib/lp/translations/utilities/tests/test_file_importer.py (+78/-17)
lib/lp/translations/utilities/tests/test_translation_sharing_info.py (+40/-0)
lib/lp/translations/utilities/translation_import.py (+26/-5)
lib/lp/translations/utilities/translationsharinginfo.py (+2/-2)
scripts/rosetta/upload-translations.py (+19/-0)
To merge this branch: bzr merge lp:~henninge/launchpad/devel-710591-importer
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) code Approve
Ian Booth (community) release-critical Approve
Review via email: mp+48682@code.launchpad.net

This proposal has been superseded by a proposal from 2011-02-11.

Description of the change

= Summary =

This is the final branch to fix bug 710591. It brings together
the functions and methods that the previous two introduced.

== Proposed fix ==

Add a boolean property to FileImporter that makes use
has_upstream_template to determine which "accept" method to
use.
Replace the use of "approve" in the importer with the
"accept" mtethods.

== Pre-implementation notes ==

I still refer to my chat with Danilo about this but also
Jeroen and I talked a bit while he reviewed the last
branch.

== Implementation details ==

Pretty straight forward implementation, I'd say.

== Tests ==

bin/test -vvcm lp.translations.utilities.tests.test_file_importer

I also ran this to check that anything related to "import" might
be affected:

bin/test -vvcm lp.translations -t import

== Demo and Q/A ==

Import on a source package and see what happens.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/interfaces/translationmessage.py
  lib/lp/translations/tests/test_translationmessage.py
  lib/lp/testing/factory.py
  lib/lp/translations/utilities/tests/test_file_importer.py
  lib/lp/translations/model/potmsgset.py
  lib/lp/translations/utilities/translation_import.py
  lib/lp/translations/model/translationmessage.py

./lib/lp/translations/tests/test_translationmessage.py
     365: E302 expected 2 blank lines, found 1

To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

Hi Ian,
thanks for the approval of the previous branch. I am requesting r-c for this final branch for the same bug, even though it did not have a code review yet just to make you aware of it.
Cheers,
Henning

Revision history for this message
Ian Booth (wallyworld) wrote :

Approved so long as the formal code review is ok and it's fully QAed as soon as possible after deployment to qastaging.

review: Approve (release-critical)
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Beautiful.

In accordance to my personal prove-you're-really-reading-it policy, I managed to find one very very minor thing to comment on:

107 + # - The by_maintainer flag must be set.

You may want to mention where this flag is (on the import-queue entry), since it's not where the reader would be most likely to look for it. You could say something like "the translation must come from an upload with the by_maintainer flag set."

Jeroen

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2011-02-09 19:25:43 +0000
+++ lib/lp/registry/model/distribution.py 2011-02-11 14:44:33 +0000
@@ -205,6 +205,9 @@
205 HasTranslationImportsMixin,205 HasTranslationImportsMixin,
206 )206 )
207from lp.translations.model.translationpolicy import TranslationPolicyMixin207from lp.translations.model.translationpolicy import TranslationPolicyMixin
208from lp.translations.utilities.translationsharinginfo import (
209 has_upstream_template,
210 )
208211
209212
210class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,213class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
@@ -1845,11 +1848,11 @@
1845 assert sourcepackage is not None, (1848 assert sourcepackage is not None, (
1846 "Translations sharing policy requires a SourcePackage.")1849 "Translations sharing policy requires a SourcePackage.")
18471850
1848 sharing_productseries = sourcepackage.productseries1851 if not has_upstream_template(
1849 if sharing_productseries is None:1852 sourcepackage.distroseries, sourcepackage.sourcepackagename):
1850 # There is no known upstream series. Take the uploader's1853 # There is no known upstream template or series. Take the
1851 # word for whether these are upstream translations (in which1854 # uploader's word for whether these are upstream translations
1852 # case they're shared) or not.1855 # (in which case they're shared) or not.
1853 # What are the consequences if that value is incorrect? In1856 # What are the consequences if that value is incorrect? In
1854 # the case where translations from upstream are purportedly1857 # the case where translations from upstream are purportedly
1855 # from Ubuntu, we miss a chance at sharing when the package1858 # from Ubuntu, we miss a chance at sharing when the package
@@ -1861,7 +1864,7 @@
1861 # translations for upstream.1864 # translations for upstream.
1862 return purportedly_upstream1865 return purportedly_upstream
18631866
1864 upstream_product = sharing_productseries.product1867 upstream_product = sourcepackage.productseries.product
1865 return upstream_product.invitesTranslationEdits(person, language)1868 return upstream_product.invitesTranslationEdits(person, language)
18661869
18671870
18681871
=== modified file 'lib/lp/translations/model/potmsgset.py'
--- lib/lp/translations/model/potmsgset.py 2011-02-04 17:14:20 +0000
+++ lib/lp/translations/model/potmsgset.py 2011-02-11 14:44:33 +0000
@@ -796,11 +796,12 @@
796 template, pofile.language, template.translation_side)796 template, pofile.language, template.translation_side)
797 other = self.getOtherTranslation(797 other = self.getOtherTranslation(
798 pofile.language, template.translation_side)798 pofile.language, template.translation_side)
799 if other is not None:799 if current is None or other is None or current == other:
800 other.is_current_upstream = False
801 if current is None or other is None:
802 translator = suggestion.submitter800 translator = suggestion.submitter
803 potranslations = dictify_translations(suggestion.all_msgstrs)801 potranslations = dictify_translations(suggestion.all_msgstrs)
802 if other is not None:
803 # Steal flag beforehand.
804 other.is_current_upstream = False
804 self._setTranslation(805 self._setTranslation(
805 pofile, translator, suggestion.origin, potranslations,806 pofile, translator, suggestion.origin, potranslations,
806 share_with_other_side=True,807 share_with_other_side=True,
@@ -808,8 +809,9 @@
808 lock_timestamp=lock_timestamp)809 lock_timestamp=lock_timestamp)
809 else:810 else:
810 # Make it only current in upstream.811 # Make it only current in upstream.
811 suggestion.is_current_upstream = True
812 if suggestion != other:812 if suggestion != other:
813 other.is_current_upstream = False
814 suggestion.is_current_upstream = True
813 pofile.markChanged(translator=suggestion.submitter)815 pofile.markChanged(translator=suggestion.submitter)
814816
815 def _cloneAndDiverge(self, original_message, pofile):817 def _cloneAndDiverge(self, original_message, pofile):
816818
=== added file 'lib/lp/translations/scripts/upload_translations.py'
--- lib/lp/translations/scripts/upload_translations.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/scripts/upload_translations.py 2011-02-11 14:44:33 +0000
@@ -0,0 +1,96 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6__all__ = [
7 'UploadPackageTranslations',
8 ]
9
10import os
11
12from zope.component import getUtility
13
14from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
15from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
16from lp.services.scripts.base import (
17 LaunchpadScript,
18 LaunchpadScriptFailure,
19 )
20from lp.translations.interfaces.translationimportqueue import (
21 ITranslationImportQueue,
22 )
23
24
25class UploadPackageTranslations(LaunchpadScript):
26 """Upload translations for given distribution package."""
27 description = "Upload translation files for a package."
28
29 def add_my_options(self):
30 """See `LaunchpadScript`."""
31 self.parser.add_option('-d', '--distribution', dest='distro',
32 help="Distribution to upload for.", default='ubuntu')
33 self.parser.add_option('-s', '--series', dest='distroseries',
34 help="Distribution release series to upload for.")
35 self.parser.add_option('-p', '--package', dest='package',
36 help="Name of source package to upload to.")
37 self.parser.add_option('-l', '--dry-run', dest='dryrun',
38 action='store_true', default=False,
39 help="Pretend to upload, but make no actual changes.")
40
41 def main(self):
42 """See `LaunchpadScript`."""
43 self._setDistroDetails()
44 self._setPackage()
45
46 if self.options.dryrun:
47 self.logger.info("Dry run. Not really uploading anything.")
48
49 queue = getUtility(ITranslationImportQueue)
50 rosetta_team = getUtility(ILaunchpadCelebrities).rosetta_experts
51
52 for filename in self.args:
53 if not os.access(filename, os.R_OK):
54 self.logger.info("Skipping: %s" % filename)
55 continue
56 self.logger.info("Uploading: %s." % filename)
57 content = open(filename).read()
58 queue.addOrUpdateEntry(
59 filename, content, True, rosetta_team,
60 sourcepackagename = self.sourcepackagename,
61 distroseries = self.distroseries)
62 self._commit()
63
64 self.logger.info("Done.")
65
66 def _commit(self):
67 """Commit transaction (or abort if dry run)."""
68 if self.txn:
69 if self.options.dryrun:
70 self.txn.abort()
71 else:
72 self.txn.commit()
73
74 def _setDistroDetails(self):
75 """Figure out the `Distribution`/`DistroSeries` to act upon."""
76 # Avoid circular imports.
77 from lp.registry.interfaces.distribution import IDistributionSet
78
79 distroset = getUtility(IDistributionSet)
80 self.distro = distroset.getByName(self.options.distro)
81
82 if not self.options.distroseries:
83 raise LaunchpadScriptFailure(
84 "Specify a distribution release series.")
85
86 self.distroseries = self.distro.getSeries(self.options.distroseries)
87
88 def _setPackage(self):
89 """Find `SourcePackage` of given name."""
90 # Avoid circular imports.
91 if not self.options.package:
92 raise LaunchpadScriptFailure("No package specified.")
93
94 nameset = getUtility(ISourcePackageNameSet)
95
96 self.sourcepackagename = nameset.queryByName(self.options.package)
097
=== modified file 'lib/lp/translations/tests/test_translationmessage.py'
--- lib/lp/translations/tests/test_translationmessage.py 2011-02-04 18:04:14 +0000
+++ lib/lp/translations/tests/test_translationmessage.py 2011-02-11 14:44:33 +0000
@@ -362,6 +362,7 @@
362362
363 self.assertEqual([], karmarecorder.karma_events)363 self.assertEqual([], karmarecorder.karma_events)
364364
365
365class TestAcceptFromUpstreamImportOnPackage(TestCaseWithFactory):366class TestAcceptFromUpstreamImportOnPackage(TestCaseWithFactory):
366 """Tests for `TranslationMessage.acceptFromUpstreamImportOnPackage`.367 """Tests for `TranslationMessage.acceptFromUpstreamImportOnPackage`.
367368
@@ -371,100 +372,199 @@
371372
372 layer = ZopelessDatabaseLayer373 layer = ZopelessDatabaseLayer
373374
375 def _getStates(self, *messages):
376 """Get is_current_* states for messages.
377
378 :param messages: A list of messages to get state for.
379 :returns: List of tuples of two boolean values for the
380 values of (is_current_ubuntu, is_current_upstream) for each
381 message.
382 """
383 return [
384 (message.is_current_ubuntu, message.is_current_upstream)
385 for message in messages]
386
387 def _makeMessages(self, pofile,
388 identical_ubuntu=None, identical_upstream=None,
389 is_tracking=False):
390 """ Create a suggestion and possible pre-existing translations.
391
392 The two identical_* parameters are tri-state:
393 - If None, do not create such a message.
394 - If True, the suggestion is identical to this message.
395 - If False, the suggestion is different from this message.
396
397 :param pofile: The pofile to create messages in.
398 :param identical_ubuntu: If and how to create the ubuntu message.
399 :param identical upstream: If and how to create the upstream
400 message.
401 :param is_trackging: Used if both messages are created to indicate
402 that they should be identical to each other.
403 :returns: One, two or three messages, as requested.
404 """
405 ubuntu = None
406 upstream = None
407
408 potmsgset = self.factory.makePOTMsgSet(potemplate=pofile.potemplate)
409
410 if identical_upstream is not None:
411 upstream = self.factory.makeCurrentTranslationMessage(
412 potmsgset=potmsgset, pofile=pofile, current_other=True)
413 upstream.is_current_ubuntu = False
414 if identical_ubuntu is not None:
415 if is_tracking or (identical_ubuntu and identical_upstream):
416 assert upstream is not None, (
417 "Don't use is_tracking without identical_upstream")
418 upstream.is_current_ubuntu = True
419 ubuntu = upstream
420 else:
421 ubuntu = self.factory.makeCurrentTranslationMessage(
422 potmsgset=potmsgset, pofile=pofile)
423 if identical_upstream:
424 translations = upstream.translations
425 elif identical_ubuntu:
426 translations = ubuntu.translations
427 else:
428 translations = None
429 suggestion = self.factory.makeSuggestion(
430 pofile=pofile, potmsgset=potmsgset, translations=translations)
431
432 return [message for message in (suggestion, ubuntu, upstream)
433 if message is not None]
434
374 def test_accept_activates_message_if_untranslated(self):435 def test_accept_activates_message_if_untranslated(self):
375 # An untranslated message accepts an imported translation.436 # An untranslated message accepts an imported translation.
376 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)437 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
377 suggestion = self.factory.makeSuggestion(pofile=pofile)438 suggestion = self._makeMessages(pofile)[0]
378 reviewer = self.factory.makePerson()
379 self.assertFalse(suggestion.is_current_ubuntu)
380 self.assertFalse(suggestion.is_current_upstream)
381439
382 suggestion.acceptFromUpstreamImportOnPackage(pofile)440 suggestion.acceptFromUpstreamImportOnPackage(pofile)
383441
384 # Messages are always accepted on the other side, too.442 # Messages are always accepted on the other side, too.
385 self.assertTrue(suggestion.is_current_ubuntu)443 self.assertEqual([(True, True)], self._getStates(suggestion))
386 self.assertTrue(suggestion.is_current_upstream)
387444
388 def test_accept_no_previously_imported(self):445 def test_accept_no_previously_imported(self):
389 # If there was already a current translation, but no previously446 # If there was already a current translation, but no previously
390 # imported one, it is disabled when a suggestion is accepted.447 # imported one, it is disabled when a suggestion is accepted.
391 pofile, potmsgset = self.factory.makePOFileAndPOTMsgSet(448 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
392 side=TranslationSide.UBUNTU)449 (suggestion, incumbent_message) = self._makeMessages(
393 suggestion = self.factory.makeSuggestion(450 pofile, identical_ubuntu=False)
394 pofile=pofile, potmsgset=potmsgset)451
395 incumbent_message = self.factory.makeCurrentTranslationMessage(452 suggestion.acceptFromUpstreamImportOnPackage(pofile)
396 pofile=pofile, potmsgset=potmsgset)453
397454 self.assertEqual(
398 self.assertTrue(incumbent_message.is_current_ubuntu)455 [(True, True), (False, False)],
399 self.assertFalse(suggestion.is_current_ubuntu)456 self._getStates(suggestion, incumbent_message))
400457
401 suggestion.acceptFromUpstreamImportOnPackage(pofile)458 def test_accept_upstream_no_ubuntu(self):
402459 # If there was already an upstream translation, but no ubuntu
403 self.assertFalse(incumbent_message.is_current_ubuntu)460 # one, the suggestion replaces both.
404 self.assertTrue(suggestion.is_current_ubuntu)461 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
405 # Messages are always accepted on the other side, too.462 (suggestion, upstream_message) = self._makeMessages(
406 self.assertTrue(suggestion.is_current_upstream)463 pofile, identical_upstream=False)
464
465 suggestion.acceptFromUpstreamImportOnPackage(pofile)
466
467 self.assertEqual(
468 [(True, True), (False, False)],
469 self._getStates(suggestion, upstream_message))
407470
408 def test_accept_previously_imported(self):471 def test_accept_previously_imported(self):
409 # If there was already a current translation, and a previously472 # If there was already an ubuntu translation, and an upstream
410 # imported one, the current translation is left untouched.473 # one, the ubuntu translation is left untouched.
411 pofile, potmsgset = self.factory.makePOFileAndPOTMsgSet(474 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
412 side=TranslationSide.UBUNTU)475 (suggestion, ubuntu_message, upstream_message) = self._makeMessages(
413 imported_message = self.factory.makeCurrentTranslationMessage(476 pofile, identical_ubuntu=False, identical_upstream=False)
414 pofile=pofile, potmsgset=potmsgset, current_other=True)477
415 imported_message.is_current_ubuntu = False478 suggestion.acceptFromUpstreamImportOnPackage(pofile)
416479
417 suggestion = self.factory.makeSuggestion(480 # The suggestion is accepted as the upstream translation.
418 pofile=pofile, potmsgset=potmsgset)481 self.assertEqual(
419 incumbent_message = self.factory.makeCurrentTranslationMessage(482 [(False, True), (True, False), (False, False)],
420 pofile=pofile, potmsgset=potmsgset)483 self._getStates(suggestion, ubuntu_message, upstream_message))
421484
422 self.assertTrue(incumbent_message.is_current_ubuntu)485 def test_accept_previously_imported_tracking(self):
423 self.assertFalse(suggestion.is_current_ubuntu)486 # If there was already an ubuntu translation, and an identical
424 self.assertTrue(imported_message.is_current_upstream)487 # upstream one, the new suggestion replaces both.
425488 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
426 suggestion.acceptFromUpstreamImportOnPackage(pofile)489 (suggestion, ubuntu_message, upstream_message) = self._makeMessages(
427490 pofile, identical_ubuntu=False, identical_upstream=False,
428 self.assertTrue(incumbent_message.is_current_ubuntu)491 is_tracking=True)
429 self.assertFalse(suggestion.is_current_ubuntu)492
430 # Messages are always accepted on the other side, too.493 suggestion.acceptFromUpstreamImportOnPackage(pofile)
431 self.assertFalse(imported_message.is_current_upstream)494
432 self.assertTrue(suggestion.is_current_upstream)495 self.assertEqual(
433496 [(True, True), (False, False), (False, False)],
434 def test_accept_current_message(self):497 self._getStates(suggestion, ubuntu_message, upstream_message))
435 # Accepting a message that's already current does nothing on this498
436 # side but makes sure the other side's flag is set.499 def test_accept_different_upstream(self):
437 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)500 # If there was already an identcal ubuntu translation, and a
438 translation = self.factory.makeCurrentTranslationMessage(501 # different upstream one, the new suggestion will become both.
439 pofile=pofile)502 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
440 self.assertTrue(translation.is_current_ubuntu)503 (suggestion, ubuntu_message, upstream_message) = self._makeMessages(
441 self.assertFalse(translation.is_current_upstream)504 pofile, identical_ubuntu=True, identical_upstream=False)
442505
443 translation.acceptFromUpstreamImportOnPackage(pofile)506 suggestion.acceptFromUpstreamImportOnPackage(pofile)
444507
445 self.assertTrue(translation.is_current_ubuntu)508 self.assertEqual(
446 self.assertTrue(translation.is_current_upstream)509 [(True, True), (True, True), (False, False)],
447510 self._getStates(suggestion, ubuntu_message, upstream_message))
448 def test_accept_current_and_imported_message(self):511
512 def test_accept_different_ubuntu(self):
513 # If there was already an identcal upstream translation, and a
514 # different ubuntu one, the ubuntu translation is not touched.
515 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
516 (suggestion, ubuntu_message, upstream_message) = self._makeMessages(
517 pofile, identical_ubuntu=False, identical_upstream=True)
518
519 suggestion.acceptFromUpstreamImportOnPackage(pofile)
520
521 self.assertEqual(
522 [(False, True), (True, False), (False, True)],
523 self._getStates(suggestion, ubuntu_message, upstream_message))
524
525 def test_accept_ubuntu_message(self):
526 # Accepting a message that's identical to the ubuntu message makes
527 # sure that the message also becomes current upstream.
528 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
529 (suggestion, ubuntu_message) = self._makeMessages(
530 pofile, identical_ubuntu=True)
531
532 suggestion.acceptFromUpstreamImportOnPackage(pofile)
533
534 self.assertEqual(
535 [(True, True), (True, True)],
536 self._getStates(suggestion, ubuntu_message))
537
538 def test_accept_upstream_message(self):
539 # Accepting a message that's identical to the upstream message makes
540 # sure that the message also becomes current in ubuntu.
541 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
542 (suggestion, upstream_message) = self._makeMessages(
543 pofile, identical_upstream=True)
544
545 suggestion.acceptFromUpstreamImportOnPackage(pofile)
546
547 self.assertEqual(
548 [(True, True), (True, True)],
549 self._getStates(suggestion, upstream_message))
550
551 def test_accept_ubuntu_and_upstream_message(self):
449 # Accepting a message that's already current and was also imported552 # Accepting a message that's already current and was also imported
450 # does nothing.553 # does nothing.
451 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)554 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
452 translation = self.factory.makeCurrentTranslationMessage(555 (suggestion, ubuntu_message, upstream_message) = self._makeMessages(
453 pofile=pofile, current_other=True)556 pofile, identical_ubuntu=True, identical_upstream=True)
454 self.assertTrue(translation.is_current_ubuntu)557
455 self.assertTrue(translation.is_current_upstream)558 suggestion.acceptFromUpstreamImportOnPackage(pofile)
456559
457 translation.acceptFromUpstreamImportOnPackage(pofile)560 self.assertEqual(
458561 [(True, True), (True, True), (True, True)],
459 self.assertTrue(translation.is_current_ubuntu)562 self._getStates(suggestion, ubuntu_message, upstream_message))
460 self.assertTrue(translation.is_current_upstream)
461563
462 def test_accept_detects_conflict(self):564 def test_accept_detects_conflict(self):
463 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)565 pofile = self.factory.makePOFile(side=TranslationSide.UBUNTU)
464 current = self.factory.makeCurrentTranslationMessage(pofile=pofile)566 (suggestion, ubuntu_message) = self._makeMessages(
465 potmsgset = current.potmsgset567 pofile, identical_ubuntu=False)
466 suggestion = self.factory.makeSuggestion(
467 pofile=pofile, potmsgset=potmsgset)
468 old = datetime.now(UTC) - timedelta(days=1)568 old = datetime.now(UTC) - timedelta(days=1)
469569
470 self.assertRaises(570 self.assertRaises(
471571
=== modified file 'lib/lp/translations/tests/test_translationpolicy.py'
--- lib/lp/translations/tests/test_translationpolicy.py 2010-11-10 05:04:16 +0000
+++ lib/lp/translations/tests/test_translationpolicy.py 2011-02-11 14:44:33 +0000
@@ -379,33 +379,63 @@
379 self.user = self.factory.makePerson()379 self.user = self.factory.makePerson()
380 self.language = self.factory.makeLanguage()380 self.language = self.factory.makeLanguage()
381381
382 def _doesPackageShare(self, sourcepackage, from_upstream=False):382 def _doesPackageShare(self, sourcepackage, by_maintainer=False):
383 """Does this `SourcePackage` share with upstream?"""383 """Does this `SourcePackage` share with upstream?"""
384 distro = sourcepackage.distroseries.distribution384 distro = sourcepackage.distroseries.distribution
385 return distro.sharesTranslationsWithOtherSide(385 return distro.sharesTranslationsWithOtherSide(
386 self.user, self.language, sourcepackage=sourcepackage,386 self.user, self.language, sourcepackage=sourcepackage,
387 purportedly_upstream=from_upstream)387 purportedly_upstream=by_maintainer)
388388
389 def test_product_always_shares(self):389 def test_product_always_shares(self):
390 product = self.factory.makeProduct()390 product = self.factory.makeProduct()
391 self.assertTrue(391 self.assertTrue(
392 product.sharesTranslationsWithOtherSide(self.user, self.language))392 product.sharesTranslationsWithOtherSide(self.user, self.language))
393393
394 def test_distribution_shares_only_if_invited(self):394 def _makePackageAndProductSeries(self):
395 package = self.factory.makeSourcePackage()395 package = self.factory.makeSourcePackage()
396 self.factory.makePackagingLink(396 self.factory.makePackagingLink(
397 sourcepackagename=package.sourcepackagename,397 sourcepackagename=package.sourcepackagename,
398 distroseries=package.distroseries)398 distroseries=package.distroseries)
399 product = package.productseries.product399 return (package, package.productseries)
400
401 def test_distribution_shares_only_if_invited_with_template(self):
402 # With an upstream template, translations will be shared if the
403 # product invites edits.
404 package, productseries = self._makePackageAndProductSeries()
405 product = productseries.product
406 self.factory.makePOTemplate(productseries=productseries)
400407
401 product.translationpermission = TranslationPermission.OPEN408 product.translationpermission = TranslationPermission.OPEN
402 self.assertTrue(self._doesPackageShare(package))409 self.assertTrue(self._doesPackageShare(package))
403 product.translationpermission = TranslationPermission.CLOSED410 product.translationpermission = TranslationPermission.CLOSED
404 self.assertFalse(self._doesPackageShare(package))411 self.assertFalse(self._doesPackageShare(package))
405412
406 def test_unlinked_package_shares_only_upstream_translations(self):413 def test_distribution_shares_not_without_template(self):
414 # Without an upstream template, translations will not be shared
415 # if they do not originate from uploads done by the maintainer.
416 package, productseries = self._makePackageAndProductSeries()
417 product = productseries.product
418
419 product.translationpermission = TranslationPermission.OPEN
420 self.assertFalse(self._doesPackageShare(package))
421 product.translationpermission = TranslationPermission.CLOSED
422 self.assertFalse(self._doesPackageShare(package))
423
424 def test_distribution_shares_only_by_maintainer_without_template(self):
425 # Without an upstream template, translations will be shared
426 # if they do originate from uploads done by the maintainer.
427 package, productseries = self._makePackageAndProductSeries()
428 product = productseries.product
429
430 product.translationpermission = TranslationPermission.OPEN
431 self.assertTrue(self._doesPackageShare(package, by_maintainer=True))
432 product.translationpermission = TranslationPermission.CLOSED
433 self.assertTrue(self._doesPackageShare(package, by_maintainer=True))
434
435 def test_distribution_shares_only_by_maintainer_without_upstream(self):
436 # Without an upstream product series, translations will only be
437 # shared if they do originate from uploads done by the maintainer.
407 package = self.factory.makeSourcePackage()438 package = self.factory.makeSourcePackage()
408 distro = package.distroseries.distribution439 for by_maintainer in [False, True]:
409 for from_upstream in [False, True]:
410 self.assertEqual(440 self.assertEqual(
411 from_upstream, self._doesPackageShare(package, from_upstream))441 by_maintainer, self._doesPackageShare(package, by_maintainer))
412442
=== modified file 'lib/lp/translations/utilities/tests/test_file_importer.py'
--- lib/lp/translations/utilities/tests/test_file_importer.py 2011-01-24 15:51:18 +0000
+++ lib/lp/translations/utilities/tests/test_file_importer.py 2011-02-11 14:44:33 +0000
@@ -11,7 +11,6 @@
11from zope.component import getUtility11from zope.component import getUtility
12from zope.security.proxy import removeSecurityProxy12from zope.security.proxy import removeSecurityProxy
1313
14from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
15from canonical.librarian.testing.fake import FakeLibrarian14from canonical.librarian.testing.fake import FakeLibrarian
16from canonical.testing import (15from canonical.testing import (
17 LaunchpadZopelessLayer,16 LaunchpadZopelessLayer,
@@ -21,6 +20,7 @@
21from lp.testing import TestCaseWithFactory20from lp.testing import TestCaseWithFactory
22from lp.translations.enums import TranslationPermission21from lp.translations.enums import TranslationPermission
23from lp.translations.interfaces.potemplate import IPOTemplateSet22from lp.translations.interfaces.potemplate import IPOTemplateSet
23from lp.translations.interfaces.side import TranslationSide
24from lp.translations.interfaces.translationfileformat import (24from lp.translations.interfaces.translationfileformat import (
25 TranslationFileFormat,25 TranslationFileFormat,
26 )26 )
@@ -582,9 +582,6 @@
582 """Class test for the sharing operation of the FileImporter base class."""582 """Class test for the sharing operation of the FileImporter base class."""
583 layer = LaunchpadZopelessLayer583 layer = LaunchpadZopelessLayer
584584
585 UPSTREAM = 0
586 UBUNTU = 1
587
588 POFILE = dedent("""\585 POFILE = dedent("""\
589 msgid ""586 msgid ""
590 msgstr ""587 msgstr ""
@@ -612,18 +609,15 @@
612609
613 def _makeImportEntry(self, side, by_maintainer=False, uploader=None,610 def _makeImportEntry(self, side, by_maintainer=False, uploader=None,
614 no_upstream=False):611 no_upstream=False):
615 if side == self.UPSTREAM:612 if side == TranslationSide.UPSTREAM:
616 potemplate = self.upstream_template613 potemplate = self.upstream_template
617 else:614 else:
618 # Create a template in a source package.615 # Create a template in a source package.
619 ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
620 distroseries = self.factory.makeDistroSeries(distribution=ubuntu)
621 ubuntu.translation_focus = distroseries
622 sourcepackagename = self.factory.makeSourcePackageName()
623 potemplate = self.factory.makePOTemplate(616 potemplate = self.factory.makePOTemplate(
624 distroseries=distroseries,617 name=self.upstream_template.name, side=side)
625 sourcepackagename=sourcepackagename,618 distroseries = potemplate.distroseries
626 name=self.upstream_template.name)619 sourcepackagename = potemplate.sourcepackagename
620 distroseries.distribution.translation_focus = distroseries
627 if not no_upstream:621 if not no_upstream:
628 # Link the source package to the upstream series to622 # Link the source package to the upstream series to
629 # enable sharing.623 # enable sharing.
@@ -657,7 +651,7 @@
657651
658 def test_makeImportEntry_templates_are_sharing(self):652 def test_makeImportEntry_templates_are_sharing(self):
659 # Sharing between upstream and Ubuntu was set up correctly.653 # Sharing between upstream and Ubuntu was set up correctly.
660 entry = self._makeImportEntry(self.UBUNTU)654 entry = self._makeImportEntry(TranslationSide.UBUNTU)
661 subset = getUtility(IPOTemplateSet).getSharingSubset(655 subset = getUtility(IPOTemplateSet).getSharingSubset(
662 distribution=entry.distroseries.distribution,656 distribution=entry.distroseries.distribution,
663 sourcepackagename=entry.sourcepackagename)657 sourcepackagename=entry.sourcepackagename)
@@ -667,7 +661,7 @@
667661
668 def test_share_with_other_side_upstream(self):662 def test_share_with_other_side_upstream(self):
669 # An upstream queue entry will be shared with ubuntu.663 # An upstream queue entry will be shared with ubuntu.
670 entry = self._makeImportEntry(self.UPSTREAM)664 entry = self._makeImportEntry(TranslationSide.UPSTREAM)
671 importer = POFileImporter(665 importer = POFileImporter(
672 entry, importers[TranslationFileFormat.PO], None)666 entry, importers[TranslationFileFormat.PO], None)
673 self.assertTrue(667 self.assertTrue(
@@ -676,7 +670,7 @@
676670
677 def test_share_with_other_side_ubuntu(self):671 def test_share_with_other_side_ubuntu(self):
678 # An ubuntu queue entry will not be shared with upstream.672 # An ubuntu queue entry will not be shared with upstream.
679 entry = self._makeImportEntry(self.UBUNTU)673 entry = self._makeImportEntry(TranslationSide.UBUNTU)
680 importer = POFileImporter(674 importer = POFileImporter(
681 entry, importers[TranslationFileFormat.PO], None)675 entry, importers[TranslationFileFormat.PO], None)
682 self.assertFalse(676 self.assertFalse(
@@ -685,7 +679,8 @@
685679
686 def test_share_with_other_side_ubuntu_no_upstream(self):680 def test_share_with_other_side_ubuntu_no_upstream(self):
687 # An ubuntu queue entry cannot share with a non-existent upstream.681 # An ubuntu queue entry cannot share with a non-existent upstream.
688 entry = self._makeImportEntry(self.UBUNTU, no_upstream=True)682 entry = self._makeImportEntry(
683 TranslationSide.UBUNTU, no_upstream=True)
689 importer = POFileImporter(684 importer = POFileImporter(
690 entry, importers[TranslationFileFormat.PO], None)685 entry, importers[TranslationFileFormat.PO], None)
691 self.assertFalse(686 self.assertFalse(
@@ -696,9 +691,75 @@
696 # If the uploader in ubuntu has rights on upstream as well, the691 # If the uploader in ubuntu has rights on upstream as well, the
697 # translations are shared.692 # translations are shared.
698 entry = self._makeImportEntry(693 entry = self._makeImportEntry(
699 self.UBUNTU, uploader=self.translator.translator)694 TranslationSide.UBUNTU, uploader=self.translator.translator)
700 importer = POFileImporter(695 importer = POFileImporter(
701 entry, importers[TranslationFileFormat.PO], None)696 entry, importers[TranslationFileFormat.PO], None)
702 self.assertTrue(697 self.assertTrue(
703 importer.share_with_other_side,698 importer.share_with_other_side,
704 "Ubuntu import should share with upstream.")699 "Ubuntu import should share with upstream.")
700
701 def test_is_upstream_import_on_sourcepackage_none(self):
702 # To do an upstream import on a sourcepackage, three conditions must
703 # be met.
704 # - It has to be on a sourcepackage.
705 # - The by_maintainer flag must be set on the queue entry.
706 # - There must be no matching template in the upstream project or
707 # even no upstream project at all.
708 # This case meets none of them.
709 entry = self._makeImportEntry(
710 TranslationSide.UPSTREAM, uploader=self.translator.translator)
711 importer = POFileImporter(
712 entry, importers[TranslationFileFormat.PO], None)
713 self.assertFalse(importer.is_upstream_import_on_sourcepackage)
714
715 def test_is_upstream_import_on_sourcepackage_by_maintainer(self):
716 # This entry is by_maintainer.
717 entry = self._makeImportEntry(
718 TranslationSide.UPSTREAM, by_maintainer=True,
719 uploader=self.translator.translator)
720 importer = POFileImporter(
721 entry, importers[TranslationFileFormat.PO], None)
722 self.assertFalse(importer.is_upstream_import_on_sourcepackage)
723
724 def test_is_upstream_import_on_sourcepackage_upstream_template(self):
725 # This entry is for a sourcepackage with an upstream potemplate.
726 entry = self._makeImportEntry(
727 TranslationSide.UBUNTU, uploader=self.translator.translator)
728 importer = POFileImporter(
729 entry, importers[TranslationFileFormat.PO], None)
730 self.assertFalse(importer.is_upstream_import_on_sourcepackage)
731
732 def test_is_upstream_import_on_sourcepackage_upstream_any_template(self):
733 # Actually any upstream potemplate will disallow upstream imports.
734
735 # Use _makeImportEntry to create upstream template and packaging
736 # link.
737 unused_entry = self._makeImportEntry(
738 TranslationSide.UBUNTU, uploader=self.translator.translator)
739
740 sourcepackagename = unused_entry.sourcepackagename
741 distroseries = unused_entry.distroseries
742 other_potemplate = self.factory.makePOTemplate(
743 distroseries=distroseries, sourcepackagename=sourcepackagename)
744
745 entry = self.factory.makeTranslationImportQueueEntry(
746 potemplate=other_potemplate, by_maintainer=True,
747 uploader=self.translator.translator, content=self.POFILE)
748 entry.potemplate = other_potemplate
749 entry.pofile = self.factory.makePOFile(potemplate=other_potemplate)
750 transaction.commit()
751
752 importer = POFileImporter(
753 entry, importers[TranslationFileFormat.PO], None)
754
755 self.assertFalse(importer.is_upstream_import_on_sourcepackage)
756
757 def test_is_upstream_import_on_sourcepackage_ok(self):
758 # This entry qualifies.
759 entry = self._makeImportEntry(
760 TranslationSide.UBUNTU, by_maintainer=True, no_upstream=True,
761 uploader=self.translator.translator)
762 importer = POFileImporter(
763 entry, importers[TranslationFileFormat.PO], None)
764 self.assertTrue(importer.is_upstream_import_on_sourcepackage)
765
705766
=== modified file 'lib/lp/translations/utilities/tests/test_translation_sharing_info.py'
--- lib/lp/translations/utilities/tests/test_translation_sharing_info.py 2011-02-03 18:40:36 +0000
+++ lib/lp/translations/utilities/tests/test_translation_sharing_info.py 2011-02-11 14:44:33 +0000
@@ -264,6 +264,27 @@
264 has_upstream_template(264 has_upstream_template(
265 distroseries, sourcepackagename, different_templatename))265 distroseries, sourcepackagename, different_templatename))
266266
267 def test_has_upstream_template_any_template(self):
268 # There is one template on the upstream project, not specifying
269 # a template name still indicates that there is a template.
270 distroseries, sourcepackagename = self._makeSourcePackage()
271 productseries = self._makeUpstreamProductSeries(
272 distroseries, sourcepackagename)
273 self.factory.makePOTemplate(
274 productseries=productseries)
275
276 self.assertTrue(
277 has_upstream_template(distroseries, sourcepackagename))
278
279 def test_has_upstream_template_any_template_none(self):
280 # There is no template on the upstream project.
281 distroseries, sourcepackagename = self._makeSourcePackage()
282 productseries = self._makeUpstreamProductSeries(
283 distroseries, sourcepackagename)
284
285 self.assertFalse(
286 has_upstream_template(distroseries, sourcepackagename))
287
267 def test_has_ubuntu_template_no_sourcepackage(self):288 def test_has_ubuntu_template_no_sourcepackage(self):
268 # There is no Ubuntu source package, so no Ubuntu template can be289 # There is no Ubuntu source package, so no Ubuntu template can be
269 # found.290 # found.
@@ -308,3 +329,22 @@
308329
309 self.assertFalse(330 self.assertFalse(
310 has_ubuntu_template(productseries, different_templatename))331 has_ubuntu_template(productseries, different_templatename))
332
333 def test_has_ubuntu_template_any_template(self):
334 # There is one template on the Ubuntu source package, not specifying
335 # a template name still indicates that there is a template.
336 distroseries, sourcepackagename = self._makeSourcePackage()
337 productseries = self._makeUpstreamProductSeries(
338 distroseries, sourcepackagename)
339 self.factory.makePOTemplate(
340 distroseries=distroseries, sourcepackagename=sourcepackagename)
341
342 self.assertTrue(has_ubuntu_template(productseries))
343
344 def test_has_ubuntu_template_any_template_none(self):
345 # There is no template on the Ubuntu source package.
346 distroseries, sourcepackagename = self._makeSourcePackage()
347 productseries = self._makeUpstreamProductSeries(
348 distroseries, sourcepackagename)
349
350 self.assertFalse(has_ubuntu_template(productseries))
311351
=== modified file 'lib/lp/translations/utilities/translation_import.py'
--- lib/lp/translations/utilities/translation_import.py 2011-01-04 17:23:42 +0000
+++ lib/lp/translations/utilities/translation_import.py 2011-02-11 14:44:33 +0000
@@ -432,6 +432,23 @@
432 purportedly_upstream=from_upstream)432 purportedly_upstream=from_upstream)
433433
434 @cachedproperty434 @cachedproperty
435 def is_upstream_import_on_sourcepackage(self):
436 """Use TranslationMessage.acceptFromUpstreamImportOnPackage`."""
437 if self.pofile is None:
438 return False
439 if not self.translation_import_queue_entry.by_maintainer:
440 return False
441 if self.translation_import_queue_entry.sourcepackagename is None:
442 return False
443 distroseries = self.translation_import_queue_entry.distroseries
444 sourcepackagename = (
445 self.translation_import_queue_entry.sourcepackagename)
446 from lp.translations.utilities.translationsharinginfo import (
447 has_upstream_template)
448 return not has_upstream_template(
449 distroseries=distroseries, sourcepackagename=sourcepackagename)
450
451 @cachedproperty
435 def translations_are_msgids(self):452 def translations_are_msgids(self):
436 """Are these English strings instead of translations?453 """Are these English strings instead of translations?
437454
@@ -466,12 +483,16 @@
466 message.validation_status = TranslationValidationStatus.OK483 message.validation_status = TranslationValidationStatus.OK
467 return True484 return True
468485
469 def _approveMessage(self, potmsgset, message, message_data):486 def _acceptMessage(self, potmsgset, message, message_data):
470 """Try to approve the message, return None on TranslationConflict."""487 """Try to approve the message, return None on TranslationConflict."""
471 try:488 try:
472 message.approve(489 if self.is_upstream_import_on_sourcepackage:
473 self.pofile, self.last_translator,490 message.acceptFromUpstreamImportOnPackage(
474 self.share_with_other_side, self.lock_timestamp)491 self.pofile, self.lock_timestamp)
492 else:
493 message.acceptFromImport(
494 self.pofile, self.share_with_other_side,
495 self.lock_timestamp)
475 except TranslationConflict:496 except TranslationConflict:
476 self._addConflictError(message_data, potmsgset)497 self._addConflictError(message_data, potmsgset)
477 if self.logger is not None:498 if self.logger is not None:
@@ -529,7 +550,7 @@
529 validation_ok = self._validateMessage(550 validation_ok = self._validateMessage(
530 potmsgset, new_message, sanitized_translations, message_data)551 potmsgset, new_message, sanitized_translations, message_data)
531 if validation_ok and self.is_editor:552 if validation_ok and self.is_editor:
532 return self._approveMessage(potmsgset, new_message, message_data)553 return self._acceptMessage(potmsgset, new_message, message_data)
533554
534 return new_message555 return new_message
535556
536557
=== modified file 'lib/lp/translations/utilities/translationsharinginfo.py'
--- lib/lp/translations/utilities/translationsharinginfo.py 2011-02-03 18:40:36 +0000
+++ lib/lp/translations/utilities/translationsharinginfo.py 2011-02-11 14:44:33 +0000
@@ -161,14 +161,14 @@
161 distroseries, sourcepackagename, templatename))161 distroseries, sourcepackagename, templatename))
162162
163163
164def has_ubuntu_template(productseries, templatename):164def has_ubuntu_template(productseries, templatename=None):
165 """Check for existence of ubuntu template."""165 """Check for existence of ubuntu template."""
166 result = find_ubuntu_sharing_info(166 result = find_ubuntu_sharing_info(
167 productseries, templatename, template_only=True)167 productseries, templatename, template_only=True)
168 return not result.is_empty()168 return not result.is_empty()
169169
170170
171def has_upstream_template(distroseries, sourcepackagename, templatename):171def has_upstream_template(distroseries, sourcepackagename, templatename=None):
172 """Check for existence of upstream template."""172 """Check for existence of upstream template."""
173 result = find_upstream_sharing_info(173 result = find_upstream_sharing_info(
174 distroseries, sourcepackagename, templatename,174 distroseries, sourcepackagename, templatename,
175175
=== added file 'scripts/rosetta/upload-translations.py'
--- scripts/rosetta/upload-translations.py 1970-01-01 00:00:00 +0000
+++ scripts/rosetta/upload-translations.py 2011-02-11 14:44:33 +0000
@@ -0,0 +1,19 @@
1#!/usr/bin/python -S
2#
3# Copyright 2009 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5# pylint: disable-msg=W0403
6
7"""Upload translations to given package."""
8
9__metaclass__ = type
10
11import _pythonpath
12
13from lp.translations.scripts.upload_translations import (
14 UploadPackageTranslations)
15
16
17if __name__ == '__main__':
18 script = UploadPackageTranslations('upload-translations')
19 script.run()