9
9
'DistroSeriesDifference',
12
from collections import defaultdict
12
13
from itertools import chain
13
14
from operator import itemgetter
15
17
from debian.changelog import (
19
21
from lazr.enum import DBItem
20
22
from sqlobject import StringCol
23
from storm.exceptions import NotOneError
21
24
from storm.expr import (
23
compile as storm_compile,
27
from storm.info import ClassAlias
28
30
from storm.locals import (
61
63
from lp.registry.interfaces.distroseriesdifferencecomment import (
62
64
IDistroSeriesDifferenceCommentSource,
66
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
64
67
from lp.registry.interfaces.person import IPersonSet
65
68
from lp.registry.model.distroseries import DistroSeries
66
69
from lp.registry.model.distroseriesdifferencecomment import (
107
115
conditions = And(
108
116
DistroSeriesDifference.id.is_in(dsd.id for dsd in dsds),
109
DistroSeries.id == DistroSeriesDifference.derived_series_id,
110
117
SourcePackagePublishingHistory.archiveID == Archive.id,
111
118
SourcePackagePublishingHistory.sourcepackagereleaseID == (
112
119
SourcePackageRelease.id),
117
124
# Check in the parent archive or the child?
119
ParentDistroSeries = ClassAlias(DistroSeries)
120
126
conditions = And(
122
ParentDistroSeries.id == DistroSeries.previous_seriesID,
123
Archive.distributionID == ParentDistroSeries.distributionID,
128
DistroSeries.id == DistroSeriesDifference.parent_series_id,
129
Archive.distributionID == DistroSeries.distributionID,
124
130
Archive.purpose == ArchivePurpose.PRIMARY,
127
133
conditions = And(
135
DistroSeries.id == DistroSeriesDifference.derived_series_id,
129
136
Archive.distributionID == DistroSeries.distributionID,
130
137
Archive.purpose == ArchivePurpose.PRIMARY,
182
189
return DecoratedResultSet(comments, itemgetter(0))
192
def packagesets(dsds, in_parent):
193
"""Return the packagesets for the given dsds inside the parent or
194
the derived `DistroSeries`.
196
Returns a dict with the corresponding packageset list for each dsd id.
198
:param dsds: An iterable of `DistroSeriesDifference` instances.
199
:param in_parent: A boolean indicating if we should look in the parent
200
series' archive instead of the derived series' archive.
205
PackagesetSources = Table("PackageSetSources")
206
FlatPackagesetInclusion = Table("FlatPackagesetInclusion")
208
tables = IStore(Packageset).using(
209
DistroSeriesDifference, Packageset,
210
PackagesetSources, FlatPackagesetInclusion)
211
results = tables.find(
212
(DistroSeriesDifference.id, Packageset),
213
Column("packageset", PackagesetSources) == (
214
Column("child", FlatPackagesetInclusion)),
215
Packageset.distroseries_id == (
216
DistroSeriesDifference.parent_series_id if in_parent else
217
DistroSeriesDifference.derived_series_id),
218
Column("parent", FlatPackagesetInclusion) == Packageset.id,
219
Column("sourcepackagename", PackagesetSources) == (
220
DistroSeriesDifference.source_package_name_id),
221
DistroSeriesDifference.id.is_in(dsd.id for dsd in dsds))
222
results = results.order_by(
223
Column("sourcepackagename", PackagesetSources),
226
grouped = defaultdict(list)
227
for dsd_id, packageset in results:
228
grouped[dsd_id].append(packageset)
232
def message_chunks(messages):
233
"""Return the message chunks for the given messages.
235
Returns a dict with the list of `MessageChunk` for each message id.
237
:param messages: An iterable of `Message` instances.
239
store = IStore(MessageChunk)
240
chunks = store.find(MessageChunk,
241
MessageChunk.messageID.is_in(m.id for m in messages))
243
grouped = defaultdict(list)
245
grouped[chunk.messageID].append(chunk)
185
249
class DistroSeriesDifference(StormBase):
186
250
"""See `DistroSeriesDifference`."""
187
251
implements(IDistroSeriesDifference)
194
258
derived_series = Reference(
195
259
derived_series_id, 'DistroSeries.id')
261
parent_series_id = Int(name='parent_series', allow_none=False)
262
parent_series = Reference(parent_series_id, 'DistroSeries.id')
197
264
source_package_name_id = Int(
198
265
name='source_package_name', allow_none=False)
199
266
source_package_name = Reference(
219
286
base_version = StringCol(dbName='base_version', notNull=False)
222
def new(derived_series, source_package_name):
289
def new(derived_series, source_package_name, parent_series=None):
223
290
"""See `IDistroSeriesDifferenceSource`."""
224
if not derived_series.is_derived_series:
225
raise NotADerivedSeriesError()
291
if parent_series is None:
293
dsps = getUtility(IDistroSeriesParentSet)
294
dsp = dsps.getByDerivedSeries(
295
derived_series).one()
297
raise MultipleParentsForDerivedSeriesError()
300
raise NotADerivedSeriesError()
302
parent_series = dsp.parent_series
227
304
store = IMasterStore(DistroSeriesDifference)
228
305
diff = DistroSeriesDifference()
229
306
diff.derived_series = derived_series
307
diff.parent_series = parent_series
230
308
diff.source_package_name = source_package_name
232
310
# The status and type is set to default values - they will be
241
319
def getForDistroSeries(
243
difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
321
difference_type=None,
244
322
source_package_name_filter=None,
246
child_version_higher=False):
324
child_version_higher=False,
247
326
"""See `IDistroSeriesDifferenceSource`."""
327
if difference_type is None:
328
difference_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
248
329
if status is None:
250
331
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
310
395
parent_source_pubs_for_release.itervalues()),
311
396
("sourcepackagereleaseID",))
398
# Get packagesets and parent_packagesets for each DSD.
399
dsd_packagesets = packagesets(dsds, in_parent=False)
400
dsd_parent_packagesets = packagesets(dsds, in_parent=True)
402
# Cache latest messages contents (MessageChunk).
403
messages = bulk.load_related(
404
Message, latest_comments, ['message_id'])
405
chunks = message_chunks(messages)
407
cache = get_property_cache(msg)
408
cache.text_contents = Message.chunks_text(
409
chunks.get(msg.id, []))
314
412
spn_id = dsd.source_package_name_id
315
413
cache = get_property_cache(dsd)
316
414
cache.source_pub = source_pubs.get(spn_id)
317
415
cache.parent_source_pub = parent_source_pubs.get(spn_id)
416
cache.packagesets = dsd_packagesets.get(dsd.id)
417
cache.parent_packagesets = dsd_parent_packagesets.get(dsd.id)
318
418
if spn_id in source_pubs_for_release:
319
419
spph = source_pubs_for_release[spn_id]
320
420
cache.source_package_release = (
327
427
spph = parent_source_pubs_for_release[spn_id]
328
428
cache.parent_source_package_release = (
329
429
DistroSeriesSourcePackageRelease(
330
dsd.derived_series.previous_series,
331
spph.sourcepackagerelease))
430
dsd.parent_series, spph.sourcepackagerelease))
333
432
cache.parent_source_package_release = None
334
433
cache.latest_comment = latest_comment_by_dsd_id.get(dsd.id)
361
460
differences, pre_iter_hook=eager_load)
364
def getByDistroSeriesAndName(distro_series, source_package_name):
463
def getByDistroSeriesNameAndParentSeries(distro_series,
365
466
"""See `IDistroSeriesDifferenceSource`."""
366
468
return IStore(DistroSeriesDifference).find(
367
469
DistroSeriesDifference,
368
470
DistroSeriesDifference.derived_series == distro_series,
471
DistroSeriesDifference.parent_series == parent_series,
369
472
DistroSeriesDifference.source_package_name == (
370
473
SourcePackageName.id),
371
474
SourcePackageName.name == source_package_name).one()
477
def getSimpleUpgrades(distro_series):
478
"""See `IDistroSeriesDifferenceSource`.
480
Eager-load related `ISourcePackageName` records.
482
differences = IStore(DistroSeriesDifference).find(
483
(DistroSeriesDifference, SourcePackageName),
484
DistroSeriesDifference.derived_series == distro_series,
485
DistroSeriesDifference.difference_type ==
486
DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
487
DistroSeriesDifference.status ==
488
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
489
DistroSeriesDifference.parent_source_version !=
490
DistroSeriesDifference.base_version,
491
DistroSeriesDifference.source_version ==
492
DistroSeriesDifference.base_version,
493
SourcePackageName.id ==
494
DistroSeriesDifference.source_package_name_id)
495
return DecoratedResultSet(differences, itemgetter(0))
498
def collateDifferencesByParentArchive(differences):
500
for difference in differences:
501
archive = difference.parent_series.main_archive
502
if archive in by_archive:
503
by_archive[archive].append(difference)
505
by_archive[archive] = [difference]
374
509
def source_pub(self):
375
510
"""See `IDistroSeriesDifference`."""
384
519
"""Helper to keep source_pub/parent_source_pub DRY."""
385
520
distro_series = self.derived_series
387
distro_series = self.derived_series.previous_series
522
distro_series = self.parent_series
389
524
pubs = distro_series.getPublishedSources(
390
525
self.source_package_name, include_pending=True)
399
534
def base_source_pub(self):
400
535
"""See `IDistroSeriesDifference`."""
401
536
if self.base_version is not None:
402
parent = self.derived_series.previous_series
537
parent = self.parent_series
403
538
result = parent.main_archive.getPublishedSources(
404
539
name=self.source_package_name.name,
405
540
version=self.base_version).first()
466
601
"""See `IDistroSeriesDifference`."""
467
602
return self._getPackageDiffURL(self.parent_package_diff)
469
def getPackageSets(self):
605
def packagesets(self):
470
606
"""See `IDistroSeriesDifference`."""
471
607
if self.derived_series is not None:
472
return getUtility(IPackagesetSet).setsIncludingSource(
473
self.source_package_name, self.derived_series)
608
return list(getUtility(IPackagesetSet).setsIncludingSource(
609
self.source_package_name, self.derived_series))
477
def getParentPackageSets(self):
614
def parent_packagesets(self):
478
615
"""See `IDistroSeriesDifference`."""
479
has_previous_series = self.derived_series is not None and (
480
self.derived_series.previous_series is not None)
481
if has_previous_series:
482
return getUtility(IPackagesetSet).setsIncludingSource(
483
self.source_package_name,
484
self.derived_series.previous_series)
616
return list(getUtility(IPackagesetSet).setsIncludingSource(
617
self.source_package_name, self.parent_series))
489
620
def package_diff_status(self):
505
636
def parent_source_package_release(self):
506
637
return self._package_release(
507
self.derived_series.previous_series,
508
self.parent_source_version)
638
self.parent_series, self.parent_source_version)
511
641
def source_package_release(self):
512
642
return self._package_release(
643
self.derived_series, self.source_version)
516
645
def _package_release(self, distro_series, version):
533
662
return DistroSeriesSourcePackageRelease(
534
663
distro_series, pub.sourcepackagerelease)
665
def update(self, manual=False):
537
666
"""See `IDistroSeriesDifference`."""
538
667
# Updating is expected to be a heavy operation (not called
539
668
# during requests). We clear the cache beforehand - even though
564
693
if new_type != self.difference_type:
565
694
self.difference_type = new_type
567
def _updateVersionsAndStatus(self):
696
def _updateVersionsAndStatus(self, manual):
568
697
"""Helper for the update() interface method.
570
699
Check whether the status of this difference should be updated.
701
:param manual: Boolean, True if this is a user-requested change.
702
This overrides auto-blacklisting.
704
# XXX 2011-05-20 bigjools bug=785657
705
# This method needs updating to use some sort of state
706
# transition dictionary instead of this crazy mess of
573
710
new_source_version = new_parent_source_version = None
574
711
if self.source_pub:
575
712
new_source_version = self.source_pub.source_package_version
576
if self.source_version != new_source_version:
713
if self.source_version is None or apt_pkg.VersionCompare(
714
self.source_version, new_source_version) != 0:
577
715
self.source_version = new_source_version
579
717
# If the derived version has change and the previous version
584
722
if self.parent_source_pub:
585
723
new_parent_source_version = (
586
724
self.parent_source_pub.source_package_version)
587
if self.parent_source_version != new_parent_source_version:
725
if self.parent_source_version is None or apt_pkg.VersionCompare(
726
self.parent_source_version,
727
new_parent_source_version) != 0:
588
728
self.parent_source_version = new_parent_source_version
731
if not self.source_pub or not self.parent_source_pub:
732
# This is unlikely to happen in reality but return early so
733
# that bad data cannot make us OOPS.
591
736
# If this difference was resolved but now the versions don't match
592
737
# then we re-open the difference.
593
738
if self.status == DistroSeriesDifferenceStatus.RESOLVED:
594
if self.source_version != self.parent_source_version:
739
if apt_pkg.VersionCompare(
740
self.source_version, self.parent_source_version) < 0:
741
# Higher parent version.
596
743
self.status = DistroSeriesDifferenceStatus.NEEDS_ATTENTION
745
apt_pkg.VersionCompare(
746
self.source_version, self.parent_source_version) > 0
748
# The child was updated with a higher version so it's
751
self.status = DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT
597
752
# If this difference was needing attention, or the current version
598
753
# was blacklisted and the versions now match we resolve it. Note:
599
754
# we don't resolve it if this difference was blacklisted for all
601
756
elif self.status in (
602
757
DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
603
758
DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT):
604
if self.source_version == self.parent_source_version:
759
if apt_pkg.VersionCompare(
760
self.source_version, self.parent_source_version) == 0:
606
762
self.status = DistroSeriesDifferenceStatus.RESOLVED
764
apt_pkg.VersionCompare(
765
self.source_version, self.parent_source_version) < 0
767
# If the derived version is lower than the parent's, we
768
# ensure the diff status is blacklisted.
769
self.status = DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT
608
771
if self._updateBaseVersion():