~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/translations/utilities/translationsplitter.py

Merged pending-db-changes into db-cleanups.

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 
7
7
import logging
8
8
 
9
 
from storm.locals import ClassAlias, Store
 
9
from storm.expr import (
 
10
    And,
 
11
    Join,
 
12
    LeftJoin,
 
13
    Not,
 
14
    Or,
 
15
    )
 
16
from storm.locals import (
 
17
    ClassAlias,
 
18
    Store,
 
19
    )
10
20
import transaction
11
21
 
 
22
from lp.registry.model.distroseries import DistroSeries
 
23
from lp.registry.model.packaging import Packaging
 
24
from lp.registry.model.productseries import ProductSeries
12
25
from lp.translations.model.potemplate import POTemplate
13
26
from lp.translations.model.translationtemplateitem import (
14
27
    TranslationTemplateItem,
15
28
    )
16
29
 
17
30
 
18
 
class TranslationSplitter:
19
 
    """Split translations for a productseries, sourcepackage pair.
20
 
 
21
 
    If a productseries and sourcepackage were linked in error, and then
22
 
    unlinked, they may still share some translations.  This class breaks those
23
 
    associations.
24
 
    """
25
 
 
26
 
    def __init__(self, productseries, sourcepackage):
27
 
        """Constructor.
28
 
 
29
 
        :param productseries: The `ProductSeries` to split from.
30
 
        :param sourcepackage: The `SourcePackage` to split from.
31
 
        """
32
 
        self.productseries = productseries
33
 
        self.sourcepackage = sourcepackage
34
 
 
35
 
    def findShared(self):
36
 
        """Provide tuples of upstream, ubuntu for each shared POTMsgSet."""
37
 
        store = Store.of(self.productseries)
38
 
        UpstreamItem = ClassAlias(TranslationTemplateItem, 'UpstreamItem')
39
 
        UpstreamTemplate = ClassAlias(POTemplate, 'UpstreamTemplate')
40
 
        UbuntuItem = ClassAlias(TranslationTemplateItem, 'UbuntuItem')
41
 
        UbuntuTemplate = ClassAlias(POTemplate, 'UbuntuTemplate')
42
 
        return store.find(
43
 
            (UpstreamItem, UbuntuItem),
44
 
            UpstreamItem.potmsgsetID == UbuntuItem.potmsgsetID,
45
 
            UbuntuItem.potemplateID == UbuntuTemplate.id,
46
 
            UbuntuTemplate.sourcepackagenameID ==
47
 
                self.sourcepackage.sourcepackagename.id,
48
 
            UbuntuTemplate.distroseriesID ==
49
 
                self.sourcepackage.distroseries.id,
50
 
            UpstreamItem.potemplateID == UpstreamTemplate.id,
51
 
            UpstreamTemplate.productseriesID == self.productseries.id,
52
 
        )
 
31
class TranslationSplitterBase:
 
32
    """Base class for translation splitting jobs."""
53
33
 
54
34
    @staticmethod
55
35
    def splitPOTMsgSet(ubuntu_item):
86
66
        """Split the translations for the ProductSeries and SourcePackage."""
87
67
        logger = logging.getLogger()
88
68
        shared = enumerate(self.findShared(), 1)
 
69
        total = 0
89
70
        for num, (upstream_item, ubuntu_item) in shared:
90
71
            self.splitPOTMsgSet(ubuntu_item)
91
72
            self.migrateTranslations(upstream_item.potmsgset, ubuntu_item)
92
73
            if num % 100 == 0:
93
74
                logger.info('%d entries split.  Committing...', num)
94
75
                transaction.commit()
 
76
            total = num
 
77
 
 
78
        if total % 100 != 0 or total == 0:
 
79
            transaction.commit()
 
80
            logger.info('%d entries split.', total)
 
81
 
 
82
 
 
83
class TranslationSplitter(TranslationSplitterBase):
 
84
    """Split translations for a productseries, sourcepackage pair.
 
85
 
 
86
    If a productseries and sourcepackage were linked in error, and then
 
87
    unlinked, they may still share some translations.  This class breaks those
 
88
    associations.
 
89
    """
 
90
 
 
91
    def __init__(self, productseries, sourcepackage):
 
92
        """Constructor.
 
93
 
 
94
        :param productseries: The `ProductSeries` to split from.
 
95
        :param sourcepackage: The `SourcePackage` to split from.
 
96
        """
 
97
        self.productseries = productseries
 
98
        self.sourcepackage = sourcepackage
 
99
 
 
100
    def findShared(self):
 
101
        """Provide tuples of upstream, ubuntu for each shared POTMsgSet."""
 
102
        store = Store.of(self.productseries)
 
103
        UpstreamItem = ClassAlias(TranslationTemplateItem, 'UpstreamItem')
 
104
        UpstreamTemplate = ClassAlias(POTemplate, 'UpstreamTemplate')
 
105
        UbuntuItem = ClassAlias(TranslationTemplateItem, 'UbuntuItem')
 
106
        UbuntuTemplate = ClassAlias(POTemplate, 'UbuntuTemplate')
 
