~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/model/distroseriesdifference.py

Merge db-devel.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
    )
19
19
from lazr.enum import DBItem
20
20
from sqlobject import StringCol
 
21
from storm.exceptions import NotOneError
21
22
from storm.expr import (
22
23
    And,
 
24
    Column,
23
25
    compile as storm_compile,
24
26
    Desc,
25
27
    SQL,
 
28
    Table,
26
29
    )
27
 
from storm.info import ClassAlias
28
30
from storm.locals import (
29
31
    Int,
30
32
    Reference,
40
42
from canonical.launchpad.components.decoratedresultset import (
41
43
    DecoratedResultSet,
42
44
    )
43
 
from canonical.launchpad.database.message import Message
 
45
from lp.services.messages.model.message import Message
44
46
from canonical.launchpad.interfaces.lpstorm import (
45
47
    IMasterStore,
46
48
    IStore,
52
54
    )
53
55
from lp.registry.errors import (
54
56
    DistroSeriesDifferenceError,
 
57
    MultipleParentsForDerivedSeriesError,
55
58
    NotADerivedSeriesError,
56
59
    )
57
60
from lp.registry.interfaces.distroseriesdifference import (
61
64
from lp.registry.interfaces.distroseriesdifferencecomment import (
62
65
    IDistroSeriesDifferenceCommentSource,
63
66
    )
 
67
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
64
68
from lp.registry.interfaces.person import IPersonSet
65
69
from lp.registry.model.distroseries import DistroSeries
66
70
from lp.registry.model.distroseriesdifferencecomment import (
86
90
from lp.soyuz.model.distroseriessourcepackagerelease import (
87
91
    DistroSeriesSourcePackageRelease,
88
92
    )
 
93
from lp.soyuz.model.packageset import Packageset
89
94
from lp.soyuz.model.publishing import SourcePackagePublishingHistory
90
95
from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
91
96
 
100
105
    :param in_parent: A boolean indicating if we should look in the parent
101
106
        series' archive instead of the derived series' archive.
102
107
    """
103
 
    distinct_on = "DistroSeriesDifference.source_package_name"
104
108
    columns = (
105
 
        # XXX: GavinPanella 2010-04-06 bug=374777: This SQL(...) is a hack; it
106
 
        # does not seem to be possible to express DISTINCT ON with Storm.
107
 
        SQL("DISTINCT ON (%s) 0 AS ignore" % distinct_on),
108
109
        DistroSeriesDifference.source_package_name_id,
109
110
        SourcePackagePublishingHistory,
110
111
        )
111
112
    conditions = And(
112
113
        DistroSeriesDifference.id.is_in(dsd.id for dsd in dsds),
113
 
        DistroSeries.id == DistroSeriesDifference.derived_series_id,
114
114
        SourcePackagePublishingHistory.archiveID == Archive.id,
115
115
        SourcePackagePublishingHistory.sourcepackagereleaseID == (
116
116
            SourcePackageRelease.id),
120
120
        )
121
121
    # Check in the parent archive or the child?
122
122
    if in_parent:
123
 
        ParentDistroSeries = ClassAlias(DistroSeries)
124
123
        conditions = And(
125
124
            conditions,
126
 
            ParentDistroSeries.id == DistroSeries.parent_seriesID,
127
 
            Archive.distributionID == ParentDistroSeries.distributionID,
 
125
            DistroSeries.id == DistroSeriesDifference.parent_series_id,
 
126
            Archive.distributionID == DistroSeries.distributionID,
128
127
            Archive.purpose == ArchivePurpose.PRIMARY,
129
128
            )
130
129
    else:
131
130
        conditions = And(
132
131
            conditions,
 
132
            DistroSeries.id == DistroSeriesDifference.derived_series_id,
133
133
            Archive.distributionID == DistroSeries.distributionID,
134
134
            Archive.purpose == ArchivePurpose.PRIMARY,
135
135
            )
149
149
        DistroSeriesDifference.source_package_name_id,
150
150
        Desc(SourcePackagePublishingHistory.id),
151
151
        )
 
152
    distinct_on = (
 
153
        DistroSeriesDifference.source_package_name_id,
 
154
        )
152
155
    store = IStore(SourcePackagePublishingHistory)
153
 
    results = store.find(columns, conditions).order_by(*order_by)
154
 
    return DecoratedResultSet(results, itemgetter(1, 2))
 
156
    return store.find(
 
157
        columns, conditions).order_by(*order_by).config(distinct=distinct_on)
155
158
 
156
159
 
157
160
def most_recent_comments(dsds):
162
165
 
163
166
    :param dsds: An iterable of `DistroSeriesDifference` instances.
164
167
    """
165
 
    distinct_on = storm_compile(
166
 
        DistroSeriesDifferenceComment.distro_series_difference_id)
167
168
    columns = (
168
 
        # XXX: GavinPanella 2010-04-06 bug=374777: This SQL(...) is a
169
 
        # hack; it does not seem to be possible to express DISTINCT ON
170
 
        # with Storm.
171
 
        SQL("DISTINCT ON (%s) 0 AS ignore" % distinct_on),
172
169
        DistroSeriesDifferenceComment,
173
170
        Message,
174
171
        )
180
177
        DistroSeriesDifferenceComment.distro_series_difference_id,
181
178
        Desc(DistroSeriesDifferenceComment.id),
182
179
        )
 
180
    distinct_on = (
 
181
        DistroSeriesDifferenceComment.distro_series_difference_id,
 
182
        )
183
183
    store = IStore(DistroSeriesDifferenceComment)
184
 
    comments = store.find(columns, conditions).order_by(*order_by)
185
 
    return DecoratedResultSet(comments, itemgetter(1))
 
184
    comments = store.find(
 
185
        columns, conditions).order_by(*order_by).config(distinct=distinct_on)
 
186
    return DecoratedResultSet(comments, itemgetter(0))
 
187
 
 
188
 
 
189
def packagesets(dsds, in_parent):
 
190
    """Return the packagesets for the given dsds inside the parent or
 
191
    the derived `DistroSeries`.
 
192
 
 
193
    Returns a dict with the corresponding packageset list for each dsd id.
 
194
 
 
195
    :param dsds: An iterable of `DistroSeriesDifference` instances.
 
196
    :param in_parent: A boolean indicating if we should look in the parent
 
197
        series' archive instead of the derived series' archive.
 
198
    """
 
199
    if len(dsds) == 0:
 
200
        return {}
 
201
 
 
202
    PackagesetSources = Table("PackageSetSources")
 
203
    FlatPackagesetInclusion = Table("FlatPackagesetInclusion")
 
204
 
 
205
    tables = IStore(Packageset).using(
 
206
        DistroSeriesDifference, Packageset,
 
207
        PackagesetSources, FlatPackagesetInclusion)
 
208
    results = tables.find(
 
209
        (DistroSeriesDifference.id, Packageset),
 
210
        Column("packageset", PackagesetSources) == (
 
211
            Column("child", FlatPackagesetInclusion)),
 
212
        Packageset.distroseries_id == (
 
213
            DistroSeriesDifference.parent_series_id if in_parent else
 
214
            DistroSeriesDifference.derived_series_id),
 
215
        Column("parent", FlatPackagesetInclusion) == Packageset.id,
 
216
        Column("sourcepackagename", PackagesetSources) == (
 
217
            DistroSeriesDifference.source_package_name_id),
 
218
        DistroSeriesDifference.id.is_in(dsd.id for dsd in dsds))
 
219
    results = results.order_by(
 
220
        Column("sourcepackagename", PackagesetSources),
 
221
        Packageset.name)
 
222
 
 
223
    grouped = {}
 
224
    for dsd_id, packageset in results:
 
225
        if dsd_id in grouped:
 
226
            grouped[dsd_id].append(packageset)
 
227
        else:
 
228
            grouped[dsd_id] = [packageset]
 
229
 
 
230
    return grouped
186
231
 
187
232
 
188
233
class DistroSeriesDifference(StormBase):
197
242
    derived_series = Reference(
198
243
        derived_series_id, 'DistroSeries.id')
199
244
 
 
245
    parent_series_id = Int(name='parent_series', allow_none=False)
 
246
    parent_series = Reference(parent_series_id, 'DistroSeries.id')
 
247
 
200
248
    source_package_name_id = Int(
201
249
        name='source_package_name', allow_none=False)
202
250
    source_package_name = Reference(
222
270
    base_version = StringCol(dbName='base_version', notNull=False)
223
271
 
224
272
    @staticmethod
225
 
    def new(derived_series, source_package_name):
 
273
    def new(derived_series, source_package_name, parent_series=None):
226
274
        """See `IDistroSeriesDifferenceSource`."""
227
 
        if not derived_series.is_derived_series:
228
 
            raise NotADerivedSeriesError()
 
275
        if parent_series is None:
 
276
            try:
 
277
                dsps = getUtility(IDistroSeriesParentSet)
 
278
                dsp = dsps.getByDerivedSeries(
 
279
                    derived_series).one()
 
280
            except NotOneError:
 
281
                raise MultipleParentsForDerivedSeriesError()
 
282
            else:
 
283
                if dsp is None:
 
284
                    raise NotADerivedSeriesError()
 
285
                else:
 
286
                    parent_series = dsp.parent_series
229
287
 
230
288
        store = IMasterStore(DistroSeriesDifference)
231
289
        diff = DistroSeriesDifference()
232
290
        diff.derived_series = derived_series
 
291
        diff.parent_series = parent_series
233
292
        diff.source_package_name = source_package_name
234
293
 
235
294
        # The status and type is set to default values - they will be
243
302
    @staticmethod
244
303
    def getForDistroSeries(
245
304
        distro_series,
246
 
        difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
 
305
        difference_type=None,
247
306
        source_package_name_filter=None,
248
307
        status=None,
249
 
        child_version_higher=False):
 
308
        child_version_higher=False,
 
309
        parent_series=None):
250
310
        """See `IDistroSeriesDifferenceSource`."""
 
311
        if difference_type is None:
 
312
            difference_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
251
313
        if status is None:
252
314
            status = (
253
315
                DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
261
323
            DistroSeriesDifference.status.is_in(status),
262
324
            DistroSeriesDifference.source_package_name ==
263
325
                SourcePackageName.id,
264
 
         ]
 
326
        ]
 
327
 
 
328
        if parent_series:
 
329
            conditions.extend([
 
330
               DistroSeriesDifference.parent_series == parent_series.id])
265
331
 
266
332
        if source_package_name_filter:
267
333
            conditions.extend([
313
379
                    parent_source_pubs_for_release.itervalues()),
314
380
                ("sourcepackagereleaseID",))
315
381
 
 
382
            # Get packagesets and parent_packagesets for each DSD.
 
383
            dsd_packagesets = packagesets(dsds, in_parent=False)
 
384
            dsd_parent_packagesets = packagesets(dsds, in_parent=True)
 
385
 
316
386
            for dsd in dsds:
317
387
                spn_id = dsd.source_package_name_id
318
388
                cache = get_property_cache(dsd)
319
389
                cache.source_pub = source_pubs.get(spn_id)
320
390
                cache.parent_source_pub = parent_source_pubs.get(spn_id)
 
391
                cache.packagesets = dsd_packagesets.get(dsd.id)
 
392
                cache.parent_packagesets = dsd_parent_packagesets.get(dsd.id)
321
393
                if spn_id in source_pubs_for_release:
322
394
                    spph = source_pubs_for_release[spn_id]
323
395
                    cache.source_package_release = (
330
402
                    spph = parent_source_pubs_for_release[spn_id]
331
403
                    cache.parent_source_package_release = (
332
404
                        DistroSeriesSourcePackageRelease(
333
 
                            dsd.derived_series.parent_series,
334
 
                            spph.sourcepackagerelease))
 
405
                            dsd.parent_series, spph.sourcepackagerelease))
