~launchpad-pqm/launchpad/devel

14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
7675.455.2 by Michael Hudson
add skeleton files
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
10498.5.44 by Aaron Bentley
More lint fixes.
4
# pylint: disable-msg=F0401,W1001
10498.5.31 by Aaron Bentley
Lint fixes.
5
7675.455.21 by Michael Hudson
final (?) tweaks
6
"""Implementation of the `SourcePackageRecipe` content type."""
7675.455.2 by Michael Hudson
add skeleton files
7
8
__metaclass__ = type
7675.462.1 by Jonathan Lange
The basic bits of a SourcePackageBuild model class.
9
__all__ = [
11151.1.16 by Paul Hummer
Sorted out the __all__ nonsense
10
    'get_buildable_distroseries_set',
7675.462.1 by Jonathan Lange
The basic bits of a SourcePackageBuild model class.
11
    'SourcePackageRecipe',
12
    ]
7675.455.2 by Michael Hudson
add skeleton files
13
12254.1.3 by Steve Kowalik
* findStaleDailyBuilds() now only returns stale recipes that haven't built
14
from datetime import (
15
    datetime,
16
    timedelta,
17
    )
12261.2.7 by Tim Penhey
Merge daily-ajax.
18
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
19
from lazr.delegates import delegates
12254.1.3 by Steve Kowalik
* findStaleDailyBuilds() now only returns stale recipes that haven't built
20
from pytz import utc
21
from storm.expr import (
12254.1.4 by Steve Kowalik
* Add another test case to make certain the query in findStaleDailyBuilds() is
22
    And,
23
    Join,
24
    RightJoin,
12254.1.3 by Steve Kowalik
* findStaleDailyBuilds() now only returns stale recipes that haven't built
25
    )
7675.602.9 by Aaron Bentley
Add build_daily field.
26
from storm.locals import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
27
    Bool,
28
    Desc,
29
    Int,
30
    Reference,
31
    ReferenceSet,
32
    Store,
33
    Storm,
34
    Unicode,
35
    )
10130.12.3 by James Westby
Add a requestBuild method on SourcePackageRecipe.
36
from zope.component import getUtility
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
37
from zope.interface import (
38
    classProvides,
39
    implements,
40
    )
7675.455.3 by Michael Hudson
typing
41
12607.5.3 by Ian Booth
Fix recipe creation
42
from canonical.database.constants import (
43
    DEFAULT,
44
    UTC_NOW,
45
    )
7675.455.3 by Michael Hudson
typing
46
from canonical.database.datetimecol import UtcDateTimeCol
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
47
from canonical.launchpad.interfaces.lpstorm import (
48
    IMasterStore,
49
    IStore,
50
    )
13130.1.12 by Curtis Hovey
Sorted imports.
51
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
11458.1.1 by Jelmer Vernooij
Move enums of buildmaster.
52
from lp.buildmaster.enums import BuildStatus
11121.4.2 by Aaron Bentley
Get all sourcepackagerecipe tests passing.
53
from lp.buildmaster.model.buildfarmjob import BuildFarmJob
54
from lp.buildmaster.model.packagebuild import PackageBuild
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
55
from lp.code.errors import (
56
    BuildAlreadyPending,
57
    BuildNotAllowedForDistro,
58
    TooManyBuilds,
59
    )
10130.12.22 by Michael Nelson
Updated imports.
60
from lp.code.interfaces.sourcepackagerecipe import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
61
    ISourcePackageRecipe,
62
    ISourcePackageRecipeData,
63
    ISourcePackageRecipeSource,
64
    )
10130.12.22 by Michael Nelson
Updated imports.
65
from lp.code.interfaces.sourcepackagerecipebuild import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
66
    ISourcePackageRecipeBuildSource,
67
    )
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
68
from lp.code.model.branch import Branch
10498.3.8 by Aaron Bentley
Get index page looking close to intended display.
69
from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
70
from lp.code.model.sourcepackagerecipedata import SourcePackageRecipeData
11151.1.14 by Paul Hummer
Moved get_buildable_distroseries_set
71
from lp.registry.interfaces.distroseries import IDistroSeriesSet
12561.3.2 by Curtis Hovey
fixes with leonardr.
72
from lp.registry.interfaces.pocket import PackagePublishingPocket
7675.602.4 by Aaron Bentley
Implement many-to-many relationship for distroseries and spr.
73
from lp.registry.model.distroseries import DistroSeries
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
74
from lp.services.database.bulk import (
75
    load_referencing,
76
    load_related,
77
    )
