~launchpad-pqm/launchpad/devel

8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
3
4687.6.10 by Celso Providelo
applying review comments, r=jml
4
"""Browser views for Soyuz publishing records."""
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
5
6
__metaclass__ = type
7
8
__all__ = [
7310.3.10 by Julian Edwards
Export SourcePackagePublishingHistory on the webservice.
9
    'BinaryPublishingRecordView',
10
    'SourcePublicationURL',
11
    'SourcePublishingRecordSelectableView',
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
12
    'SourcePublishingRecordView',
13
    ]
14
7083.2.1 by Muharem Hrnjadovic
all changes except bug #260013
15
from operator import attrgetter
16
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
17
from lazr.delegates import delegates
7310.3.10 by Julian Edwards
Export SourcePackagePublishingHistory on the webservice.
18
from zope.interface import implements
19
14185.1.4 by William Grant
Introduce SourcePackagePublishingHistoryNavigation, just with FileNavigationMixin for now.
20
from canonical.launchpad.browser.librarian import (
21
    FileNavigationMixin,
22
    ProxiedLibraryFileAlias,
23
    )
24
from canonical.launchpad.webapp import Navigation
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
25
from canonical.launchpad.webapp.authorization import check_permission
26
from canonical.launchpad.webapp.interfaces import ICanonicalUrlData
12484.1.1 by Steve Kowalik
Refactor displaying recipe details savagely, and without mercy. Use structured
27
from canonical.launchpad.webapp.menu import structured
28
from canonical.launchpad.webapp.publisher import (
29
    canonical_url,
30
    LaunchpadView,
31
    )
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
32
from lp.services.propertycache import cachedproperty
11411.6.12 by Julian Edwards
Move PackagePublishingStatus/Priority
33
from lp.soyuz.enums import PackagePublishingStatus
10667.2.2 by Michael Nelson
Mass renaming of imports and references to IBuild/Build/IBuildSet
34
from lp.soyuz.interfaces.binarypackagebuild import BuildSetStatus
8294.6.1 by Julian Edwards
First stab at code-reorg. Still got a discrepancy on stuff I assigned to registry but not migrated yet.
35
from lp.soyuz.interfaces.packagediff import IPackageDiff
36
from lp.soyuz.interfaces.publishing import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
37
    IBinaryPackagePublishingHistory,
38
    ISourcePackagePublishingHistory,
39
    )
7310.3.10 by Julian Edwards
Export SourcePackagePublishingHistory on the webservice.
40
41
8053.6.11 by Julian Edwards
Salgado's review comments.
42
class PublicationURLBase:
43
    """Dynamic URL declaration for `I*PackagePublishingHistory`"""
44
    implements(ICanonicalUrlData)
45
    rootsite = None
46
47
    def __init__(self, context):
48
        self.context = context
49
50
    @property
51
    def inside(self):
52
        return self.context.archive
53
54
55
class SourcePublicationURL(PublicationURLBase):
7310.3.10 by Julian Edwards
Export SourcePackagePublishingHistory on the webservice.
56
    """Dynamic URL declaration for `ISourcePackagePublishingHistory`"""
57
    @property
58
    def path(self):
59
        return u"+sourcepub/%s" % self.context.id
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
60
4687.6.15 by Celso Providelo
applying review comments, r=spiv.
61
8053.6.11 by Julian Edwards
Salgado's review comments.
62
class BinaryPublicationURL(PublicationURLBase):
8053.6.3 by Julian Edwards
Add tests for the exported binarypackagepublishinghistory.
63
    """Dynamic URL declaration for `IBinaryPackagePublishingHistory`"""
64
    @property
65
    def path(self):
66
        return u"+binarypub/%s" % self.context.id
67
68
14185.1.4 by William Grant
Introduce SourcePackagePublishingHistoryNavigation, just with FileNavigationMixin for now.
69
class SourcePackagePublishingHistoryNavigation(Navigation,
70
                                               FileNavigationMixin):
71
    usedfor = ISourcePackagePublishingHistory
72
73
8213.2.2 by Celso Providelo
Using ProxiedLibraryFileAlias to build absolute URLs for source publication package_diffs.
74
class ProxiedPackageDiff:
75
    """A `PackageDiff` extension.
76
77
    Instead of `LibraryFileAlias` returns `ProxiedLibraryFileAlias`, so
78
    their 'http_url' attribute can be used in the template.
79
    """
80
    delegates(IPackageDiff)
