← Back to team overview

launchpad-reviewers team mailing list archive

lp:~henninge/launchpad/devel-737422-remove-translationsharinginfo into lp:launchpad

 

Henning Eggers has proposed merging lp:~henninge/launchpad/devel-737422-remove-translationsharinginfo into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #737422 in Launchpad itself: "Update translation sharing info utility to its current usage"
  https://bugs.launchpad.net/launchpad/+bug/737422

For more details, see:
https://code.launchpad.net/~henninge/launchpad/devel-737422-remove-translationsharinginfo/+merge/54184

Details
=======

This is a clean-up branch that resulted in removing the module that I had meant to clean up. I found out that all of its needed (some was not needed) functionality could be achieved by using existing code (with some additions). A lot of files were touched but in total more code was removed than was added, so that must be good.


Proposed Fix
------------

Both sourcepackage and productseries implement IHasTranslationTemplates by using the HasTranslationTemplatesMixin. That mixin has methods like "had_current_translation_templates" which translationsharinginfo implemented, too. It also uses POTemplateCollection which is very useful for building queries.

The only thing that was missing was to get the "other side" of a packaging link. For a sourcepackage that's simple because each sourcepackage packages exactly one productseries, so SourcePackage.productseries is what I want. For a productseries there is not one corresponding sourcepackage, it only provides a list of packages. So I had to pick one. First criterium is that it needs to be a source package in Ubuntu. Second is that is is packaged in the "translation_focus" distroseris of Ubuntu or if not there in the "current" series or if not there in "any" series. In practice those fall backs won't be very important, though, because on Ubuntu main is translated in Launchpad and so all packages are very likely to be present in the translation focus. I added "getUbuntuTranslationFocusPackage" but better suggestions for a better method name are welcome. ;)

I found that I had implemented "getOtherSidePOFile" but that it was really finding the template on the other side and then looking up the POFile for on the given language on it. So I moved that code into POTemplate where it belongs (up on level), so I can use both "getOtherSidePOTemplate" and "getOtherSidePOFile".

Finally, POTemplateCollection is a nice tool but it was missing "restrictName" to search for a specific template name. So I added that, too.


Implementation details
----------------------

When you look at the browser code you'll see that the "is_sharing" is more complex now than it was before and is repeated in the different views. This is a result of bug 605924 which prevents me from using getCurrentTemplatesCollection and had_current_translation_templates. I intend to fix that bug in my next branch and update the is_sharing code with that.


Tests
-----

When writing the tests for getUbuntuTranslationFocusPackage I stumbled over the fact that a distroseries status is EXPERIMENTAL by default but the factory method sets it to DEVELOPMENT by default combined with the way Distroseries.currentseries finds a "current" series (which is not necessarily the series marked as CURRENT). Just so you know. ;-)

Here are the tests for the new code:

bin/test -vvcm lp.registry.tests.test_productseries \
    -t TestProductSeriesGetUbuntuTranslationFocusPackage
bin/test -vvcm lp.translations.tests.test_potemplate \
    -t test_getOtherSidePOTemplate
bin/test -vvcm lp.translations.tests.test_translationtemplatescollection

But to test for possible regressions, it would be safest to run all translation tests.

bin/test -vvcm lp.translations


= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/interfaces/productseries.py
  lib/lp/registry/model/distribution.py
  lib/lp/registry/model/productseries.py
  lib/lp/registry/tests/test_productseries.py
  lib/lp/translations/browser/pofile.py
  lib/lp/translations/browser/potemplate.py
  lib/lp/translations/browser/productseries.py
  lib/lp/translations/browser/sourcepackage.py
  lib/lp/translations/interfaces/potemplate.py
  lib/lp/translations/model/pofile.py
  lib/lp/translations/model/potemplate.py
  lib/lp/translations/tests/test_potemplate.py
  lib/lp/translations/tests/test_translationtemplatescollection.py
  lib/lp/translations/utilities/translation_import.py

./lib/lp/translations/browser/pofile.py
     788: E301 expected 1 blank line, found 2
     904: E301 expected 1 blank line, found 2

-- 
https://code.launchpad.net/~henninge/launchpad/devel-737422-remove-translationsharinginfo/+merge/54184
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~henninge/launchpad/devel-737422-remove-translationsharinginfo into lp:launchpad.
=== modified file 'lib/lp/registry/interfaces/productseries.py'
--- lib/lp/registry/interfaces/productseries.py	2011-03-08 15:28:40 +0000
+++ lib/lp/registry/interfaces/productseries.py	2011-03-21 11:01:19 +0000
@@ -241,8 +241,7 @@
         description=_("Specify which files will be imported from the "
                       "source code branch.")),
         ('devel', {'exported': True}),
-        exported=False
-        )
+        exported=False)
 
     potemplate_count = Int(
         title=_("The total number of POTemplates in this series."),
@@ -276,6 +275,10 @@
         it will also work through the ancestry of the distroseries to try
         to find a Packaging entry that may be relevant."""
 
+    def getUbuntuTranslationFocusPackage(self):
+        """Return the SourcePackage that packages this project in Ubuntu's
+        translation focus or current series or any series, in that order."""
+
     def setPackaging(distroseries, sourcepackagename, owner):
         """Create or update a Packaging record for this product series,
         connecting it to the given distroseries and source package name.

=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py	2011-03-01 05:05:26 +0000
+++ lib/lp/registry/model/distribution.py	2011-03-21 11:01:19 +0000
@@ -207,9 +207,6 @@
     HasTranslationImportsMixin,
     )
 from lp.translations.model.translationpolicy import TranslationPolicyMixin
-from lp.translations.utilities.translationsharinginfo import (
-    has_upstream_template,
-    )
 
 
 class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
