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 `parent_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
221
"""Find all derived `DistroSeries`.
216
Derived `DistroSeries` are ones that have a `parent_series`, but
217
where the `parent_series` is not in the same distribution.
223
Child = ClassAlias(DistroSeries, "Child")
219
224
Parent = ClassAlias(DistroSeries, "Parent")
220
return IStore(DistroSeries).find(
222
Parent.id == DistroSeries.parent_seriesID,
223
Parent.distributionID != DistroSeries.distributionID).order_by(
224
(DistroSeries.parent_seriesID, DistroSeries.id))
225
relations = IStore(DistroSeries).find(
227
DistroSeriesParent.derived_series_id == Child.id,
228
DistroSeriesParent.parent_series_id == Parent.id)
229
collated = defaultdict(list)
230
for child, parent in relations:
231
collated[child].append(parent)
227
235
class DSDUpdater(TunableLoop):
301
309
def getDistroSeries(self):
302
310
"""Return the `DistroSeries` that are to be processed."""
303
311
if self.options.all:
304
return list(find_derived_series())
312
return find_derived_series()
306
314
distro = getUtility(IDistributionSet).getByName(
307
315
self.options.distribution)
308
316
series = distro.getSeries(self.options.series)
317
augmented_series = defaultdict(list)
309
318
if series is None:
310
319
raise OptionValueError(
311
320
"Could not find %s series %s." % (
312
321
self.options.distribution, self.options.series))
313
if series.parent_series is None:
322
dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
325
augmented_series[rel.derived_series].append(
327
if len(augmented_series) == 0:
314
328
raise OptionValueError(
315
329
"%s series %s is not derived." % (
316
330
self.options.distribution, self.options.series))
331
return augmented_series
319
def processDistroSeries(self, distroseries):
333
def processDistroSeries(self, distroseries, parent):
320
334
"""Generate `DistroSeriesDifference`s for `distroseries`."""
321
self.logger.info("Looking for differences in %s.", distroseries)
322
populate_distroseriesdiff(self.logger, distroseries)
336
"Looking for differences in %s with regards to %s.",
337
distroseries, parent)
338
populate_distroseriesdiff(self.logger, distroseries, parent)
324
340
self.logger.info("Updating base_versions.")
325
341
self.update(distroseries)
336
352
def listDerivedSeries(self):
337
353
"""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)
354
relationships = self.getDistroSeries()
355
for child in relationships:
356
for parent in relationships[child]:
358
"%s %s with a parent of %s %s", child.distribution.name,
359
child.name, parent.distribution.name, parent.name)
341
361
def checkOptions(self):
342
362
"""Verify command-line options."""
363
383
if self.options.dry_run:
364
384
self.logger.info("Dry run requested. Not committing changes.")
366
for series in self.getDistroSeries():
367
self.processDistroSeries(series)
386
relationships = self.getDistroSeries()
387
for child in relationships.keys():
388
for parent in relationships[child]:
389
self.processDistroSeries(child, parent)
369
391
def update(self, distroseries):
370
392
"""Call `DistroSeriesDifference.update()` where appropriate.