81
82
    def __init__(self, context, parent):
83
        self.context = context
84
        self.parent = parent
85
86
    @property
87
    def diff_content(self):
88
        library_file = self.context.diff_content
89
        if library_file is None:
90
            return None
91
        return ProxiedLibraryFileAlias(library_file, self.parent)
92
93
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
94
class BasePublishingRecordView(LaunchpadView):
95
    """Base Publishing view class."""
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
96
97
    @property
98
    def is_source(self):
6149.2.3 by Celso Providelo
applying review comments, r=bigjools.
99
        return ISourcePackagePublishingHistory.providedBy(self.context)
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
100
101
    @property
102
    def is_binary(self):
6149.2.3 by Celso Providelo
applying review comments, r=bigjools.
103
        return IBinaryPackagePublishingHistory.providedBy(self.context)
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
104
7083.2.1 by Muharem Hrnjadovic
all changes except bug #260013
105
    # The reason we define the map below outside the only function that uses
106
    # it (date_last_changed()) is that this allows us to test whether the map
107
    # covers all PackagePublishingStatus enumeration values.
108
    # The pertinent tests in doc/publishing-pages.txt will fail if we add a
109
    # new value to the PackagePublishingStatus enumeration but do not update
110
    # this map.
111
    timestamp_map = {
112
        PackagePublishingStatus.DELETED: 'dateremoved',
113
        PackagePublishingStatus.OBSOLETE: 'scheduleddeletiondate',
114
        PackagePublishingStatus.PENDING: 'datecreated',
115
        PackagePublishingStatus.PUBLISHED: 'datepublished',
116
        PackagePublishingStatus.SUPERSEDED: 'datesuperseded'
117
    }
118
119
    @property
120
    def date_last_changed(self):
121
        """Return the date of last change considering the publishing status.
122
123
        The date returned is as follows:
124
            * pending        -> datecreated
125
            * published      -> datepublished
126
            * superseded     -> datesuperseded
127
            * deleted        -> dateremoved
128
            * obsolete       -> scheduleddeletiondate
129
        """
130
        accessor = attrgetter(self.timestamp_map[self.context.status])
131
        return accessor(self.context)
132
4687.6.10 by Celso Providelo
applying review comments, r=jml
133
    def wasDeleted(self):
4687.6.11 by Celso Providelo
more review comments.
134
        """Whether or not a publishing record deletion was requested.
4687.6.10 by Celso Providelo
applying review comments, r=jml
135
136
        A publishing record deletion represents the explicit request from a
137
        archive-administrator (self.remove_by) to purge the published contents
4687.6.15 by Celso Providelo
applying review comments, r=spiv.
138
        of this record from the archive for an arbitrary reason
4687.6.10 by Celso Providelo
applying review comments, r=jml
139
        (self.removal_comment).
140
        """
141
        return self.context.status == PackagePublishingStatus.DELETED
142
143
    def wasSuperseded(self):
144
        """Whether or not a publishing record was superseded.
145
146
        'Superseded' means that a new and higher version of this package was
147
        uploaded/built after it was published or the publishing attributes
148
        (section, component, priority/urgency) was modified.
149
        """
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
150
        return self.context.supersededby is not None
151
152
    def isPendingRemoval(self):
4687.6.10 by Celso Providelo
applying review comments, r=jml
153
        """Whether or not a publishing record is marked for removal.
154
155
        This package will be removed from the archive respecting the Soyuz
156
        'death row' quarantine period and the absence of file references in
157
        the target archive.
158
        """
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
159
        return self.context.scheduleddeletiondate is not None
160
161
    def isRemoved(self):
4687.6.10 by Celso Providelo
applying review comments, r=jml
162
        """Whether or not a publishing records was removed from the archive.
163
164
        A publishing record (all files related to it) is removed from the
165
        archive disk once it pass through its quarantine period and it's not
166
        referred by any other archive publishing record.
167
        Archive removal represents the act of having its content purged from
14185.1.5 by William Grant
Fix lint.
168
        archive disk, such situation can be triggered for different
169
        status, each one representing a distinct step in the Soyuz
170
        publishing workflow:
4687.6.10 by Celso Providelo
applying review comments, r=jml
171
14185.1.5 by William Grant
Fix lint.
172
         * SUPERSEDED -> the publication is not necessary since there is
173
           already a newer/higher/modified version available
4687.6.10 by Celso Providelo
applying review comments, r=jml
174
175
         * DELETED -> the publishing was explicitly marked for removal by a
176
           archive-administrator, it's not wanted in the archive.
177
14185.1.5 by William Grant
Fix lint.
178
         * OBSOLETE -> the publication has become obsolete because its
179
           targeted distroseries has become obsolete (not supported by its
180
           developers).
4687.6.10 by Celso Providelo
applying review comments, r=jml
181
        """
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
182
        return self.context.dateremoved is not None