12287.2.1 by Ian Booth
Fix recipe build job query and refactor Storm extensions to services class
78
from lp.services.database.stormexpr import Greatest
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
79
from lp.services.propertycache import (
80
    cachedproperty,
81
    get_property_cache,
82
    )
12981.6.5 by Aaron Bentley
Rollback.
83
from lp.soyuz.interfaces.archive import IArchiveSet
12392.3.1 by Steve Kowalik
Don't return recipe builds that built/are building into a disabled archive,
84
from lp.soyuz.model.archive import Archive
7675.455.3 by Michael Hudson
typing
85
12499.1.5 by Leonard Richardson
Fix bug 728507 by removing calls to export().
86
11151.1.14 by Paul Hummer
Moved get_buildable_distroseries_set
87
def get_buildable_distroseries_set(user):
88
    ppas = getUtility(IArchiveSet).getPPAsForUser(user)
11929.11.16 by Tim Penhey
Make sure ubuntu is in supported distros for the recipe distroseries.
89
    supported_distros = set([ppa.distribution for ppa in ppas])
90
    # Now add in Ubuntu.
91
    supported_distros.add(getUtility(ILaunchpadCelebrities).ubuntu)
11151.1.14 by Paul Hummer
Moved get_buildable_distroseries_set
92
    distros = getUtility(IDistroSeriesSet).search()
93
94
    buildables = []
95
    for distro in distros:
96
        if distro.active and distro.distribution in supported_distros:
97
            buildables.append(distro)
98
    return buildables
99
100
12607.5.1 by Ian Booth
Ensure date_last_modified is updated
101
def recipe_modified(recipe, event):
102
    """Update the date_last_modified property when a recipe is modified.
103
104
    This method is registered as a subscriber to `IObjectModifiedEvent` events
105
    on recipes.
106
    """
107
    recipe.date_last_modified = UTC_NOW
108
109
10130.12.4 by James Westby
Add some permission checking to requestBuild
110
class NonPPABuildRequest(Exception):
10130.12.17 by Michael Nelson
Lint removal.
111
    """A build was requested to a non-PPA and this is currently
112
    unsupported."""
10130.12.4 by James Westby
Add some permission checking to requestBuild
113
114
7675.602.4 by Aaron Bentley
Implement many-to-many relationship for distroseries and spr.
115
class _SourcePackageRecipeDistroSeries(Storm):
116
    """Link table for many-to-many relationship."""
117
118
    __storm_table__ = "SourcePackageRecipeDistroSeries"
119
    id = Int(primary=True)
120
    sourcepackagerecipe_id = Int(name='sourcepackagerecipe', allow_none=False)
10899.2.2 by Aaron Bentley
Implement creating daily builds.
121
    sourcepackage_recipe = Reference(
122
        sourcepackagerecipe_id, 'SourcePackageRecipe.id')
7675.602.4 by Aaron Bentley
Implement many-to-many relationship for distroseries and spr.
123
    distroseries_id = Int(name='distroseries', allow_none=False)
10899.2.2 by Aaron Bentley
Implement creating daily builds.
124
    distroseries = Reference(distroseries_id, 'DistroSeries.id')
7675.602.4 by Aaron Bentley
Implement many-to-many relationship for distroseries and spr.
125
126
7675.455.10 by Michael Hudson
test passes!
127
class SourcePackageRecipe(Storm):
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
128
    """See `ISourcePackageRecipe` and `ISourcePackageRecipeSource`."""
129
7675.455.3 by Michael Hudson
typing
130
    __storm_table__ = 'SourcePackageRecipe'
7675.455.20 by Michael Hudson
mostly docstrings
131
10936.6.2 by Aaron Bentley
Handle over-quota builds
132
    def __str__(self):
133
        return '%s/%s' % (self.owner.name, self.name)
134
10498.3.21 by Aaron Bentley
Unlist unnecessary implements.
135
    implements(ISourcePackageRecipe)
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
136
7675.455.20 by Michael Hudson
mostly docstrings
137
    classProvides(ISourcePackageRecipeSource)
