← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~abentley/launchpad/sharingKey into lp:launchpad

 

Aaron Bentley has proposed merging lp:~abentley/launchpad/sharingKey into lp:launchpad with lp:~abentley/launchpad/refactor-migration as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~abentley/launchpad/sharingKey/+merge/49091

= Summary =
Support mixed product and package POTemplates in TranslationMerger.

== Proposed fix ==
Rewrite POTemplateSet.compareSharingPrecedence as POTemplate.sharingKey.

== Pre-implementation notes ==
None.

== Implementation details ==
POTemplate.sharingKey is supplied as a sort key rather than a comparison
operation, which is more efficient, AIUI.  I believe it is also easier to
understand.

The ordering is exactly the inverse of compareSharingPrecedence, for cases that
were already supported-- the "most relevant" templates sort last rather than
first.  For mixed product and package templates, product translation foci have
precedence over package translation foci.

== Tests ==
bin/test -v test_potemplate


== Demo and Q/A ==
None


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/translations/tests/test_potemplate.py
  lib/lp/translations/interfaces/potemplate.py
  lib/lp/translations/tests/test_translationmerger.py
  lib/lp/translations/translationmerger.py
  lib/lp/translations/model/potemplate.py
  scripts/rosetta/message-sharing-merge.py

./scripts/rosetta/message-sharing-merge.py
      10: '_pythonpath' imported but unused
      24: E303 too many blank lines (3)
-- 
https://code.launchpad.net/~abentley/launchpad/sharingKey/+merge/49091
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/sharingKey into lp:launchpad.
=== modified file 'lib/lp/translations/interfaces/potemplate.py'
--- lib/lp/translations/interfaces/potemplate.py	2011-01-06 12:03:35 +0000
+++ lib/lp/translations/interfaces/potemplate.py	2011-02-09 16:57:01 +0000
@@ -385,6 +385,15 @@
         """Same as getPOTMsgSetByMsgIDText(), with only_current=True
         """
 
+    def sharingKey():
+        """A key for determining the sharing precedence of a template.
+
+        Active templates have precedence over inactive ones.
+        Development foci have precendence over non-development foci.
+        Product development foci have precedence over Package development
+        foci.
+        """
+
     def getPOTMsgSetByID(id):
         """Return the POTMsgSet object related to this POTemplate with the id.
 
@@ -659,14 +668,6 @@
         Return None if there is no such `IPOTemplate`.
         """
 