183
5217.6.3 by Celso Providelo
Using mochikit utility to safely connect signals for toggleExpandableTableRow function.
184
    @property
7337.4.1 by Michael Nelson
Fix for UI bug 260013 - adds a truncated reason for deletion to the
185
    def removal_comment(self):
186
        """Return the removal comment or 'None provided'."""
187
        removal_comment = self.context.removal_comment
7337.4.2 by Michael Nelson
Added salgado's changes after review: improving readability of test,
188
        if removal_comment is None or not removal_comment.strip():
7337.4.1 by Michael Nelson
Fix for UI bug 260013 - adds a truncated reason for deletion to the
189
            removal_comment = u'None provided.'
190
191
        return removal_comment
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
192
7434.3.3 by Michael Nelson
Initial commit of all front-end-only changes from larger branch. Still
193
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
194
class SourcePublishingRecordView(BasePublishingRecordView):
195
    """View class for `ISourcePackagePublishingHistory`."""
196
7434.3.3 by Michael Nelson
Initial commit of all front-end-only changes from larger branch. Still
197
    @cachedproperty
198
    def build_status_summary(self):
199
        """Returns a dict with a summary of the build status."""
8137.17.24 by Barry Warsaw
thread merge
200
        return self.context.getStatusSummaryForBuilds()
201
202
    @property
203
    def builds_successful_and_published(self):
204
        """Return whether all builds were successful and published."""
205
        status = self.build_status_summary['status']
206
        return status == BuildSetStatus.FULLYBUILT
207
208
    @property
209
    def builds_successful_and_pending(self):
210
        """Return whether builds were successful but not all published."""
211
        status = self.build_status_summary['status']
212
        return status == BuildSetStatus.FULLYBUILT_PENDING
213
214
    @property
215
    def pending_builds(self):
216
        """Return a list of successful builds pending publication."""
217
        if self.builds_successful_and_pending:
218
            return self.build_status_summary['builds']
219
        else:
220
            return []
8262.2.13 by Michael Nelson
UI changes after chatting more with beuno and cprov.
221
222
    @property
7434.3.3 by Michael Nelson
Initial commit of all front-end-only changes from larger branch. Still
223
    def build_status_img_src(self):
224
        """Return the image path for the current build status summary."""
225
        image_map = {
8478.4.2 by Michael Nelson
Updated to the new build icon 'processing.gif'
226
            BuildSetStatus.BUILDING: '/@@/processing',
7434.5.3 by Michael Nelson
Changes for abel's second review reply
227
            BuildSetStatus.NEEDSBUILD: '/@@/build-needed',
228
            BuildSetStatus.FAILEDTOBUILD: '/@@/no',
8463.1.2 by Michael Nelson
Updated the display of the FULLYBUILT_PENDING status to use the new icon.
229
            BuildSetStatus.FULLYBUILT_PENDING: '/@@/build-success-publishing'
7434.3.3 by Michael Nelson
Initial commit of all front-end-only changes from larger branch. Still
230
            }
231
232
        return image_map.get(self.build_status_summary['status'], '/@@/yes')
233
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
234
    def wasCopied(self):
235
        """Whether or not a source is published in its original location.
236
237
        A source is not in its original location when:
238
239
         * The publishing `Archive` is not the same than where the source
240
            was uploaded. (SSPPH -> SPR -> Archive != SSPPH -> Archive).
241
        Or
242
243
          * The publishing `DistroSeries` is not the same than where the
244
            source was uploaded (SSPPH -> SPR -> DS != SSPPH -> DS).
245
        """
246
        source = self.context.sourcepackagerelease
247
248
        if self.context.archive != source.upload_archive:
249
            return True
250
251
        if self.context.distroseries != source.upload_distroseries:
252
            return True
253
254
        return False
255
5084.5.4 by Christian Robottom Reis
Use a plain property instead of a cachedproperty, define instead in the template.
256
    @property
5461.2.11 by Celso Providelo
applying review comments, r=sinzui.
257
    def allow_selection(self):
