14538.2.49
by Curtis Hovey
Updated copyright. |
1 |
# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
|
11887.1.2
by Ian Booth
Initial coding |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
4 |
# pylint: disable-msg=E0611,W0212
|
|
5 |
||
6 |
__metaclass__ = type |
|
7 |
||
8 |
__all__ = [ |
|
9 |
'RecipeBuildRecord', |
|
10 |
'RecipeBuildRecordSet', |
|
11 |
]
|
|
12 |
||
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
13 |
from collections import namedtuple |
11887.3.10
by Ian Booth
Style guide fixes |
14 |
from datetime import ( |
15 |
datetime, |
|
16 |
timedelta, |
|
17 |
)
|
|
11887.1.2
by Ian Booth
Initial coding |
18 |
|
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
19 |
import pytz |
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
20 |
from storm import Undef |
11887.1.2
by Ian Booth
Initial coding |
21 |
from storm.expr import ( |
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
22 |
Desc, |
11887.1.2
by Ian Booth
Initial coding |
23 |
Join, |
24 |
Max, |
|
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
25 |
Select, |
26 |
)
|
|
11887.1.2
by Ian Booth
Initial coding |
27 |
from zope.interface import implements |
28 |
||
29 |
from lp.buildmaster.enums import BuildStatus |
|
30 |
from lp.buildmaster.model.buildfarmjob import BuildFarmJob |
|
31 |
from lp.buildmaster.model.packagebuild import PackageBuild |
|
11887.1.5
by Ian Booth
Next round |
32 |
from lp.code.interfaces.recipebuild import IRecipeBuildRecordSet |
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
33 |
from lp.code.model.sourcepackagerecipe import SourcePackageRecipe |
11887.1.2
by Ian Booth
Initial coding |
34 |
from lp.code.model.sourcepackagerecipebuild import SourcePackageRecipeBuild |
35 |
from lp.registry.model.person import Person |
|
36 |
from lp.registry.model.sourcepackagename import SourcePackageName |
|
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
37 |
from lp.services.database.decoratedresultset import DecoratedResultSet |
14575.1.1
by Jeroen Vermeulen
Lint. |
38 |
from lp.services.database.lpstorm import ISlaveStore |
14550.1.1
by Steve Kowalik
Run format-imports over lib/lp and lib/canonical/launchpad |
39 |
from lp.services.database.stormexpr import CountDistinct |
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
40 |
from lp.services.webapp.publisher import canonical_url |
11887.1.2
by Ian Booth
Initial coding |
41 |
from lp.soyuz.model.archive import Archive |
42 |
from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild |
|
43 |
from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease |
|
44 |
||
45 |
||
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
46 |
class RecipeBuildRecord(namedtuple( |
47 |
'RecipeBuildRecord', |
|
12060.5.1
by Ian Booth
Initial coding |
48 |
"""sourcepackagename, recipeowner, archive, recipe,
|
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
49 |
most_recent_build_time""")): |
12510.3.5
by Ian Booth
Add check to ensure recipe is not none when creating RecipeBuildRecord |
50 |
|
12510.3.6
by Ian Booth
Make __new__ args more explicit |
51 |
def __new__(cls, sourcepackagename, recipeowner, archive, recipe, |
52 |
most_recent_build_time): |
|
12510.3.5
by Ian Booth
Add check to ensure recipe is not none when creating RecipeBuildRecord |
53 |
# Ensure that a valid (not None) recipe is used. This may change in
|
54 |
# future if we support build records with no recipe.
|
|
55 |
assert recipe is not None, "RecipeBuildRecord requires a recipe." |
|
12510.3.6
by Ian Booth
Make __new__ args more explicit |
56 |
self = super(RecipeBuildRecord, cls).__new__( |
57 |
cls, sourcepackagename, recipeowner, archive, recipe, |
|
58 |
most_recent_build_time) |
|
12510.3.5
by Ian Booth
Add check to ensure recipe is not none when creating RecipeBuildRecord |
59 |
return self |
60 |
||
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
61 |
# We need to implement our own equality check since __eq__ is broken on
|
62 |
# SourcePackageRecipe. It's broken there because __eq__ is broken,
|
|
63 |
# or not supported, on storm's ReferenceSet implementation.
|
|
64 |
def __eq__(self, other): |
|
65 |
return (self.sourcepackagename == other.sourcepackagename |
|
66 |
and self.recipeowner == other.recipeowner |
|
12060.5.1
by Ian Booth
Initial coding |
67 |
and self.recipe.name == other.recipe.name |
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
68 |
and self.archive == other.archive |
11887.1.10
by Ian Booth
Lint fixes |
69 |
and self.most_recent_build_time == other.most_recent_build_time) |
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
70 |
|
71 |
def __hash__(self): |
|
72 |
return ( |
|
73 |
hash(self.sourcepackagename.name) ^ |
|
74 |
hash(self.recipeowner.name) ^ |
|
12060.5.1
by Ian Booth
Initial coding |
75 |
hash(self.recipe.name) ^ |
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
76 |
hash(self.archive.name) ^ |
11887.1.10
by Ian Booth
Lint fixes |
77 |
hash(self.most_recent_build_time)) |
11887.1.2
by Ian Booth
Initial coding |
78 |
|
11887.1.18
by Ian Booth
Fixes as per code review |
79 |
@property
|
80 |
def distro_source_package(self): |
|
12060.5.1
by Ian Booth
Initial coding |
81 |
return self.archive.distribution.getSourcePackage( |
11887.1.18
by Ian Booth
Fixes as per code review |
82 |
self.sourcepackagename) |
83 |
||
12510.3.1
by Ian Booth
More performance enhancements |
84 |
@property
|
85 |
def recipe_name(self): |
|
86 |
return self.recipe.name |
|
87 |
||
88 |
@property
|
|
89 |
def recipe_url(self): |
|
90 |
return canonical_url(self.recipe, rootsite='code') |
|
91 |
||
11887.1.2
by Ian Booth
Initial coding |
92 |
|
93 |
class RecipeBuildRecordSet: |
|
94 |
"""See `IRecipeBuildRecordSet`."""
|
|
95 |
||
96 |
implements(IRecipeBuildRecordSet) |
|
97 |
||
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
98 |
def findCompletedDailyBuilds(self, epoch_days=30): |
11887.1.2
by Ian Booth
Initial coding |
99 |
"""See `IRecipeBuildRecordSet`."""
|
100 |
||
11887.1.18
by Ian Booth
Fixes as per code review |
101 |
store = ISlaveStore(SourcePackageRecipe) |
11887.1.2
by Ian Booth
Initial coding |
102 |
tables = [ |
103 |
SourcePackageRecipe, |
|
104 |
Join(SourcePackageRecipeBuild, |
|
105 |
SourcePackageRecipeBuild.recipe_id == |
|
106 |
SourcePackageRecipe.id), |
|
107 |
Join(SourcePackageRelease, |
|
108 |
SourcePackageRecipeBuild.id == |
|
109 |
SourcePackageRelease.source_package_recipe_build_id), |
|
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
110 |
Join(SourcePackageName, |
111 |
SourcePackageRelease.sourcepackagename == |
|
112 |
SourcePackageName.id), |
|
11887.1.2
by Ian Booth
Initial coding |
113 |
Join(BinaryPackageBuild, |
114 |
BinaryPackageBuild.source_package_release_id == |
|
115 |
SourcePackageRelease.id), |
|
116 |
Join(PackageBuild, |
|
117 |
PackageBuild.id == |
|
118 |
BinaryPackageBuild.package_build_id), |
|
119 |
Join(BuildFarmJob, |
|
120 |
BuildFarmJob.id == |
|
121 |
PackageBuild.build_farm_job_id), |
|
122 |
]
|
|
123 |
||
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
124 |
where = [BuildFarmJob.status == BuildStatus.FULLYBUILT, |
125 |
SourcePackageRecipe.build_daily] |
|
126 |
if epoch_days is not None: |
|
127 |
epoch = datetime.now(pytz.UTC) - timedelta(days=epoch_days) |
|
128 |
where.append(BuildFarmJob.date_finished >= epoch) |
|
11887.1.5
by Ian Booth
Next round |
129 |
|
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
130 |
# We include SourcePackageName directly in the query instead of just
|
131 |
# selecting its id and fetching the objects later. This is because
|
|
132 |
# SourcePackageName only has an id and and a name and we use the name
|
|
133 |
# for the order by so there's no benefit in introducing another query
|
|
134 |
# for eager fetching later. If SourcePackageName gets new attributes,
|
|
135 |
# this can be re-evaluated.
|
|
11887.1.4
by Ian Booth
More coding |
136 |
result_set = store.using(*tables).find( |
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
137 |
(SourcePackageRecipe.id, |
138 |
SourcePackageName, |
|
11887.1.4
by Ian Booth
More coding |
139 |
Max(BuildFarmJob.date_finished), |
140 |
),
|
|
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
141 |
*where |
11887.1.4
by Ian Booth
More coding |
142 |
).group_by( |
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
143 |
SourcePackageRecipe.id, |
144 |
SourcePackageName, |
|
11887.1.4
by Ian Booth
More coding |
145 |
).order_by( |
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
146 |
SourcePackageName.name, |
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
147 |
Desc(Max(BuildFarmJob.date_finished)), |
148 |
)
|
|
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
149 |
|
150 |
def _makeRecipeBuildRecord(values): |
|
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
151 |
(recipe_id, sourcepackagename, date_finished) = values |
152 |
recipe = store.get(SourcePackageRecipe, recipe_id) |
|
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
153 |
return RecipeBuildRecord( |
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
154 |
sourcepackagename, recipe.owner, |
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
155 |
recipe.daily_build_archive, recipe, |
11887.1.9
by Ian Booth
Fix source package name lins in view, improve tests |
156 |
date_finished) |
157 |
||
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
158 |
to_recipes_ids = lambda rows: [row[0] for row in rows] |
159 |
||
160 |
def eager_load_recipes(recipe_ids): |
|
161 |
if not recipe_ids: |
|
162 |
return [] |
|
163 |
return list(store.find( |
|
164 |
SourcePackageRecipe, |
|
165 |
SourcePackageRecipe.id.is_in(recipe_ids))) |
|
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
166 |
|
167 |
def eager_load_owners(recipes): |
|
168 |
owner_ids = set(recipe.owner_id for recipe in recipes) |
|
169 |
owner_ids.discard(None) |
|
170 |
if not owner_ids: |
|
171 |
return
|
|
172 |
list(store.find(Person, Person.id.is_in(owner_ids))) |
|
173 |
||
174 |
def eager_load_archives(recipes): |
|
175 |
archive_ids = set( |
|
176 |
recipe.daily_build_archive_id for recipe in recipes) |
|
177 |
archive_ids.discard(None) |
|
178 |
if not archive_ids: |
|
179 |
return
|
|
180 |
list(store.find(Archive, Archive.id.is_in(archive_ids))) |
|
181 |
||
182 |
def _prefetchRecipeBuildData(rows): |
|
12425.2.1
by Ian Booth
Rework record sort order and add some tests |
183 |
recipe_ids = set(to_recipes_ids(rows)) |
184 |
recipe_ids.discard(None) |
|
185 |
recipes = eager_load_recipes(recipe_ids) |
|
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
186 |
eager_load_owners(recipes) |
187 |
eager_load_archives(recipes) |
|
188 |
||
11887.1.4
by Ian Booth
More coding |
189 |
return RecipeBuildRecordResultSet( |
12425.1.2
by Ian Booth
Quick stab at improving daily recipe builds query |
190 |
result_set, _makeRecipeBuildRecord, |
191 |
pre_iter_hook=_prefetchRecipeBuildData) |
|
11887.1.4
by Ian Booth
More coding |
192 |
|
11887.1.5
by Ian Booth
Next round |
193 |
|
11887.1.4
by Ian Booth
More coding |
194 |
class RecipeBuildRecordResultSet(DecoratedResultSet): |
11887.1.18
by Ian Booth
Fixes as per code review |
195 |
"""A ResultSet which can count() queries with group by."""
|
11887.1.2
by Ian Booth
Initial coding |
196 |
|
197 |
def count(self, expr=Undef, distinct=True): |
|
11887.1.4
by Ian Booth
More coding |
198 |
"""This count() knows how to handle result sets with group by."""
|
199 |
||
11887.1.2
by Ian Booth
Initial coding |
200 |
# We don't support distinct=False for this result set
|
201 |
select = Select( |
|
11887.1.8
by Ian Booth
Improve count() for result set |
202 |
columns=CountDistinct(self.result_set._group_by), |
14575.1.1
by Jeroen Vermeulen
Lint. |
203 |
tables=self.result_set._tables, |
204 |
where=self.result_set._where, |
|
11887.1.2
by Ian Booth
Initial coding |
205 |
)
|
11887.1.4
by Ian Booth
More coding |
206 |
result = self.result_set._store.execute(select) |
11887.1.2
by Ian Booth
Initial coding |
207 |
return result.get_one()[0] |