@@ -478,8 +475,9 @@
             arch_mirrors = list(Store.of(self).find(
                 (MirrorDistroArchSeries.distribution_mirrorID,
                  Max(MirrorDistroArchSeries.freshness)),
-                MirrorDistroArchSeries.distribution_mirrorID.is_in(mirror_ids)
-            ).group_by(MirrorDistroArchSeries.distribution_mirrorID))
+                MirrorDistroArchSeries.distribution_mirrorID.is_in(
+                    mirror_ids)).group_by(
+                        MirrorDistroArchSeries.distribution_mirrorID))
             arch_mirror_freshness = {}
             arch_mirror_freshness.update(
                 [(mirror_id, MirrorFreshness.items[mirror_freshness]) for
@@ -778,7 +776,7 @@
     def getCurrentSourceReleases(self, source_package_names):
         """See `IDistribution`."""
         return getUtility(IDistributionSet).getCurrentSourceReleases(
-            {self:source_package_names})
+            {self: source_package_names})
 
     @property
     def has_any_specifications(self):
@@ -1824,7 +1822,7 @@
         assert sourcepackage is not None, (
             "Translations sharing policy requires a SourcePackage.")
 
-        if not has_upstream_template(sourcepackage):
+        if not sourcepackage.productseries.has_current_translation_templates:
             # There is no known upstream template or series.  Take the
             # uploader's word for whether these are upstream translations
             # (in which case they're shared) or not.

=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py	2011-01-21 08:12:29 +0000
+++ lib/lp/registry/model/productseries.py	2011-03-21 11:01:19 +0000
@@ -481,6 +481,22 @@
         # the distroseries to try to find a relevant packaging record
         raise NotFoundError(distroseries)
 
+    def getUbuntuTranslationFocusPackage(self):
+        """See `IProductSeries`."""
+        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
+        translation_focus = ubuntu.translation_focus
+        current_series = ubuntu.currentseries
+        candidate = None
+        for package in self.sourcepackages:
+            if package.distroseries == translation_focus:
+                return package
+            if package.distroseries == current_series:
+                candidate = package
+            elif package.distroseries.distribution == ubuntu:
+                if candidate is None:
+                    candidate = package
+        return candidate
+
     def setPackaging(self, distroseries, sourcepackagename, owner):
         """See IProductSeries."""
         if distroseries.distribution.full_functionality:

=== modified file 'lib/lp/registry/tests/test_productseries.py'
--- lib/lp/registry/tests/test_productseries.py	2011-03-04 21:15:00 +0000
+++ lib/lp/registry/tests/test_productseries.py	2011-03-21 11:01:19 +0000
@@ -7,6 +7,7 @@
 
 import transaction
 from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
 
 from canonical.launchpad.ftests import login
 from canonical.testing.layers import (
@@ -20,6 +21,7 @@
     IProductSeries,
     IProductSeriesSet,
     )
+from lp.registry.interfaces.series import SeriesStatus
 from lp.testing import (
     TestCaseWithFactory,
     WebServiceTestCase,
@@ -80,6 +82,70 @@
             self.debian_series, self.sourcepackagename, self.person)
 
 
+class TestProductSeriesGetUbuntuTranslationFocusPackage(TestCaseWithFactory):
+    """Test for ProductSeries.getUbuntuTranslationFocusPackage."""
+
+    layer = DatabaseFunctionalLayer
+
+    def _makeSourcePackage(self, productseries,
+                           series_status=SeriesStatus.EXPERIMENTAL):
+        """Make a sourcepckage that packages the productseries."""
+        distroseries = self.factory.makeUbuntuDistroSeries(
+            status=series_status)
+        packaging = self.factory.makePackagingLink(
+            productseries=productseries, distroseries=distroseries)
+        return packaging.sourcepackage
+
+    def _test_packaged_in_series(
+            self, in_translation_focus, in_current_series, in_other_series):
+        """Test the given combination of packagings."""
+        productseries = self.factory.makeProductSeries()
+        package = None
+        if in_other_series:
+            package = self._makeSourcePackage(productseries)
+        if in_current_series:
+            package = self._makeSourcePackage(
+                productseries, SeriesStatus.FROZEN)
+        if in_translation_focus:
+            package = self._makeSourcePackage(productseries)
+            naked_distribution = removeSecurityProxy(
+                package.distroseries.distribution)
+            naked_distribution.translation_focus = package.distroseries
+        self.assertEqual(
+            package,
+            productseries.getUbuntuTranslationFocusPackage())
+
+    def test_no_sourcepackage(self):
+        self._test_packaged_in_series(
+            in_translation_focus=False,
+            in_current_series=False,
+            in_other_series=False)
+
+    def test_packaged_in_translation_focus(self):
+        # The productseries is packaged in the translation focus series
+        # and others but only the focus is returned.
+        self._test_packaged_in_series(
+            in_translation_focus=True,
+            in_current_series=True,
+            in_other_series=True)
+
+    def test_packaged_in_current_series(self):
+        # The productseries is packaged in the current series and others but
+        # only the current is returned.
+        self._test_packaged_in_series(
+            in_translation_focus=False,
+            in_current_series=True,
+            in_other_series=True)
+
+    def test_packaged_in_other_series(self):
+        # The productseries is not packaged in the translation focus or the
+        # current series, so that packaging is returned.
+        self._test_packaged_in_series(
+            in_translation_focus=False,
+            in_current_series=False,
+            in_other_series=True)
+
+
 class TestProductSeriesDrivers(TestCaseWithFactory):
     """Test the 'drivers' attribute of a ProductSeries."""
 

=== modified file 'lib/lp/translations/browser/pofile.py'
--- lib/lp/translations/browser/pofile.py	2011-03-01 20:05:57 +0000
+++ lib/lp/translations/browser/pofile.py	2011-03-21 11:01:19 +0000
@@ -59,12 +59,6 @@
     ITranslationImportQueue,
     )
 from lp.translations.interfaces.translationsperson import ITranslationsPerson