258
        """Do not render the checkbox corresponding to this record."""
259
        return False
260
261
    @property
5084.5.3 by Christian Robottom Reis
Further improve the display of PPA package listings: include an expander that lists files under the published packages. Refactor the +queue code to use the same JS. Improve queue listing of packages a bit.
262
    def published_source_and_binary_files(self):
7722.1.1 by Celso Providelo
Applying UI and JS fixes suggested by Maris.
263
        """Return list of dictionaries representing published files."""
264
        files = sorted(
8213.2.1 by Celso Providelo
Using ProxiedLibraryFileAlias to build absolute URLs for source publication files.
265
            (ProxiedLibraryFileAlias(lfa, self.context.archive)
266
             for lfa in self.context.getSourceAndBinaryLibraryFiles()),
7722.1.1 by Celso Providelo
Applying UI and JS fixes suggested by Maris.
267
            key=attrgetter('filename'))
268
        result = []
5084.5.3 by Christian Robottom Reis
Further improve the display of PPA package listings: include an expander that lists files under the published packages. Refactor the +queue code to use the same JS. Improve queue listing of packages a bit.
269
        urls = set()
7722.1.1 by Celso Providelo
Applying UI and JS fixes suggested by Maris.
270
        for library_file in files:
271
            url = library_file.http_url
5099.2.7 by Christian Robottom Reis
Fix for bug #157322, Pending PPA uploads shouldn't have files listed as published. Link to librarian files instead of (404able) archive URLs. Also disable table sorting for that table -- it doesn't work with expanders.
272
            if url in urls:
5084.5.3 by Christian Robottom Reis
Further improve the display of PPA package listings: include an expander that lists files under the published packages. Refactor the +queue code to use the same JS. Improve queue listing of packages a bit.
273
                # Don't print out the same file multiple times. This
274
                # actually happens for arch-all builds, and is
275
                # particularly irritating for PPAs.
276
                continue
5099.2.7 by Christian Robottom Reis
Fix for bug #157322, Pending PPA uploads shouldn't have files listed as published. Link to librarian files instead of (404able) archive URLs. Also disable table sorting for that table -- it doesn't work with expanders.
277
            urls.add(url)
7722.1.1 by Celso Providelo
Applying UI and JS fixes suggested by Maris.
278
279
            custom_dict = {}
280
            custom_dict["url"] = url
281
            custom_dict["filename"] = library_file.filename
282
            custom_dict["filesize"] = library_file.content.filesize
283
            if (library_file.filename.endswith('.deb') or
284
                library_file.filename.endswith('.udeb')):
285
                custom_dict['class'] = 'binary'
286
            else:
287
                custom_dict['class'] = 'source'
288
289
            result.append(custom_dict)
290
291
        return result
5084.5.3 by Christian Robottom Reis
Further improve the display of PPA package listings: include an expander that lists files under the published packages. Refactor the +queue code to use the same JS. Improve queue listing of packages a bit.
292
5622.4.1 by Julian Edwards
Include the changelog and the package descriptions in the PPA source package
293
    @property
8213.2.2 by Celso Providelo
Using ProxiedLibraryFileAlias to build absolute URLs for source publication package_diffs.
294
    def available_diffs(self):
295
        package_diffs = self.context.sourcepackagerelease.package_diffs
296
        return [
297
            ProxiedPackageDiff(package_diff, self.context.archive)
298
            for package_diff in package_diffs]
299
300
    @property
5622.4.1 by Julian Edwards
Include the changelog and the package descriptions in the PPA source package
301
    def built_packages(self):
302
        """Return a list of dictionaries with package names and their summary.
303
304
        For each built package from this published source, return a
305
        dictionary with keys "binarypackagename" and "summary", where
306
        the binarypackagename is unique (i.e. it ignores the same package
307
        published in more than one place/architecture.)
308
        """
309
        results = []
310
        packagenames = set()
311
        for pub in self.context.getPublishedBinaries():
312
            package = pub.binarypackagerelease
313
            packagename = package.binarypackagename.name
314
            if packagename not in packagenames:
315
                entry = {
14185.1.5 by William Grant
Fix lint.
316
                    "binarypackagename": packagename,
317
                    "summary": package.summary,
5622.4.1 by Julian Edwards
Include the changelog and the package descriptions in the PPA source package
318
                    }
319
                results.append(entry)
320
                packagenames.add(packagename)
321
        return results
