Merge lp:~jtv/launchpad/pre-675426 into lp:launchpad

Proposed by Jeroen T. Vermeulen on 2010-11-16
Status: Merged
Approved by: Jeroen T. Vermeulen on 2010-11-16
Approved revision: no longer in the source branch.
Merged at revision: 11928
Proposed branch: lp:~jtv/launchpad/pre-675426
Merge into: lp:launchpad
Diff against target: 1150 lines (+0/-1090)
7 files modified
lib/lp/services/scripts/tests/__init__.py (+0/-2)
lib/lp/translations/scripts/migrate_kde_potemplates.py (+0/-339)
lib/lp/translations/scripts/remove_obsolete_translations.py (+0/-383)
scripts/rosetta/message-sharing-populate-test.py (+0/-134)
scripts/rosetta/message-sharing-populate.py (+0/-209)
scripts/rosetta/remove-obsolete-translations.py (+0/-21)
utilities/migrater/file-ownership.txt (+0/-2)
To merge this branch: bzr merge lp:~jtv/launchpad/pre-675426
Reviewer Review Type Date Requested Status
Henning Eggers (community) code 2010-11-16 Approve on 2010-11-16
Review via email: mp+40953@code.launchpad.net

Commit Message

[r=henninge][ui=none][no-qa] Clean up dead Translations code.

Description of the Change

= Remove dead Translations code =

This gets rid of some dead, untested, obsolete Translations script code that we're only carrying around out of historical interest. We have bzr for historical interests.

Jeroen

To post a comment you must log in.
Henning Eggers (henninge) wrote :

