16
16
'PopulateDistroSeriesDiff',
19
from collections import defaultdict
19
20
from optparse import (
23
from storm.info import ClassAlias
24
from storm.locals import ClassAlias
25
26
from zope.component import getUtility
36
37
from canonical.launchpad.utilities.looptuner import TunableLoop
37
38
from lp.registry.interfaces.distribution import IDistributionSet
39
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
38
40
from lp.registry.interfaces.pocket import PackagePublishingPocket
39
41
from lp.registry.model.distroseries import DistroSeries
40
42
from lp.registry.model.distroseriesdifference import DistroSeriesDifference
43
from lp.registry.model.distroseriesparent import DistroSeriesParent
41
44
from lp.services.scripts.base import LaunchpadScript
42
45
from lp.soyuz.interfaces.publishing import active_publishing_status
81
def compose_sql_find_differences(derived_distroseries):
84
def compose_sql_find_differences(derived_series, parent_series):
82
85
"""Produce SQL that finds differences for a `DistroSeries`.
84
87
The query compares `derived_distroseries` and its `previous_series`
140
def compose_sql_populate_distroseriesdiff(derived_distroseries, temp_table):
143
def compose_sql_populate_distroseriesdiff(derived_series, parent_series,
141
145
"""Create `DistroSeriesDifference` rows based on found differences.
143
147
Uses field values that describe the difference, as produced by the
154
158
:return: SQL query, as a string.
157
'derived_series': quote(derived_distroseries),
161
'derived_series': quote(derived_series),
162
'parent_series': quote(parent_series),
158
163
'difference_type_expression': compose_sql_difference_type(),
159
164
'needs_attention': quote(
160
165
DistroSeriesDifferenceStatus.NEEDS_ATTENTION),
188
195
store.execute("DROP TABLE IF EXISTS %s" % quote_identifier(table))
191
def populate_distroseriesdiff(logger, derived_distroseries):
198
def populate_distroseriesdiff(logger, derived_series, parent_series):
192
199
"""Compare `derived_distroseries` to parent, and register differences.
194
201
The differences are registered by creating `DistroSeriesDifference`
197
204
temp_table = "temp_potentialdistroseriesdiff"
199
store = IStore(derived_distroseries)
206
store = IStore(derived_series)
200
207
drop_table(store, temp_table)
201
208
store.execute("CREATE TEMP TABLE %s AS %s" % (
202
209
quote_identifier(temp_table),
203
compose_sql_find_differences(derived_distroseries)))
210
compose_sql_find_differences(derived_series, parent_series)))
205
212
"Found %d potential difference(s).",
206
213
store.execute("SELECT count(*) FROM %s" % temp_table).get_one()[0])
208
215
compose_sql_populate_distroseriesdiff(
209
derived_distroseries, temp_table))
216
derived_series, parent_series, temp_table))
210
217
drop_table(store, temp_table)
213
220
def find_derived_series():
214
"""Find all derived `DistroSeries`.
216
Derived `DistroSeries` are ones that have a `previous_series`, but
217
where the `previous_series` is not in the same distribution.
221
"""Find all derived `DistroSeries`."""
222
Child = ClassAlias(DistroSeries, "Child")
219
223
Parent = ClassAlias(DistroSeries, "Parent")
220
return IStore(DistroSeries).find(
222
Parent.id == DistroSeries.previous_seriesID,
223
Parent.distributionID != DistroSeries.distributionID).order_by(
224
(DistroSeries.previous_seriesID, DistroSeries.id))
224
relations = IStore(DistroSeries).find(
226
DistroSeriesParent.derived_series_id == Child.id,
227
DistroSeriesParent.parent_series_id == Parent.id)
228
collated = defaultdict(list)
229
for child, parent in relations:
230
collated[child].append(parent)
227
234
class DSDUpdater(TunableLoop):
301
308
def getDistroSeries(self):
302
309
"""Return the `DistroSeries` that are to be processed."""
303
310
if self.options.all:
304
return list(find_derived_series())
311
return find_derived_series()
306
313
distro = getUtility(IDistributionSet).getByName(
307
314
self.options.distribution)
308
315
series = distro.getSeries(self.options.series)
316
augmented_series = defaultdict(list)
309
317
if series is None:
310
318
raise OptionValueError(
311
319
"Could not find %s series %s." % (
312
320
self.options.distribution, self.options.series))
313
if series.previous_series is None:
321
dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
324
augmented_series[rel.derived_series].append(
326
if len(augmented_series) == 0:
314
327
raise OptionValueError(
315
328
"%s series %s is not derived." % (
316
329
self.options.distribution, self.options.series))
330
return augmented_series
319
def processDistroSeries(self, distroseries):
332
def processDistroSeries(self, distroseries, parent):
320
333
"""Generate `DistroSeriesDifference`s for `distroseries`."""
321
self.logger.info("Looking for differences in %s.", distroseries)
322
populate_distroseriesdiff(self.logger, distroseries)
335
"Looking for differences in %s with regards to %s.",
336
distroseries, parent)
337
populate_distroseriesdiff(self.logger, distroseries, parent)
324
339
self.logger.info("Updating base_versions.")
325
340
self.update(distroseries)
336
351
def listDerivedSeries(self):
337
352
"""Log all `DistroSeries` that the --all option would cover."""
338
for series in self.getDistroSeries():
339
self.logger.info("%s %s", series.distribution.name, series.name)
353
relationships = self.getDistroSeries()
354
for child in relationships:
355
for parent in relationships[child]:
357
"%s %s with a parent of %s %s", child.distribution.name,
358
child.name, parent.distribution.name, parent.name)
341
360
def checkOptions(self):
342
361
"""Verify command-line options."""
363
382
if self.options.dry_run:
364
383
self.logger.info("Dry run requested. Not committing changes.")
366
for series in self.getDistroSeries():
367
self.processDistroSeries(series)
385
relationships = self.getDistroSeries()
386
for child in relationships.keys():
387
for parent in relationships[child]:
388
self.processDistroSeries(child, parent)
369
390
def update(self, distroseries):
370
391
"""Call `DistroSeriesDifference.update()` where appropriate.