107
        return store.find(
 
108
            (UpstreamItem, UbuntuItem),
 
109
            UpstreamItem.potmsgsetID == UbuntuItem.potmsgsetID,
 
110
            UbuntuItem.potemplateID == UbuntuTemplate.id,
 
111
            UbuntuTemplate.sourcepackagenameID ==
 
112
                self.sourcepackage.sourcepackagename.id,
 
113
            UbuntuTemplate.distroseriesID ==
 
114
                self.sourcepackage.distroseries.id,
 
115
            UpstreamItem.potemplateID == UpstreamTemplate.id,
 
116
            UpstreamTemplate.productseriesID == self.productseries.id,
 
117
        )
 
118
 
 
119
 
 
120
class TranslationTemplateSplitter(TranslationSplitterBase):
 
121
    """Split translations for an extracted potemplate.
 
122
 
 
123
    When a POTemplate is removed from a set of sharing templates,
 
124
    it keeps sharing POTMsgSets with other templates.  This class
 
125
    removes those associations.
 
126
    """
 
127
 
 
128
    def __init__(self, potemplate):
 
129
        """Constructor.
 
130
 
 
131
        :param potemplate: The `POTemplate` to sanitize.
 
132
        """
 
133
        self.potemplate = potemplate
 
134
 
 
135
    def findShared(self):
 
136
        """Provide tuples of (other, this) items for each shared POTMsgSet.
 
137
 
 
138
        Only return those that are shared but shouldn't be because they
 
139
        are now in non-sharing templates.
 
140
        """
 
141
        store = Store.of(self.potemplate)
 
142
        ThisItem = ClassAlias(TranslationTemplateItem, 'ThisItem')
 
143
        OtherItem = ClassAlias(TranslationTemplateItem, 'OtherItem')
 
144
        OtherTemplate = ClassAlias(POTemplate, 'OtherTemplate')
 
145
 
 
146
        tables = [
 
147
            OtherTemplate,
 
148
            Join(OtherItem, OtherItem.potemplateID == OtherTemplate.id),
 
149
            Join(ThisItem,
 
150
                 And(ThisItem.potmsgsetID == OtherItem.potmsgsetID,
 
151
                     ThisItem.potemplateID == self.potemplate.id)),
 
152
            ]
 
153
 
 
154
        if self.potemplate.productseries is not None:
 
155
            # If the template is now in a product, we look for all
 
156
            # effectively sharing templates that are in *different*
 
157
            # products, or that are in a sourcepackage which is not
 
158
            # linked (through Packaging table) with this product.
 
159
            ps = self.potemplate.productseries
 
160
            productseries_join = LeftJoin(
 
161
                ProductSeries,
 
162
                ProductSeries.id == OtherTemplate.productseriesID)
 
163
            packaging_join = LeftJoin(
 
164
                Packaging,
 
165
                And(Packaging.productseriesID == ps.id,
 
166
                    (Packaging.sourcepackagenameID ==
 
167
                     OtherTemplate.sourcepackagenameID),
 
168
                    Packaging.distroseriesID == OtherTemplate.distroseriesID
 
169
                    ))
 
170
            tables.extend([productseries_join, packaging_join])
 
171
            # Template should not be sharing if...
 
172
            other_clauses = Or(
 
173
                # The name is different, or...
 
174
                OtherTemplate.name != self.potemplate.name,
 
175
                # It's in a different product, or...
 
176
                And(Not(ProductSeries.id == None),
 
177
                    ProductSeries.productID != ps.productID),
 
178
                # There is no link between this product series and
 
179
                # a source package the template is in.
 
180
                And(Not(OtherTemplate.distroseriesID == None),
 
181
                    Packaging.id == None))
 
182
        else:
 
183
            # If the template is now in a source package, we look for all
 
184
            # effectively sharing templates that are in *different*
 
185
            # distributions or source packages, or that are in a product
 
186
            # which is not linked with this source package.
 
187
            ds = self.potemplate.distroseries
 
188
            spn = self.potemplate.sourcepackagename
 
189
            distroseries_join = LeftJoin(
 
190
                DistroSeries,
 
191
                DistroSeries.id == OtherTemplate.distroseriesID)
 
192
            packaging_join = LeftJoin(
 
193
                Packaging,
 
194
                And(Packaging.distroseriesID == ds.id,
 
195
                    Packaging.sourcepackagenameID == spn.id,
 
196
                    Packaging.productseriesID == OtherTemplate.productseriesID
 
197
                    ))
 
198
            tables.extend([distroseries_join, packaging_join])
 
199
            # Template should not be sharing if...
 
200
            other_clauses = Or(
 
201
                # The name is different, or...
 
202
                OtherTemplate.name != self.potemplate.name,
 
203
                # It's in a different distribution or source package, or...
 
204
                And(Not(DistroSeries.id == None),
 
205
                    Or(DistroSeries.distributionID != ds.distributionID,
 
206
                       OtherTemplate.sourcepackagenameID != spn.id)),
 
207
                # There is no link between this source package and
 
208
                # a product the template is in.
 
209
                And(Not(OtherTemplate.productseriesID == None),
 
210
                    Packaging.id == None))
 
211
 
 
212
        return store.using(*tables).find(
 
213
            (OtherItem, ThisItem),
 
214
            OtherTemplate.id != self.potemplate.id,
 
215
            other_clauses,
 
216
            )