1 # vim:ts=2:sw=2:et:ai:sts=2
2 # Copyright 2011, Ansgar Burchardt <ansgar@debian.org>
3 #
4 # Permission to use, copy, modify, and/or distribute this software for any
5 # purpose with or without fee is hereby granted, provided that the above
6 # copyright notice and this permission notice appear in all copies.
7 #
8 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 from pet.models import *
18 import apt_pkg
19 import sqlalchemy
20 import sqlalchemy.orm
22 class ClassifiedPackage(object):
23 def __init__(self, named_tree, bugs, suite_packages, tags):
24 self.named_tree = named_tree
25 self.bugs = bugs
26 self.suite_packages = suite_packages
27 self.tags = tags
28 self.watch = self.named_tree.watch_result
29 name = property(lambda self: self.named_tree.package.name)
30 source = property(lambda self: self.named_tree.source)
31 version = property(lambda self: self.named_tree.version)
32 distribution = property(lambda self: self.named_tree.distribution)
33 last_changed_by = property(lambda self: self.named_tree.last_changed_by)
34 last_changed = property(lambda self: self.named_tree.last_changed)
35 todo = property(lambda self: self.named_tree.todo)
37 @property
38 def has_rc_bugs(self):
39 for b in self.bugs:
40 if b.severity in ('serious', 'grave', 'critical'):
41 return True
42 return False
44 @property
45 def ready_for_upload(self):
46 highest_tag = self.highest_tag
47 if self.distribution is not None and self.distribution != 'UNRELEASED' and not self.is_tagged and not self.is_in_archive:
48 return True
49 return False
51 @property
52 def is_tagged(self):
53 for t in self.tags:
54 if t.version == self.version:
55 return True
56 return False
58 @property
59 def missing_tag(self):
60 if self.is_in_archive and not self.is_tagged:
61 return True
62 return False
64 @property
65 def is_in_archive(self):
66 for sp in self.suite_packages:
67 if sp.version == self.version:
68 return True
69 return False
71 @property
72 def highest_tag(self):
73 try:
74 return self.tags[0]
75 except IndexError:
76 return None
78 @property
79 def highest_archive(self):
80 try:
81 return self.suite_packages[0]
82 except IndexError:
83 return None
85 @property
86 def todo_bugs(self):
87 for b in self.bugs:
88 if not b.forwarded and 'fixed-upstream' not in b.tags and 'pending' not in b.tags and 'wontfix' not in b.tags and 'moreinfo' not in b.tags:
89 return True
90 return False
92 @property
93 def newer_upstream(self):
94 if self.watch and self.watch.upstream_version:
95 return apt_pkg.version_compare(self.watch.upstream_version,
96 self.watch.debian_version) > 0
97 return False
99 @property
100 def watch_problem(self):
101 if self.watch:
102 if self.watch.error is not None:
103 return True
104 if apt_pkg.version_compare(self.watch.upstream_version,
105 self.watch.debian_version) < 0:
106 return True
107 return False
109 class Classifier(object):
110 def __init__(self, session, named_trees, suite_condition, bug_tracker_condition):
111 self.session = session
112 sorted_named_trees = named_trees.join(NamedTree.package) \
113 .options(sqlalchemy.orm.joinedload(NamedTree.watch_result)) \
114 .order_by(Package.name, Package.repository_id, Package.id)
116 bug_sources = session.query(BugSource) \
117 .join(BugSource.bug).join((NamedTree, BugSource.source == NamedTree.source)) \
118 .filter(NamedTree.id.in_(named_trees.from_self(NamedTree.id).subquery())) \
119 .filter(bug_tracker_condition) \
120 .order_by(Bug.severity.desc(), Bug.bug_number) \
121 .filter(Bug.done == False) \
122 .options(sqlalchemy.orm.joinedload(BugSource.bug))
123 bugs = {}
124 for bs in bug_sources:
125 bugs.setdefault(bs.source, []).append(bs.bug)
127 suite_packages_query = session.query(SuitePackage).join(SuitePackage.suite) \
128 .join((NamedTree, SuitePackage.source == NamedTree.source)) \
129 .filter(NamedTree.id.in_(named_trees.from_self(NamedTree.id).subquery())) \
130 .filter(suite_condition) \
131 .order_by(SuitePackage.version.desc()) \
132 .options(sqlalchemy.orm.joinedload(SuitePackage.suite))
133 suite_packages = {}
134 for sp in suite_packages_query:
135 suite_packages.setdefault(sp.source, []).append(sp)
137 Tags = sqlalchemy.orm.aliased(NamedTree)
138 Reference = sqlalchemy.orm.aliased(NamedTree)
139 max_version = session.query(sqlalchemy.func.max(Reference.version)) \
140 .filter(Reference.type == 'tag') \
141 .filter(Reference.package_id == Tags.package_id) \
142 .correlate(Tags) \
143 .as_scalar()
145 tags_query = session.query(Tags) \
146 .filter(Tags.type == 'tag') \
147 .filter(Tags.version == max_version) \
148 .order_by(Tags.package_id, Tags.version.desc()) \
149 .filter(Tags.package_id.in_(named_trees.from_self(NamedTree.package_id).subquery()))
150 tags = {}
151 for t in tags_query:
152 tags.setdefault(t.package_id, []).append(t)
154 self.packages = []
155 for nt in sorted_named_trees:
156 self.packages.append(ClassifiedPackage(nt, bugs.get(nt.source, []), suite_packages.get(nt.source, []), tags.get(nt.package_id, [])))
158 def classify(self):
159 classified = dict()
160 for p in self.packages:
161 if p.ready_for_upload:
162 cls = 'ready_for_upload'
163 elif p.has_rc_bugs:
164 cls = 'rc_bugs'
165 elif p.missing_tag:
166 cls = 'missing_tag'
167 elif not p.tags:
168 cls = 'new'
169 elif p.newer_upstream:
170 cls = 'new_upstream'
171 elif p.watch_problem:
172 cls = 'watch_problem'
173 elif p.bugs:
174 cls = 'bugs'
175 elif not p.is_tagged:
176 cls = 'wip'
177 else:
178 cls = 'other'
179 classified.setdefault(cls, []).append(p)
180 return classified
181 def classes(self):
182 return [
183 { 'name': "Ready For Upload", 'key': 'ready_for_upload' },
184 { 'name': "Packages with RC bugs", 'key': 'rc_bugs' },
185 { 'name': "Missing tags", 'key': 'missing_tag' },
186 { 'name': "Newer upstream version", 'key': 'new_upstream' },
187 { 'name': 'Problems with debian/watch', 'key': 'watch_problem' },
188 { 'name': 'New packages', 'key': 'new' },
189 { 'name': 'With bugs', 'key': 'bugs' },
190 { 'name': 'Work in progress', 'key': 'wip' },
191 #{ 'name': "Other packages", 'key': 'other' },
192 ]