322
5796.15.5 by Celso Providelo
Users can see 'at a glace' if there we failures while building a source in the PPA page.. Also listing the builds and corresponding icons and links in the expandable area.
323
    @cachedproperty
5796.15.11 by Celso Providelo
applying review comments, r=danilos.
324
    def builds(self):
5796.15.5 by Celso Providelo
Users can see 'at a glace' if there we failures while building a source in the PPA page.. Also listing the builds and corresponding icons and links in the expandable area.
325
        """Return a list of Builds for the context published source."""
326
        return list(self.context.getBuilds())
327
328
    @property
6754.1.1 by Julian Edwards
Don't linkify the source PPA in the publishing details for copied sources if the PPA is not viewable by the user.
329
    def linkify_source_archive(self):
330
        """Return True if the source's upload_archive should be linkified.
7021.2.1 by Celso Providelo
Fixing bug #270957 (displaying PPA changesfiles publicly). This is now possible because changesfiles signatures are stripped in upload-time, so not chance for reply-attacks.
331
6754.1.1 by Julian Edwards
Don't linkify the source PPA in the publishing details for copied sources if the PPA is not viewable by the user.
332
        The source archive is the upload_archive for any source that was
333
        copied.  It should be linkified only if it's a PPA and the user
334
        has permission to view that PPA.
335
        """
336
        archive = self.context.sourcepackagerelease.upload_archive
337
338
        if not archive.is_ppa:
339
            return False
340
341
        return check_permission('launchpad.View', archive)
342
12484.1.1 by Steve Kowalik
Refactor displaying recipe details savagely, and without mercy. Use structured
343
    @property
14185.1.5 by William Grant
Fix lint.
344
    def recipe_build_details(self):
12484.1.1 by Steve Kowalik
Refactor displaying recipe details savagely, and without mercy. Use structured
345
        """Return a linkified string containing details about a
346
        SourcePackageRecipeBuild.
347
        """
348
        sprb = self.context.sourcepackagerelease.source_package_recipe_build
349
        if sprb is not None:
350
            if sprb.recipe is None:
351
                recipe = 'deleted recipe'
352
            else:
353
                recipe = structured(
354
                    'recipe <a href="%s">%s</a>',
355
                    canonical_url(sprb.recipe), sprb.recipe.name)
356
            return structured(
357
                '<a href="%s">Built</a> by %s for <a href="%s">%s</a>',
358
                    canonical_url(sprb), recipe,
359
                    canonical_url(sprb.requester),
360
                    sprb.requester.displayname).escapedtext
361
        return None
362
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
363
5461.2.11 by Celso Providelo
applying review comments, r=sinzui.
364
class SourcePublishingRecordSelectableView(SourcePublishingRecordView):
365
    """View class for a selectable `ISourcePackagePublishingHistory`."""
366
367
    @property
368
    def allow_selection(self):
369
        """Allow the checkbox corresponding to this record to be rendered."""
370
        return True
371
372
4687.6.3 by Celso Providelo
Sorting publishing-record presentation for ubuntu packages.
373
class BinaryPublishingRecordView(BasePublishingRecordView):
374
    """View class for `IBinaryPackagePublishingHistory`."""
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
375
376
    def wasCopied(self):
377
        """Whether or not a binary is published in its original location.
378
379
        A binary is not in its original location when:
380
381
         * The publishing `Archive` is not the same than where the binary
382
           was built. (SBPPH -> BPR -> Build -> Archive != SBPPH -> Archive).
383
        Or
384
385
          * The publishing `DistroArchSeries` is not the same than where
386
            the binary was built (SBPPH -> BPR -> B -> DAS != SBPPH -> DAS).
6149.2.2 by Celso Providelo
Tracing binaries copied from another pocket.
387
388
        Or
389
390
          * The publishing pocket is not the same than where the binary was
391
            built (SBPPH -> BPR -> B -> Pocket != SBPPH -> Pocket).
392
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
393
        """
394
        build = self.context.binarypackagerelease.build
395
396
        if self.context.archive != build.archive:
397
            return True
398
7675.687.103 by Michael Nelson
package-pages-navigation.txt
399
        if self.context.distroarchseries != build.distro_arch_series:
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
400
            return True
401
6149.2.2 by Celso Providelo
Tracing binaries copied from another pocket.
402
        if self.context.pocket != build.pocket:
403
            return True
404
6149.2.1 by Celso Providelo
Fixing bug 219829 (Presenting the original location for copied packages).
405
        return False