7675.455.3 by Michael Hudson
typing
138
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
139
    delegates(ISourcePackageRecipeData, context='_recipe_data')
140
7675.455.3 by Michael Hudson
typing
141
    id = Int(primary=True)
142
7675.651.5 by Aaron Bentley
Updates from review.
143
    daily_build_archive_id = Int(name='daily_build_archive', allow_none=True)
144
    daily_build_archive = Reference(daily_build_archive_id, 'Archive.id')
7675.651.1 by Aaron Bentley
Add archive to SourcePackageRecipe.
145
7675.455.10 by Michael Hudson
test passes!
146
    date_created = UtcDateTimeCol(notNull=True)
7675.455.3 by Michael Hudson
typing
147
    date_last_modified = UtcDateTimeCol(notNull=True)
148
149
    owner_id = Int(name='owner', allow_none=True)
150
    owner = Reference(owner_id, 'Person.id')
7675.455.10 by Michael Hudson
test passes!
151
7675.455.3 by Michael Hudson
typing
152
    registrant_id = Int(name='registrant', allow_none=True)
153
    registrant = Reference(registrant_id, 'Person.id')
154
7675.602.4 by Aaron Bentley
Implement many-to-many relationship for distroseries and spr.
155
    distroseries = ReferenceSet(
156
        id, _SourcePackageRecipeDistroSeries.sourcepackagerecipe_id,
157
        _SourcePackageRecipeDistroSeries.distroseries_id, DistroSeries.id)
7675.455.10 by Michael Hudson
test passes!
158
7675.602.9 by Aaron Bentley
Add build_daily field.
159
    build_daily = Bool()
160
7675.727.3 by Aaron Bentley
Provide is_stale and manifest on model objects.
161
    is_stale = Bool()
162
10747.1.2 by Aaron Bentley
Ensure members of Recipe are exported.
163
    @property
164
    def _sourcepackagename_text(self):
165
        return self.sourcepackagename.name
166
7675.455.3 by Michael Hudson
typing
167
    name = Unicode(allow_none=True)
7675.1049.1 by Steve Kowalik
Fix SourcePackageRecipe.description to be null.
168
    description = Unicode(allow_none=True)
7675.455.3 by Michael Hudson
typing
169
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
170
    @cachedproperty
7675.455.67 by Michael Hudson
slightly nicer...
171
    def _recipe_data(self):
172
        return Store.of(self).find(
10498.3.10 by Aaron Bentley
Delegate base_branch and deb_version_template via a new Interface.
173
            SourcePackageRecipeData,
174
            SourcePackageRecipeData.sourcepackage_recipe == self).one()
7675.455.13 by Michael Hudson
woop, another test passes
175
11476.1.2 by Aaron Bentley
Use permitted_instructions when parsing.
176
    @property
177
    def builder_recipe(self):
178
        """Accesses of the recipe go to the SourcePackageRecipeData."""
179
        return self._recipe_data.getRecipe()
7675.455.17 by Michael Hudson
dtrt (approximately) on assignment to recipe_text
180
10591.1.2 by William Grant
Add ISPRecipe.base_branch, proxying to SPRD.base_branch.
181
    @property
182
    def base_branch(self):
183
        return self._recipe_data.base_branch
184
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
185
    @staticmethod
186
    def preLoadDataForSourcePackageRecipes(sourcepackagerecipes):
14265.6.3 by Raphael Badin
Add comments, refactor pre feching methods.
187
        # Load the referencing SourcePackageRecipeData.
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
188
        spr_datas = load_referencing(
189
            SourcePackageRecipeData,
190
            sourcepackagerecipes, ['sourcepackage_recipe_id'])
14265.6.3 by Raphael Badin
Add comments, refactor pre feching methods.
191
        # Load the related branches.
192
        load_related(Branch, spr_datas, ['base_branch_id'])
193
        # Store the SourcePackageRecipeData in the sourcepackagerecipes
194
        # objects.
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
195
        for spr_data in spr_datas:
14265.6.3 by Raphael Badin
Add comments, refactor pre feching methods.
196
            cache = get_property_cache(spr_data.sourcepackage_recipe)
14265.6.1 by Raphael Badin
Cache data associated with SourcePackageRecipe.
197
            cache._recipe_data = spr_data
