139
def compose_approval_conflict_notice(domain, templates_count, sample):
140
"""Create a note to warn about an approval conflict.
142
The note warns about the situation where one productseries, or source
143
package, or in some cases distroseries has multiple actice templates
144
with the same translation domain.
146
:param domain: The domain that's causing trouble.
147
:param templates_count: The number of clashing templates.
148
:param sample: Iterable of matching templates. Does not need to be
149
complete, just enough to report the problem usefully.
150
:return: A string describing the problematic clash.
152
sample_names = sorted([
153
'"%s"' % template.displayname for template in sample])
154
if templates_count > len(sample_names):
155
sample_names.append("and more (not shown here)")
157
Can't auto-approve upload: it is not clear what template it belongs
160
There are %d competing templates with translation domain '%s':
163
This may mean that Launchpad's auto-approver is looking for the wrong
164
domain, or that these templates' domains should be changed, or that
165
some of these templates are obsolete and need to be disabled.
167
) % (templates_count, domain, ';\n'.join(sample_names))
139
170
class TranslationImportQueueEntry(SQLBase):
140
171
implements(ITranslationImportQueueEntry)
407
438
# We don't know where this entry should be imported.
441
def reportApprovalConflict(self, domain, templates_count, sample):
442
"""Report an approval conflict."""
443
# Not sending out email for now; just tack a notice onto the
444
# queue entry where the user can find it through the queue UI.
445
notice = compose_approval_conflict_notice(
446
domain, templates_count, sample)
447
if notice != self.error_output:
448
self.setErrorOutput(notice)
450
def matchPOTemplateByDomain(self, domain, sourcepackagename=None):
451
"""Attempt to find the one matching template, by domain.
453
Looks within the context of the queue entry. If multiple templates
454
match, reports an approval conflict.
456
:param domain: Translation domain to look for.
457
:param sourcepackagename: Optional `SourcePackageName` to look for.
458
If not given, source package name is not considered in the
460
:return: A single `POTemplate`, or None.
462
potemplateset = getUtility(IPOTemplateSet)
463
subset = potemplateset.getSubset(
464
productseries=self.productseries, distroseries=self.distroseries,
465
sourcepackagename=sourcepackagename, iscurrent=True)
466
templates_query = subset.getPOTemplatesByTranslationDomain(domain)
468
# Get a limited sample of the templates. All we need from the
469
# sample is (1) to detect the presence or more than one match,
470
# and (2) to report a helpful sampling of the problem.
471
samples = list(templates_query[:5])
473
if len(samples) == 0:
474
# No matches found, sorry.
476
elif len(samples) == 1:
477
# Found the one template we're looking for.
480
# There's a conflict. Report the real number of competing
481
# templates, plus a sampling of template names.
482
self.reportApprovalConflict(
483
domain, templates_query.count(), samples)
410
486
def _get_pofile_from_language(self, lang_code, translation_domain,
411
487
sourcepackagename=None):
412
488
"""Return an IPOFile for the given language and domain.
431
507
# of just 'es' or 'fr'.
434
potemplateset = getUtility(IPOTemplateSet)
436
# Let's try first the sourcepackagename or productseries where the
437
# translation comes from.
438
potemplate_subset = potemplateset.getSubset(
439
distroseries=self.distroseries,
440
sourcepackagename=self.sourcepackagename,
441
productseries=self.productseries,
443
potemplate = potemplate_subset.getPOTemplateByTranslationDomain(
510
# Normally we find the translation's template in the
511
# source package or productseries where the translation was
512
# uploaded. Exactly one template should have the domain we're
514
potemplate = self.matchPOTemplateByDomain(
515
translation_domain, sourcepackagename=self.sourcepackagename)
446
517
is_for_distro = self.distroseries is not None
450
521
self.sourcepackagename.name == sourcepackagename.name)
452
523
if potemplate is None and is_for_distro and not know_package:
453
# The source package from where this translation doesn't have the
454
# template that this translation needs it, and thus, we look for
455
# it in a different source package as a second try. To do it, we
456
# need to get a subset of all packages in current distro series.
457
potemplate_subset = potemplateset.getSubset(
458
distroseries=self.distroseries, iscurrent=True)
459
potemplate = potemplate_subset.getPOTemplateByTranslationDomain(
524
# This translation was uploaded to a source package, but the
525
# package does not have the matching template. Try finding
526
# it elsewhere in the distribution.
527
potemplate = self.matchPOTemplateByDomain(translation_domain)
462
529
if potemplate is None:
463
530
# The potemplate is not yet imported; we cannot attach this
1237
1304
# Yay! We have a POTemplate or POFile to import this entry
1238
1305
# into. Approve.
1239
entry.setStatus(RosettaImportStatus.APPROVED,
1240
getUtility(ILaunchpadCelebrities).rosetta_experts)
1307
RosettaImportStatus.APPROVED,
1308
getUtility(ILaunchpadCelebrities).rosetta_experts)
1309
entry.setErrorOutput(None)