Yeah for code deletion!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/services/scripts/tests/__init__.py'
2--- lib/lp/services/scripts/tests/__init__.py 2010-08-20 20:31:18 +0000
3+++ lib/lp/services/scripts/tests/__init__.py 2010-11-16 13:00:58 +0000
4@@ -30,7 +30,6 @@
5 'scripts/clean-sourceforge-project-entries.py',
6 'scripts/import-zope-specs.py',
7 'scripts/rosetta/gettext_check_messages.py',
8- 'scripts/rosetta/remove-obsolete-translations.py',
9 # sqlobject.DatbaseIndex ?
10 'scripts/linkreport.py',
11 # Python executable without '.py' extension.
12@@ -38,7 +37,6 @@
13 'scripts/queue',
14 # Bad script, no help.
15 'scripts/librarian-report.py',
16- 'scripts/rosetta/message-sharing-populate-test.py',
17 'scripts/get-stacked-on-branches.py',
18 'scripts/start-loggerhead.py',
19 'scripts/stop-loggerhead.py',
20
21=== removed file 'lib/lp/translations/scripts/migrate_kde_potemplates.py'
22--- lib/lp/translations/scripts/migrate_kde_potemplates.py 2010-08-20 20:31:18 +0000
23+++ lib/lp/translations/scripts/migrate_kde_potemplates.py 1970-01-01 00:00:00 +0000
24@@ -1,339 +0,0 @@
25-# Copyright 2009 Canonical Ltd. This software is licensed under the
26-# GNU Affero General Public License version 3 (see the file LICENSE).
27-
28-"""Migrate KDE POTemplates to native support for plural forms and context ."""
29-
30-__metaclass__ = type
31-
32-__all__ = [
33- 'migrate_potemplates',
34- ]
35-
36-from sqlobject import SQLObjectNotFound
37-from zope.security.proxy import removeSecurityProxy
38-
39-from canonical.database.sqlbase import (
40- cursor,
41- sqlvalues,
42- )
43-from lp.translations.interfaces.translationfileformat import (
44- TranslationFileFormat,
45- )
46-from lp.translations.interfaces.translations import TranslationConstants
47-from lp.translations.model.pomsgid import POMsgID
48-from lp.translations.model.potemplate import POTemplate
49-from lp.translations.model.potmsgset import POTMsgSet
50-from lp.translations.model.potranslation import POTranslation
51-from lp.translations.model.translationmessage import TranslationMessage
52-
53-
54-def getOrCreatePOMsgID(msgid):
55- try:
56- pomsgid = POMsgID.byMsgid(msgid)
57- except SQLObjectNotFound:
58- pomsgid = POMsgID(msgid=msgid)
59- return pomsgid
60-
61-def get_potranslations(translations):
62- potranslations = {}
63- for index, translation in enumerate(translations):
64- if translation != '':
65- potranslations[index] = (
66- POTranslation.getOrCreateTranslation(translation))
67- else:
68- potranslations[index] = None
69- for index in range(len(potranslations),
70- TranslationConstants.MAX_PLURAL_FORMS):
71- potranslations[index] = None
72- return potranslations
73-
74-
75-def find_existing_translation(potmsgset, pofile, potranslations):
76- unprotected_potmsgset = removeSecurityProxy(potmsgset)
77- existing_message = (
78- unprotected_potmsgset._findTranslationMessage(
79- pofile=pofile, potranslations=potranslations,
80- pluralforms=TranslationConstants.MAX_PLURAL_FORMS))
81- return existing_message
82-
83-
84-def migrate_translations_for_potmsgset(potmsgset, from_potmsgset,
85- logger, ztm):
86- """Migrate translations from `from_potmsgset` to `potmsgset`.
87-
88- `from_potmsgset` is an old, unmigrated POTMsgSet we are migrating
89- translations from. Translations are migrated to native context
90- and plural forms support along the way.
91-
92- `from_potmsgset` and `potmsgset` might be the same, which happens when
93- we are migrating templates as well.
94- """
95- messages = TranslationMessage.select(
96- "potmsgset = %s" % sqlvalues(from_potmsgset))
97- logger.debug("Migrating %d translations for '%s'..." % (
98- messages.count(), potmsgset.singular_text))
99- for message in messages:
100- msgstrs = message.translations
101- # Let's see if translations have only the first plural
102- # form defined: if they do, then they need migration.
103- single_string = False
104- if len(msgstrs) > 0 and msgstrs[0] is not None:
105- single_string = True
106- for msgstr in msgstrs[1:]:
107- if msgstr is not None:
108- single_string = False
109-
110- if single_string:
111- translations = msgstrs[0].split('\n')
112-
113- # If there's only a single plural form, no need to change
114- # anything. If POTMsgSets are different, we still need
115- # to move the translation from one to the other.
116- if len(translations) == 1 and potmsgset == from_potmsgset:
117- continue
118-
119- # If there is an existing TranslationMessage with
120- # these translations, re-use that and remove this one,
121- # otherwise modify this one in-place.
122- potranslations = get_potranslations(translations)
123- existing_message = find_existing_translation(
124- potmsgset, message.pofile, potranslations)
125- if existing_message:
126- if existing_message.id != message.id:
127- # Only transfer is_current and is_imported
128- # properties to an existing translation.
129- if message.is_current:
130- existing_message.is_current = True
131- if message.is_imported:
132- existing_message.is_imported = True
133- # And remove the current message.
134- message.destroySelf()
135- else:
136- # Modify `message` in-place.
137- message.msgstr0 = potranslations[0]
138- message.msgstr1 = potranslations[1]
139- message.msgstr2 = potranslations[2]
140- message.msgstr3 = potranslations[3]
141- message.msgstr4 = potranslations[4]
142- message.msgstr5 = potranslations[5]
143- if potmsgset.id != from_potmsgset.id:
144- # Point TranslationMessage to a new POTMsgSet.
145- # To avoid hitting constraints, first unset
146- # the is_current and is_imported flags, and
147- # restore them afterwards.
148- stored_is_current = message.is_current
149- stored_is_imported = message.is_imported
150- message.is_current = False
151- message.is_imported = False
152- message.potmsgset = potmsgset
153- message.sync()
154- message.is_current = stored_is_current
155- message.is_imported = stored_is_imported
156- message.sync()
157-
158-
159-def migrate_kde_potemplate_translations(potemplate, logger, ztm):
160- assert potemplate.source_file_format == TranslationFileFormat.KDEPO, (
161- "Trying to move translations for non-KDEPO template.")
162- cur = cursor()
163- cur.execute("""
164- SELECT old_msg.id, new_msg.id
165- FROM POTMsgSet AS old_msg, POMsgID AS old_msgid,
166- POTMsgSet AS new_msg, POMsgID AS singular, POMsgID AS plural
167- WHERE
168- -- they are both from this template
169- old_msg.potemplate=%s AND
170- new_msg.potemplate=old_msg.potemplate AND
171- -- old one is obsolete
172- old_msg.sequence=0 AND
173- -- old POTMsgSet has a singular form of the form '_n:...',
174- -- and no plural form
175- old_msg.msgid_singular=old_msgid.id AND
176- old_msg.msgid_plural IS NULL AND
177- old_msgid.msgid LIKE E'\\\\_n: %%' AND
178- -- and new POTMsgSet has singular and plural which when joined
179- -- give the old plural form
180- new_msg.msgid_singular=singular.id AND
181- new_msg.msgid_plural=plural.id AND
182- '_n: ' || singular.msgid || E'\\n' || plural.msgid = old_msgid.msgid
183- """ % sqlvalues(potemplate))
184- plural_potmsgsets = cur.fetchall()
185-
186- logger.info("Migrating translations for %d plural POTMsgSets..." % (
187- len(plural_potmsgsets)))
188- for old_potmsgset_id, new_potmsgset_id in plural_potmsgsets:
189- old_potmsgset = POTMsgSet.get(old_potmsgset_id)
190- new_potmsgset = POTMsgSet.get(new_potmsgset_id)
191- migrate_translations_for_potmsgset(new_potmsgset, old_potmsgset,
192- logger, ztm)
193-
194- cur.execute("""
195- SELECT old_msg.id, new_msg.id
196- FROM POTMsgSet AS old_msg, POMsgID AS old_msgid,
197- POTMsgSet AS new_msg, POMsgID AS new_msgid
198- WHERE
199- -- they are both from this template
200- old_msg.potemplate=%s AND
201- new_msg.potemplate=old_msg.potemplate AND
202- -- old one is obsolete
203- old_msg.sequence=0 AND
204- -- old POTMsgSet has a singular form of the form '_:...',
205- -- and no plural form
206- old_msg.msgid_singular=old_msgid.id AND
207- old_msg.msgid_plural IS NULL AND
208- old_msgid.msgid LIKE E'\\\\_: %%' AND
209- -- and new POTMsgSet has singular and context which when joined
210- -- give the old contextual message
211- new_msg.msgid_singular=new_msgid.id AND
212- '_: ' || new_msg.context || E'\\n' || new_msgid.msgid = old_msgid.msgid
213- """ % sqlvalues(potemplate))
214-
215- plural_potmsgsets = cur.fetchall()
216-
217- logger.info("Migrating translations for %d context POTMsgSets..." % (
218- len(plural_potmsgsets)))
219- for old_potmsgset_id, new_potmsgset_id in plural_potmsgsets:
220- old_potmsgset = POTMsgSet.get(old_potmsgset_id)
221- new_potmsgset = POTMsgSet.get(new_potmsgset_id)
222- messages = TranslationMessage.select(
223- "potmsgset=%s" % sqlvalues(old_potmsgset))
224- logger.debug(
225- "Moving %d context translations from POTMsgSet %d to %d..." % (
226- messages.count(), old_potmsgset_id, new_potmsgset_id))
227- migrate_translations_for_potmsgset(new_potmsgset, old_potmsgset,
228- logger, ztm)
229-
230- ztm.commit()
231-
232-
233-def existing_potmsgset(potemplate, msgid_singular, context):
234- clauses = ['potemplate=%s' % sqlvalues(potemplate),
235- 'msgid_singular=%s' % sqlvalues(msgid_singular)]
236- if context is None:
237- clauses.append("context IS NULL")
238- else:
239- clauses.append("context=%s" % sqlvalues(context))
240-
241- return POTMsgSet.selectOne(" AND ".join(clauses))
242-
243-
244-def migrate_potemplate(potemplate, logger, ztm):
245- """Migrate PO templates to KDEPO format if needed."""
246-
247- plural_prefix = u'_n: '
248- context_prefix = u'_: '
249-
250- assert(potemplate.source_file_format == TranslationFileFormat.PO)
251-
252- potmsgsets = POTMsgSet.select("""
253- POTMsgSet.potemplate = %s AND
254- POTMsgSet.msgid_singular=POMsgID.id AND
255- POTMsgSet.msgid_plural IS NULL AND
256- (POMsgID.msgid LIKE E'\\\\_n: %%' OR POMsgID.msgid LIKE E'\\\\_: %%')
257- """ % sqlvalues(potemplate),
258- clauseTables=['POMsgID'])
259-
260- logger.debug("Fixing %d POTMsgSets..." % potmsgsets.count())
261-
262- # We go potmsgset by potmsgset because it's easier to preserve
263- # correctness. It'd be faster to go through translation messages
264- # but then we'd be fixing potmsgsets after fixing translation messages.
265- for potmsgset in potmsgsets:
266- msgid = potmsgset.singular_text
267- fix_plurals = fix_context = False
268-
269- # Detect plural form and context messages: use the same
270- # logic as in utilities/kde_po_importer.py.
271- if msgid.startswith(plural_prefix) and '\n' in msgid:
272- # This is a KDE plural form.
273- singular_text, plural_text = (
274- msgid[len(plural_prefix):].split('\n', 1))
275-
276- msgid_singular = getOrCreatePOMsgID(singular_text)
277-
278- if existing_potmsgset(potemplate, msgid_singular, None):
279- logger.warn("POTMsgSet %d conflicts with another one." % (
280- potmsgset.id))
281- else:
282- potmsgset.msgid_singular = msgid_singular
283- potmsgset.msgid_plural = getOrCreatePOMsgID(plural_text)
284- potmsgset.sync()
285- fix_plurals = True
286- elif msgid.startswith(context_prefix) and '\n' in msgid:
287- # This is a KDE context message: it needs no fixing apart
288- # from changing msgid_singular.
289- context, singular_text = (
290- msgid[len(context_prefix):].split('\n', 1))
291- msgid_singular = getOrCreatePOMsgID(singular_text)
292-
293- if existing_potmsgset(potemplate, msgid_singular, context):
294- logger.warn("POTMsgSet %d conflicts with another one." % (
295- potmsgset.id))
296- else:
297- potmsgset.context = context
298- potmsgset.msgid_singular = msgid_singular
299- potmsgset.sync()
300- fix_context = True
301- else:
302- # Other messages here are the ones which begin like
303- # context or plural form messages, but are actually neither.
304- pass
305-
306- if fix_plurals:
307- # Fix translations for this potmsgset.
308- migrate_translations_for_potmsgset(potmsgset, potmsgset,
309- logger, ztm)
310-
311- potemplate.source_file_format = TranslationFileFormat.KDEPO
312- # Commit a PO template one by one.
313- ztm.commit()
314-
315-
316-def migrate_translations_for_kdepo_templates(ztm, logger):
317- """Migrate translations for already re-imported KDE PO templates."""
318- potemplates = POTemplate.select("""
319- source_file_format=%s AND
320- POTemplate.id IN
321- (SELECT DISTINCT potemplate
322- FROM POTMsgSet
323- WHERE POTMsgSet.potemplate = POTemplate.id AND
324- (POTMsgSet.msgid_plural IS NOT NULL OR
325- POTMsgSet.context IS NOT NULL))
326- """ % sqlvalues(TranslationFileFormat.KDEPO))
327-
328- count = potemplates.count()
329- index = 0
330- for potemplate in potemplates:
331- index += 1
332- logger.info("Migrating translations for KDE POTemplate %s [%d/%d]" % (
333- potemplate.displayname, index, count))
334- migrate_kde_potemplate_translations(potemplate, logger, ztm)
335-
336-
337-def migrate_unmigrated_templates_to_kdepo(ztm, logger):
338- """Go through all non-KDE PO templates and migrate to KDEPO as needed."""
339-
340- potemplates = POTemplate.select("""
341- source_file_format=%s AND
342- POTemplate.id IN
343- (SELECT DISTINCT potemplate
344- FROM POTMsgSet
345- JOIN POMsgID ON POMsgID.id = POTMsgSet.msgid_singular
346- WHERE POTMsgSet.potemplate = POTemplate.id AND
347- POTMsgSet.msgid_plural IS NULL AND
348- (POMsgID.msgid LIKE E'\\\\_n: %%' OR
349- POMsgID.msgid LIKE E'\\\\_: %%'))
350- """ % sqlvalues(TranslationFileFormat.PO))
351-
352- count = potemplates.count()
353- index = 0
354- for potemplate in potemplates:
355- index += 1
356- logger.info("Migrating POTemplate %s [%d/%d]" % (
357- potemplate.displayname, index, count))
358- migrate_potemplate(potemplate, logger, ztm)
359-
360-
361-def migrate_potemplates(ztm, logger):
362- migrate_translations_for_kdepo_templates(ztm, logger)
363- migrate_unmigrated_templates_to_kdepo(ztm, logger)
364
365=== removed file 'lib/lp/translations/scripts/remove_obsolete_translations.py'
366--- lib/lp/translations/scripts/remove_obsolete_translations.py 2010-08-20 20:31:18 +0000
367+++ lib/lp/translations/scripts/remove_obsolete_translations.py 1970-01-01 00:00:00 +0000
368@@ -1,383 +0,0 @@
369-# Copyright 2009 Canonical Ltd. This software is licensed under the
370-# GNU Affero General Public License version 3 (see the file LICENSE).
371-
372-__metaclass__ = type
373-
374-__all__ = ['RemoveObsoleteTranslations']
375-
376-from zope.component import getUtility
377-from zope.interface import implements
378-
379-from canonical.database.sqlbase import quote
380-from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
381-from canonical.launchpad.interfaces.looptuner import ITunableLoop
382-from canonical.launchpad.utilities.looptuner import DBLoopTuner
383-from canonical.launchpad.webapp.interfaces import (
384- IStoreSelector,
385- MAIN_STORE,
386- MASTER_FLAVOR,
387- )
388-from lp.registry.interfaces.series import SeriesStatus
389-from lp.services.scripts.base import LaunchpadScript
390-
391-
392-collect_order = [
393- 'POTemplate',
394- 'POFile',
395- 'TranslationImportQueueEntry',
396- 'POTMsgSet',
397- 'TranslationTemplateItem',
398- 'TranslationMessage',
399- 'POFileTranslator',
400- ]
401-
402-
403-remove_order = list(reversed(collect_order))
404-
405-
406-collection_query = """
407- DROP TABLE IF EXISTS %(temporary_table)s;
408- CREATE TEMP TABLE %(temporary_table)s
409- AS SELECT %(table)s.id AS id
410- FROM %(table)s
411- JOIN %(join_table)s ON %(table)s.%(join_column)s = %(join_table)s.id;
412- CREATE UNIQUE INDEX %(temporary_table)s_idx ON %(temporary_table)s (id);
413- ANALYZE %(temporary_table)s"""
414-
415-
416-# POTemplate
417-collect_obsolete_potemplates_query = """
418- DROP TABLE IF EXISTS %(temporary_table)s;
419- CREATE TEMP TABLE %(temporary_table)s
420- AS SELECT %(table)s.id AS id
421- FROM %(table)s
422- JOIN %(join_table)s ON %(table)s.%(join_column)s = %(join_table)s.id
423- WHERE distroseries.distribution = %(distribution)s
424- AND distroseries.releasestatus = %(releasestatus)s;
425- CREATE UNIQUE INDEX %(temporary_table)s_idx ON %(temporary_table)s (id);
426- ANALYZE %(temporary_table)s"""
427-
428-
429-# Query to remove subset of entries based on a temporary table.
430-deletion_query = """
431- DELETE FROM %(table)s
432- WHERE id IN (
433- SELECT id
434- FROM %(temporary_table)s
435- ORDER BY id
436- LIMIT %%d OFFSET %%d
437- )"""
438-
439-
440-def commit_transaction(transaction, logger, dry_run=False):
441- """Commit ongoing transaction, start a new one.
442-
443- Pauses process execution to give the database slave a chance
444- to keep up.
445- """
446- if transaction is None:
447- return
448-
449- if not dry_run:
450- transaction.commit()
451- transaction.begin()
452-
453-
454-class DeletionLoopRunner(object):
455- """Generic loop tuner for removal of obsolete translations data."""
456- implements(ITunableLoop)
457-
458- def __init__(self, table_entry, transaction, logger, store,
459- dry_run=False):
460- """Initialize the loop."""
461- size = table_entry['total']
462- self.table = table_entry['table']
463- self.removal_sql = deletion_query % table_entry
464-
465- self._txn = transaction
466- self._logger = logger
467- self._store = store
468- self._dry_run = dry_run
469- self._iteration_end = size
470- self._iterations_done = 0
471- self._commit_count = 0
472-
473- def isDone(self):
474- """See `ITunableLoop`."""
475- return self._iterations_done >= self._iteration_end
476-
477- def __call__(self, chunk_size):
478- """See `ITunableLoop`."""
479- chunk_size = int(chunk_size)
480- query = self.removal_sql % (chunk_size, self._iterations_done)
481- result = self._store.execute(query)
482- self._logger.debug(
483- " * Removed another %d %ss (%d of %d)." % (
484- result._raw_cursor.rowcount,
485- self.table,
486- self._iterations_done + result._raw_cursor.rowcount,
487- self._iteration_end))
488- self._iterations_done += result._raw_cursor.rowcount
489- commit_transaction(self._txn, self._logger, dry_run=self._dry_run)
490- self._commit_count += 1
491-
492- def getTotalCommits(self):
493- return self._commit_count
494-
495-
496-class TranslationsStatusChecker:
497- """Check if translations data is consistent after removal."""
498- check_methods = {
499- 'productseries_potemplates' : 'collectProductSeriesStats',
500- 'other_distroseries_potemplates' : 'collectOtherDistroSeriesStats',
501- 'potemplate_potmsgsets' : 'collectPOTemplatePOTMsgSetStats',
502- 'potemplate_pofiles' : 'collectPOTemplatePOFileStats',
503- 'pofile_pofiletranslators' : 'collectPOFileTranslatorStats',
504- 'translationimportqueue_size' : 'getTranslationImportQueueSize',
505- }
506-
507- def __init__(self, store, logger):
508- self.store = store
509- self.logger = logger
510- self.problems = 0
511- for attribute in self.check_methods:
512- method = getattr(self, self.check_methods[attribute])
513- self.logger.debug("Collecting %s..." % attribute)
514- setattr(self, attribute, method())
515-
516- def postCheck(self):
517- self.checkObsoletePOTemplates()
518-
519- for attribute in self.check_methods:
520- self.genericCheck(attribute)
521-
522- if self.problems > 0:
523- self.logger.info("%d problems found." % self.problems)
524- else:
525- self.logger.info("All checks passed.")
526-
527- def genericCheck(self, attribute_name):
528- method = getattr(self, self.check_methods[attribute_name])
529- new_stats = method()
530- old_stats = getattr(self, attribute_name)
531- if old_stats != new_stats:
532- if not isinstance(old_stats, int):
533- old_stats = len(old_stats)
534- new_stats = len(new_stats)
535- self.logger.warn(
536- "Mismatch in %s (was %d long, now %d)." % (
537- attribute_name, old_stats, new_stats))
538- self.problems += 1
539-
540- def checkObsoletePOTemplates(self):
541- query = """SELECT COUNT(POTemplate.id) FROM POTemplate
542- JOIN DistroSeries
543- ON POTemplate.distroseries=DistroSeries.id
544- WHERE DistroSeries.releasestatus=%s AND
545- DistroSeries.distribution=%s""" % (
546- quote(SeriesStatus.OBSOLETE),
547- quote(getUtility(ILaunchpadCelebrities).ubuntu))
548- result = self.store.execute(query)
549- count = result.get_one()
550- if int(count[0]) != 0:
551- self.logger.warn("\tObsolete POTemplates remaining: %s." % count)
552- self.problems += 1
553-
554- def collectProductSeriesStats(self):
555- query = """SELECT ProductSeries.id, COUNT(POTemplate.id)
556- FROM ProductSeries
557- JOIN POTemplate
558- ON POTemplate.productseries=ProductSeries.id
559- GROUP BY ProductSeries.id
560- ORDER BY ProductSeries.id"""
561- result = self.store.execute(query)
562- return result.get_all()
563-
564- def collectOtherDistroSeriesStats(self):
565- query = """SELECT DistroSeries.id, COUNT(POTemplate.id)
566- FROM DistroSeries
567- JOIN POTemplate
568- ON POTemplate.distroseries=DistroSeries.id
569- WHERE DistroSeries.releasestatus != %s
570- GROUP BY DistroSeries.id
571- ORDER BY DistroSeries.id""" % quote(
572- SeriesStatus.OBSOLETE)
573- result = self.store.execute(query)
574- return result.get_all()
575-
576- def collectPOTemplatePOFileStats(self):
577- query = """SELECT POTemplate.id, COUNT(POFile.id)
578- FROM POTemplate
579- LEFT OUTER JOIN DistroSeries
580- ON POTemplate.distroseries=DistroSeries.id
581- JOIN POFile
582- ON POFile.potemplate=POTemplate.id
583- WHERE (distroseries IS NOT NULL AND
584- DistroSeries.releasestatus != %s) OR
585- productseries IS NOT NULL
586- GROUP BY POTemplate.id
587- ORDER BY POTemplate.id""" % quote(
588- SeriesStatus.OBSOLETE)
589- result = self.store.execute(query)
590- return result.get_all()
591-
592- def collectPOTemplatePOTMsgSetStats(self):
593- query = """SELECT POTemplate.id, COUNT(POTMsgSet.id)
594- FROM POTemplate
595- LEFT OUTER JOIN DistroSeries
596- ON POTemplate.distroseries=DistroSeries.id
597- JOIN POTMsgSet
598- ON POTMsgSet.potemplate=POTemplate.id
599- WHERE (distroseries IS NOT NULL AND
600- DistroSeries.releasestatus != %s) OR
601- productseries IS NOT NULL
602- GROUP BY POTemplate.id
603- ORDER BY POTemplate.id""" % quote(
604- SeriesStatus.OBSOLETE)
605- result = self.store.execute(query)
606- return result.get_all()
607-
608- def collectPOFileTranslatorStats(self):
609- query = """SELECT POFile.id, COUNT(POFileTranslator.id)
610- FROM POFile
611- JOIN POTemplate
612- ON POFile.potemplate=POTemplate.id
613- LEFT OUTER JOIN DistroSeries
614- ON POTemplate.distroseries=DistroSeries.id
615- JOIN POFileTranslator
616- ON POFileTranslator.pofile=POFile.id
617- WHERE (distroseries IS NOT NULL AND
618- DistroSeries.releasestatus != %s) OR
619- productseries IS NOT NULL
620- GROUP BY POFile.id
621- ORDER BY POFile.id""" % quote(
622- SeriesStatus.OBSOLETE)
623- result = self.store.execute(query)
624- return result.get_all()
625-
626- def getTranslationImportQueueSize(self):
627- query = """SELECT count(id) FROM TranslationImportQueueEntry"""
628- result = self.store.execute(query)
629- return int(result.get_one()[0])
630-
631-
632-class RemoveObsoleteTranslations(LaunchpadScript):
633-
634- def add_my_options(self):
635- self.parser.add_option(
636- '-d', '--dry-run', action="store_true", dest='dry_run',
637- default=False, help="Don't really make any database changes.")
638-
639- self.parser.add_option(
640- '-l', '--loop-timing', dest='loop_time',
641- default=5, help="Time in seconds for the loop to run.")
642-
643- def main(self):
644- removal_traits = {
645- 'TranslationMessage' :
646- { 'table' : 'TranslationMessage',
647- 'temporary_table' : 'obsolete_translationmessage',
648- 'join_table' : 'obsolete_pofile',
649- 'join_column' : 'pofile',
650- },
651- 'TranslationTemplateItem' :
652- { 'table' : 'TranslationTemplateItem',
653- 'temporary_table' : 'obsolete_tti',
654- 'join_table' : 'obsolete_potemplate',
655- 'join_column' : 'potemplate',
656- },
657- 'POTMsgSet' :
658- { 'table' : 'POTMsgSet',
659- 'temporary_table' : 'obsolete_potmsgset',
660- 'join_table' : 'obsolete_potemplate',
661- 'join_column' : 'potemplate',
662- },
663- 'POFileTranslator' :
664- { 'table' : 'POFileTranslator',
665- 'temporary_table' : 'obsolete_pofiletranslator',
666- 'join_table' : 'obsolete_pofile',
667- 'join_column' : 'pofile',
668- },
669- 'POFile' :
670- { 'table' : 'POFile',
671- 'temporary_table' : 'obsolete_pofile',
672- 'join_table' : 'obsolete_potemplate',
673- 'join_column' : 'potemplate',
674- },
675- 'TranslationImportQueueEntry' :
676- { 'table' : 'TranslationImportQueueEntry',
677- 'temporary_table' : 'obsolete_queueentries',
678- 'join_table' : 'obsolete_potemplate',
679- 'join_column' : 'potemplate',
680- },
681- 'POTemplate' :
682- { 'table' : 'POTemplate',
683- 'temporary_table' : 'obsolete_potemplate',
684- 'join_table' : 'distroseries',
685- 'join_column' : 'distroseries',
686- 'distribution' :
687- quote(getUtility(ILaunchpadCelebrities).ubuntu),
688- 'releasestatus' : quote(SeriesStatus.OBSOLETE),
689- 'collection_sql' : collect_obsolete_potemplates_query,
690- },
691- }
692-
693- if self.options.dry_run:
694- self.logger.info("Dry run. Not making any changes.")
695-
696- self.logger.debug(
697- "Removing translations of obsolete Ubuntu versions")
698-
699- self._commit_count = 0
700-
701- # Working on the writable master store
702- store = getUtility(IStoreSelector).get(MAIN_STORE, MASTER_FLAVOR)
703- self._store = store
704-
705- checker = TranslationsStatusChecker(store, self.logger)
706- for table in collect_order:
707- entry = removal_traits[table]
708- collect_sql = entry.get('collection_sql', collection_query)
709- collect = store.execute(collect_sql % entry)
710- count = self._count_rows(entry['temporary_table'])
711- entry['total'] = count
712- self._do_commit()
713-
714- for table in remove_order:
715- entry = removal_traits[table]
716- self.logger.info(
717- "Removing %d %s rows." % (entry['total'], table))
718- loop = DeletionLoopRunner(
719- entry, self.txn, self.logger, store,
720- dry_run=self.options.dry_run)
721- DBLoopTuner(loop, self.options.loop_time, log=self.logger).run()
722- self._commit_count += loop.getTotalCommits()
723-
724- self.logger.info("Done with %d commits." % self._commit_count)
725- self.logger.info("Statistics:")
726- for table in remove_order:
727- self.logger.info("\t%-30s: %d removed" % (
728- table, removal_traits[table]['total']))
729-
730- self.logger.info("Checks:")
731- checker.postCheck()
732-
733- if self.options.dry_run:
734- self.txn.abort()
735-
736- def _count_rows(self, tablename):
737- """Helper to count all rows in a table."""
738- count_query = "SELECT count(*) FROM %s"
739- result = self._store.execute(
740- count_query % tablename).get_one()
741- return result[0]
742-
743- def _do_commit(self):
744- """Commit ongoing transaction, start a new one.
745-
746- Pauses process execution to give the database slave a chance
747- to keep up.
748- """
749- commit_transaction(self.txn, self.logger,
750- dry_run=self.options.dry_run)
751- self._commit_count += 1
752
753=== removed file 'scripts/rosetta/message-sharing-populate-test.py'
754--- scripts/rosetta/message-sharing-populate-test.py 2010-07-13 15:40:33 +0000
755+++ scripts/rosetta/message-sharing-populate-test.py 1970-01-01 00:00:00 +0000
756@@ -1,134 +0,0 @@
757-#!/usr/bin/python -S
758-#
759-# Copyright 2009 Canonical Ltd. This software is licensed under the
760-# GNU Affero General Public License version 3 (see the file LICENSE).
761-
762-# pylint: disable-msg=W0403
763-
764-"""Test Translation Message Sharing schema initializations.
765-
766-This script verifies the results of the message-sharing-populate.py
767-one-off script. It checks that all schema elements that were added for
768-translation message sharing were initialized correctly.
769-
770-Run this after message-sharing-populate.py, but also occasionally
771-afterwards to ensure that the code that initializes the added schema
772-elements on new data is working correctly.
773-
774-The new schema elements are not used yet. Once they are, a next step is
775-to start unifying TranslationMessages that are common between
776-POTemplates. When that happens, this script will start reporting errors
777-at points marked in the source below. Change those points & run again!
778-"""
779-
780-import _pythonpath
781-
782-from canonical.database.sqlbase import cursor
783-from canonical.launchpad.scripts import execute_zcml_for_scripts
784-
785-
786-class SchemaElementsInitializationFailure(Exception):
787- """Convenience exception class for errors found in our data."""
788- pass
789-
790-
791-def check(query, error_message, expected_result=0):
792- """Report error if `query` result does not match `expected_result`."""
793- cur = cursor()
794- cur.execute(query)
795- result, = cur.fetchone()
796-
797- if result != expected_result:
798- counts = "expected %s, found %s" % (expected_result, result)
799- raise SchemaElementsInitializationFailure(
800- "%s (%s)" % (error_message, counts))
801-
802-
803-def test_schema():
804- """Test schema initializations."""
805- cur = cursor()
806-
807- # Are all TranslationMessage.language fields initialized?
808- query = """
809- SELECT count(*)
810- FROM TranslationMessage
811- WHERE language IS NULL
812- """
813- check(query, "Found uninitialized TranslationMessage.language")
814-
815- # Are all TranslationMessages.potemplate fields initialized?
816- # XXX JeroenVermeulen 2008-10-06 spec=message-sharing-migration:
817- # potemplate will be allowed to be null later on, when we really
818- # start sharing messages. The field means "this message is specific
819- # to this potemplate, rather than shared"). Remove this check then.
820- query = """
821- SELECT count(*)
822- FROM TranslationMessage
823- WHERE potemplate IS NULL OR language IS NULL
824- """
825- check(query, "Found uninitialized TranslationMessages.potemplate.")
826-
827- # Are all TranslationMessages linked to the right languages?
828- query = """
829- SELECT count(*)
830- FROM TranslationMessage
831- LEFT JOIN POFile ON POFile.id = TranslationMessage.pofile
832- WHERE
833- POFile.id IS NULL OR
834- POFile.language <> TranslationMessage.language
835- """
836- check(query, "Found TranslationMessages with incorrect language.")
837-
838- # Do all POTMsgSets with nonzero sequence numbers have linking-table
839- # entries linking them to their POTemplates? (Zero sequence number
840- # means "does not participate in this POTemplate," a wart that will
841- # go away with this schema change)
842- query = """
843- SELECT count(*)
844- FROM POTMsgSet
845- LEFT JOIN TranslationTemplateItem AS i ON i.potmsgset = POTMsgSet.id
846- WHERE POTMsgSet.sequence <> 0 AND i.id IS NULL
847- """
848- check(query, "Found unlinked POTMsgSets.")
849-
850- # Conversely, is the linking table free of unexpected rows?
851- query = """
852- SELECT count(*)
853- FROM TranslationTemplateItem i
854- LEFT JOIN POTMsgSet ON i.potmsgset = POTMsgSet.id
855- WHERE POTMsgSet.id IS NULL or POTMsgSet.sequence = 0
856- """
857- check(query, "Found unexpected TranslationTemplateItem rows.")
858-
859- # Are all TranslationTemplateItems correct?
860- query = """
861- SELECT count(*)
862- FROM POTMsgSet
863- JOIN TranslationTemplateItem AS i ON i.potmsgset = POTMsgSet.id
864- WHERE
865- i.potemplate <> POTMsgSet.potemplate OR
866- i.sequence <> POTMsgSet.sequence
867- """
868- check(query, "Found incorrect TranslationTemplateItem contents.")
869-
870- # Is each POTMsgSet linked to no more than one POTemplate?
871- # XXX JeroenVermeulen 2008-10-06 spec=message-sharing-migration:
872- # Once we start sharing messages across templates, this will become
873- # a real m:n relationship between POTMsgSet and POTemplate. At that
874- # point, remove his check.
875- query = """
876- SELECT count(*)
877- FROM (
878- SELECT count(*)
879- FROM TranslationTemplateItem
880- GROUP BY potmsgset
881- HAVING count(*) > 1
882- ) AS shared_potmsgset
883- """
884- check(query, "Found shared POTMsgSets.")
885-
886-
887-if __name__ == '__main__':
888- execute_zcml_for_scripts()
889- test_schema()
890- print "Done."
891
892=== removed file 'scripts/rosetta/message-sharing-populate.py'
893--- scripts/rosetta/message-sharing-populate.py 2010-08-06 07:02:32 +0000
894+++ scripts/rosetta/message-sharing-populate.py 1970-01-01 00:00:00 +0000
895@@ -1,209 +0,0 @@
896-#!/usr/bin/python -S
897-#
898-# Copyright 2009 Canonical Ltd. This software is licensed under the
899-# GNU Affero General Public License version 3 (see the file LICENSE).
900-
901-# pylint: disable-msg=W0403
902-
903-"""Populate schema additions for Translations Message Sharing.
904-
905-This fills two new `TranslationMessage` columns: potemplate, language.
906-It also creates linking table entries connecting the existing `POTMsgSet`s
907-to their `POTemplate`s.
908-
909-Since the schema additions are not in use yet, this script doesn't need
910-to be careful about grouping by template, preserving any kind of order,
911-and so on.
912-
913-On the other hand, the Python code tree should already be initializing
914-the new columns and the linking table by the time this script is run.
915-So we do have to be careful not to interfere with that, or stumble when
916-some records have already been initialized properly.
917-"""
918-
919-import _pythonpath
920-
921-from zope.interface import implements
922-
923-from canonical.database.postgresql import drop_tables
924-from canonical.database.sqlbase import cursor, sqlvalues
925-from canonical.launchpad.interfaces.looptuner import ITunableLoop
926-from lp.services.scripts.base import LaunchpadScript
927-from canonical.launchpad.utilities.looptuner import DBLoopTuner
928-
929-
930-class PopulateTranslationMessage:
931- """`ITunableLoop` to populate new TranslationMessage columns."""
932-
933- implements(ITunableLoop)
934- def __init__(self, txn, logger):
935- self.txn = txn
936- self.logger = logger
937- self.last_id = 0
938-
939- cur = cursor()
940- cur.execute("""
941- SELECT id
942- INTO TEMP TABLE temp_todo
943- FROM TranslationMessage
944- WHERE potemplate IS NULL
945- ORDER BY id
946- """)
947- cur.execute(
948- "CREATE UNIQUE INDEX temp_todo__pkey ON temp_todo(id)")
949- cur.execute("ANALYZE temp_todo(id)")
950-
951- cur.execute("SELECT max(id) FROM temp_todo")
952- max_id, = cur.fetchone()
953- if max_id is None:
954- self.finish_id = 0
955- else:
956- self.finish_id = max_id + 1
957-
958- def isDone(self):
959- """See `ITunableLoop`."""
960- done = (self.last_id >= self.finish_id)
961- if done:
962- drop_tables(cursor(), 'temp_todo')
963- return done
964-
965- def __call__(self, chunk_size):
966- """See `ITunableLoop`."""
967- chunk_size = int(chunk_size)
968-
969- cur = cursor()
970- cur.execute("""
971- SELECT id
972- FROM temp_todo
973- WHERE id >= %s
974- ORDER BY id
975- OFFSET %s
976- LIMIT 1
977- """ % sqlvalues(self.last_id, chunk_size))
978- batch_limit = cur.fetchone()
979- if batch_limit is None:
980- end_id = self.finish_id
981- else:
982- end_id, = batch_limit
983-
984- cur.execute("""
985- UPDATE TranslationMessage AS Msg
986- SET
987- potemplate = POFile.potemplate,
988- language = POFile.language
989- FROM POFile
990- WHERE
991- POFile.id = Msg.pofile AND
992- Msg.potemplate IS NULL AND
993- Msg.id IN (
994- SELECT id
995- FROM temp_todo
996- WHERE id >= %s AND id < %s
997- )
998- """ % sqlvalues(self.last_id, end_id))
999- self.logger.info(
1000- "Updated %d rows: %d - %d." % (
1001- cur.rowcount, self.last_id, end_id))
1002- self.txn.commit()
1003- self.txn.begin()
1004- self.last_id = end_id
1005-
1006-
1007-class PopulateTranslationTemplateItem:
1008- """`ITunableLoop` to populate TranslationTemplateItem linking table."""
1009-
1010- implements(ITunableLoop)
1011- def __init__(self, txn, logger):
1012- self.txn = txn
1013- self.done = False
1014- self.logger = logger
1015- self.last_id = 0
1016-
1017- cur = cursor()
1018- cur.execute("""
1019- SELECT POTMsgSet.id
1020- INTO TEMP TABLE temp_todo
1021- FROM POTMsgSet
1022- LEFT JOIN TranslationTemplateItem AS ExistingEntry ON
1023- ExistingEntry.potmsgset = potmsgset.id
1024- WHERE
1025- POTMsgSet.sequence > 0 AND
1026- ExistingEntry.id IS NULL
1027- ORDER BY id
1028- """)
1029- cur.execute(
1030- "CREATE UNIQUE INDEX temp_todo__pkey ON temp_todo(id)")
1031- cur.execute("ANALYZE temp_todo(id)")
1032-
1033- cur.execute("SELECT max(id) FROM temp_todo")
1034- max_id, = cur.fetchone()
1035- if max_id is None:
1036- self.finish_id = 0
1037- else:
1038- self.finish_id = max_id + 1
1039-
1040- def isDone(self):
1041- """See `ITunableLoop`."""
1042- done = (self.last_id >= self.finish_id)
1043- if done:
1044- drop_tables(cursor(), 'temp_todo')
1045- return done
1046-
1047- def __call__(self, chunk_size):
1048- """See `ITunableLoop`."""
1049- chunk_size = int(chunk_size)
1050- cur = cursor()
1051-
1052- cur.execute("""
1053- SELECT id
1054- FROM temp_todo
1055- WHERE id >= %s
1056- ORDER BY id
1057- OFFSET %s
1058- LIMIT 1
1059- """ % sqlvalues(self.last_id, chunk_size))
1060- batch_limit = cur.fetchone()
1061- if batch_limit is None:
1062- end_id = self.finish_id
1063- else:
1064- end_id, = batch_limit
1065-
1066- cur.execute("""
1067- INSERT INTO TranslationTemplateItem(
1068- potemplate, sequence, potmsgset)
1069- SELECT POTMsgSet.potemplate, POTMsgSet.sequence, POTMsgSet.id
1070- FROM POTMsgSet
1071- LEFT JOIN TranslationTemplateItem AS ExistingEntry ON
1072- ExistingEntry.potmsgset = potmsgset.id
1073- WHERE
1074- POTMsgSet.id >= %s AND
1075- POTMsgSet.id < %s AND
1076- POTMsgSet.sequence > 0 AND
1077- ExistingEntry.id IS NULL
1078- """ % sqlvalues(self.last_id, end_id))
1079- self.logger.info("Inserted %d rows." % cur.rowcount)
1080- self.txn.commit()
1081- self.txn.begin()
1082- self.last_id = end_id
1083-
1084-
1085-class PopulateMessageSharingSchema(LaunchpadScript):
1086- description = (
1087- "Populate columns and linking table added for Translations Message "
1088- "sharing.")
1089-
1090- def main(self):
1091- self.logger.info("Populating new TranslationMessage columns.")
1092- tm_loop = PopulateTranslationMessage(self.txn, self.logger)
1093- DBLoopTuner(tm_loop, 2, log=self.logger).run()
1094-
1095- self.logger.info("Populating TranslationTemplateItem.")
1096- tti_loop = PopulateTranslationTemplateItem(self.txn, self.logger)
1097- DBLoopTuner(tti_loop, 2, log=self.logger).run()
1098-
1099-
1100-if __name__ == '__main__':
1101- script = PopulateMessageSharingSchema(
1102- 'canonical.launchpad.scripts.message-sharing-populate')
1103- script.run()
1104-
1105
1106=== removed file 'scripts/rosetta/remove-obsolete-translations.py'
1107--- scripts/rosetta/remove-obsolete-translations.py 2010-04-27 19:48:39 +0000
1108+++ scripts/rosetta/remove-obsolete-translations.py 1970-01-01 00:00:00 +0000
1109@@ -1,21 +0,0 @@
1110-#!/usr/bin/python -S
1111-#
1112-# Copyright 2009 Canonical Ltd. This software is licensed under the
1113-# GNU Affero General Public License version 3 (see the file LICENSE).
1114-
1115-# pylint: disable-msg=W0403
1116-# (Suppressing pylint "relative import" warning 0403 for _pythonpath)
1117-
1118-__metaclass__ = type
1119-
1120-import _pythonpath
1121-
1122-from lp.translations.scripts.remove_obsolete_translations import (
1123- RemoveObsoleteTranslations)
1124-
1125-
1126-if __name__ == '__main__':
1127- script = RemoveObsoleteTranslations(
1128- 'canonical.launchpad.scripts.remove-obsolete-translations',
1129- dbuser='rosettaadmin')
1130- script.run()
1131
1132=== modified file 'utilities/migrater/file-ownership.txt'
1133--- utilities/migrater/file-ownership.txt 2010-10-31 20:18:45 +0000
1134+++ utilities/migrater/file-ownership.txt 2010-11-16 13:00:58 +0000
1135@@ -604,7 +604,6 @@
1136 reg ./scripts/listteammembers.py
1137 ./scripts/logger.py
1138 ./scripts/loghandlers.py
1139- ./scripts/migrate_kde_potemplates.py
1140 ./scripts/mlistimport.py
1141 ./scripts/oops.py
1142 ./scripts/packagecopier.py
1143@@ -623,7 +622,6 @@
1144 ./scripts/publishdistro.py
1145 ./scripts/questionexpiration.py
1146 ./scripts/queue.py
1147- ./scripts/remove_obsolete_translations.py
1148 ./scripts/remove_translations.py
1149 ./scripts/revisionkarma.py
1150 ./scripts/runlaunchpad.py