198
        SourcePackageRecipeData.preLoadReferencedBranches(spr_datas)
199
10747.1.2 by Aaron Bentley
Ensure members of Recipe are exported.
200
    def setRecipeText(self, recipe_text):
12499.1.5 by Leonard Richardson
Fix bug 728507 by removing calls to export().
201
        parsed = SourcePackageRecipeData.getParsedRecipe(recipe_text)
202
        self._recipe_data.setRecipe(parsed)
10747.1.2 by Aaron Bentley
Ensure members of Recipe are exported.
203
204
    @property
205
    def recipe_text(self):
12178.1.1 by Tim Penhey
Use the un-validated recipe text for viewing, and initialising the edit view.
206
        return self.builder_recipe.get_recipe_text()
10747.1.2 by Aaron Bentley
Ensure members of Recipe are exported.
207
12547.1.2 by Ian Booth
Add inline distro series editing - first cut
208
    def updateSeries(self, distroseries):
209
        if distroseries != self.distroseries:
210
            self.distroseries.clear()
211
            for distroseries_item in distroseries:
212
                self.distroseries.add(distroseries_item)
213
7675.455.55 by Michael Hudson
fill out some XXXed comments and fix some coding standard thingies
214
    @staticmethod
11476.1.4 by Aaron Bentley
Tests passing
215
    def new(registrant, owner, name, recipe, description,
12607.5.1 by Ian Booth
Ensure date_last_modified is updated
216
            distroseries=None, daily_build_archive=None, build_daily=False,
12607.5.3 by Ian Booth
Fix recipe creation
217
            date_created=DEFAULT):
7675.455.20 by Michael Hudson
mostly docstrings
218
        """See `ISourcePackageRecipeSource.new`."""
7675.455.10 by Michael Hudson
test passes!
219
        store = IMasterStore(SourcePackageRecipe)
7675.455.12 by Michael Hudson
actually parse the recipe
220
        sprecipe = SourcePackageRecipe()
11476.1.4 by Aaron Bentley
Tests passing
221
        builder_recipe = SourcePackageRecipeData.getParsedRecipe(recipe)
222
        SourcePackageRecipeData(builder_recipe, sprecipe)
7675.455.12 by Michael Hudson
actually parse the recipe
223
        sprecipe.registrant = registrant
224
        sprecipe.owner = owner
225
        sprecipe.name = name
10899.2.2 by Aaron Bentley
Implement creating daily builds.
226
        if distroseries is not None:
227
            for distroseries_item in distroseries:
228
                sprecipe.distroseries.add(distroseries_item)
7675.601.4 by Paul Hummer
Added review changes.
229
        sprecipe.description = description
10899.2.2 by Aaron Bentley
Implement creating daily builds.
230
        sprecipe.daily_build_archive = daily_build_archive
10899.2.1 by Aaron Bentley
Start daily builds with idempotent approach.
231
        sprecipe.build_daily = build_daily
12607.5.2 by Ian Booth
Code review fixes
232
        sprecipe.date_created = date_created
233
        sprecipe.date_last_modified = date_created
7675.455.12 by Michael Hudson
actually parse the recipe
234
        store.add(sprecipe)
235
        return sprecipe
10130.12.3 by James Westby
Add a requestBuild method on SourcePackageRecipe.
236
10899.2.1 by Aaron Bentley
Start daily builds with idempotent approach.
237
    @classmethod
10899.2.2 by Aaron Bentley
Implement creating daily builds.
238
    def findStaleDailyBuilds(cls):
12254.1.3 by Steve Kowalik
* findStaleDailyBuilds() now only returns stale recipes that haven't built
239
        one_day_ago = datetime.now(utc) - timedelta(hours=23, minutes=50)
12254.1.5 by Steve Kowalik
* Use archive.default_component, rather than IComponentSet directly.
240
        joins = RightJoin(
241
            Join(
242
                Join(SourcePackageRecipeBuild, PackageBuild,
243
                    PackageBuild.id ==
244
                    SourcePackageRecipeBuild.package_build_id),
245
                BuildFarmJob,
246
                And(BuildFarmJob.id == PackageBuild.build_farm_job_id,
247
                    BuildFarmJob.date_created > one_day_ago)),
248
            SourcePackageRecipe,
12254.1.6 by Steve Kowalik
Also restrict on daily_build_archive, and add a test for that.
249
            And(SourcePackageRecipeBuild.recipe == SourcePackageRecipe.id,
250
                SourcePackageRecipe.daily_build_archive_id ==
12254.1.7 by Steve Kowalik
Correct indenting
251
                    PackageBuild.archive_id))