-    def compareSharingPrecedence(left, right):
-        """Sort comparison: order sharing templates by precedence.
-
-        Sort using this function to order sharing templates from most
-        representative to least representative, as per the message-sharing
-        migration spec.
-        """
-
     def wipeSuggestivePOTemplatesCache():
         """Erase suggestive-templates cache.
 

=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py	2011-01-26 22:52:03 +0000
+++ lib/lp/translations/model/potemplate.py	2011-02-09 16:57:01 +0000
@@ -269,6 +269,22 @@
         else:
             return potmsgset
 
+    def sharingKey(self):
+        """See `IPOTemplate.`"""
+        if self.productseries is not None:
+            product_focus = (
+                self.productseries ==
+                self.productseries.product.primary_translatable)
+        else:
+            product_focus = False
+        if self.distroseries is not None:
+            distro_focus = (
+                self.distroseries ==
+                self.distroseries.distribution.translation_focus)
+        else:
+            distro_focus = False
+        return self.iscurrent, product_focus, distro_focus, self.id
+
     @property
     def displayname(self):
         """See `IPOTemplate`."""
@@ -1347,42 +1363,6 @@
                     len(preferred_matches))
                 return None
 
-    @staticmethod
-    def compareSharingPrecedence(left, right):
-        """See `IPOTemplateSet`."""
-        if left == right:
-            return 0
-
-        # Current templates always have precedence over non-current ones.
-        if left.iscurrent != right.iscurrent:
-            if left.iscurrent:
-                return -1
-            else:
-                return 1
-
-        if left.productseries:
-            left_series = left.productseries
-            right_series = right.productseries
-            assert left_series.product == right_series.product
-            focus = left_series.product.primary_translatable
-        else:
-            left_series = left.distroseries
-            right_series = right.distroseries
-            assert left_series.distribution == right_series.distribution
-            focus = left_series.distribution.translation_focus
-
-        # Translation focus has precedence.  In case of a tie, newest
-        # template wins.
-        if left_series == focus:
-            return -1
-        elif right_series == focus:
-            return 1
-        elif left.id > right.id:
-            return -1
-        else:
-            assert left.id < right.id, "Got unordered ids."
-            return 1
-
     def wipeSuggestivePOTemplatesCache(self):
         """See `IPOTemplateSet`."""
         return IMasterStore(POTemplate).execute(
@@ -1576,7 +1556,7 @@
         for equivalence_list in equivalents.itervalues():
             # Sort potemplates from "most representative" to "least
             # representative."
-            equivalence_list.sort(cmp=POTemplateSet.compareSharingPrecedence)
+            equivalence_list.sort(key=POTemplate.sharingKey, reverse=True)
 
         return equivalents
 

=== modified file 'lib/lp/translations/tests/test_potemplate.py'
--- lib/lp/translations/tests/test_potemplate.py	2010-12-02 16:13:51 +0000
+++ lib/lp/translations/tests/test_potemplate.py	2011-02-09 16:57:01 +0000
@@ -10,11 +10,17 @@
 from lp.app.enums import ServiceUsage
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.services.worlddata.interfaces.language import ILanguageSet
-from lp.testing import TestCaseWithFactory
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
+    )
 from lp.translations.interfaces.potemplate import IPOTemplateSet
+from lp.translations.interfaces.side import (
+    TranslationSide,
+    )
 from lp.translations.model.potemplate import (
     get_pofiles_for,
-    POTemplateSet,
+    POTemplate,
     )
 
 
@@ -414,7 +420,7 @@
         """Order templates by precedence."""
         if templates is None:
             templates = self.templates
-        return sorted(templates, cmp=POTemplateSet.compareSharingPrecedence)
+        return sorted(templates, key=POTemplate.sharingKey, reverse=True)
 
     def _getPrimaryTemplate(self, templates=None):
         """Get first template in order of precedence."""
@@ -481,6 +487,75 @@
         self.test_ageBreaksTie()
 
 
+class TestTranslationFoci(TestCaseWithFactory):
+    """Test the precedence rules for tranlation foci."""
+
+    layer = DatabaseFunctionalLayer
+
+    def assertFirst(self, expected, templates):
+        templates = sorted(
+            templates, key=POTemplate.sharingKey)
+        self.assertEqual(expected, templates[0])
+
+    @staticmethod
+    def makeProductFocus(template):
+        with person_logged_in(template.productseries.product.owner):
+            template.productseries.product.translation_focus = (
+                template.productseries)
+
+    @staticmethod
+    def makePackageFocus(template):
+        distribution = template.distroseries.distribution
+        removeSecurityProxy(distribution).translation_focus = (
+        template.distroseries)
+
+    def makeProductPOTemplate(self):
+        """Create a product that is not the translation focus."""
+        # Manually creating a productseries to get one that is not the
+        # translation focus.
+        other_productseries = self.factory.makeProductSeries()
+        other_template = self.factory.makePOTemplate(
+            productseries=other_productseries)
+        product = other_productseries.product
+        productseries = self.factory.makeProductSeries(
+            product=product,
+            owner=product.owner)
+        with person_logged_in(product.owner):
+            product.translation_focus = other_productseries
+            other_productseries.product.translations_usage = (
+                ServiceUsage.LAUNCHPAD)
+            productseries.product.translations_usage = ServiceUsage.LAUNCHPAD
+        return self.factory.makePOTemplate(productseries=productseries)
+
+    def test_product_focus(self):
+        """Template priority respects product translation focus."""
+        product = self.makeProductPOTemplate()
+        package = self.factory.makePOTemplate(side=TranslationSide.UBUNTU)
+        # default ordering is database id.
+        self.assertFirst(package, [package, product])
+        self.makeProductFocus(product)
+        self.assertFirst(product, [package, product])
+
+    def test_package_focus(self):
+        """Template priority respects package translation focus."""
+        package = self.factory.makePOTemplate(side=TranslationSide.UBUNTU)
+        product = self.makeProductPOTemplate()
+        self.assertFirst(product, [package, product])
+        # default ordering is database id.
+        self.makePackageFocus(package)
+        self.assertFirst(package, [package, product])
+
+    def test_product_package_focus(self):
+        """Template priority respects product translation focus."""
+        product = self.makeProductPOTemplate()
+        package = self.factory.makePOTemplate(side=TranslationSide.UBUNTU)
+        # default ordering is database id.
+        self.assertFirst(package, [package, product])
+        self.makeProductFocus(product)
+        self.makePackageFocus(package)
+        self.assertFirst(product, [package, product])
+
+
 class TestGetPOFilesFor(TestCaseWithFactory):
     """Test `get_pofiles_for`."""
 

=== modified file 'lib/lp/translations/translationmerger.py'
--- lib/lp/translations/translationmerger.py	2011-02-09 16:57:01 +0000
+++ lib/lp/translations/translationmerger.py	2011-02-09 16:57:01 +0000
@@ -9,6 +9,8 @@
     ]
 
 
+from operator import methodcaller
+
 from storm.locals import Store
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -205,8 +207,6 @@
         """
         if self.template_set is None:
             self.template_set = getUtility(IPOTemplateSet)