-from lp.translations.utilities.translationsharinginfo import (
-    has_ubuntu_template,
-    has_upstream_template,
-    get_ubuntu_sharing_info,
-    get_upstream_sharing_info,
-    )
 
 
 class POFileNavigation(Navigation):
@@ -999,27 +993,24 @@
 
     def is_sharing(self):
         if self.is_upstream_pofile:
-            return has_ubuntu_template(
-                productseries=self.context.potemplate.productseries,
-                templatename=self.context.potemplate.name)
+            productseries = self.context.potemplate.productseries
+            other_side_object = (
+                productseries.getUbuntuTranslationFocusPackage())
         else:
-            return has_upstream_template(
-                sourcepackage=self.context.potemplate.sourcepackage,
-                templatename=self.context.potemplate.name)
+            sourcepackage = self.context.potemplate.sourcepackage
+            other_side_object = sourcepackage.productseries
+        if other_side_object is None:
+            return False
+        collection = other_side_object.getTemplatesCollection()
+        name = self.context.potemplate.name
+        return bool(
+            collection.restrictCurrent().restrictName(name).select().any())
 
     @property
     def sharing_pofile(self):
-        if self.is_upstream_pofile:
-            infos = get_ubuntu_sharing_info(
-                productseries=self.context.potemplate.productseries,
-                templatename=self.context.potemplate.name)
-        else:
-            infos = get_upstream_sharing_info(
-                sourcepackage=self.context.potemplate.sourcepackage,
-                templatename=self.context.potemplate.name)
-        if len(infos) == 0:
+        potemplate = self.context.potemplate.getOtherSidePOTemplate()
+        if potemplate is None:
             return None
-        obj, potemplate = infos[0]
         pofile = potemplate.getPOFileByLang(self.context.language.code)
         if pofile is None:
             pofile = potemplate.getDummyPOFile(

=== modified file 'lib/lp/translations/browser/potemplate.py'
--- lib/lp/translations/browser/potemplate.py	2011-03-08 09:59:36 +0000
+++ lib/lp/translations/browser/potemplate.py	2011-03-21 11:01:19 +0000
@@ -87,12 +87,6 @@
 from lp.translations.interfaces.translationimportqueue import (
     ITranslationImportQueue,
     )
-from lp.translations.utilities.translationsharinginfo import (
-    has_ubuntu_template,
-    get_ubuntu_sharing_info,
-    has_upstream_template,
-    get_upstream_sharing_info,
-    )
 
 
 class POTemplateNavigation(Navigation):
@@ -353,29 +347,23 @@
         return self.context.translation_side == TranslationSide.UPSTREAM
 
     def is_sharing(self):
-        if self.is_upstream_template:
-            return has_ubuntu_template(
-                productseries=self.context.productseries,
-                templatename=self.context.name)
+        if self.is_upstream_pofile:
+            productseries = self.context.productseries
+            other_side_object = (
+                productseries.getUbuntuTranslationFocusPackage())
         else:
-            return has_upstream_template(
-                sourcepackage=self.context.sourcepackage,
-                templatename=self.context.name)
+            sourcepackage = self.potemplate.sourcepackage
+            other_side_object = sourcepackage.productseries
+        if other_side_object is None:
+            return False
+        collection = other_side_object.getTemplatesCollection()
+        name = self.context.name
+        return bool(
+            collection.restrictCurrent().restrictName(name).select().any())
 
     @property
     def sharing_template(self):
-        if self.is_upstream_template:
-            infos = get_ubuntu_sharing_info(
-                productseries=self.context.productseries,
-                templatename=self.context.name)
-        else:
-            infos = get_upstream_sharing_info(
-                sourcepackage=self.context.sourcepackage,
-                templatename=self.context.name)
-        if len(infos) == 0:
-            return None
-        obj, template = infos[0]
-        return template
+        return self.context.getOtherSidePOTemplate()
 
     def getTranslationTarget(self):
         """See `TranslationSharingDetailsMixin`."""

=== modified file 'lib/lp/translations/browser/productseries.py'
--- lib/lp/translations/browser/productseries.py	2011-03-16 14:52:37 +0000
+++ lib/lp/translations/browser/productseries.py	2011-03-21 11:01:19 +0000
@@ -65,10 +65,6 @@
 from lp.translations.interfaces.translations import (
     TranslationsBranchImportMode,
     )
-from lp.translations.utilities.translationsharinginfo import (
-    has_ubuntu_template,
-    get_ubuntu_sharing_info,
-    )
 
 
 class ProductSeriesTranslationsMenuMixIn:
@@ -460,15 +456,15 @@
         return check_permission("launchpad.TranslationsAdmin", self.context)
 
     def is_sharing(self):
-        return has_ubuntu_template(productseries=self.context)
+        sourcepackage = self.context.getUbuntuTranslationFocusPackage()
+        if sourcepackage is None:
+            return False
+        collection = sourcepackage.getTemplateCollection().restrictCurrent()
+        return bool(collection.select().any())
 
     @property
     def sharing_sourcepackage(self):
-        infos = get_ubuntu_sharing_info(productseries=self.context)
-        if len(infos) == 0:
-            return None
-        sourcepackage, template = infos[0]
-        return sourcepackage
+        return self.context.getUbuntuTranslationFocusPackage()
 
     def getTranslationTarget(self):
         """See `TranslationSharingDetailsMixin`."""

=== modified file 'lib/lp/translations/browser/sourcepackage.py'
--- lib/lp/translations/browser/sourcepackage.py	2011-03-16 14:33:28 +0000
+++ lib/lp/translations/browser/sourcepackage.py	2011-03-21 11:01:19 +0000
@@ -34,10 +34,6 @@
     TranslationsBranchImportMode,
     )
 from lp.translations.model.translationpackagingjob import TranslationMergeJob