12254.1.3 by Steve Kowalik
* findStaleDailyBuilds() now only returns stale recipes that haven't built
252
        return IStore(cls).using(joins).find(
253
            cls, cls.is_stale == True, cls.build_daily == True,
12254.1.4 by Steve Kowalik
* Add another test case to make certain the query in findStaleDailyBuilds() is
254
            BuildFarmJob.date_created == None).config(distinct=True)
10899.2.2 by Aaron Bentley
Implement creating daily builds.
255
10931.1.3 by Paul Hummer
Moved the validation code
256
    @staticmethod
257
    def exists(owner, name):
10931.1.4 by Paul Hummer
Fixed typo
258
        """See `ISourcePackageRecipeSource.new`."""
10931.1.3 by Paul Hummer
Moved the validation code
259
        store = IMasterStore(SourcePackageRecipe)
260
        recipe = store.find(
261
            SourcePackageRecipe,
262
            SourcePackageRecipe.owner == owner,
263
            SourcePackageRecipe.name == name).one()
264
        if recipe:
265
            return True
266
        else:
267
            return False
268
10498.5.36 by Aaron Bentley
Deleting branches used in recipes deletes the recipes.
269
    def destroySelf(self):
270
        store = Store.of(self)
271
        self.distroseries.clear()
10498.5.45 by Aaron Bentley
Updates from review.
272
        self._recipe_data.instructions.find().remove()
7675.910.4 by Aaron Bentley
Revert subselect to avoid order_by bug.
273
        builds = store.find(
13228.3.13 by Francis J. Lacoste
Lint blows but hoover sucks
274
            SourcePackageRecipeBuild, SourcePackageRecipeBuild.recipe == self)
7675.910.4 by Aaron Bentley
Revert subselect to avoid order_by bug.
275
        builds.set(recipe_id=None)
10498.5.36 by Aaron Bentley
Deleting branches used in recipes deletes the recipes.
276
        store.remove(self._recipe_data)
277
        store.remove(self)
278
10936.6.2 by Aaron Bentley
Handle over-quota builds
279
    def isOverQuota(self, requester, distroseries):
280
        """See `ISourcePackageRecipe`."""
281
        return SourcePackageRecipeBuild.getRecentBuilds(
282
            requester, self, distroseries).count() >= 5
283
12013.4.2 by Ian Booth
Initial implementation
284
    def requestBuild(self, archive, requester, distroseries,
285
                     pocket=PackagePublishingPocket.RELEASE,
10899.2.10 by Aaron Bentley
Make scoring automatic and give manual builds a higher score.
286
                     manual=False):
10130.12.16 by Michael Nelson
Updated with recommendations from the review of the prerequisite branch.
287
        """See `ISourcePackageRecipe`."""
11411.6.2 by Julian Edwards
Change code imports for ArchivePurpose and ArchiveStatus
288
        if not archive.is_ppa:
10130.12.4 by James Westby
Add some permission checking to requestBuild
289
            raise NonPPABuildRequest
11151.1.5 by Paul Hummer
Using code in the model to validate the distroseries
290
291
        buildable_distros = get_buildable_distroseries_set(archive.owner)
292
        if distroseries not in buildable_distros:
11151.1.6 by Paul Hummer
Added specific instruction
293
            raise BuildNotAllowedForDistro(self, distroseries)
11151.1.5 by Paul Hummer
Using code in the model to validate the distroseries
294
10156.2.8 by Jelmer Vernooij
Remove more references to lp.archiveuploader.permission.
295
        reject_reason = archive.checkUpload(
12254.1.5 by Steve Kowalik
* Use archive.default_component, rather than IComponentSet directly.
296
            requester, self.distroseries, None, archive.default_component,
297
            pocket)
10130.12.4 by James Westby
Add some permission checking to requestBuild
298
        if reject_reason is not None:
10130.12.6 by James Westby
Raise specific errors if the requester can't upload to the archive.
299
            raise reject_reason
10936.6.2 by Aaron Bentley
Handle over-quota builds
300
        if self.isOverQuota(requester, distroseries):
301
            raise TooManyBuilds(self, distroseries)
7675.729.1 by Aaron Bentley
Raise an exception for duplicate builds.
302
        pending = IStore(self).find(SourcePackageRecipeBuild,
303
            SourcePackageRecipeBuild.recipe_id == self.id,
304
            SourcePackageRecipeBuild.distroseries_id == distroseries.id,
11121.4.2 by Aaron Bentley
Get all sourcepackagerecipe tests passing.
305
            PackageBuild.archive_id == archive.id,
306
            PackageBuild.id == SourcePackageRecipeBuild.package_build_id,
307
            BuildFarmJob.id == PackageBuild.build_farm_job_id,
308
            BuildFarmJob.status == BuildStatus.NEEDSBUILD)
7675.729.1 by Aaron Bentley
Raise an exception for duplicate builds.
309
        if pending.any() is not None:
310
            raise BuildAlreadyPending(self, distroseries)
10130.12.16 by Michael Nelson
Updated with recommendations from the review of the prerequisite branch.
311
7675.700.1 by Paul Hummer
Removed sourcepackagename from ISourcePackageRecipe and ISourcePackageRecipeBuild
312
        build = getUtility(ISourcePackageRecipeBuildSource).new(distroseries,
10130.12.16 by Michael Nelson
Updated with recommendations from the review of the prerequisite branch.
313
            self, requester, archive)
11121.4.2 by Aaron Bentley
Get all sourcepackagerecipe tests passing.
314
        build.queueBuild()
11243.3.1 by Aaron Bentley
Update scoring of recipe builds.
315
        queue_record = build.buildqueue_record
10899.2.10 by Aaron Bentley
Make scoring automatic and give manual builds a higher score.
316
        if manual:
11435.7.2 by Ian Booth
Do not mess with manual build increments, just increase the base score for build recipes
317
            queue_record.manualScore(queue_record.lastscore + 100)
10130.12.3 by James Westby
Add a requestBuild method on SourcePackageRecipe.
318
        return build
10498.3.8 by Aaron Bentley
Get index page looking close to intended display.
319
12378.2.6 by Ian Booth
Add tests and cleanup javascript
320
    def performDailyBuild(self):
321
        """See `ISourcePackageRecipe`."""
12378.2.11 by Ian Booth
Code review changes plus add new build notification for non-ajax version
322
        builds = []
12378.2.6 by Ian Booth
Add tests and cleanup javascript
323
        self.is_stale = False
12981.6.5 by Aaron Bentley
Rollback.
324
        for distroseries in self.distroseries:
325
            try:
326
                build = self.requestBuild(
327
                    self.daily_build_archive, self.owner,
328
                    distroseries, PackagePublishingPocket.RELEASE)
329
                builds.append(build)
330
            except BuildAlreadyPending:
331
                continue
332
        return builds
12378.2.6 by Ian Booth
Add tests and cleanup javascript
333
12397.2.8 by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors
334
    @property
335
    def builds(self):
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
336
        """See `ISourcePackageRecipe`."""
12397.2.7 by Ian Booth
Test fixes
337
        order_by = (Desc(Greatest(
338
                            BuildFarmJob.date_started,
339
                            BuildFarmJob.date_finished)),
340
                   Desc(BuildFarmJob.date_created), Desc(BuildFarmJob.id))
12397.2.4 by Ian Booth
Rework getBuild apis
341
        return self._getBuilds(None, order_by)
342
12397.2.8 by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors
343
    @property
344
    def completed_builds(self):
12397.2.4 by Ian Booth
Rework getBuild apis
345
        """See `ISourcePackageRecipe`."""
346
        filter_term = BuildFarmJob.status != BuildStatus.NEEDSBUILD
12397.2.7 by Ian Booth
Test fixes
347
        order_by = (Desc(Greatest(
348
                            BuildFarmJob.date_started,
349
                            BuildFarmJob.date_finished)),
350
                   Desc(BuildFarmJob.id))
12397.2.4 by Ian Booth
Rework getBuild apis
351
        return self._getBuilds(filter_term, order_by)
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
352
12397.2.8 by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors
353
    @property