-            self.compare_template_precedence = (
-                self.template_set.compareSharingPrecedence)
 
     def main(self):
         actions = (
@@ -347,16 +347,14 @@
 
         :param potemplates: The templates to merge across.
         """
-        self.template_set = getUtility(IPOTemplateSet)
-        self.compare_template_precedence = (
-            self.template_set.compareSharingPrecedence)
         self.potemplates = potemplates
         self.tm = tm
 
     def _removeDuplicateMessages(self):
         """Get rid of duplicate `TranslationMessages` where needed."""
         representatives = {}
-        order_check = OrderingCheck(cmp=self.compare_template_precedence)
+        order_check = OrderingCheck(
+            key=methodcaller('sharingKey'), reverse=True)
 
         for template in self.potemplates:
             order_check.check(template)
@@ -397,7 +395,8 @@
         # through the templates, starting at the most representative and
         # moving towards the least representative.  For any unique potmsgset
         # key we find, the first POTMsgSet is the representative one.
-        order_check = OrderingCheck(cmp=self.compare_template_precedence)
+        order_check = OrderingCheck(
+            key=methodcaller('sharingKey'), reverse=True)
 
         for template in self.potemplates:
             order_check.check(template)
@@ -496,7 +495,8 @@
 
     def mergeTranslationMessages(self):
         """Share `TranslationMessage`s between templates where possible."""
-        order_check = OrderingCheck(cmp=self.compare_template_precedence)
+        order_check = OrderingCheck(
+            key=methodcaller('sharingKey'), reverse=True)
         for template_number, template in enumerate(self.potemplates):
             log.info("Merging template %d/%d." % (
                 template_number + 1, len(self.potemplates)))
@@ -672,7 +672,8 @@
             # least one template by making it diverged.
             target_ttis = source_potmsgset.getAllTranslationTemplateItems()
             target_templates = [tti.potemplate for tti in target_ttis]
-            target_templates.sort(self.compare_template_precedence)
+            target_templates.sort(
+                key=methodcaller('sharingKey'), reverse=True)
             for template in target_templates:
                 if self._divergeTo(message, target_potmsgset, template):
                     return True