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