335
406
                else:
336
407
                    cache.parent_source_package_release = None
337
408
                cache.latest_comment = latest_comment_by_dsd_id.get(dsd.id)
364
435
            differences, pre_iter_hook=eager_load)
365
436
 
366
437
    @staticmethod
367
 
    def getByDistroSeriesAndName(distro_series, source_package_name):
 
438
    def getByDistroSeriesNameAndParentSeries(distro_series,
 
439
                                             source_package_name,
 
440
                                             parent_series):
368
441
        """See `IDistroSeriesDifferenceSource`."""
 
442
 
369
443
        return IStore(DistroSeriesDifference).find(
370
444
            DistroSeriesDifference,
371
445
            DistroSeriesDifference.derived_series == distro_series,
 
446
            DistroSeriesDifference.parent_series == parent_series,
372
447
            DistroSeriesDifference.source_package_name == (
373
448
                SourcePackageName.id),
374
449
            SourcePackageName.name == source_package_name).one()
375
450
 
 
451
    @staticmethod
 
452
    def getSimpleUpgrades(distro_series):
 
453
        """See `IDistroSeriesDifferenceSource`.
 
454
 
 
455
        Eager-load related `ISourcePackageName` records.
 
456
        """
 
457
        differences = IStore(DistroSeriesDifference).find(
 
458
            (DistroSeriesDifference, SourcePackageName),
 
459
            DistroSeriesDifference.derived_series == distro_series,
 
460
            DistroSeriesDifference.difference_type ==
 
461
                DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
 
462
            DistroSeriesDifference.status ==
 
463
                DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
 
464
            DistroSeriesDifference.parent_source_version !=
 
465
                DistroSeriesDifference.base_version,
 
466
            DistroSeriesDifference.source_version ==
 
467
                DistroSeriesDifference.base_version,
 
468
            SourcePackageName.id ==
 
469
                DistroSeriesDifference.source_package_name_id)
 
