launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00277
[Merge] lp:~danilo/launchpad/translator_count_removal into lp:launchpad/devel
Данило Шеган has proposed merging lp:~danilo/launchpad/translator_count_removal into lp:launchpad/devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
= Remove unused field =
DistroSeriesLanguage has a translator_count which is not used anywhere. Get rid of it.
There are neither any tests for it.
(there is a translator_count on IRosettaApplication which *is* used, so I am not getting rid of that)
--
https://code.launchpad.net/~danilo/launchpad/translator_count_removal/+merge/30929
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~danilo/launchpad/translator_count_removal into lp:launchpad/devel.
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
=== modified file 'lib/lp/code/model/tests/test_branch.py'
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2010-07-15 15:01:18 +0000
+++ lib/lp/registry/model/productseries.py 2010-07-26 12:07:51 +0000
@@ -465,12 +465,15 @@
for language, pofile in ordered_results:
psl = ProductSeriesLanguage(self, language, pofile=pofile)
- psl.setCounts(pofile.potemplate.messageCount(),
- pofile.currentCount(),
- pofile.updatesCount(),
- pofile.rosettaCount(),
- pofile.unreviewedCount(),
- pofile.date_changed)
+ total = pofile.potemplate.messageCount()
+ imported = pofile.currentCount()
+ changed = pofile.updatesCount()
+ rosetta = pofile.rosettaCount()
+ unreviewed = pofile.unreviewedCount()
+ translated = imported + rosetta
+ new = rosetta - changed
+ psl.setCounts(total, translated, new, changed, unreviewed)
+ psl.last_changed_date = pofile.date_changed
results.append(psl)
else:
# If there is more than one template, do a single
@@ -498,22 +501,15 @@
POTemplate.iscurrent==True,
Language.id!=english.id).group_by(Language)
- # XXX: Ursinha 2009-11-02: The Max(POFile.date_changed) result
- # here is a naive datetime. My guess is that it happens
- # because UTC awareness is attibuted to the field in the POFile
- # model class, and in this case the Max function deals directly
- # with the value returned from the database without
- # instantiating it.
- # This seems to be irrelevant to what we're trying to achieve
- # here, but making a note either way.
-
ordered_results = query.order_by(['Language.englishname'])
- for (language, imported, changed, new, unreviewed,
- last_changed) in ordered_results:
+ for (language, imported, changed, rosetta, unreviewed,
+ last_changed) in ordered_results:
psl = ProductSeriesLanguage(self, language)
- psl.setCounts(
- total, imported, changed, new, unreviewed, last_changed)
+ translated = imported + rosetta
+ new = rosetta - changed
+ psl.setCounts(total, translated, new, changed, unreviewed)
+ psl.last_changed_date = last_changed
results.append(psl)
return results
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-07-26 06:21:45 +0000
+++ lib/lp/testing/factory.py 2010-07-26 12:07:51 +0000
@@ -158,7 +158,6 @@
ANONYMOUS,
login,
login_as,
- logout,
run_with_login,
temp_dir,
time_counter,
@@ -840,7 +839,7 @@
url = self.getUniqueURL()
else:
raise UnknownBranchTypeError(
- 'Unrecognized branch type: %r' % (branch_type,))
+ 'Unrecognized branch type: %r' % (branch_type, ))
namespace = get_branch_namespace(
owner, product=product, distroseries=distroseries,
@@ -1625,15 +1624,28 @@
syncUpdate(series)
return series
- def makeLanguage(self, language_code=None, name=None):
- if language_code is None:
- language_code = self.getUniqueString('lang')
- if name is None:
- name = "Language %s" % language_code
-
- language_set = getUtility(ILanguageSet)
- return language_set.createLanguage(language_code, name)
-
+<<<<<<< TREE
+ def makeLanguage(self, language_code=None, name=None):
+ if language_code is None:
+ language_code = self.getUniqueString('lang')
+ if name is None:
+ name = "Language %s" % language_code
+
+ language_set = getUtility(ILanguageSet)
+ return language_set.createLanguage(language_code, name)
+
+=======
+ def makeLanguage(self, language_code=None, name=None):
+ """Makes a language given the language_code and name."""
+ if language_code is None:
+ language_code = self.getUniqueString('lang')
+ if name is None:
+ name = "Language %s" % language_code
+
+ language_set = getUtility(ILanguageSet)
+ return language_set.createLanguage(language_code, name)
+
+>>>>>>> MERGE-SOURCE
def makeLibraryFileAlias(self, filename=None, content=None,
content_type='text/plain', restricted=False,
expires=None):
=== modified file 'lib/lp/translations/browser/configure.zcml'
--- lib/lp/translations/browser/configure.zcml 2010-07-16 16:58:55 +0000
+++ lib/lp/translations/browser/configure.zcml 2010-07-26 12:07:51 +0000
@@ -269,7 +269,7 @@
<browser:url
for="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguage"
path_expression="string:+lang/${language/code}"
- attribute_to_parent="productseries"
+ attribute_to_parent="parent"
rootsite="translations"/>
<browser:navigation
module="lp.translations.browser.serieslanguage"
=== modified file 'lib/lp/translations/browser/productseries.py'
--- lib/lp/translations/browser/productseries.py 2010-07-22 14:59:48 +0000
+++ lib/lp/translations/browser/productseries.py 2010-07-26 12:07:51 +0000
@@ -370,10 +370,12 @@
productserieslang = (
productserieslangset.getProductSeriesLanguage(
self.context, lang, pofile=pofile))
+ productserieslang.recalculateCounts()
else:
productserieslang = (
productserieslangset.getProductSeriesLanguage(
self.context, lang))
+ productserieslang.recalculateCounts()
productserieslangs.append(
productserieslang)
=== modified file 'lib/lp/translations/browser/serieslanguage.py'
--- lib/lp/translations/browser/serieslanguage.py 2010-03-04 07:31:38 +0000
+++ lib/lp/translations/browser/serieslanguage.py 2010-07-26 12:07:51 +0000
@@ -29,7 +29,7 @@
class BaseSeriesLanguageView(LaunchpadView):
- """View base class to render translation status for an
+ """View base class to render translation status for an
`IDistroSeries` and `IProductSeries`
This class should not be directly instantiated.
@@ -46,12 +46,15 @@
self.translationgroup = translationgroup
self.form = self.request.form
- self.batchnav = BatchNavigator(
- self.series.getCurrentTranslationTemplates(),
- self.request)
-
- self.pofiles = self.context.getPOFilesFor(
- self.batchnav.currentBatch())
+ if IDistroSeriesLanguage.providedBy(self.context):
+ self.batchnav = BatchNavigator(
+ self.series.getCurrentTranslationTemplates(),
+ self.request)
+ self.pofiles = self.context.getPOFilesFor(
+ self.batchnav.currentBatch())
+ else:
+ self.batchnav = BatchNavigator(self.context.pofiles, self.request)
+ self.pofiles = self.batchnav.currentBatch()
@property
def translation_group(self):
@@ -77,7 +80,7 @@
@property
def access_level_description(self):
"""Must not be called when there's no translation group."""
-
+
if is_read_only():
return (
"No work can be done on these translations while Launchpad "
=== modified file 'lib/lp/translations/configure.zcml'
--- lib/lp/translations/configure.zcml 2010-07-22 02:41:43 +0000
+++ lib/lp/translations/configure.zcml 2010-07-26 12:07:51 +0000
@@ -399,6 +399,16 @@
interface="lp.translations.interfaces.productserieslanguage.IProductSeriesLanguageSet"/>
</securedutility>
+ <!-- TranslatedLanguage -->
+ <facet
+ facet="translations">
+ <class
+ class="lp.translations.model.translatedlanguage.POFilesByPOTemplates">
+ <allow
+ interface="lp.translations.interfaces.translatedlanguage.IPOFilesByPOTemplates"/>
+ </class>
+ </facet>
+
<!-- POTemplate -->
<facet
facet="translations">
@@ -430,6 +440,13 @@
provides="lp.translations.interfaces.translationcommonformat.ITranslationFileData"
factory="lp.translations.model.potemplate.POTemplateToTranslationFileDataAdapter"/>
+ <!-- TranslationTemplatesCollection -->
+ <class
+ class="lp.translations.model.potemplate.TranslationTemplatesCollection">
+ <allow
+ interface="lp.translations.interfaces.potemplate.ITranslationTemplatesCollection"/>
+ </class>
+
<!-- POTemplateSet -->
<securedutility
=== modified file 'lib/lp/translations/interfaces/distroserieslanguage.py'
--- lib/lp/translations/interfaces/distroserieslanguage.py 2009-12-16 15:21:36 +0000
+++ lib/lp/translations/interfaces/distroserieslanguage.py 2010-07-26 12:07:51 +0000
@@ -35,9 +35,6 @@
"language. This includes only the real pofiles where translations "
"exist.")
- translator_count = Attribute("The number of registered translators "
- "for this language in the distribution.")
-
contributor_count = Attribute("The number of contributors in total "
"for this language in the distribution.")
=== modified file 'lib/lp/translations/interfaces/potemplate.py'
--- lib/lp/translations/interfaces/potemplate.py 2010-07-22 14:59:48 +0000
+++ lib/lp/translations/interfaces/potemplate.py 2010-07-26 12:07:51 +0000
@@ -781,5 +781,19 @@
exist for it.
"""
+class ITranslationTemplatesCollection(Interface):
+ """A `Collection` of `POTemplate`s."""
+
+ def joinOuterPOFile(language=None):
+ """Outer-join `POFile` into the collection.
+
+ :return: A `TranslationTemplatesCollection` with an added outer
+ join to `POFile`.
+ """
+
+ def select(*args):
+ """Return a ResultSet for this collection with values set to args."""
+
+
# Monkey patch for circular import avoidance done in
# _schema_circular_imports.py
=== modified file 'lib/lp/translations/interfaces/productserieslanguage.py'
--- lib/lp/translations/interfaces/productserieslanguage.py 2010-07-19 15:31:57 +0000
+++ lib/lp/translations/interfaces/productserieslanguage.py 2010-07-26 12:07:51 +0000
@@ -5,13 +5,13 @@
from lazr.restful.fields import Reference
-from zope.interface import Attribute, Interface
-from zope.schema import (
- Choice, Datetime, TextLine)
+from zope.interface import Interface
+from zope.schema import Choice, TextLine
from canonical.launchpad import _
from lp.translations.interfaces.pofile import IPOFile
from lp.translations.interfaces.rosettastats import IRosettaStats
+from lp.translations.interfaces.translatedlanguage import ITranslatedLanguage
__metaclass__ = type
@@ -21,13 +21,9 @@
]
-class IProductSeriesLanguage(IRosettaStats):
+class IProductSeriesLanguage(IRosettaStats, ITranslatedLanguage):
"""Per-language statistics for a product series."""
- language = Choice(
- title=_('Language to gather statistics for.'),
- vocabulary='Language', required=True, readonly=True)
-
pofile = Reference(
title=_("A POFile if there is only one POTemplate for the series."),
schema=IPOFile, required=False, readonly=True)
@@ -41,27 +37,6 @@
title=_("Title for the per-language per-series page."),
required=False)
- pofiles = Attribute("The set of pofiles in this distroseries for this "
- "language. This includes only the real pofiles where translations "
- "exist.")
-
-
- last_changed_date = Datetime(
- title=_('When this file was last changed.'))
-
- def getPOFilesFor(potemplates):
- """Return `POFiles` for each of `potemplates`, in the same order.
-
- For any `POTemplate` that does not have a translation to the
- required language, a `DummyPOFile` is provided.
- """
-
- def setCounts(total, imported, changed, new, unreviewed, last_changed):
- """Set aggregated message counts for ProductSeriesLanguage."""
-
- def recalculateCounts(total, imported, changed, new, unreviewed):
- """Recalculate message counts for this ProductSeriesLanguage."""
-
class IProductSeriesLanguageSet(Interface):
"""The set of productserieslanguages."""
=== added file 'lib/lp/translations/interfaces/translatedlanguage.py'
--- lib/lp/translations/interfaces/translatedlanguage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/interfaces/translatedlanguage.py 2010-07-26 12:07:51 +0000
@@ -0,0 +1,79 @@
+# Copyright 2009 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# pylint: disable-msg=E0211,E0213
+
+from zope.interface import Attribute, Interface
+from zope.interface.common.sequence import IFiniteSequence
+from zope.schema import Datetime, Object
+
+from canonical.launchpad import _
+from lp.services.worlddata.interfaces.language import ILanguage
+from lp.translations.interfaces.potemplate import IHasTranslationTemplates
+from lp.registry.interfaces.person import IPerson
+
+__metaclass__ = type
+
+__all__ = [
+ 'IPOFilesByPOTemplates',
+ 'ITranslatedLanguage',
+ ]
+
+
+class ITranslatedLanguage(Interface):
+ """Interface for providing translations for context by language.
+
+ It expects `parent` to provide `IHasTranslationTemplates`.
+ """
+
+ language = Object(
+ title=_('Language to gather statistics and POFiles for.'),
+ schema=ILanguage)
+
+ parent = Object(
+ title=_('A parent with translation templates.'),
+ schema=IHasTranslationTemplates)
+
+ pofiles = Attribute(
+ _('Iterator over all POFiles for this context and language.'))
+
+ translation_statistics = Attribute(
+ _('A dict containing relevant aggregated statistics counts.'))
+
+ def setCounts(total, translated, new, changed, unreviewed):
+ """Set aggregated message counts for ITranslatedLanguage."""
+
+ def recalculateCounts():
+ """Recalculate message counts for this ITranslatedLanguage."""
+
+ last_changed_date = Datetime(
+ title=_('When was this translation last changed.'),
+ readonly=False, required=True)
+
+ last_translator = Object(
+ title=_('Last person that translated something in this context.'),
+ schema=IPerson)
+
+
+class IPOFilesByPOTemplates(IFiniteSequence):
+ """Iterate `IPOFile`s for (`ILanguage`, `ITranslationTemplateCollection`).
+
+ This is a wrapper for Storm ResultSet that enables optimized slicing
+ by doing it lazily on the query, thus allowing DummyPOFile objects
+ to be returned while still not doing more than one database query.
+
+ It subclasses `IFiniteSequence` so it can easily be used with the
+ BatchNavigator.
+ """
+
+ def __getitem__(selector):
+ """Get an element or slice of `IPOFile`s for given templates."""
+
+ def __getslice__(start, end):
+ """Deprecated, and implemented through __getitem__."""
+
+ def __iter__():
+ """Iterates over all `IPOFile`s for given templates."""
+
+ def __len__():
+ """Provides count of `IPOTemplate`s in a template collection."""
=== modified file 'lib/lp/translations/model/distroserieslanguage.py'
--- lib/lp/translations/model/distroserieslanguage.py 2009-09-11 06:57:21 +0000
+++ lib/lp/translations/model/distroserieslanguage.py 2010-07-26 12:07:51 +0000
@@ -88,13 +88,6 @@
distinct=True)
@property
- def translator_count(self):
- translators = set()
- for translator in self.translators:
- translators = translators.union(translator.allmembers)
- return len(translators)
-
- @property
def contributor_count(self):
return self.contributorcount
@@ -156,7 +149,6 @@
self.distroseries = distroseries
self.messageCount = distroseries.messagecount
self.dateupdated = datetime.now(tz=pytz.timezone('UTC'))
- self.translator_count = 0
self.contributor_count = 0
self.title = '%s translations of %s %s' % (
self.language.englishname,
=== modified file 'lib/lp/translations/model/potemplate.py'
--- lib/lp/translations/model/potemplate.py 2010-07-23 19:44:16 +0000
+++ lib/lp/translations/model/potemplate.py 2010-07-26 12:07:51 +0000
@@ -1560,7 +1560,8 @@
@property
def has_current_translation_templates(self):
"""See `IHasTranslationTemplates`."""
- return bool(self.getCurrentTranslationTemplates(just_ids=True).any())
+ return bool(
+ self.getCurrentTranslationTemplates(just_ids=True).any())
def getCurrentTranslationFiles(self, just_ids=False):
"""See `IHasTranslationTemplates`."""
@@ -1655,10 +1656,16 @@
"""
return self.joinInner(POFile, POTemplate.id == POFile.potemplateID)
- def joinOuterPOFile(self):
+ def joinOuterPOFile(self, language=None):
"""Outer-join `POFile` into the collection.
:return: A `TranslationTemplatesCollection` with an added outer
join to `POFile`.
"""
- return self.joinOuter(POFile, POTemplate.id == POFile.potemplateID)
+ if language is not None:
+ return self.joinOuter(
+ POFile, And(POTemplate.id == POFile.potemplateID,
+ POFile.languageID == language.id))
+ else:
+ return self.joinOuter(
+ POFile, POTemplate.id == POFile.potemplateID)
=== modified file 'lib/lp/translations/model/productserieslanguage.py'
--- lib/lp/translations/model/productserieslanguage.py 2010-07-19 15:38:51 +0000
+++ lib/lp/translations/model/productserieslanguage.py 2010-07-26 12:07:51 +0000
@@ -12,17 +12,13 @@
from zope.interface import implements
-from storm.expr import Coalesce, Sum
-from storm.store import Store
-
from lp.translations.utilities.rosettastats import RosettaStats
-from lp.translations.model.pofile import POFile
-from lp.translations.model.potemplate import get_pofiles_for, POTemplate
+from lp.translations.model.translatedlanguage import TranslatedLanguageMixin
from lp.translations.interfaces.productserieslanguage import (
IProductSeriesLanguage, IProductSeriesLanguageSet)
-class ProductSeriesLanguage(RosettaStats):
+class ProductSeriesLanguage(RosettaStats, TranslatedLanguageMixin):
"""See `IProductSeriesLanguage`."""
implements(IProductSeriesLanguage)
@@ -30,56 +26,14 @@
assert 'en' != language.code, (
'English is not a translatable language.')
RosettaStats.__init__(self)
+ TranslatedLanguageMixin.__init__(self)
self.productseries = productseries
+ self.parent = productseries
self.language = language
self.variant = variant
self.pofile = pofile
self.id = 0
- self._last_changed_date = None
-
- # Reset all cached counts.
- self.setCounts()
-
- def setCounts(self, total=0, imported=0, changed=0, new=0,
- unreviewed=0, last_changed=None):
- """See `IProductSeriesLanguage`."""
- self._messagecount = total
- # "currentcount" in RosettaStats conflicts our recent terminology
- # and is closer to "imported" (except that it doesn't include
- # "changed") translations.
- self._currentcount = imported
- self._updatescount = changed
- self._rosettacount = new
- self._unreviewed_count = unreviewed
- if last_changed is not None:
- self._last_changed_date = last_changed
-
- def _getMessageCount(self):
- store = Store.of(self.language)
- query = store.find(Sum(POTemplate.messagecount),
- POTemplate.productseries==self.productseries,
- POTemplate.iscurrent==True)
- total, = query
- if total is None:
- total = 0
- return total
-
- def recalculateCounts(self):
- """See `IProductSeriesLanguage`."""
- store = Store.of(self.language)
- query = store.find(
- (Coalesce(Sum(POFile.currentcount), 0),
- Coalesce(Sum(POFile.updatescount), 0),
- Coalesce(Sum(POFile.rosettacount), 0),
- Coalesce(Sum(POFile.unreviewed_count), 0)),
- POFile.language==self.language,
- POFile.variant==None,
- POFile.potemplate==POTemplate.id,
- POTemplate.productseries==self.productseries,
- POTemplate.iscurrent==True)
- imported, changed, new, unreviewed = query[0]
- self.setCounts(self._getMessageCount(), imported, changed,
- new, unreviewed)
+ self.last_changed_date = None
@property
def title(self):
@@ -90,46 +44,29 @@
self.productseries.displayname)
def messageCount(self):
- """See `IProductSeriesLanguage`."""
- return self._messagecount
+ """See `IRosettaStats`."""
+ return self._translation_statistics['total_count']
def currentCount(self, language=None):
- """See `IProductSeriesLanguage`."""
- return self._currentcount
+ """See `IRosettaStats`."""
+ translated = self._translation_statistics['translated_count']
+ current = translated - self.rosettaCount(language)
+ return current
def updatesCount(self, language=None):
- """See `IProductSeriesLanguage`."""
- return self._updatescount
+ """See `IRosettaStats`."""
+ return self._translation_statistics['changed_count']
def rosettaCount(self, language=None):
- """See `IProductSeriesLanguage`."""
- return self._rosettacount
+ """See `IRosettaStats`."""
+ new = self._translation_statistics['new_count']
+ changed = self._translation_statistics['changed_count']
+ rosetta = new + changed
+ return rosetta
def unreviewedCount(self):
- """See `IProductSeriesLanguage`."""
- return self._unreviewed_count
-
- @property
- def last_changed_date(self):
- """See `IProductSeriesLanguage`."""
- return self._last_changed_date
-
- @property
- def pofiles(self):
- """See `IProductSeriesLanguage`."""
- store = Store.of(self.language)
- result = store.find(
- POFile,
- POFile.language==self.language,
- POFile.variant==self.variant,
- POFile.potemplate==POTemplate.id,
- POTemplate.productseries==self.productseries,
- POTemplate.iscurrent==True)
- return result.order_by(['-priority'])
-
- def getPOFilesFor(self, potemplates):
- """See `IProductSeriesLanguage`."""
- return get_pofiles_for(potemplates, self.language, self.variant)
+ """See `IRosettaStats`."""
+ return self._translation_statistics['unreviewed_count']
class ProductSeriesLanguageSet:
=== added file 'lib/lp/translations/model/translatedlanguage.py'
--- lib/lp/translations/model/translatedlanguage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/model/translatedlanguage.py 2010-07-26 12:07:51 +0000
@@ -0,0 +1,132 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__all__ = ['TranslatedLanguageMixin']
+
+import pytz
+
+from zope.interface import implements
+
+from storm.expr import Coalesce, Desc, Max, Sum
+
+from lp.translations.interfaces.potemplate import IHasTranslationTemplates
+from lp.translations.interfaces.translatedlanguage import (
+ IPOFilesByPOTemplates, ITranslatedLanguage)
+from lp.translations.model.pofile import POFile
+from lp.translations.model.potemplate import POTemplate
+
+
+class POFilesByPOTemplates(object):
+ """See `IPOFilesByPOTemplates`."""
+ implements(IPOFilesByPOTemplates)
+
+ def __init__(self, templates_collection, language):
+ self.templates_collection = templates_collection
+ self.language = language
+
+ def _getDummyOrPOFile(self, potemplate, pofile):
+ if pofile is None:
+ return potemplate.getDummyPOFile(self.language,
+ check_for_existing=False)
+ else:
+ return pofile
+
+ def _getPOTemplatesAndPOFilesResultSet(self):
+ current_templates = self.templates_collection
+ pofiles = current_templates.joinOuterPOFile(self.language)
+ results = pofiles.select(POTemplate, POFile).order_by(
+ Desc(POTemplate.priority), POTemplate.name)
+ return results
+
+ def _getPOFilesForResultSet(self, resultset, selector=None):
+ pofiles_list = []
+ if selector is None:
+ results = resultset
+ else:
+ results = resultset[selector]
+ for potemplate, pofile in results:
+ pofiles_list.append(self._getDummyOrPOFile(potemplate, pofile))
+ return pofiles_list
+
+ def __getitem__(self, selector):
+ resultset = self._getPOTemplatesAndPOFilesResultSet()
+ if isinstance(selector, slice):
+ return self._getPOFilesForResultSet(resultset, selector)
+ else:
+ potemplate, pofile = resultset[selector]
+ return self._getDummyOrPOFile(potemplate, pofile)
+
+ def __iter__(self):
+ resultset = self._getPOTemplatesAndPOFilesResultSet()
+ for pofile in self._getPOFilesForResultSet(resultset):
+ yield pofile
+
+ def __len__(self):
+ return self.templates_collection.select(POTemplate).count()
+
+ def __nonzero__(self):
+ return bool(self.templates_collection.select(POTemplate).any())
+
+
+class TranslatedLanguageMixin(object):
+ """See `ITranslatedLanguage`."""
+ implements(ITranslatedLanguage)
+
+ language = None
+ parent = None
+
+ def __init__(self):
+ self.setCounts(total=0, translated=0, new=0, changed=0, unreviewed=0)
+
+ @property
+ def pofiles(self):
+ """See `ITranslatedLanguage`."""
+ assert IHasTranslationTemplates.providedBy(self.parent), (
+ "Parent object should implement `IHasTranslationTemplates`.")
+ current_templates = self.parent.getCurrentTemplatesCollection()
+ return POFilesByPOTemplates(current_templates, self.language)
+
+ @property
+ def translation_statistics(self):
+ """See `ITranslatedLanguage`."""
+ # This is a temporary translation statistics 'object' to allow
+ # smoother migration from IRosettaStats to something much nicer.
+ return self._translation_statistics
+
+ def setCounts(self, total, translated, new, changed, unreviewed):
+ """See `ITranslatedLanguage`."""
+ untranslated = total - translated
+ self._translation_statistics = {
+ 'total_count': total,
+ 'translated_count': translated,
+ 'new_count': new,
+ 'changed_count': changed,
+ 'unreviewed_count': unreviewed,
+ 'untranslated_count': untranslated,
+ }
+
+ def recalculateCounts(self):
+ """See `ITranslatedLanguage`."""
+ templates = self.parent.getCurrentTemplatesCollection()
+ pofiles = templates.joinOuterPOFile(self.language)
+ total_count_results = list(
+ pofiles.select(Coalesce(Sum(POTemplate.messagecount), 0),
+ Coalesce(Sum(POFile.currentcount), 0),
+ Coalesce(Sum(POFile.updatescount), 0),
+ Coalesce(Sum(POFile.rosettacount), 0),
+ Coalesce(Sum(POFile.unreviewed_count), 0),
+ Max(POFile.date_changed)))
+ total, imported, changed, rosetta, unreviewed, date_changed = (
+ total_count_results[0])
+ translated = imported + rosetta
+ new = rosetta - changed
+ self.setCounts(total, translated, new, changed, unreviewed)
+
+ # We have to add a timezone to the otherwise naive-datetime object
+ # (because we've gotten it using Max() aggregate function).
+ if date_changed is not None:
+ date_changed = date_changed.replace(tzinfo=pytz.UTC)
+ self.last_changed_date = date_changed
+
+ last_changed_date = None
+ last_translator = None
=== modified file 'lib/lp/translations/tests/test_productserieslanguage.py'
--- lib/lp/translations/tests/test_productserieslanguage.py 2010-07-21 09:35:41 +0000
+++ lib/lp/translations/tests/test_productserieslanguage.py 2010-07-26 12:07:51 +0000
@@ -89,8 +89,11 @@
self.assertEquals(sr_psl.language, serbian)
self.assertEquals(sr_psl.pofile, None)
- # Only this POFile is returned by the `pofiles` property.
- self.assertEquals(list(sr_psl.pofiles), [pofile1])
+ # A POFile is returned where it exists, and a DummyPOFile where
+ # it doesn't.
+ self.assertEquals(2, len(sr_psl.pofiles))
+ self.assertEquals(potemplate2, sr_psl.pofiles[0].potemplate)
+ self.assertEquals(pofile1, sr_psl.pofiles[1])
# If we provide a POFile for the other template, `pofiles`
# returns both (ordered by decreasing priority).
@@ -173,8 +176,9 @@
self.productseries, self.language)
self.assertEquals(psl.messageCount(), 0)
- # So, we need to get it through productseries.productserieslanguages.
- psl = self.productseries.productserieslanguages[0]
+ # We explicitely ask for stats to be recalculated.
+ psl.recalculateCounts()
+
self.assertPSLStatistics(psl,
(pofile.messageCount(),
pofile.translatedCount(),
@@ -199,17 +203,14 @@
self.setPOFileStatistics(pofile2, 1, 1, 1, 1, pofile2.date_changed)
psl = self.productseries.productserieslanguages[0]
-
- # The psl.last_changed_date here is a naive datetime. So, for sake of
- # the tests, we should make pofile2 naive when checking if it matches
- # the last calculated changed date, that should be the same as
- # pofile2, created last.
+ # We explicitely ask for stats to be recalculated.
+ psl.recalculateCounts()
# Total is a sum of totals in both POTemplates (10+20).
# Translated is a sum of imported and rosetta translations,
# which adds up as (4+3)+(1+1).
self.assertPSLStatistics(psl, (30, 9, 5, 4, 3, 6,
- pofile2.date_changed.replace(tzinfo=None)))
+ pofile2.date_changed))
self.assertPSLStatistics(psl, (
pofile1.messageCount() + pofile2.messageCount(),
pofile1.translatedCount() + pofile2.translatedCount(),
@@ -217,7 +218,7 @@
pofile1.rosettaCount() + pofile2.rosettaCount(),
pofile1.updatesCount() + pofile2.updatesCount(),
pofile1.unreviewedCount() + pofile2.unreviewedCount(),
- pofile2.date_changed.replace(tzinfo=None)))
+ pofile2.date_changed))
def test_recalculateCounts(self):
# Test that recalculateCounts works correctly.
@@ -236,13 +237,14 @@
psl = self.psl_set.getProductSeriesLanguage(self.productseries,
self.language)
- # recalculateCounts() doesn't recalculate the last changed date.
+
psl.recalculateCounts()
# Total is a sum of totals in both POTemplates (10+20).
# Translated is a sum of imported and rosetta translations,
# which adds up as (1+3)+(1+1).
+ # recalculateCounts() recalculates even the last changed date.
self.assertPSLStatistics(psl, (30, 6, 2, 4, 3, 5,
- None))
+ pofile2.date_changed))
def test_recalculateCounts_no_pofiles(self):
# Test that recalculateCounts works correctly even when there
=== added file 'lib/lp/translations/tests/test_translatedlanguage.py'
--- lib/lp/translations/tests/test_translatedlanguage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/tests/test_translatedlanguage.py 2010-07-26 12:07:51 +0000
@@ -0,0 +1,462 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+
+from zope.component import getUtility
+from zope.interface.verify import verifyObject
+from zope.security.proxy import removeSecurityProxy
+
+from lp.translations.interfaces.productserieslanguage import (
+ IProductSeriesLanguageSet)
+from lp.translations.interfaces.translatedlanguage import ITranslatedLanguage
+from lp.translations.model.pofile import DummyPOFile
+from lp.testing import TestCaseWithFactory
+from canonical.testing import ZopelessDatabaseLayer
+
+
+class TestTranslatedLanguageMixin(TestCaseWithFactory):
+ """Test TranslatedLanguageMixin."""
+
+ layer = ZopelessDatabaseLayer
+
+ def setUp(self):
+ # Create a productseries that uses translations.
+ TestCaseWithFactory.setUp(self)
+ self.productseries = self.factory.makeProductSeries()
+ self.productseries.product.official_rosetta = True
+ self.parent = self.productseries
+ self.psl_set = getUtility(IProductSeriesLanguageSet)
+ self.language = self.factory.makeLanguage('sr@test')
+
+ def getTranslatedLanguage(self, language):
+ return self.psl_set.getProductSeriesLanguage(self.productseries,
+ language)
+
+ def addPOTemplate(self, number_of_potmsgsets=0, priority=0):
+ potemplate = self.factory.makePOTemplate(
+ productseries=self.productseries)
+ for sequence in range(number_of_potmsgsets):
+ self.factory.makePOTMsgSet(potemplate, sequence=sequence+1)
+ removeSecurityProxy(potemplate).messagecount = number_of_potmsgsets
+ potemplate.priority = priority
+ return potemplate
+
+ def test_interface(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ self.assertTrue(verifyObject(ITranslatedLanguage,
+ translated_language))
+
+ def test_language(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ self.assertEqual(self.language,
+ translated_language.language)
+
+ def test_parent(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ self.assertEqual(self.parent,
+ translated_language.parent)
+
+ def test_pofiles_notemplates(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ self.assertEqual([], list(translated_language.pofiles))
+
+ def test_pofiles_template_no_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate()
+ dummy_pofile = potemplate.getDummyPOFile(self.language)
+ pofiles = list(translated_language.pofiles)
+ self.assertEqual(1, len(pofiles))
+
+ # When there are no actual PO files, we get a DummyPOFile object
+ # instead.
+ dummy_pofile = pofiles[0]
+ naked_dummy = removeSecurityProxy(dummy_pofile)
+ self.assertEqual(DummyPOFile, type(naked_dummy))
+ self.assertEqual(self.language, dummy_pofile.language)
+ self.assertEqual(potemplate, dummy_pofile.potemplate)
+
+ # Two queries get executed when listifying
+ # TranslatedLanguageMixin.pofiles: a len() does a count, and
+ # then all POTemplates and POFiles are fetched with the other.
+ self.assertStatementCount(2, list, translated_language.pofiles)
+
+ def test_pofiles_template_with_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate()
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ self.assertEqual([pofile], list(translated_language.pofiles))
+
+ # Two queries get executed when listifying
+ # TranslatedLanguageMixin.pofiles: a len() does a count, and
+ # then all POTemplates and POFiles are fetched with the other.
+ self.assertStatementCount(2, list, translated_language.pofiles)
+
+ def test_pofiles_two_templates(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ # Two templates with different priorities so they get sorted
+ # appropriately.
+ potemplate1 = self.addPOTemplate(priority=2)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ potemplate2 = self.addPOTemplate(priority=1)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ self.assertEqual([pofile1, pofile2],
+ list(translated_language.pofiles))
+
+ # Two queries get executed when listifying
+ # TranslatedLanguageMixin.pofiles: a len() does a count, and
+ # then all POTemplates and POFiles are fetched with the other.
+ self.assertStatementCount(2, list, translated_language.pofiles)
+
+ def test_pofiles_two_templates_one_dummy(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ # Two templates with different priorities so they get sorted
+ # appropriately.
+ potemplate1 = self.addPOTemplate(priority=2)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ potemplate2 = self.addPOTemplate(priority=1)
+ pofiles = translated_language.pofiles
+ self.assertEqual(pofile1, pofiles[0])
+ dummy_pofile = removeSecurityProxy(pofiles[1])
+ self.assertEqual(DummyPOFile, type(dummy_pofile))
+
+ # Two queries get executed when listifying
+ # TranslatedLanguageMixin.pofiles: a len() does a count, and
+ # then all POTemplates and POFiles are fetched with the other.
+ self.assertStatementCount(2, list, translated_language.pofiles)
+
+ def test_pofiles_slicing(self):
+ # Slicing still works, and always does the same constant number
+ # of queries (1).
+ translated_language = self.getTranslatedLanguage(self.language)
+ # Three templates with different priorities so they get sorted
+ # appropriately.
+ potemplate1 = self.addPOTemplate(priority=2)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ potemplate2 = self.addPOTemplate(priority=1)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ potemplate3 = self.addPOTemplate(priority=0)
+
+ pofiles = translated_language.pofiles[0:2]
+ self.assertEqual([pofile1, pofile2], list(pofiles))
+
+ # Slicing executes only a single query.
+ get_slice = lambda of, start, end: list(of[start:end])
+ self.assertStatementCount(1, get_slice,
+ translated_language.pofiles, 1, 3)
+
+ def test_pofiles_slicing_dummies(self):
+ # Slicing includes DummyPOFiles.
+ translated_language = self.getTranslatedLanguage(self.language)
+ # Three templates with different priorities so they get sorted
+ # appropriately.
+ potemplate1 = self.addPOTemplate(priority=2)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ potemplate2 = self.addPOTemplate(priority=1)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ potemplate3 = self.addPOTemplate(priority=0)
+
+ pofiles = translated_language.pofiles[1:3]
+ self.assertEqual(pofile2, pofiles[0])
+ dummy_pofile = removeSecurityProxy(pofiles[1])
+ self.assertEqual(DummyPOFile, type(dummy_pofile))
+
+ def test_statistics_empty(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+
+ expected = {
+ 'total_count': 0,
+ 'translated_count': 0,
+ 'new_count': 0,
+ 'changed_count': 0,
+ 'unreviewed_count': 0,
+ 'untranslated_count': 0,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
+
+ def test_setCounts_statistics(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+
+ total = 5
+ translated = 4
+ new = 3
+ changed = 2
+ unreviewed = 1
+ untranslated = total - translated
+
+ translated_language.setCounts(
+ total, translated, new, changed, unreviewed)
+
+ expected = {
+ 'total_count': total,
+ 'translated_count': translated,
+ 'new_count': new,
+ 'changed_count': changed,
+ 'unreviewed_count': unreviewed,
+ 'untranslated_count': untranslated,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
+
+ def test_recalculateCounts_empty(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+
+ translated_language.recalculateCounts()
+
+ expected = {
+ 'total_count': 0,
+ 'translated_count': 0,
+ 'new_count': 0,
+ 'changed_count': 0,
+ 'unreviewed_count': 0,
+ 'untranslated_count': 0,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
+
+ def test_recalculateCounts_total_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 5, translated_language.translation_statistics['total_count'])
+
+ def test_recalculateCounts_total_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 5+3, translated_language.translation_statistics['total_count'])
+
+ def test_recalculateCounts_translated_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ naked_pofile = removeSecurityProxy(pofile)
+ # translated count is current + rosetta
+ naked_pofile.currentcount = 3
+ naked_pofile.rosettacount = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 4, translated_language.translation_statistics['translated_count'])
+
+ def test_recalculateCounts_translated_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ # translated count is current + rosetta
+ naked_pofile1.currentcount = 3
+ naked_pofile1.rosettacount = 1
+
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ naked_pofile2 = removeSecurityProxy(pofile2)
+ # translated count is current + rosetta
+ naked_pofile2.currentcount = 1
+ naked_pofile2.rosettacount = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 6, translated_language.translation_statistics['translated_count'])
+
+ def test_recalculateCounts_changed_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ naked_pofile = removeSecurityProxy(pofile)
+ # translated count is current + rosetta
+ naked_pofile.updatescount = 3
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 3, translated_language.translation_statistics['changed_count'])
+
+ def test_recalculateCounts_changed_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ naked_pofile1.updatescount = 3
+
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ naked_pofile2 = removeSecurityProxy(pofile2)
+ naked_pofile2.updatescount = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 4, translated_language.translation_statistics['changed_count'])
+
+ def test_recalculateCounts_new_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ naked_pofile = removeSecurityProxy(pofile)
+ # new count is rosetta - changed
+ naked_pofile.rosettacount = 3
+ naked_pofile.updatescount = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 2, translated_language.translation_statistics['new_count'])
+
+ def test_recalculateCounts_new_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ # new count is rosetta - changed
+ naked_pofile1.rosettacount = 3
+ naked_pofile1.updatescount = 1
+
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ naked_pofile2 = removeSecurityProxy(pofile2)
+ # new count is rosetta - changed
+ naked_pofile2.rosettacount = 2
+ naked_pofile2.updatescount = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 3, translated_language.translation_statistics['new_count'])
+
+ def test_recalculateCounts_unreviewed_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ naked_pofile = removeSecurityProxy(pofile)
+ # translated count is current + rosetta
+ naked_pofile.unreviewed_count = 3
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 3, translated_language.translation_statistics['unreviewed_count'])
+
+ def test_recalculateCounts_unreviewed_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ naked_pofile1.unreviewed_count = 3
+
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ naked_pofile2 = removeSecurityProxy(pofile2)
+ naked_pofile2.unreviewed_count = 1
+
+ translated_language.recalculateCounts()
+ self.assertEqual(
+ 4, translated_language.translation_statistics['unreviewed_count'])
+
+ def test_recalculateCounts_one_pofile(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+ potemplate = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile = self.factory.makePOFile(self.language.code, potemplate)
+ naked_pofile = removeSecurityProxy(pofile)
+ # translated count is current + rosetta
+ naked_pofile.currentcount = 3
+ naked_pofile.rosettacount = 1
+ # Changed count is 'updatescount' on POFile.
+ # It has to be lower or equal to currentcount.
+ naked_pofile.updatescount = 1
+ # new is rosettacount-updatescount.
+ naked_pofile.newcount = 0
+ naked_pofile.unreviewed_count = 3
+
+ translated_language.recalculateCounts()
+
+ expected = {
+ 'total_count': 5,
+ 'translated_count': 4,
+ 'new_count': 0,
+ 'changed_count': 1,
+ 'unreviewed_count': 3,
+ 'untranslated_count': 1,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
+
+ def test_recalculateCounts_two_pofiles(self):
+ translated_language = self.getTranslatedLanguage(self.language)
+
+ # Set up one template with a single PO file.
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ # translated count is current + rosetta
+ naked_pofile1.currentcount = 2
+ naked_pofile1.rosettacount = 2
+ # Changed count is 'updatescount' on POFile.
+ # It has to be lower or equal to currentcount.
+ # new is rosettacount-updatescount.
+ naked_pofile1.updatescount = 1
+ naked_pofile1.unreviewed_count = 3
+
+ # Set up second template with a single PO file.
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+ pofile2 = self.factory.makePOFile(self.language.code, potemplate2)
+ naked_pofile2 = removeSecurityProxy(pofile2)
+ # translated count is current + rosetta
+ naked_pofile2.currentcount = 1
+ naked_pofile2.rosettacount = 2
+ # Changed count is 'updatescount' on POFile.
+ # It has to be lower or equal to currentcount.
+ # new is rosettacount-updatescount.
+ naked_pofile2.updatescount = 1
+ naked_pofile2.unreviewed_count = 1
+
+ translated_language.recalculateCounts()
+
+ expected = {
+ 'total_count': 8,
+ 'translated_count': 7,
+ 'new_count': 2,
+ 'changed_count': 2,
+ 'unreviewed_count': 4,
+ 'untranslated_count': 1,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
+
+ def test_recalculateCounts_two_templates_one_translation(self):
+ # Make sure recalculateCounts works even if a POFile is missing
+ # for one of the templates.
+ translated_language = self.getTranslatedLanguage(self.language)
+
+ # Set up one template with a single PO file.
+ potemplate1 = self.addPOTemplate(number_of_potmsgsets=5)
+ pofile1 = self.factory.makePOFile(self.language.code, potemplate1)
+ naked_pofile1 = removeSecurityProxy(pofile1)
+ # translated count is current + rosetta
+ naked_pofile1.currentcount = 2
+ naked_pofile1.rosettacount = 2
+ # Changed count is 'updatescount' on POFile.
+ # It has to be lower or equal to currentcount.
+ # new is rosettacount-updatescount.
+ naked_pofile1.updatescount = 1
+ naked_pofile1.unreviewed_count = 3
+
+ # Set up second template with a single PO file.
+ potemplate2 = self.addPOTemplate(number_of_potmsgsets=3)
+
+ translated_language.recalculateCounts()
+
+ expected = {
+ 'total_count': 8,
+ 'translated_count': 4,
+ 'new_count': 1,
+ 'changed_count': 1,
+ 'unreviewed_count': 3,
+ 'untranslated_count': 4,
+ }
+ self.assertEqual(expected,
+ translated_language.translation_statistics)
=== modified file 'lib/lp/translations/tests/test_translationtemplatescollection.py'
--- lib/lp/translations/tests/test_translationtemplatescollection.py 2010-07-17 16:19:38 +0000
+++ lib/lp/translations/tests/test_translationtemplatescollection.py 2010-07-26 12:07:51 +0000
@@ -205,3 +205,22 @@
]
self.assertContentEqual(
expected_outcome, joined.select(POTemplate, POFile))
+
+ def test_joinOuterPOFile_language(self):
+ trunk = self.factory.makeProduct().getSeries('trunk')
+ translated_template = self.factory.makePOTemplate(productseries=trunk)
+ untranslated_template = self.factory.makePOTemplate(
+ productseries=trunk)
+ nl = translated_template.newPOFile('nl')
+ de = translated_template.newPOFile('de')
+
+ collection = TranslationTemplatesCollection()
+ by_series = collection.restrictProductSeries(trunk)
+ joined = by_series.joinOuterPOFile(language=nl.language)
+
+ expected_outcome = [
+ (translated_template, nl),
+ (untranslated_template, None),
+ ]
+ self.assertContentEqual(
+ expected_outcome, joined.select(POTemplate, POFile))
Follow ups