-from lp.translations.utilities.translationsharinginfo import (
-    has_upstream_template,
-    get_upstream_sharing_info,
-    )
 
 
 class SharingDetailsPermissionsMixin:
@@ -59,16 +55,15 @@
         return "Translations for %s" % self.context.displayname
 
     def is_sharing(self):
-        return has_upstream_template(self.context)
+        productseries = self.context.productseries
+        if productseries is None:
+            return False
+        collection = productseries.getTemplateCollection().restrictCurrent()
+        return bool(collection.select().any())
 
     @property
     def sharing_productseries(self):
-        infos = get_upstream_sharing_info(self.context)
-        if len(infos) == 0:
-            return None
-
-        productseries, template = infos[0]
-        return productseries
+        return self.context.productseries
 
     def getTranslationTarget(self):
         """See `TranslationSharingDetailsMixin`."""
@@ -124,14 +119,18 @@
 
     page_title = "Sharing details"
 
+    def is_sharing(self):
+        productseries = self.context.productseries
+        if productseries is None:
+            return False
+        collection = productseries.getTemplateCollection().restrictCurrent()
+        return bool(collection.select().any())
+
     def initialize(self):
         if not getFeatureFlag('translations.sharing_information.enabled'):
             raise NotFound(self.context, '+sharing-details')
         super(SourcePackageTranslationSharingDetailsView, self).initialize()