470
        return DecoratedResultSet(differences, itemgetter(0))
 
471
 
 
472
    @staticmethod
 
473
    def collateDifferencesByParentArchive(differences):
 
474
        by_archive = dict()
 
475
        for difference in differences:
 
476
            archive = difference.parent_series.main_archive
 
477
            if archive in by_archive:
 
478
                by_archive[archive].append(difference)
 
479
            else:
 
480
                by_archive[archive] = [difference]
 
481
        return by_archive
 
482
 
376
483
    @cachedproperty
377
484
    def source_pub(self):
378
485
        """See `IDistroSeriesDifference`."""
387
494
        """Helper to keep source_pub/parent_source_pub DRY."""
388
495
        distro_series = self.derived_series
389
496
        if for_parent:
390
 
            distro_series = self.derived_series.parent_series
 
497
            distro_series = self.parent_series
391
498
 
392
499
        pubs = distro_series.getPublishedSources(
393
500
            self.source_package_name, include_pending=True)
402
509
    def base_source_pub(self):
403
510
        """See `IDistroSeriesDifference`."""
404
511
        if self.base_version is not None:
405
 
            parent = self.derived_series.parent_series
 
512
            parent = self.parent_series
406
513
            result = parent.main_archive.getPublishedSources(
407
514
                name=self.source_package_name.name,
408
515
                version=self.base_version).first()