354
    def pending_builds(self):
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
355
        """See `ISourcePackageRecipe`."""
12397.2.4 by Ian Booth
Rework getBuild apis
356
        filter_term = BuildFarmJob.status == BuildStatus.NEEDSBUILD
12397.2.10 by Ian Booth
Improve webservice descriptions and adjust sort order
357
        # We want to order by date_created but this is the same as ordering
358
        # by id (since id increases monotonically) and is less expensive.
359
        order_by = Desc(BuildFarmJob.id)
12397.2.4 by Ian Booth
Rework getBuild apis
360
        return self._getBuilds(filter_term, order_by)
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
361
12397.2.4 by Ian Booth
Rework getBuild apis
362
    def _getBuilds(self, filter_term, order_by):
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
363
        """The actual query to get the builds."""
12397.2.4 by Ian Booth
Rework getBuild apis
364
        query_args = [
13228.3.13 by Francis J. Lacoste
Lint blows but hoover sucks
365
            SourcePackageRecipeBuild.recipe == self,
7675.910.4 by Aaron Bentley
Revert subselect to avoid order_by bug.
366
            SourcePackageRecipeBuild.package_build_id == PackageBuild.id,
367
            PackageBuild.build_farm_job_id == BuildFarmJob.id,
12392.3.1 by Steve Kowalik
Don't return recipe builds that built/are building into a disabled archive,
368
            And(PackageBuild.archive_id == Archive.id,
369
                Archive._enabled == True),
12397.2.4 by Ian Booth
Rework getBuild apis
370
            ]
371
        if filter_term is not None:
372
            query_args.append(filter_term)
373
        result = Store.of(self).find(SourcePackageRecipeBuild, *query_args)
12178.2.1 by Tim Penhey
Separate out the pending builds to a different method, and add a test.
374
        result.order_by(order_by)
10498.3.22 by Aaron Bentley
Restrict the number of old builds shown.
375
        return result
10875.1.1 by Aaron Bentley
Provide a reasonable implementation of estimateDuration.
376
12577.1.1 by Ian Booth
Fix pending build handling in popup form
377
    def getPendingBuildInfo(self):
378
        """See `ISourcePackageRecipe`."""
379
        builds = self.pending_builds
380
        result = []
381
        for build in builds:
382
            result.append(
383
                {"distroseries": build.distroseries.displayname,
12981.6.5 by Aaron Bentley
Rollback.
384
                 "archive": '%s/%s' %
385
                           (build.archive.owner.name, build.archive.name)})
12577.1.1 by Ian Booth
Fix pending build handling in popup form
386
        return result
387
12397.2.8 by Ian Booth
Change from using getter methods to properties for exported recipe and build accessors
388
    @property
389
    def last_build(self):
10875.2.1 by Aaron Bentley
Use last build archive as initial value
390
        """See `ISourcePackageRecipeBuild`."""
12254.1.1 by Steve Kowalik
* Bump the component we search for recipes to main, multiverse has no meaning
391
        return self._getBuilds(
392
            True, Desc(BuildFarmJob.date_finished)).first()
10875.2.1 by Aaron Bentley
Use last build archive as initial value
393
10875.1.1 by Aaron Bentley
Provide a reasonable implementation of estimateDuration.
394
    def getMedianBuildDuration(self):
395
        """Return the median duration of builds of this recipe."""
396
        store = IStore(self)
397
        result = store.find(
11121.4.2 by Aaron Bentley
Get all sourcepackagerecipe tests passing.
398
            BuildFarmJob,
399
            SourcePackageRecipeBuild.recipe == self.id,
400
            BuildFarmJob.date_finished != None,
401
            BuildFarmJob.id == PackageBuild.build_farm_job_id,
402
            SourcePackageRecipeBuild.package_build_id == PackageBuild.id)
403
        durations = [build.date_finished - build.date_started for build in
404
                     result]
405
        if len(durations) == 0:
10875.1.1 by Aaron Bentley
Provide a reasonable implementation of estimateDuration.
406
            return None
11121.4.2 by Aaron Bentley
Get all sourcepackagerecipe tests passing.
407
        durations.sort(reverse=True)
408
        return durations[len(durations) / 2]