-        has_no_upstream_templates = (
-            self.is_configuration_complete and
-            not has_upstream_template(self.context))
-        if has_no_upstream_templates:
+        if self.is_configuration_complete and not self.is_sharing():
             self.request.response.addInfoNotification(
                 structured(
                 'No upstream templates have been found yet. Please follow '

=== modified file 'lib/lp/translations/interfaces/potemplate.py'
--- lib/lp/translations/interfaces/potemplate.py	2011-03-01 17:40:06 +0000
+++ lib/lp/translations/interfaces/potemplate.py	2011-03-21 11:01:19 +0000
@@ -415,6 +415,11 @@
         Return None if there is no such POFile.
         """
 
+    def getOtherSidePOTemplate():
+        """Get the POTemplate with the same name on the other side of a
+        packaging link.
+        """
+
     def hasPluralMessage():
         """Test whether this template has any message sets which are plural
         message sets."""

=== modified file 'lib/lp/translations/model/pofile.py'
--- lib/lp/translations/model/pofile.py	2011-02-23 16:56:01 +0000
+++ lib/lp/translations/model/pofile.py	2011-03-21 11:01:19 +0000
@@ -66,7 +66,6 @@
     MASTER_FLAVOR,
     )
 from canonical.launchpad.webapp.publisher import canonical_url
-from lp.app.errors import NotFoundError
 from lp.registry.interfaces.person import validate_public_person
 from lp.services.propertycache import cachedproperty
 from lp.translations.enums import RosettaImportStatus
@@ -74,7 +73,6 @@
     IPOFile,
     IPOFileSet,
     )
-from lp.translations.interfaces.potemplate import IPOTemplateSet
 from lp.translations.interfaces.potmsgset import TranslationCreditsType
 from lp.translations.interfaces.side import (
     ITranslationSideTraitsSet,
@@ -401,30 +399,7 @@
 
     def getOtherSidePOFile(self):
         """See `IPOFile`."""
-        potemplateset = getUtility(IPOTemplateSet)
-        if self.potemplate.translation_side == TranslationSide.UBUNTU:
-            from lp.registry.model.sourcepackage import SourcePackage
-            productseries = SourcePackage(
-                self.potemplate.sourcepackagename,
-                self.potemplate.distroseries).productseries
-            if productseries is None:
-                return None
-            subset = potemplateset.getSubset(productseries=productseries)
-        else:
-            ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
-            distroseries = ubuntu.translation_focus
-            if distroseries is None:
-                distroseries = ubuntu.currentseries
-            try:
-                sourcepackage = self.potemplate.productseries.getPackage(
-                    distroseries)
-            except NotFoundError:
-                return None
-            subset = potemplateset.getSubset(
-                distroseries=distroseries,
-                sourcepackagename=sourcepackage.sourcepackagename)
-
-        other_potemplate = subset.getPOTemplateByName(self.potemplate.name)
+        other_potemplate = self.potemplate.getOtherSidePOTemplate()
         if other_potemplate is None:
             return None
         return other_potemplate.getPOFileByLang(self.language.code)

=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py	2011-03-01 17:40:06 +0000
+++ lib/lp/translations/model/potemplate.py	2011-03-21 11:01:19 +0000
@@ -541,6 +541,19 @@
 
         return self._cached_pofiles_by_language[language_code]
 
+    def getOtherSidePOTemplate(self):
+        """See `IPOTemplate`."""
+        if self.translation_side == TranslationSide.UBUNTU:
+            other_side_object = self.sourcepackage.productseries
+        else:
+            other_side_object = (
+                self.productseries.getUbuntuTranslationFocusPackage())
+        if other_side_object is None:
+            return None
+        collection = (
+            other_side_object.getTemplatesCollection().restrictCurrent())
+        return collection.restrictName(self.name).select().one()
+
     def messageCount(self):
         """See `IRosettaStats`."""
         return self.messagecount
@@ -1684,6 +1697,15 @@
         """
         return self.refine(POTemplate.iscurrent == current_value)
 
+    def restrictName(self, template_name):
+        """Select based on `POTemplate.name`.
+
+        :param template: The value for `name` that you are looking for.
+        :return: A `TranslationTemplatesCollection based on this one but
+            restricted to ones with the desired `name` value.
+        """
+        return self.refine(POTemplate.name == template_name)
+
     def joinPOFile(self):
         """Join `POFile` into the collection.
 

=== modified file 'lib/lp/translations/tests/test_potemplate.py'
--- lib/lp/translations/tests/test_potemplate.py	2011-02-10 15:08:52 +0000
+++ lib/lp/translations/tests/test_potemplate.py	2011-03-21 11:01:19 +0000
@@ -8,7 +8,10 @@
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
-from canonical.testing.layers import DatabaseFunctionalLayer
+from canonical.testing.layers import (
+    DatabaseFunctionalLayer,
+    ZopelessDatabaseLayer,
+    )
 from lp.app.enums import ServiceUsage
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.services.worlddata.interfaces.language import ILanguageSet
@@ -22,7 +25,6 @@
     )
 from lp.translations.model.potemplate import (
     get_pofiles_for,
-    POTemplate,
     )
 
 
@@ -595,3 +597,85 @@
         pofiles = get_pofiles_for([self.potemplate], self.greek)
         pofile = pofiles[0]
         self.assertIsInstance(pofile, DummyPOFile)
+
+
+class TestPOTemplateUbuntuUpstreamSharingMixin:
+    """Test sharing between Ubuntu und upstream POTemplates."""
+
+    layer = ZopelessDatabaseLayer
+
+    def createData(self):
+        self.shared_template_name = self.factory.getUniqueString()
+        self.distroseries = self.factory.makeUbuntuDistroSeries()
+        self.distroseries.distribution.translation_focus = (
+            self.distroseries)
+        self.sourcepackage = self.factory.makeSourcePackage(
+            distroseries=self.distroseries)
+        self.productseries = self.factory.makeProductSeries()
+
+    def makeThisSidePOTemplate(self):
+        """Create POTemplate on this side."""
+        raise NotImplementedError
+
+    def makeOtherSidePOTemplate(self):
+        """Create POTemplate on the other side. Override in subclass."""
+        raise NotImplementedError
+
+    def _setPackagingLink(self):
+        """Create the packaging link from source package to product series."""
+        self.factory.makePackagingLink(
+            productseries=self.productseries,
+            sourcepackage=self.sourcepackage)
+
+    def test_getOtherSidePOTemplate_none(self):
+        # Without a packaging link, None is returned.
+        potemplate = self.makeThisSidePOTemplate()
+        self.assertIs(None, potemplate.getOtherSidePOTemplate())
+
+    def test_getOtherSidePOTemplate_linked_no_template(self):
+        # No sharing template exists on the other side.
+        self._setPackagingLink()
+        potemplate = self.makeThisSidePOTemplate()
+        self.assertIs(None, potemplate.getOtherSidePOTemplate())
+
+    def test_getOtherSidePOTemplate_shared(self):
+        # This is how sharing should look like.
+        this_potemplate = self.makeThisSidePOTemplate()
+        other_potemplate = self.makeOtherSidePOTemplate()
+        self._setPackagingLink()
+        self.assertEquals(
+            other_potemplate, this_potemplate.getOtherSidePOTemplate())
+
+
+class TestPOTemplateUbuntuSharing(TestCaseWithFactory,
+                                  TestPOTemplateUbuntuUpstreamSharingMixin):
+    """Test sharing on Ubuntu side."""
+
+    def setUp(self):
+        super(TestPOTemplateUbuntuSharing, self).setUp()
+        self.createData()
+
+    def makeThisSidePOTemplate(self):
+        return self.factory.makePOTemplate(
+            sourcepackage=self.sourcepackage, name=self.shared_template_name)
+
+    def makeOtherSidePOTemplate(self):
+        return self.factory.makePOTemplate(
+            productseries=self.productseries, name=self.shared_template_name)
+
+
+class TestPOTemplateUpstreamSharing(TestCaseWithFactory,
+                                    TestPOTemplateUbuntuUpstreamSharingMixin):
+    """Test sharing on upstream side."""
+
+    def setUp(self):
+        super(TestPOTemplateUpstreamSharing, self).setUp()
+        self.createData()
+
+    def makeThisSidePOTemplate(self):
+        return self.factory.makePOTemplate(
+            productseries=self.productseries, name=self.shared_template_name)
+
+    def makeOtherSidePOTemplate(self):
+        return self.factory.makePOTemplate(
+            sourcepackage=self.sourcepackage, name=self.shared_template_name)

=== modified file 'lib/lp/translations/tests/test_translationtemplatescollection.py'
--- lib/lp/translations/tests/test_translationtemplatescollection.py	2010-10-04 19:50:45 +0000
+++ lib/lp/translations/tests/test_translationtemplatescollection.py	2011-03-21 11:01:19 +0000
@@ -171,6 +171,19 @@
         self.assertContentEqual(
             [template], obsolete_templates.select())
 
+    def test_restrictName(self):
+        trunk = self.factory.makeProduct().getSeries('trunk')
+        template = self.factory.makePOTemplate(productseries=trunk)
+        template_name = template.name
+        # Other template that will not be returned.
+        self.factory.makePOTemplate(productseries=trunk)
+        collection = TranslationTemplatesCollection()
+        by_series = collection.restrictProductSeries(trunk)
+
+        same_name_templates = by_series.restrictName(template_name)
+        self.assertContentEqual(
+            [template], same_name_templates.select())
+
     def test_joinPOFile(self):
         trunk = self.factory.makeProduct().getSeries('trunk')
         translated_template = self.factory.makePOTemplate(productseries=trunk)

=== removed file 'lib/lp/translations/utilities/tests/test_translation_sharing_info.py'
--- lib/lp/translations/utilities/tests/test_translation_sharing_info.py	2011-02-23 12:16:59 +0000
+++ lib/lp/translations/utilities/tests/test_translation_sharing_info.py	1970-01-01 00:00:00 +0000
@@ -1,308 +0,0 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-__metaclass__ = type
-
-from canonical.testing.layers import ZopelessDatabaseLayer
-from lp.testing import TestCaseWithFactory
-from lp.translations.utilities.translationsharinginfo import (
-    get_ubuntu_sharing_info,
-    get_upstream_sharing_info,
-    has_ubuntu_template,
-    has_upstream_template,
-    )
-
-
-class TestTranslationSharingInfo(TestCaseWithFactory):
-    """Tests for `get_upstream_sharing_info` and `get_ubuntu_sharing_info`"""
-
-    layer = ZopelessDatabaseLayer
-
-    def _makeSourcePackage(self):
-        """Create an Ubuntu source package."""
-        distroseries = self.factory.makeUbuntuDistroSeries()
-        return self.factory.makeSourcePackage(distroseries=distroseries)
-
-    def _makeUpstreamProductSeries(self, sourcepackage):
-        """Create a product series and link it to the source package."""
-        productseries = self.factory.makeProductSeries()
-        self.factory.makePackagingLink(
-            distroseries=sourcepackage.distroseries,
-            sourcepackagename=sourcepackage.sourcepackagename,
-            productseries=productseries)
-        return productseries
-
-    def test_no_upstream(self):
-        # With no upstream the sharing information on a source package will
-        # be empty.
-        sourcepackage = self._makeSourcePackage()
-        self.assertEquals(
-            [],
-            get_upstream_sharing_info(sourcepackage=sourcepackage))
-
-    def test_no_upstream_with_name(self):
-        # With no upstream the sharing information on a source package will
-        # be empty, even when searching for a specific template name.
-        sourcepackage = self._makeSourcePackage()
-        templatename = self.factory.getUniqueString()
-        self.assertEquals(
-            [],
-            get_upstream_sharing_info(
-                sourcepackage=sourcepackage,
-                templatename=templatename))
-
-    def test_upstream_no_template(self):
-        # With an upstream without a template the sharing information on a
-        # source package will be empty.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        self.assertEquals(
-            [(productseries, None)],
-            get_upstream_sharing_info(sourcepackage=sourcepackage))
-
-    def test_upstream_no_template_with_name(self):
-        # With an upstream without a template the sharing information on a
-        # source package will be empty, even when searching for a specific
-        # template name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.assertEquals(
-            [(productseries, None)],
-            get_upstream_sharing_info(
-                sourcepackage=sourcepackage,
-                templatename=templatename))
-
-    def test_upstream_one_template(self):
-        # With an upstream template the sharing information on a
-        # source package will return that.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        potemplate = self.factory.makePOTemplate(productseries=productseries)
-        self.assertEquals(
-            [(productseries, potemplate)],
-            get_upstream_sharing_info(sourcepackage=sourcepackage))
-
-    def test_upstream_one_template_with_name(self):
-        # With an upstream template the sharing information on a
-        # source package will return that, even when searching for a
-        # specific template name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        potemplate = self.factory.makePOTemplate(
-            productseries=productseries, name=templatename)
-        self.assertEquals(
-            [(productseries, potemplate)],
-            get_upstream_sharing_info(
-                sourcepackage=sourcepackage,
-                templatename=templatename))
-
-    def test_upstream_one_template_with_different_name(self):
-        # With an upstream template the sharing information on a
-        # source package will be empty if a different name is queried.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.factory.makePOTemplate(
-            productseries=productseries, name=templatename)
-        different_templatename = self.factory.getUniqueString()
-        self.assertEquals(
-            [(productseries, None)],
-            get_upstream_sharing_info(
-                sourcepackage=sourcepackage,
-                templatename=different_templatename))
-
-    def test_no_ubuntu(self):
-        # With no sourcepackage the sharing information on a source package
-        # will be empty.
-        productseries = self.factory.makeProductSeries()
-        self.assertContentEqual(
-            [],
-            get_ubuntu_sharing_info(productseries=productseries))
-
-    def test_no_ubuntu_with_name(self):
-        # With no sourcepackage the sharing information on a source package
-        # will be empty, even when searching for a specific template name.
-        productseries = self.factory.makeProductSeries()
-        templatename = self.factory.getUniqueString()
-        self.assertContentEqual(
-            [],
-            get_ubuntu_sharing_info(
-                productseries=productseries, templatename=templatename))
-
-    def test_ubuntu_no_template(self):
-        # With a sourcepackage without a template the sharing information
-        # on a productseries will be empty.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        self.assertContentEqual(
-            [(sourcepackage, None)],
-            get_ubuntu_sharing_info(productseries=productseries))
-
-    def test_ubuntu_no_template_with_name(self):
-        # With a sourcepackage without a template the sharing information
-        # on a productseries will be empty, even when searching for a
-        # specific template name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.assertContentEqual(
-            [(sourcepackage, None)],
-            get_ubuntu_sharing_info(
-                productseries=productseries, templatename=templatename))
-
-    def test_ubuntu_one_template(self):
-        # With a sourcepackage template the sharing information on a
-        # source package will return that.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        potemplate = self.factory.makePOTemplate(sourcepackage=sourcepackage)
-        self.assertContentEqual(
-            [(sourcepackage, potemplate)],
-            get_ubuntu_sharing_info(
-                productseries=productseries))
-
-    def test_ubuntu_one_template_with_name(self):
-        # With a sourcepackage template the sharing information on a
-        # productseries will return that, even when searching for a
-        # specific template name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        potemplate = self.factory.makePOTemplate(
-            sourcepackage=sourcepackage,
-            name=templatename)
-        self.assertContentEqual(
-            [(sourcepackage, potemplate)],
-            get_ubuntu_sharing_info(
-                productseries=productseries, templatename=templatename))
-
-    def test_ubuntu_one_template_with_different_name(self):
-        # With a sourcepackage template the sharing information on a
-        # productseries will  be empty if a different name is queried.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.factory.makePOTemplate(
-            sourcepackage=sourcepackage,
-            name=templatename)
-        different_templatename = self.factory.getUniqueString()
-        self.assertContentEqual(
-            [(sourcepackage, None)],
-            get_ubuntu_sharing_info(
-                productseries=productseries,
-                templatename=different_templatename))
-
-    def test_has_upstream_template_no_productseries(self):
-        # Without an upstream project, no upstream templates won't be
-        # available either.
-        sourcepackage = self._makeSourcePackage()
-        templatename = self.factory.getUniqueString()
-
-        self.assertFalse(
-            has_upstream_template(sourcepackage, templatename))
-
-    def test_has_upstream_template_no_template(self):
-        # No template exists on the upstream project.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-
-        self.assertFalse(
-            has_upstream_template(sourcepackage, templatename))
-
-    def test_has_upstream_template_one_template(self):
-        # There is one template on the upstream project that matches the
-        # name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.factory.makePOTemplate(
-            productseries=productseries, name=templatename)
-
-        self.assertTrue(
-            has_upstream_template(sourcepackage, templatename))
-
-    def test_has_upstream_template_one_template_wrong_name(self):
-        # There is one template on the upstream project but it matches not
-        # the requested name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        self.factory.makePOTemplate(productseries=productseries)
-        different_templatename = self.factory.getUniqueString()
-
-        self.assertFalse(
-            has_upstream_template(sourcepackage, different_templatename))
-
-    def test_has_upstream_template_any_template(self):
-        # There is one template on the upstream project, not specifying
-        # a template name still indicates that there is a template.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        self.factory.makePOTemplate(productseries=productseries)
-
-        self.assertTrue(has_upstream_template(sourcepackage))
-
-    def test_has_upstream_template_any_template_none(self):
-        # There is no template on the upstream project.
-        sourcepackage = self._makeSourcePackage()
-        self._makeUpstreamProductSeries(sourcepackage)
-
-        self.assertFalse(has_upstream_template(sourcepackage))
-
-    def test_has_ubuntu_template_no_sourcepackage(self):
-        # There is no Ubuntu source package, so no Ubuntu template can be
-        # found.
-        productseries = self.factory.makeProductSeries()
-        templatename = self.factory.getUniqueString()
-
-        self.assertFalse(has_ubuntu_template(productseries, templatename))
-
-    def test_has_ubuntu_template_no_template(self):
-        # The Ubuntu source package has no template.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-
-        self.assertFalse(has_ubuntu_template(productseries, templatename))
-
-    def test_has_ubuntu_template_one_template(self):
-        # There is one template on the Ubuntu source package that matches
-        # the name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.factory.makePOTemplate(
-            sourcepackage=sourcepackage, name=templatename)
-
-        self.assertTrue(has_ubuntu_template(productseries, templatename))
-
-    def test_has_ubuntu_template_one_template_wrong_name(self):
-        # There is one template on the Ubuntu source package but it matches
-        # not the requested name.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        templatename = self.factory.getUniqueString()
-        self.factory.makePOTemplate(
-            sourcepackage=sourcepackage, name=templatename)
-        different_templatename = self.factory.getUniqueString()
-
-        self.assertFalse(
-            has_ubuntu_template(productseries, different_templatename))
-
-    def test_has_ubuntu_template_any_template(self):
-        # There is one template on the Ubuntu source package, not specifying
-        # a template name still indicates that there is a template.
-        sourcepackage= self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-        self.factory.makePOTemplate(sourcepackage=sourcepackage)
-
-        self.assertTrue(has_ubuntu_template(productseries))
-
-    def test_has_ubuntu_template_any_template_none(self):
-        # There is no template on the Ubuntu source package.
-        sourcepackage = self._makeSourcePackage()
-        productseries = self._makeUpstreamProductSeries(sourcepackage)
-
-        self.assertFalse(has_ubuntu_template(productseries))

=== modified file 'lib/lp/translations/utilities/translation_import.py'
--- lib/lp/translations/utilities/translation_import.py	2011-02-23 12:02:36 +0000
+++ lib/lp/translations/utilities/translation_import.py	2011-03-21 11:01:19 +0000
@@ -444,9 +444,11 @@
         sourcepackage = getUtility(ISourcePackageFactory).new(
             self.translation_import_queue_entry.sourcepackagename,
             self.translation_import_queue_entry.distroseries)
-        from lp.translations.utilities.translationsharinginfo import (
-            has_upstream_template)
-        return not has_upstream_template(sourcepackage)
+        productseries = sourcepackage.productseries
+        if productseries is None:
+            return True
+        collection = productseries.getTemplateCollection().restrictCurrent()
+        return not bool(collection.select().any())
 
     @cachedproperty
     def translations_are_msgids(self):

=== removed file 'lib/lp/translations/utilities/translationsharinginfo.py'
--- lib/lp/translations/utilities/translationsharinginfo.py	2011-02-24 17:33:35 +0000
+++ lib/lp/translations/utilities/translationsharinginfo.py	1970-01-01 00:00:00 +0000
@@ -1,177 +0,0 @@
-# Copyright 2011 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Provide sharing information.
-
-This module defines two different types of functions that provide
-information about what sharing options are available on the other side of a
-packaging link. Since they perform similar but slightly different complex
-database queries combining them in any way will usually be wrong.
-
-get_ubuntu_sharing_info and get_upstream_sharing_info will give you
-information about the source package or productseries respectively,
-combined with possibly available templates. You can restrict the search
-by specifying a template name.
-
-has_ubuntu_template and has_upstream_template make a direct search for a
-template of the given name on the other side. They do not search for
-source package or productseries but will only return True if an actual
-template exists. That is a significant difference to the get_* functions.
-"""
-
-__metaclass__ = type
-__all__ = [
-    'get_ubuntu_sharing_info',
-    'get_upstream_sharing_info',
-    'has_ubuntu_template',
-    'has_upstream_template',
-    ]
-
-from storm.expr import (\
-    And,
-    Join,
-    LeftJoin,
-    )
-
-from canonical.launchpad.interfaces.lpstorm import IStore
-from lp.registry.model.distroseries import DistroSeries
-from lp.registry.model.packaging import Packaging
-from lp.registry.model.productseries import ProductSeries
-from lp.registry.model.sourcepackage import SourcePackage
-from lp.registry.model.sourcepackagename import SourcePackageName
-from lp.translations.model.potemplate import POTemplate
-
-
-def find_ubuntu_sharing_info(productseries, templatename=None,
-                             template_only=False):
-    """Return a `ResultSet` of sharing information for this productseries.
-
-    Target is either a productseries or a source package.
-    :param productseries: The target productseries or None.
-    :param templatename: The name of the template to find information for or
-        None to get information about any sharing template in any series.
-    :param template_only: Return only `POTemplate` instances.
-    :returns: A result set of ('Distroseries', SourcePackageName, POTemplate)
-        tuples.
-    """
-
-    # SELECT *
-    # FROM Packaging
-    # JOIN Distroseries
-    #   ON Packaging.distroseries = Distroseries.id
-    # JOIN SourcePackageName
-    #   ON Packaging.sourcepackagename = SourcePackageName.id
-    # LEFT JOIN POTemplate
-    #   ON Packaging.distroseries = POTemplate.distroseries AND
-    #      Packaging.sourcepackagename = POTemplate.sourcepackagename AND
-    #      POTemplate.name = templatename
-    # WHERE Packaging.productseries = productseries
-    #
-    if templatename is None:
-        potemplate_condition = And(
-            Packaging.distroseriesID == POTemplate.distroseriesID,
-            Packaging.sourcepackagenameID == POTemplate.sourcepackagenameID)
-    else:
-        potemplate_condition = And(
-            Packaging.distroseriesID == POTemplate.distroseriesID,
-            Packaging.sourcepackagenameID ==
-                POTemplate.sourcepackagenameID,
-            POTemplate.name == templatename)
-    if template_only:
-        prejoin = Join(
-            Packaging,
-            POTemplate,
-            potemplate_condition)
-        result_classes = POTemplate
-    else:
-        prejoin = LeftJoin(
-            Join(
-                Join(
-                    Packaging, DistroSeries,
-                    Packaging.distroseriesID == DistroSeries.id),
-                SourcePackageName,
-                Packaging.sourcepackagenameID == SourcePackageName.id),
-            POTemplate,
-            potemplate_condition)
-        result_classes = (DistroSeries, SourcePackageName, POTemplate)
-    conditions = [
-        Packaging.productseries == productseries,
-        ]
-    return IStore(Packaging).using(prejoin).find(
-        result_classes, *conditions)
-
-
-def find_upstream_sharing_info(sourcepackage,
-                              templatename=None, template_only=False):
-    """Return a `ResultSet` of sharing information for this sourcepackage.
-
-    :param distroseries: The target distroseries or None.
-    :param sourcepackagename: The target sourcepackagename or None.
-    :param templatename: The name of the template to find information for or
-        None to get information about any sharing template in any series.
-    :param template_only: Return only `POTemplate` instances.
-    :returns: A ResultSet of (ProductSeries, POTemplate) tuples.
-    """
-    # SELECT *
-    # FROM Packaging
-    # JOIN ProductSeries
-    #   ON Packaging.productseries = Productseris.id
-    # LEFT JOIN POTemplate
-    #   ON Packaging.productseries = POTemplate.productseries AND
-    #      POTemplate.name = templatename
-    # WHERE Packaging.distroseries = distroseries AND
-    #      Packaging.sourcepackagename = sourcepackagename
-    #
-    if templatename is None:
-        potemplate_condition = (
-            Packaging.productseriesID == POTemplate.productseriesID)
-    else:
-        potemplate_condition = And(
-            Packaging.productseriesID == POTemplate.productseriesID,
-            POTemplate.name == templatename)
-    if template_only:
-        prejoin = Join(
-            Packaging, POTemplate, potemplate_condition)
-        result_classes = POTemplate
-    else:
-        prejoin = LeftJoin(
-            Join(
-                Packaging, ProductSeries,
-                Packaging.productseriesID == ProductSeries.id),
-            POTemplate,
-            potemplate_condition)
-        result_classes = (ProductSeries, POTemplate)
-    conditions = [
-        Packaging.distroseries == sourcepackage.distroseries,
-        Packaging.sourcepackagename == sourcepackage.sourcepackagename,
-        ]
-
-    return IStore(Packaging).using(prejoin).find(
-        result_classes, *conditions)
-
-
-def get_ubuntu_sharing_info(productseries, templatename=None):
-    """Return a list of sharing information for the given target."""
-    return [
-        (SourcePackage(packagename, series), name)
-        for series, packagename, name in find_ubuntu_sharing_info(
-            productseries, templatename)]
-
-
-def get_upstream_sharing_info(sourcepackage, templatename=None):
-    """Return a list of sharing information for the given target."""
-    return list(find_upstream_sharing_info(sourcepackage, templatename))
-
-
-def has_ubuntu_template(productseries, templatename=None):
-    """Check for existence of ubuntu template."""
-    result = find_ubuntu_sharing_info(
-        productseries, templatename, template_only=True)
-    return not result.is_empty()
-
-
-def has_upstream_template(sourcepackage, templatename=None):
-    """Check for existence of upstream template."""
-    result = find_upstream_sharing_info(
-            sourcepackage, templatename, template_only=True)
-    return not result.is_empty()