424
531
    @property
425
532
    def title(self):
426
533
        """See `IDistroSeriesDifference`."""
427
 
        parent_name = self.derived_series.parent_series.displayname
 
534
        parent_name = self.parent_series.displayname
428
535
        return ("Difference between distroseries '%(parent_name)s' and "
429
536
                "'%(derived_name)s' for package '%(pkg_name)s' "
430
537
                "(%(parent_version)s/%(source_version)s)" % {
469
576
        """See `IDistroSeriesDifference`."""
470
577
        return self._getPackageDiffURL(self.parent_package_diff)
471
578
 
472
 
    def getPackageSets(self):
 
579
    @cachedproperty
 
580
    def packagesets(self):
473
581
        """See `IDistroSeriesDifference`."""
474
582
        if self.derived_series is not None:
475
 
            return getUtility(IPackagesetSet).setsIncludingSource(
476
 
                self.source_package_name, self.derived_series)
 
583
            return list(getUtility(IPackagesetSet).setsIncludingSource(
 
584
                self.source_package_name, self.derived_series))
477
585
        else:
478
586
            return []
479
587
 
480
 
    def getParentPackageSets(self):
 
588
    @cachedproperty
 
589
    def parent_packagesets(self):
481
590
        """See `IDistroSeriesDifference`."""
482
 
        has_parent_series = self.derived_series is not None and (
483
 
            self.derived_series.parent_series is not None)
484
 
        if has_parent_series:
485
 
            return getUtility(IPackagesetSet).setsIncludingSource(
486
 
                self.source_package_name,
487
 
                self.derived_series.parent_series)
488
 
        else:
489
 
            return []
 
591
        return list(getUtility(IPackagesetSet).setsIncludingSource(
 
592
            self.source_package_name, self.parent_series))
490
593
 
491
594
    @property
492
595
    def package_diff_status(self):
507
610
    @cachedproperty
508
611
    def parent_source_package_release(self):
509
612
        return self._package_release(
510
 
            self.derived_series.parent_series,
511
 
            self.parent_source_version)
 
613
            self.parent_series, self.parent_source_version)
512
614
 
513
615
    @cachedproperty
514
616
    def source_package_release(self):
515
617
        return self._package_release(
516
 
            self.derived_series,
517
 
            self.source_version)
 
618
            self.derived_series, self.source_version)
518
619
 
519
620
    def _package_release(self, distro_series, version):
520
621
        statuses = (