Merge lp:~zulcss/ubuntu-reports/server-sru-reports into lp:ubuntu-reports
- server-sru-reports
- Merge into trunk
Proposed by
Chuck Short
Status: | Needs review |
---|---|
Proposed branch: | lp:~zulcss/ubuntu-reports/server-sru-reports |
Merge into: | lp:ubuntu-reports |
Diff against target: |
2199 lines (+2169/-0) 6 files modified
server/sru/accepted-nominations.py (+93/-0) server/sru/assigned-report.py (+192/-0) server/sru/collect.py (+146/-0) server/sru/launchpad.css (+1174/-0) server/sru/sru-report-series.py (+285/-0) server/sru/sru-report.py (+279/-0) |
To merge this branch: | bzr merge lp:~zulcss/ubuntu-reports/server-sru-reports |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Reports Dev Team | Pending | ||
Review via email: mp+108029@code.launchpad.net |
Commit message
Description of the change
Contains python scripts used to keep track of server SRU process.
To post a comment you must log in.
Revision history for this message
Brian Murray (brian-murray) wrote : | # |
This also could use some fixing:
ubuntu_releases = ["lucid", "karmic", "jaunty", "hardy", "dapper"]
to build a dynamic list of ubuntu releases you can do something like the following:
ubuntu = launchpad.
for series in ubuntu.series:
Unmerged revisions
- 92. By Chuck Short
-
Add server sru tracker reports
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'server/sru' |
2 | === added file 'server/sru/accepted-nominations.py' |
3 | --- server/sru/accepted-nominations.py 1970-01-01 00:00:00 +0000 |
4 | +++ server/sru/accepted-nominations.py 2012-05-30 18:11:22 +0000 |
5 | @@ -0,0 +1,93 @@ |
6 | +#!/usr/bin/python |
7 | + |
8 | +import urllib |
9 | +import re |
10 | +import datetime, time |
11 | + |
12 | +from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT |
13 | + |
14 | +def get_package_list(lp, pkgs): |
15 | + p = re.compile('>([^\s]+)\sin\subuntu') |
16 | + team_name = 'ubuntu-server' |
17 | + team = lp.people[team_name] |
18 | + (f_name, h) = urllib.urlretrieve(\ |
19 | + "https://bugs.launchpad.net/~%s/+packagebugs" % team.name) |
20 | + |
21 | + for l in open(f_name): |
22 | + m = p.search(l) |
23 | + if m: |
24 | + pkgs.append(m.group(1)) |
25 | + |
26 | + return pkgs |
27 | + |
28 | +def get_nominations(lp): |
29 | + ubuntu_releases = ["lucid", "karmic", "jaunty", "hardy", "dapper"] |
30 | + pkgs = [] |
31 | + |
32 | + # time to get ill |
33 | + start_date = datetime.date.today() - datetime.timedelta(7) |
34 | + end_date = datetime.date.today() |
35 | + |
36 | + |
37 | + ubuntu = lp.distributions['ubuntu'] |
38 | + |
39 | + pkgs = get_package_list(lp, pkgs) |
40 | + for i in ubuntu_releases: |
41 | + for k in pkgs: |
42 | + releases = ubuntu.getSeries(name_or_version=i) |
43 | + package = releases.getSourcePackage(name=k) |
44 | + bugs = package.searchTasks(omit_targeted=False) |
45 | + |
46 | + for bug_task in bugs: |
47 | + id = bug_task.bug.id |
48 | + |
49 | + bug = lp.bugs[id] |
50 | + nominations = bug.getNominations() |
51 | + |
52 | + for nom in nominations: |
53 | + date_nominated = nom.date_decided |
54 | + date = date_nominated.split("T")[0] |
55 | + if nom.date_decided \ |
56 | + and nom.date_decided.date() >= start_date: |
57 | + url_parts = nom.decider_link.split('/') |
58 | + nominator = url_parts[-1].lstrip('~') |
59 | + |
60 | + print "<tr>" |
61 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>" %(id, id) |
62 | + print "<td>%s</td>" %k |
63 | + print "<td>%s</td>" %i |
64 | + print "<td>%s</td>" %bug.title |
65 | + print "<td>%s</td>" %date |
66 | + print "<td>%s</td>" %nominator |
67 | + print "</tr>" |
68 | + |
69 | +if __name__ == '__main__': |
70 | + lp = Launchpad.login_with('server-accepted-nominations', service_root=EDGE_SERVICE_ROOT) |
71 | + |
72 | + print ''' |
73 | +<html> |
74 | +<head> |
75 | + <style type='text/css' media='screen'>@import url(launchpad.css);</style> |
76 | + <script type='text/javascript' src='sorttable.js'></script> |
77 | + <title> Server SRU Tracker </title> |
78 | +</head> |
79 | +<body> |
80 | +''' |
81 | + print "<b>Nominated bugs for week of %s</b>" %(datetime.datetime.now()) |
82 | + |
83 | + print ''' |
84 | +<table border='1' id='icons' class='sortable'> |
85 | +<th><b>Bug</b></th> |
86 | +<th><b>Source Package</b></th> |
87 | +<th><b>Release</b></th> |
88 | +<th><b>Title</b></th> |
89 | +<th><b>Date Nominated</b></th> |
90 | +<th><b>Nominated by</b></th> |
91 | +''' |
92 | + get_nominations(lp) |
93 | + |
94 | + print ''' |
95 | +</table> |
96 | +</body> |
97 | +</html> |
98 | +''' |
99 | |
100 | === added file 'server/sru/assigned-report.py' |
101 | --- server/sru/assigned-report.py 1970-01-01 00:00:00 +0000 |
102 | +++ server/sru/assigned-report.py 2012-05-30 18:11:22 +0000 |
103 | @@ -0,0 +1,192 @@ |
104 | +#!/usr/bin/python |
105 | + |
106 | +import sqlite3 as dbapi2 |
107 | +import optparse |
108 | +import datetime, time |
109 | + |
110 | +def get_db(dbpath): |
111 | + db = dbapi2.connect(dbpath) |
112 | + cur = db.cursor() |
113 | + return db |
114 | + |
115 | +def get_query(db): |
116 | + cursor = db.cursor() |
117 | + cursor.execute(' select assignee,bug_id,package,release,title,verification_status,upload_status, bug_status, date_assigned, date_uploaded, verification_needed, verification_done from sru_tracker where assignee in (select distinct assignee from sru_tracker where assignee != "None") order by assignee') |
118 | + return cursor |
119 | + |
120 | +def processAssignedInformation(date_assigned): |
121 | + assigned_age = calculateDays(str(date_assigned)) |
122 | + if assigned_age == 0: |
123 | + new_assignee = calculateHours(str(date_assigned)) |
124 | + assigned_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_assignee) |
125 | + else: |
126 | + status_color = ageCheck(assigned_age) |
127 | + assigned_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, assigned_age) |
128 | + |
129 | + return assigned_info |
130 | + |
131 | +def processUploadInformation(upload_status, date_uploaded): |
132 | + if upload_status == "SRU Uploaded": |
133 | + upload_age = calculateDays(str(date_uploaded)) |
134 | + if upload_age == 0: |
135 | + new_upload = calculateHours(str(date_uploaded)) |
136 | + upload_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_upload) |
137 | + else: |
138 | + status_color = ageCheck(upload_age) |
139 | + upload_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, upload_age) |
140 | + return upload_info |
141 | + elif upload_status == "No Upload SRU Status": |
142 | + upload_info = "<td><p align='center'>-</p></td>\n" |
143 | + return upload_info |
144 | + |
145 | +def processDateVerification(verification_status, date_verification_needed): |
146 | + if verification_status == "Verification Needed": |
147 | + verification_age = calculateDays(str(date_verification_needed)) |
148 | + if verification_age == 0: |
149 | + new_verification = calculateHours(str(date_verification_needed)) |
150 | + verification_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_verification) |
151 | + else: |
152 | + status_color = ageCheck(verification_age) |
153 | + verification_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_age) |
154 | + else: |
155 | + verification_info = "<td><p align='center'>-</p></td>\n" |
156 | + return verification_info |
157 | + |
158 | +def processDateVerificationDone(verificaton_status, date_verification_done): |
159 | + if verificaton_status== "Verfication Done": |
160 | + verification_done_age = calculateDays(str(date_verification_done)) |
161 | + if verification_done_age == 0: |
162 | + new_done_verification = calculateHours(str(date_verification_done)) |
163 | + verification_done_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_done_verification) |
164 | + else: |
165 | + status_color = ageCheck(verification_done_age) |
166 | + verification_done_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_done_age) |
167 | + else: |
168 | + verification_done_info = "<td><p align='center'>-</p></td>\n" |
169 | + return verification_done_info |
170 | + |
171 | +def ageCheck(age): |
172 | + if age >= 1 and age <= 10: |
173 | + color = "green" |
174 | + return color |
175 | + elif age >= 10 and age <= 20: |
176 | + color = "orange" |
177 | + return color |
178 | + elif age >= 20 and age <= 50: |
179 | + color = "yellow" |
180 | + return color |
181 | + elif age >= 50: |
182 | + color = "red" |
183 | + return color |
184 | + |
185 | +def calculateDays(date): |
186 | + t = datetime.datetime.now() |
187 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
188 | + age = t - now |
189 | + days = int(age.days) |
190 | + return days |
191 | + |
192 | +def calculateHours(date): |
193 | + t = datetime.datetime.now() |
194 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
195 | + age = t - now |
196 | + seconds = int(age.seconds) |
197 | + hours = seconds/3600 |
198 | + return int(hours) |
199 | + |
200 | +if __name__ == '__main__': |
201 | + optparser = optparse.OptionParser() |
202 | + optparser.add_option('-d', '--database', |
203 | + help='Path to database', dest='database', metavar='PATH') |
204 | + |
205 | + (opts, args) = optparser.parse_args() |
206 | + |
207 | + if not opts.database: |
208 | + optparser.error("Database not specified") |
209 | + |
210 | + db = get_db(opts.database) |
211 | + table = get_query(db) |
212 | + |
213 | + for row in table: |
214 | + user = row[0] |
215 | + |
216 | + filename = "%s.html" %user |
217 | + fh = open(filename, 'w') |
218 | + |
219 | + fh.write("<html>\n") |
220 | + fh.write("<head>\n") |
221 | + fh.write("<style type='text/css' media='screen'>@import url(launchpad.css);</style>\n") |
222 | + fh.write("<script type='text/javascript' src='sorttable.js'></script>\n") |
223 | + fh.write("<title> Server SRU Tracker </title>\n") |
224 | + fh.write("</head>\n") |
225 | + fh.write("<body>\n") |
226 | + |
227 | + fh.write( "<p>Generated on: %s </p>" %time.strftime('%x %X UTC', time.gmtime())) |
228 | + fh.write("<block>Colors in green, the age is between 1 and 10 days old.<br>\n") |
229 | + fh.write("Colors in orange, the age is between 10 and 20 days old.<br>\n") |
230 | + fh.write("Colors in yellow, the age is between 21 and 50 days old.<br>\n") |
231 | + fh.write("Colors in red, the age is 50 days and greater days old.</block>\n") |
232 | + fh.write("<br>") |
233 | + |
234 | + fh.write("<table border='1' id='icons' class='sortable'>\n") |
235 | + fh.write("<th><b>SRU Bug</b></th>\n") |
236 | + fh.write("<th><b>Source Package</b></th>\n") |
237 | + fh.write("<th><b>Series</b></th>\n") |
238 | + fh.write("<th><b>Assignee</b></th>\n") |
239 | + fh.write("<th><b>Title</b></th>\n") |
240 | + fh.write("<th><b>Assigned Age</b></th>\n") |
241 | + fh.write("<th><b>Upload Age</b></th>\n") |
242 | + fh.write("<th><b>Verification Needed Age</b></th>\n") |
243 | + fh.write("<th><b>Verification Success</b></th>\n") |
244 | + fh.write("<th><b>Fixed Released</b></th>\n") |
245 | + |
246 | + id = row[1] |
247 | + package = row[2] |
248 | + release = row[3] |
249 | + title = row[4] |
250 | + verification_status = row[5] |
251 | + upload_status = row[6] |
252 | + bug_status = row[7] |
253 | + date_assigned = row[8] |
254 | + date_uploaded = row[9] |
255 | + date_verification_needed = row[10] |
256 | + date_verification_done = row[11] |
257 | + |
258 | + if date_assigned is not None and upload_status is not None: |
259 | + fh.write("<tr>\n") |
260 | + fh.write("<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, id)) |
261 | + fh.write("<td><a href='https://bugs.launchpad.net/ubuntu/+source/%s'>%s</a></td>\n" %(package, package)) |
262 | + fh.write("<td>%s</td>\n" %(release)) |
263 | + fh.write("<td><a href='http://launchpad.net/~%s'>%s</a></td>\n" %(user, user)) |
264 | + fh.write("<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, title)) |
265 | + |
266 | + assigned_information = "" |
267 | + assigned_information = processAssignedInformation(date_assigned) |
268 | + if assigned_information is None: |
269 | + assigned_information = "<td></td>" |
270 | + fh.write(assigned_information) |
271 | + |
272 | + upload_information = "" |
273 | + upload_information = processUploadInformation(upload_status, date_uploaded) |
274 | + if upload_information is None: |
275 | + upload_information = "<td></td>" |
276 | + fh.write(upload_information) |
277 | + |
278 | + verification_need_information = "" |
279 | + verification_need_information = processDateVerification(verification_status, date_verification_needed) |
280 | + if verification_need_information is None: |
281 | + verification_need_information = "<td></td>" |
282 | + fh.write(verification_need_information) |
283 | + |
284 | + verification_done_information = "" |
285 | + verification_done_information = processDateVerificationDone(verification_status, date_verification_done) |
286 | + if verification_done_information is None: |
287 | + verification_done_information = "<td></td>" |
288 | + fh.write(verification_done_information) |
289 | + |
290 | + fh.write("<td></td>\n") |
291 | + fh.write("</tr>\n") |
292 | + |
293 | + fh.write("</body>") |
294 | + fh.write("</html>") |
295 | + fh.close() |
296 | |
297 | === added file 'server/sru/collect.py' |
298 | --- server/sru/collect.py 1970-01-01 00:00:00 +0000 |
299 | +++ server/sru/collect.py 2012-05-30 18:11:22 +0000 |
300 | @@ -0,0 +1,146 @@ |
301 | +#!/usr/bin/python |
302 | + |
303 | +import sqlite3 as dbapi2 |
304 | +import os |
305 | +import optparse |
306 | +import re |
307 | +import urllib |
308 | +from launchpadlib.launchpad import Launchpad |
309 | +from launchpadlib.uris import LPNET_SERVICE_ROOT |
310 | + |
311 | +def parse_argv(): |
312 | + optparser = optparse.OptionParser() |
313 | + optparser.add_option('-d', '--database', |
314 | + help='Path to database', dest='database', metavar='PATH') |
315 | + |
316 | + (opts, args) = optparser.parse_args() |
317 | + |
318 | + if not opts.database: |
319 | + optparser.error('No database given') |
320 | + |
321 | + return (opts, args) |
322 | + |
323 | +def get_db(dbpath): |
324 | + init = not os.path.exists(dbpath) |
325 | + |
326 | + db = dbapi2.connect(dbpath) |
327 | + cur = db.cursor() |
328 | + |
329 | + if init: |
330 | + print 'Create database %s' %dbpath |
331 | + cur.execute('''CREATE TABLE SRU_TRACKER ( |
332 | + bug_id varchar(255) not null, |
333 | + package varchar(50) not null, |
334 | + release varchar(50) not null, |
335 | + title varchar(255) not null, |
336 | + assignee varchar(255) not null, |
337 | + verification_status varchar(255) not null, |
338 | + upload_status varchar(255) not null, |
339 | + bug_status varchar(255) not null, |
340 | + date_assigned timestamp not null, |
341 | + date_uploaded timestamp not null, |
342 | + verification_needed timestamp not null, |
343 | + verification_done timestamp not null, |
344 | + sru_timestamp timestamp not null |
345 | + )''') |
346 | + db.commit() |
347 | + |
348 | + return db |
349 | + |
350 | +def nominations_import(lp, db): |
351 | + ubuntu_releases = ["lucid", "natty", "hardy", "precise"] |
352 | + pkgs = [] |
353 | + |
354 | + ubuntu = lp.distributions['ubuntu'] |
355 | + primary, partner = ubuntu.archives |
356 | + |
357 | + pkgs = get_package_list(lp, pkgs) |
358 | + for i in pkgs: |
359 | + for k in ubuntu_releases: |
360 | + releases = ubuntu.getSeries(name_or_version=k) |
361 | + package = releases.getSourcePackage(name=i) |
362 | + bugs = package.searchTasks(omit_targeted=False) |
363 | + |
364 | + for bug_task in bugs: |
365 | + id = bug_task.bug.id |
366 | + title = bug_task.bug.title |
367 | + importance = bug_task.importance |
368 | + |
369 | + if bug_task.assignee_link is None: |
370 | + assignee = "None" |
371 | + else: |
372 | + url_parts = bug_task.assignee_link.split('/') |
373 | + assignee = url_parts[-1].lstrip('~') |
374 | + |
375 | + nominations = bug_task.bug.getNominations() |
376 | + for nom in nominations: |
377 | + if nom.date_decided is None: |
378 | + date_nominated = nom.date_created |
379 | + assigned_date = date_nominated |
380 | + else: |
381 | + date_nominated = nom.date_decided |
382 | + assigned_date = date_nominated |
383 | + |
384 | + upload_status = "" |
385 | + upload_date = "" |
386 | + |
387 | + if 'verification-needed' in bug_task.bug.tags: |
388 | + upload_date, upload_status = get_publish_info(i, releases, primary, upload_status, upload_date) |
389 | + date_verification_needed = upload_date |
390 | + date_verification_done = "-" |
391 | + verification_status = "Verification Needed" |
392 | + elif 'verification-done' in bug_task.bug.tags: |
393 | + upload_date, upload_status = get_publish_info(i, releases, primary, upload_status, upload_date) |
394 | + date_verification_done = upload_date |
395 | + date_verification_needed = "-" |
396 | + verification_status = "Verification Succeded" |
397 | + elif 'verification-failed' in bug_task.bug.tags: |
398 | + date_verificiation_done = "-" |
399 | + date_verificiation_needed = "-" |
400 | + verification_status = "Verification Failed" |
401 | + upload_date = 0 |
402 | + upload_status = "SRU Re-upload" |
403 | + else: |
404 | + date_verification_done = "-" |
405 | + date_verification_needed = "-" |
406 | + upload_date = 0 |
407 | + upload_status = "No Upload SRU Status" |
408 | + verification_status = "No Verficiation SRU Status" |
409 | + |
410 | + cur.execute('INSERT INTO sru_tracker values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, date(CURRENT_TIMESTAMP))', (id, i, k, title, assignee, verification_status, upload_status, importance, assigned_date, upload_date, date_verification_needed, date_verification_done)) |
411 | + db.commit() |
412 | + |
413 | +def get_publish_info(source_package, releases, primary, upload_status, upload_date): |
414 | + try: |
415 | + upload_date = primary.getPublishedSources(exact_match=True, source_name=source_package, pocket="Proposed", distro_series=releases)[0].date_published |
416 | + except: |
417 | + upload_status = "Not uploaded" |
418 | + upload_date = "-" |
419 | + return upload_date, upload_status |
420 | + |
421 | +def get_package_list(lp, pkgs): |
422 | + p = re.compile('>([^\s]+)\sin\sUbuntu') |
423 | + team_name = 'ubuntu-server' |
424 | + team = lp.people[team_name] |
425 | + (f_name, h) = urllib.urlretrieve(\ |
426 | + "https://bugs.launchpad.net/~%s/+packagebugs" % team.name) |
427 | + |
428 | + for l in open(f_name): |
429 | + m = p.search(l) |
430 | + if m: |
431 | + pkgs.append(m.group(1)) |
432 | + return pkgs |
433 | + |
434 | + |
435 | +if __name__ == '__main__': |
436 | + (opts, args) = parse_argv() |
437 | + |
438 | + lp = Launchpad.login_anonymously('server-sru', service_root='production') |
439 | + |
440 | + db = get_db(opts.database) |
441 | + |
442 | + # reset status from current day |
443 | + cur = db.cursor() |
444 | + cur.execute('delete from sru_tracker') |
445 | + |
446 | + nominations_import(lp, db) |
447 | |
448 | === added file 'server/sru/launchpad.css' |
449 | --- server/sru/launchpad.css 1970-01-01 00:00:00 +0000 |
450 | +++ server/sru/launchpad.css 2012-05-30 18:11:22 +0000 |
451 | @@ -0,0 +1,1174 @@ |
452 | +/* === Things browsers should do, but don't: === */ |
453 | + |
454 | +.clearfix:after { /* use class="clearfix" whenever floats should be enclosed */ |
455 | + content: "."; |
456 | + display: block; |
457 | + height: 0; |
458 | + clear: both; |
459 | + visibility: hidden; |
460 | +} |
461 | +.clearfix {display: inline-table;} |
462 | +/* Work around float bug in MSIE for Windows, while hiding from MSIE for Mac \*/ |
463 | +* html .clearfix {height: 1%;} |
464 | +.clearfix {display: block;} |
465 | +/* End hiding from MSIE for Mac */ |
466 | + |
467 | +abbr[title], acronym[title] { |
468 | + border-bottom: 1px dotted Black; |
469 | + color: Black; |
470 | + background-color: transparent; |
471 | + cursor: help; |
472 | +} |
473 | +address { |
474 | + font-style: inherit; |
475 | +} |
476 | +dd { |
477 | + margin-bottom: 1em; |
478 | +} |
479 | +dt dfn { |
480 | + font-style: inherit; |
481 | + font-weight: bold; |
482 | +} |
483 | +img { |
484 | + border: none; |
485 | +} |
486 | +kbd { |
487 | + border: 1px solid; |
488 | + border-color: white gray gray white; |
489 | + color: black; |
490 | + background-color: #ddd; |
491 | + min-width: 1em; |
492 | +} |
493 | +label { |
494 | + white-space: nowrap; |
495 | +} |
496 | +legend { |
497 | + font-weight: bold; |
498 | +} |
499 | +legend legend { |
500 | + font-weight: normal; |
501 | +} |
502 | +tr { |
503 | + vertical-align: top; |
504 | +} |
505 | +th { |
506 | + text-align: right; |
507 | +} |
508 | +thead { |
509 | + vertical-align: bottom; |
510 | +} |
511 | +thead th, tr.thead th { |
512 | + text-align: center; |
513 | +} |
514 | + |
515 | +.listbox { /* a scrolling list of checkboxes or radio buttons */ |
516 | + border: 1px solid #8cacbb; |
517 | + display: inline-block; |
518 | + max-height: 12em; |
519 | + overflow: auto; |
520 | + overflow: -moz-scrollbars-vertical; |
521 | +} |
522 | +.listbox label { |
523 | + background-color: #f6f6f6; |
524 | + border: solid white; |
525 | + border-width: 0 0 1px 0; |
526 | + display: block; |
527 | +} |
528 | + |
529 | +/* === Global Launchpad design: === */ |
530 | + |
531 | +body { |
532 | + background-color: #fcfcfc; |
533 | + color: black; |
534 | + font-family: "Lucida Grande", "Trebuchet MS", sans-serif; |
535 | + margin: 0; |
536 | + padding: 0; |
537 | +} |
538 | +.body { |
539 | + margin: 0.75em 0.75em 0; |
540 | +} |
541 | + |
542 | +:visited { |
543 | + background: none; |
544 | + color: #8888bb; |
545 | +} |
546 | +:link, .hierarchy :visited, .footer :visited, |
547 | +.facets :visited, #pillars :visited, #subpillars :visited, .portlet :visited { |
548 | + background: none; |
549 | + color: #000088; |
550 | +} |
551 | +:link:active, :visited:active { |
552 | + background: none; |
553 | + color: #c00; |
554 | +} |
555 | + |
556 | +h1, h2, h3, h4, h5, h6 { |
557 | + background: none; |
558 | + color: #708a96; |
559 | + font-size: 100%; |
560 | + font-weight: normal; |
561 | + margin: 0; |
562 | + padding-top: 0.5em; |
563 | + text-align: left; |
564 | +} |
565 | +h1 :link, h2 :link, h3 :link, h4 :link, h5 :link, h6 :link, |
566 | +h1 :visited, h2 :visited, h3 :visited, h4 :visited, h5 :visited, h6 :visited { |
567 | + color: inherit; |
568 | +} |
569 | +h1 { |
570 | + font-size: 1.6em; |
571 | + margin-bottom: 0.5em; |
572 | +} |
573 | +h2 { |
574 | + font-size: 1.25em; |
575 | + margin: 0.5em 0; |
576 | +} |
577 | +h2.lpcontext { |
578 | + font-size: 1.5em; |
579 | + margin-top: 0; |
580 | + padding-top: 0; |
581 | +} |
582 | +h3, h4, h5, h6 { |
583 | + font-size: 1em; |
584 | + margin-bottom: 0; |
585 | +} |
586 | +h5.languages { |
587 | + background-image: url(/@@/languages.gif); |
588 | + background-repeat: no-repeat; |
589 | +} |
590 | +label { |
591 | + font-weight: bold; |
592 | +} |
593 | +pre { |
594 | + border: 1px dashed #8cacbb; |
595 | + color: Black; |
596 | + background-color: #dee7ec; |
597 | + overflow: auto; |
598 | +} |
599 | +table { |
600 | + margin-left: auto; |
601 | + margin-right: auto; |
602 | + border-collapse: collapse; |
603 | + width: 100%; |
604 | +} |
605 | +th, td { |
606 | + padding: 0.25em; |
607 | +} |
608 | +th.icon, td.icon { |
609 | + width: 1px; |
610 | +} |
611 | +th.icon.left, td.icon.left { |
612 | + padding-right: 0; |
613 | +} |
614 | +th.icon.right, td.icon.right { |
615 | + padding-left: 0; |
616 | +} |
617 | +table.listing { |
618 | + font-size: 65%; |
619 | + margin: 1em 0; |
620 | +} |
621 | +table.listing, table.listing tbody { |
622 | + border-bottom: 1px solid #8cacbb; |
623 | +} |
624 | +table.listing>thead, table.listing>thead>tr>th { |
625 | + border: 1px solid #8cacbb; |
626 | + background-color: #dee7ec; |
627 | +} |
628 | +table.listing>thead>tr>td { |
629 | + border: none; |
630 | +} |
631 | +tr.highlight { /* highlight specific table entry */ |
632 | + border: 1px solid #ffa500; |
633 | + background-color: #ffce7b; |
634 | +} |
635 | +table.listing>tr.note, table.listing>tbody>tr.note { |
636 | + font-size: smaller; |
637 | +} |
638 | +tr.amount, td.amount { |
639 | + text-align: right; |
640 | +} |
641 | +table.listing th, table.listing td { |
642 | + padding: 0.125em 0.25em; |
643 | +} |
644 | +table.listing th { |
645 | + font-weight: normal; |
646 | + white-space: nowrap; |
647 | +} |
648 | +table.listing>tr>td, table.listing>tbody>tr>td { |
649 | + border: 1px #8cacbb; |
650 | + border-style: dotted none none none; |
651 | +} |
652 | +table.listing>tr.note>td, table.listing>tbody>tr.note>td { |
653 | + border-style: none; |
654 | +} |
655 | +table.listing img { |
656 | + vertical-align: middle; |
657 | +} |
658 | +table.listing tr.secondary>th, table.listing tr.secondary td { |
659 | + border-top: none; |
660 | +} |
661 | +/* Sortable tables: */ |
662 | +table.sortable a.sortheader { |
663 | + color:#666666; |
664 | + font-weight: bold; |
665 | + text-decoration: none; |
666 | + display: block; |
667 | +} |
668 | +table.sortable img.sortarrow { |
669 | + padding-left: 2px; |
670 | +} |
671 | +th.ascending { |
672 | + background-image: url(/@@/arrowDown.gif); |
673 | + background-position: center right; |
674 | + background-repeat: no-repeat; |
675 | +} |
676 | +th.descending { |
677 | + background-image: url(/@@/arrowUp.gif); |
678 | + background-position: center right; |
679 | + background-repeat: no-repeat; |
680 | +} |
681 | +ol, ul { |
682 | + padding: 0; |
683 | +} |
684 | +ul { |
685 | + list-style-type: square; |
686 | + list-style-image: url(/@@/bullet.gif); |
687 | + margin: 0.5em 0 0 24px; /* because bullet graphics are px width */ |
688 | +} |
689 | +ol { |
690 | + margin: 0.5em 0 0 1.5em; |
691 | +} |
692 | + |
693 | +.portlet { |
694 | + border: none; |
695 | + font-size: smaller; |
696 | + margin-bottom: 1em; |
697 | + padding: 0; |
698 | + background-color: #f4f5f8; /* changed by sabdfl, 2005-10-22 */ |
699 | +} |
700 | +.portlet h4, .portlet h5, .portlet h6 { |
701 | + background-color: #dee7ec; |
702 | + border: 1px solid #8cacbb; |
703 | + font-weight: normal; |
704 | +} |
705 | +.portlet h4 { /* the base portlet heading */ |
706 | + padding: 0em 0.3em 0em 0.75em; |
707 | + display: block; |
708 | + font-size: 1em; |
709 | +} |
710 | +.portlet h5 { |
711 | + padding: 0em 1em 0em 1em; |
712 | + display: inline; |
713 | + font-size: 1em; |
714 | + white-space: nowrap; |
715 | + position: relative; |
716 | + top: -1px; |
717 | +} |
718 | +.portlet h6 { |
719 | + padding: 0em 0.3em 0em 1em; |
720 | + display: block; |
721 | + font-size: 1em; |
722 | +} |
723 | +.portletBody { |
724 | + position: relative; |
725 | + top: -1px; |
726 | + border: 1px solid #8cacbb; |
727 | +} |
728 | +.portletContent { |
729 | + padding: 0.75em; |
730 | +} |
731 | + |
732 | +/* -- Form controls: -- */ |
733 | +textarea { |
734 | + border: 1px solid #8cacbb; |
735 | + color: black; |
736 | + background-color: white; |
737 | + font-family: sans-serif; |
738 | + font-family: caption; |
739 | + width: 100%; |
740 | +} |
741 | +.fieldRequired { |
742 | + color: #999; |
743 | +} |
744 | +.formHelp { |
745 | + color: #666666; |
746 | + font-size: smaller; |
747 | + margin-bottom: 1em; |
748 | +} |
749 | +.portalError .fieldRequired { |
750 | + color: #666; |
751 | +} |
752 | +/* Temporarily make *all* buttons look plone-y. Ugh. */ |
753 | +button { |
754 | + border: 1px solid #8cacbb; |
755 | + color: Black; |
756 | + background-color: white; |
757 | +} |
758 | +/* Don't give radiobuttons a square border in Opera: */ |
759 | +input[type="radio"] { |
760 | + border: none; |
761 | +} |
762 | + |
763 | +/* -- Form errors: -- */ |
764 | +.portalMessage { |
765 | + background-color: #ffce7b; |
766 | + border: 1px solid #ffa500; |
767 | + color: black; |
768 | + margin: 1em 0em 0em 0em; |
769 | + padding: 0.5em 1em 0.5em 30px; /* 30px because the image has a pixel size */ |
770 | + vertical-align: middle; |
771 | + background-position: 5px 0.75em; |
772 | + background-image: url(/@@/info_icon.gif); |
773 | + background-repeat: no-repeat; |
774 | +} |
775 | +.portalMessage a { |
776 | + color: Black; |
777 | +} |
778 | +.portalError { |
779 | + background-color: #ff9070; |
780 | + border: 1px solid #998080; |
781 | + color: Black; |
782 | + padding: 0.25em; |
783 | +} |
784 | +.portalError .error { |
785 | + background: none; |
786 | + border: none; |
787 | + padding: 0; |
788 | + font-size: 85%; |
789 | +} |
790 | + |
791 | +/* sabdfl says portlet links must not be underlined */ |
792 | +.portlet a { |
793 | + text-decoration: none; |
794 | +} |
795 | +/* SteveA says they should still be underlined if they're in help text */ |
796 | +.portlet p a { |
797 | + text-decoration: underline; |
798 | +} |
799 | + |
800 | +/* -- Page layout: -- */ |
801 | + |
802 | +.header td, .footer { |
803 | + font-family: "Lucida Grande", "Trebuchet MS", sans-serif; |
804 | + padding: 0.25em 1em; |
805 | +} |
806 | +.header { |
807 | + font-size: 0.875em; |
808 | +} |
809 | +.footer { |
810 | + font-size: smaller; |
811 | + text-align: center; |
812 | +} |
813 | +.login { |
814 | + text-align: right; |
815 | + min-height: 1.75em; |
816 | +} |
817 | +.login input[type="submit"] { |
818 | + padding: 0; |
819 | +} |
820 | +.header, .footer, ul.app>li { |
821 | + background-color: #dee7ec; |
822 | + color: inherit; |
823 | +} |
824 | +ul.app { /* Currently not used */ |
825 | + padding: 0; |
826 | + margin: 1em 0 1em; |
827 | + border-bottom: 1px solid #8cacbb; |
828 | + font-weight: bold; |
829 | + font-size: 1em; |
830 | + font-family: "Lucida Grande", "Trebuchet MS", sans-serif; |
831 | +} |
832 | +ul.app>li |
833 | +{ |
834 | + border: 1px solid #8cacbb; |
835 | + border-bottom: none; |
836 | + display: inline; |
837 | + list-style: none; |
838 | + margin: 0 0.5em 0 0; |
839 | + padding: 0 0.5em; |
840 | +} |
841 | +ul.app a { |
842 | + text-decoration: none; |
843 | +} |
844 | +ul.app>li.current { |
845 | + background: #fcfcfc; |
846 | + border-bottom: 1px solid #fcfcfc; |
847 | +} |
848 | + |
849 | +/* Column definitions: */ |
850 | +#portal-column-content { |
851 | + display: inline; /* avoid MSIE Double Float Margin bug */ |
852 | + float: left; |
853 | + margin-left: 26%; /* = 25% + 1% whitespace */ |
854 | + width: 48%; /* = 50% - 1% left whitespace - 1% right whitespace */ |
855 | +} |
856 | +* > #portal-column-content { /* hide from MSIE/Win 5 where "relative" breaks */ |
857 | + position: relative; /* allow z-index to work */ |
858 | + z-index: 1000; /* force main column to appear on top */ |
859 | +} |
860 | +#portal-column-two { |
861 | + display: inline; |
862 | + float: left; |
863 | + margin-left: 2%; /* whitespace */ |
864 | + width: 24%; /* = 25% - 1% whitespace */ |
865 | +} |
866 | +#portal-column-one { |
867 | + display: inline; |
868 | + float: left; |
869 | + margin-left: -100%; /* flush with the left edge of the page */ |
870 | + width: 24%; /* = 25% - 1% whitespace */ |
871 | +} |
872 | + |
873 | +.sitemap, ul.facets { |
874 | + background-color: #dee7ec; |
875 | + border: 1px solid #8cacbb; |
876 | + color: #999999; |
877 | + font-size: smaller; |
878 | + margin: 0 0 1em 0; |
879 | +} |
880 | +.sitemap, #subpillars { |
881 | + background-color: #f4f5f8; |
882 | +} |
883 | +#pillars, #subpillars { |
884 | + display: inline; |
885 | + margin: 0; |
886 | + padding: 0; |
887 | +} |
888 | +#pillars { |
889 | + background-color: #dee7ec; |
890 | + float: left; |
891 | + width: 47%; |
892 | +} |
893 | +#subpillars { |
894 | + float: left; |
895 | + font-size: smaller; |
896 | + margin-left: 6%; |
897 | + width: 42%; |
898 | +} |
899 | +#pillars li, #subpillars li { |
900 | + list-style: none; |
901 | + margin: 0; |
902 | +} |
903 | +.sitemap li { |
904 | + display: block; |
905 | + padding: 0.25em 1em; |
906 | +} |
907 | +.sitemap :link, .sitemap :visited { |
908 | + text-decoration: none; |
909 | +} |
910 | +#pillars li.current, .facets li.current { |
911 | + background-color: #f4f5f8; |
912 | + color: inherit; |
913 | +} |
914 | +#subpillars li.current { |
915 | + background-color: #dee7ec; |
916 | + border: 1px solid #8cacbb; |
917 | +} |
918 | +/* The following paddings/margins aren't yet robust when zoomed. */ |
919 | +ul.facets { |
920 | + padding: 0.25em 0 0.25em 0; |
921 | +} |
922 | +ul.facets :link, ul.facets :visited { |
923 | + text-decoration: none; |
924 | +} |
925 | +ul.facets>li { |
926 | + list-style-position: inside; |
927 | + margin: 0.25em 1em; |
928 | + padding-left: 0.5em; |
929 | +} |
930 | +ul.facets>li ul { |
931 | + list-style-position: outside; |
932 | + margin-left: 1.5em; |
933 | + padding-left: 1.5em; |
934 | +} |
935 | + |
936 | +/* ----------------------- UNCHANGED FROM PLONE.CSS ----------------------- */ |
937 | + |
938 | +p { |
939 | + margin: 0.5em 0em 1em 0em; |
940 | + line-height: 1.5em; |
941 | +} |
942 | +p img { |
943 | + border: 0; |
944 | + margin: 0; |
945 | +} |
946 | + |
947 | +fieldset { |
948 | + border: 1px solid #8cacbb; |
949 | + margin: 1em 0em 1em 0em; |
950 | + padding: 0em 1em 1em 1em; |
951 | + line-height: 1.5em; |
952 | + width: auto; |
953 | +} |
954 | + |
955 | +form { |
956 | + border: none; |
957 | + margin: 0; |
958 | +} |
959 | +input { |
960 | + font-family: "Lucida Grande", Verdana, Lucida, Helvetica, Arial, sans-serif; |
961 | + visibility: visible; |
962 | + border: 1px solid #8cacbb; |
963 | + color: Black; |
964 | + background-color: white; |
965 | + vertical-align: middle; |
966 | +} |
967 | +select { |
968 | + border: 1px solid #8cacbb; |
969 | + color: Black; |
970 | + background-color: White; |
971 | + vertical-align: top; |
972 | +} |
973 | +ins { |
974 | + color: green; |
975 | + text-decoration: none; |
976 | +} |
977 | + |
978 | +.documentDescription { |
979 | + /* The summary text describing the document */ |
980 | + font-weight: bold; |
981 | + display: block; |
982 | + margin: 1em 0em; |
983 | + line-height: 1.5em; |
984 | +} |
985 | + |
986 | +/* The new form elements */ |
987 | + |
988 | +.field { |
989 | + top: 0; |
990 | + left: 0; |
991 | + margin: 0 1em 1em 0; |
992 | +} |
993 | + |
994 | +.field .field { |
995 | + margin: 1em 0 0 0; |
996 | +} |
997 | + |
998 | +.formControls { |
999 | + margin: 1em 0 0 0; |
1000 | +} |
1001 | + |
1002 | +.error { |
1003 | + /* Class for error indication in forms */ |
1004 | + background-color: #ffce7b; |
1005 | + border: 1px solid #ffa500; |
1006 | + padding: 1em; |
1007 | + margin: 0 0 1em 0; |
1008 | + width: 95% !important; |
1009 | +} |
1010 | + |
1011 | + |
1012 | +.discreet { |
1013 | + color: #76797c; |
1014 | + font-size: 85%; |
1015 | + font-weight: normal; |
1016 | +} |
1017 | + |
1018 | +.listingBar { |
1019 | + background-color: #dee7ec; |
1020 | + border-color: #8cacbb; |
1021 | + border-style: solid; |
1022 | + border-width: 1px; |
1023 | + padding: 0em 1em; |
1024 | + text-align: center; |
1025 | + vertical-align: top; |
1026 | + margin: 1em 0em; |
1027 | + font-size: 94%; |
1028 | + clear: both; |
1029 | +} |
1030 | +.listingBar span.previous, |
1031 | +.listingPrevious { |
1032 | + text-align: left; |
1033 | + float: left; |
1034 | + margin-right: 1em; |
1035 | +} |
1036 | +.listingBar span.next, |
1037 | +.listingNext { |
1038 | + text-align: right; |
1039 | + float: right; |
1040 | + margin-left: 1em; |
1041 | +} |
1042 | +.listingBar img { |
1043 | + vertical-align: middle; |
1044 | +} |
1045 | + |
1046 | +/* |
1047 | +** Accessibility and visual enhancement elements |
1048 | +*/ |
1049 | + |
1050 | +.visualClear { |
1051 | + display: block; |
1052 | + clear: both; |
1053 | +} |
1054 | +#portal-column-content fieldset > * input:focus, |
1055 | +#portal-column-content fieldset > * textarea:focus { |
1056 | + border-color: #ffa500; |
1057 | + border-width: 1px; |
1058 | +} |
1059 | + |
1060 | +/* -------------------------- END OF PLONE.CSS -------------------------- */ |
1061 | + |
1062 | +/* Launchpad classes */ |
1063 | + |
1064 | +.highlighted { |
1065 | + background-color: #f7f9fa; |
1066 | + border: 1px dotted #8cacbb; |
1067 | + padding: 0.5em; |
1068 | +} |
1069 | + |
1070 | +.interpolation { |
1071 | + background-color: #7090a0; |
1072 | +} |
1073 | + |
1074 | +/* see launchpad.js:activateCollapsables() */ |
1075 | +.collapsible legend a, .collapsible legend a:visited { |
1076 | + text-decoration: none; |
1077 | + color: #000088; |
1078 | +} |
1079 | + |
1080 | +img.collapseIcon { |
1081 | + text-decoration: none; |
1082 | + margin-bottom: -3px; |
1083 | +} |
1084 | + |
1085 | +.collapsible legend a span { |
1086 | + text-decoration: underline; |
1087 | +} |
1088 | + |
1089 | +.collapsible { |
1090 | + padding: 0; |
1091 | + margin: 0; |
1092 | +} |
1093 | + |
1094 | +.collapsed { |
1095 | + border: none; |
1096 | + margin-bottom: 0; |
1097 | +} |
1098 | + |
1099 | +/* Visually differentiate disabled and enabled form fields. */ |
1100 | +select[disabled], |
1101 | +textarea[disabled], |
1102 | +input[disabled] { |
1103 | + border-style: dotted; |
1104 | + color: #999; |
1105 | +} |
1106 | + |
1107 | +tr.highlight { |
1108 | + border: 1px solid #ffc550; |
1109 | + background-color: #ffe7ab; |
1110 | +} |
1111 | + |
1112 | +/* batch navigation links */ |
1113 | + |
1114 | +.batchnav tr:hover { |
1115 | + background-color: white; |
1116 | +} |
1117 | + |
1118 | +.batchnav td:hover { |
1119 | + background-color: white; |
1120 | +} |
1121 | + |
1122 | +.batchnav td { |
1123 | + text-align: center; |
1124 | + border-top: 1px solid #364970; |
1125 | + vertical-align: bottom; |
1126 | +} |
1127 | + |
1128 | +/* Bug Status, Severity, Priority markers */ |
1129 | +.statusUnconfirmed { color: #993300; } |
1130 | +.statusConfirmed { color: Red; } |
1131 | +.statusFixed { color: Black; } |
1132 | +.statusReleased { color: Green; } |
1133 | +.statusRejected { color: Gray; } |
1134 | + |
1135 | +.severityCritical { color: Red; } |
1136 | +.severityMajor { color: #ff6600; } |
1137 | +.severityNormal { color: Green; } |
1138 | +.severityMinor { color: Black; } |
1139 | +.severityWishlist { color: Blue; } |
1140 | + |
1141 | +.severityCriticalRow { background-color: #E1A1A1; } |
1142 | +.severityMajorRow { background-color: #D9C08F; } |
1143 | +.severityNormalRow { background-color: #9FD98F; } |
1144 | +.severityMinorRow { background-color: #A5AFC8; } |
1145 | +.severityWishlistRow { background-color: #8FA9D9; } |
1146 | + |
1147 | +.priorityHigh { color: Red; } |
1148 | +.priorityMedium { color: Orange; } |
1149 | +.priorityLow { color: Black; } |
1150 | +.priorityWontfix { color: Gray; } |
1151 | + |
1152 | +/* Spec status and priority */ |
1153 | + |
1154 | +.specstatusIMPLEMENTED { color: Green; } |
1155 | +.specstatusINFORMATIONAL { color: Green; } |
1156 | +.specstatusAPPROVED { color: Black; } |
1157 | +.specstatusPENDINGAPPROVAL { color: #f09; } |
1158 | +.specstatusPENDINGREVIEW { color: #f09; } |
1159 | +.specstatusDRAFT { color: #930; } |
1160 | +.specstatusBRAINDUMP { color: Red; } |
1161 | +.specstatusSUPERSEDED { color: Gray; } |
1162 | +.specstatusOBSOLETE { color: Gray; } |
1163 | + |
1164 | +.specpriorityNOTFORUS { color: Gray; } |
1165 | +.specpriorityPROPOSED { color: Gray; } |
1166 | +.specpriorityLOW { color: Black; } |
1167 | +.specpriorityMEDIUM { color: Orange; } |
1168 | +.specpriorityHIGH { color: Red; } |
1169 | +.specpriorityESSENTIAL { color: Red; } |
1170 | + |
1171 | +.specdeliveryUNKNOWN {color: Gray; } |
1172 | +.specdeliveryDEFERRED {color: Red; } |
1173 | +.specdeliveryNEEDSINFRASTUCTURE {color: Red; } |
1174 | +.specdeliveryBLOCKED {color: Red; } |
1175 | +.specdeliverySTARTED {color: Blue; } |
1176 | +.specdeliverySLOW {color: Red; } |
1177 | +.specdeliveryGOOD {color: Blue; } |
1178 | +.specdeliveryBETA {color: Orange; } |
1179 | +.specdeliveryNEEDSREVIEW {color: Purple; } |
1180 | +.specdeliveryAWAITINGDEPLOYMENT {color: Red; } |
1181 | +.specdeliveryIMPLEMENTED {color: Green; } |
1182 | + |
1183 | +/* Board-like comment CSS */ |
1184 | + |
1185 | +.boardComment { |
1186 | + border: 1px solid #364970; |
1187 | + margin-bottom: 1.5em; |
1188 | + margin-top: 1em; |
1189 | + position: relative; |
1190 | +} |
1191 | + |
1192 | +/* Board-like comment CSS */ |
1193 | + |
1194 | +.boardComment { |
1195 | + border: 1px solid #364970; |
1196 | + margin-bottom: 1.5em; |
1197 | + margin-top: 1em; |
1198 | + position: relative; |
1199 | +} |
1200 | + |
1201 | +/* Fix for IE float bug */ |
1202 | +* html .boardComment { |
1203 | + float: left; |
1204 | + clear: both; |
1205 | + width: 99%; |
1206 | +} |
1207 | + |
1208 | + |
1209 | +.boardCommentDetails { |
1210 | + font-size: 80%; |
1211 | + border: 1px solid #364970; |
1212 | + border-style: none none solid none; |
1213 | + background-color: #d7d9da; |
1214 | + padding: 0.25em 12px; |
1215 | +} |
1216 | + |
1217 | +.boardCommentBody { |
1218 | + padding: 0.5em 12px 0; |
1219 | +} |
1220 | + |
1221 | +.boardCommentContent { |
1222 | + line-height: 1.5em; |
1223 | +} |
1224 | + |
1225 | +.boardCommentContent pre { |
1226 | + background-color: white; |
1227 | + border: none; |
1228 | + padding: 0; |
1229 | + margin: 0; |
1230 | +} |
1231 | +/* used to mark dummy data with div entity */ |
1232 | +.dummy { |
1233 | + background-color: #eeeeee; |
1234 | + color: #656565; |
1235 | +} |
1236 | +/* Various custom list formats: */ |
1237 | +ul.add>li, li.add { |
1238 | + list-style-image: url(/@@/add.gif); |
1239 | +} |
1240 | +ul.alert>li, li.alert { |
1241 | + list-style-image: url(/@@/alert_icon.gif); |
1242 | +} |
1243 | +ul.architecture>li, li.architecture { |
1244 | + list-style-image: url(/@@/architecture); |
1245 | +} |
1246 | +ul.bounty>li, li.bounty { |
1247 | + list-style-image: url(/@@/bounty); |
1248 | +} |
1249 | +ul.bug>li, li.bug { |
1250 | + list-style-image: url(/@@/bug); |
1251 | +} |
1252 | +ul.bug.high>li, li.bug.high { |
1253 | + list-style-image: url(/@@/bug-high); |
1254 | +} |
1255 | +ul.bug.medium>li, li.bug.medium { |
1256 | + list-style-image: url(/@@/bug-medium); |
1257 | +} |
1258 | +ul.bug.low>li, li.bug.low { |
1259 | + list-style-image: url(/@@/bug-low); |
1260 | +} |
1261 | +ul.bug.remote>li, li.bug.remote { |
1262 | + list-style-image: url(/@@/bug-remote); |
1263 | +} |
1264 | +ul.bugs>li, li.bugs { |
1265 | + list-style-image: url(/@@/bugs); |
1266 | +} |
1267 | +ul.build-success>li, li.build-success { |
1268 | + list-style-image: url(/@@/build-success); |
1269 | +} |
1270 | +ul.cve>li, li.cve { |
1271 | + list-style-image: url(/@@/cve); |
1272 | +} |
1273 | +ul.document>li, li.document { |
1274 | + list-style-image: url(/@@/document_icon.gif); |
1275 | +} |
1276 | +ul.download>li, li.download { |
1277 | + list-style-image: url(/@@/download.png); |
1278 | +} |
1279 | +ul.edit>li, li.edit { |
1280 | + list-style-image: url(/@@/edit.gif); |
1281 | +} |
1282 | +ul.file>li, li.file { |
1283 | + list-style-image: url(/@@/file); |
1284 | +} |
1285 | +ul.info>li, li.info { |
1286 | + list-style-image: url(/@@/info_icon.gif); |
1287 | +} |
1288 | +ul.languages>li, li.languages { |
1289 | + list-style-image: url(/@@/languages.gif); |
1290 | +} |
1291 | +ul.list>li, li.list { |
1292 | + list-style-image: url(/@@/list); |
1293 | +} |
1294 | +ul.locked>li, li.locked { |
1295 | + list-style-image: url(/@@/lock_icon.gif); |
1296 | +} |
1297 | +ul.mail>li, li.mail { |
1298 | + list-style-image: url(/@@/mail_icon.gif); |
1299 | +} |
1300 | +ul.menu>li, li.menu { |
1301 | + list-style-image: url(/@@/favorite_icon.gif); |
1302 | +} |
1303 | +ul.milestone>li, li.milestone { |
1304 | + list-style-image: url(/@@/target); |
1305 | +} |
1306 | +ul.news>li, li.news { |
1307 | + list-style-image: url(/@@/news); |
1308 | +} |
1309 | +ul.packages>li, li.packages { |
1310 | + list-style-image: url(/@@/package-binary); |
1311 | +} |
1312 | +ul.sources>li, li.sources { |
1313 | + list-style-image: url(/@@/package-source); |
1314 | +} |
1315 | +ul.products>li, li.products { |
1316 | + list-style-image: url(/@@/product_icon.gif); |
1317 | +} |
1318 | +ul.person>li, li.person, ul.people>li, li.people { |
1319 | + list-style-image: url(/@@/user.gif); |
1320 | +} |
1321 | +ul.spec>li, li.spec { |
1322 | + list-style-image: url(/@@/spec); |
1323 | +} |
1324 | +ul.sprint>li, li.sprint { |
1325 | + list-style-image: url(/@@/sprint); |
1326 | +} |
1327 | +ul.statistic>li, ul.statistics, li.statistic { |
1328 | + list-style-image: url(/@@/statistic); |
1329 | +} |
1330 | +ul.ticket>li, li.ticket { |
1331 | + list-style-image: url(/@@/ticket); |
1332 | +} |
1333 | +ul.up>li, li.up { |
1334 | + list-style-image: url(/@@/up); |
1335 | +} |
1336 | +ul.webref>li, li.webref { |
1337 | + list-style-image: url(/@@/link_icon.gif); |
1338 | +} |
1339 | +ul.translations>li, li.translations { |
1340 | + list-style-image: url(/@@/translations); |
1341 | +} |
1342 | +dl.products>dt:before { |
1343 | + content: url(/@@/product_icon.gif); |
1344 | +} |
1345 | +ul.search>li, li.search { |
1346 | + list-style-image: url(/@@/search_icon.gif); |
1347 | +} |
1348 | + |
1349 | +/* Used for source and translator comments on the translation page. */ |
1350 | + |
1351 | +.po-comment { |
1352 | + background-color: #efe; |
1353 | + border: thin solid #8a8; |
1354 | + margin: 0.5em 0; |
1355 | + padding: 0.5em; |
1356 | +} |
1357 | + |
1358 | +.po-message-special { |
1359 | + color: white; |
1360 | + background-color: #93bee2; |
1361 | +} |
1362 | + |
1363 | +/* Used in default-error.pt so people actually notice exceptions */ |
1364 | + |
1365 | +.exception { |
1366 | + color: #cc0000; |
1367 | +} |
1368 | + |
1369 | +.personal { |
1370 | + clear: left; |
1371 | + float: left; |
1372 | + margin-right: 2%; |
1373 | + width: 48%; |
1374 | +} |
1375 | +.statistical { |
1376 | + clear: right; |
1377 | + float: right; |
1378 | + margin-left: 2%; |
1379 | + width: 48%; |
1380 | +} |
1381 | + |
1382 | +.lesser { |
1383 | + font-size: smaller; |
1384 | +} |
1385 | + |
1386 | +/* sabdfl 22/10/2005 indented and moreindented useful for listings */ |
1387 | +.indented { |
1388 | + margin-left: 1em; |
1389 | +} |
1390 | +.moreindented { |
1391 | + margin-left: 2em; |
1392 | +} |
1393 | + |
1394 | +input[type="image"] { |
1395 | + border: none; |
1396 | +} |
1397 | + |
1398 | +.actions { |
1399 | + margin: 0.5em 0; |
1400 | +} |
1401 | + |
1402 | +.unavailable { |
1403 | + color: #999; |
1404 | + background: none; |
1405 | +} |
1406 | +.list { |
1407 | + text-align: left; |
1408 | +} |
1409 | +.required:after { |
1410 | + background: url(/@@/required.gif) center left no-repeat; |
1411 | + color: #999999; |
1412 | + content: '(Required)'; |
1413 | + font-weight: normal; |
1414 | + padding: 0 0 0 8px; |
1415 | + margin: 0 0 0 4px; |
1416 | +} |
1417 | + |
1418 | + |
1419 | +td p:first-child, td ol:first-child, td blockquote:first-child { |
1420 | + margin-top: 0; |
1421 | +} |
1422 | +th { |
1423 | + text-align: right; |
1424 | +} |
1425 | +thead th { |
1426 | + text-align: center; |
1427 | +} |
1428 | +tr { |
1429 | + vertical-align: top; |
1430 | +} |
1431 | + |
1432 | +.results { |
1433 | + background-color: #dee7ec; |
1434 | + font-size: small; |
1435 | + padding: 0.25em; |
1436 | + margin: 1em 0em; |
1437 | + clear: both; |
1438 | +} |
1439 | +.results .navigation { |
1440 | + float: right; |
1441 | + text-align: right; |
1442 | +} |
1443 | + |
1444 | +.apps { |
1445 | + clear: right; |
1446 | + float: right; |
1447 | + margin-top: 0.5em; |
1448 | +} |
1449 | +.navigation li { |
1450 | + display: inline; |
1451 | + padding: 0.25em 0.5em; |
1452 | +} |
1453 | +.navigation li:before { |
1454 | + content: '\2022'; |
1455 | + margin-right: 0.25em; |
1456 | +} |
1457 | + |
1458 | +#main { |
1459 | + border-top: 2pt solid black; |
1460 | + clear: both; |
1461 | + margin: 0; |
1462 | + padding-top: 1em; |
1463 | + width: 100%; |
1464 | +} |
1465 | + |
1466 | +/* from richard braine for the series source forms */ |
1467 | +/* otherwise inputs don't disappear along with surrounding layer */ |
1468 | +input { |
1469 | + visibility:inherit |
1470 | +} |
1471 | +/* |
1472 | +Calendar stuff. |
1473 | +*/ |
1474 | +.cal-navigation { |
1475 | + width: 100%; |
1476 | + margin: 1em; |
1477 | +} |
1478 | + |
1479 | +.cal-table { |
1480 | + width: 100%; |
1481 | + border-collapse: collapse; |
1482 | +} |
1483 | + |
1484 | +.cal-table th { |
1485 | + text-align: center; |
1486 | + color: black; |
1487 | + background-color: #eee; |
1488 | + border: 1px solid black; |
1489 | +} |
1490 | + |
1491 | +.cal-daycell { |
1492 | + border: 1px solid black; |
1493 | +} |
1494 | + |
1495 | +.cal-dayhead { |
1496 | + text-align: right; |
1497 | + border-bottom: 1px dashed #999; |
1498 | + |
1499 | +} |
1500 | +.cal-dayhead a { |
1501 | + display: block; |
1502 | + color: black; |
1503 | + text-decoration: none; |
1504 | + font-weight: bold; |
1505 | +} |
1506 | + |
1507 | +.cal-freeday, .cal-busyday { |
1508 | + display: block; |
1509 | + text-align: right; |
1510 | +} |
1511 | +.cal-busyday { |
1512 | + font-weight: bold; |
1513 | + background-color: #ff9; |
1514 | +} |
1515 | + |
1516 | +.cal-addlink { |
1517 | + text-align: right; |
1518 | + font-size: smaller; |
1519 | +} |
1520 | + |
1521 | +.cal-event-list { |
1522 | + margin: 0px; |
1523 | + padding: 0px; |
1524 | +} |
1525 | + |
1526 | +.cal-event-list li { |
1527 | + list-style: none; |
1528 | + border: 1px solid #4b6982; |
1529 | + background: #9db8d2; |
1530 | + margin: 0.24em; |
1531 | +} |
1532 | + |
1533 | +#calendar-view-day { |
1534 | + border: #aaaaaa 1px solid; |
1535 | + border-top: none; |
1536 | + position: relative; |
1537 | + padding: 0; |
1538 | +} |
1539 | + |
1540 | +#calendar-view-day .hour { |
1541 | + border-top: #4b6983 1px solid; |
1542 | + border-top: 1px #bab5ab solid; |
1543 | + width: 100%; |
1544 | + position: absolute; |
1545 | + height: 4em; |
1546 | + margin: 0 !important; |
1547 | + padding: 0 !important; |
1548 | +} |
1549 | + |
1550 | +#calendar-view-day .even { |
1551 | + background: #eae8e3; |
1552 | +} |
1553 | + |
1554 | +#calendar-view-day .events { |
1555 | + position: absolute; |
1556 | + margin-left: 5em; |
1557 | + left: 0; |
1558 | + top: 0; |
1559 | + width: 100%; |
1560 | +} |
1561 | + |
1562 | +#calendar-view-day .event { |
1563 | + display: block; |
1564 | + position: absolute; |
1565 | + margin-left: 5em; |
1566 | + width: 20em; |
1567 | + min-width: 20em; |
1568 | + border: 1px #4b6983 solid; |
1569 | + background: #9db8d2; |
1570 | + opacity: 0.85; |
1571 | + z-index: 1; |
1572 | +} |
1573 | + |
1574 | +#calendar-view-day .other { |
1575 | + background: #e0b6af; |
1576 | + border: 1px #884631 solid; |
1577 | +} |
1578 | + |
1579 | +.error-notification, |
1580 | +.warning-notification { |
1581 | + background-position: 5px 0.5em; |
1582 | + padding-left: 30px; |
1583 | + background-color: #ff9070; |
1584 | + border: 1px solid #ddb0b0; |
1585 | + color: Black; |
1586 | + background-image: url(/@@/alert_icon.gif); |
1587 | + font-size: 85%; |
1588 | + font-weight: bold; |
1589 | + margin: 1em 0em 0em 0em; |
1590 | + padding: 0.5em 1em 0.5em 3em; |
1591 | + background-repeat: no-repeat; |
1592 | +} |
1593 | + |
1594 | +.notice-notification, |
1595 | +.info-notification, |
1596 | +.debug-notification { |
1597 | + background-position: 5px 0.5em; |
1598 | + padding-left: 30px; |
1599 | + background-color: #ffffcc; |
1600 | + border: 1px solid #eeee00; |
1601 | + color: Black; |
1602 | + background-image: url(/@@/info_icon.gif); |
1603 | + font-size: 85%; |
1604 | + font-weight: bold; |
1605 | + margin: 1em 0em; |
1606 | + padding: 0.5em 1em 0.5em 3em; |
1607 | + background-repeat: no-repeat; |
1608 | +} |
1609 | + |
1610 | +.oopsid { |
1611 | + font-weight: bold; |
1612 | + color: #cc0000; |
1613 | +} |
1614 | + |
1615 | +dl.faq dt { |
1616 | + font-weight: bold; |
1617 | +} |
1618 | + |
1619 | +dl.faq dd { |
1620 | + margin: 0.5em 1em; |
1621 | +} |
1622 | + |
1623 | +dl.faq ol { |
1624 | + margin: 0.5em 3em; |
1625 | +} |
1626 | |
1627 | === added file 'server/sru/sru-report-series.py' |
1628 | --- server/sru/sru-report-series.py 1970-01-01 00:00:00 +0000 |
1629 | +++ server/sru/sru-report-series.py 2012-05-30 18:11:22 +0000 |
1630 | @@ -0,0 +1,285 @@ |
1631 | +#!/usr/bin/python |
1632 | + |
1633 | +import sqlite3 as dbapi2 |
1634 | +import optparse |
1635 | +import datetime, time |
1636 | + |
1637 | +def get_db(dbpath): |
1638 | + db = dbapi2.connect(dbpath) |
1639 | + cur = db.cursor() |
1640 | + return db |
1641 | + |
1642 | +def get_query(db, series): |
1643 | + cursor = db.cursor() |
1644 | + cursor.execute("select * from sru_tracker where release='%s'" %series) |
1645 | + return cursor |
1646 | + |
1647 | +def write_assignee_table(table): |
1648 | + print ''' |
1649 | +<h2>Server SRU Tracker</h2> |
1650 | +''' |
1651 | + print "<p>Generated on: %s </p>" %time.strftime('%x %X UTC', time.gmtime()) |
1652 | + print "<block>Colors in green, the age is between 1 and 10 days old." |
1653 | + print "Colors in orange, the age is between 10 and 20 days old." |
1654 | + print "Colors in yellow, the age is between 21 and 50 days old." |
1655 | + print "Colors in red, the age is 50 days and greater days old.</block>" |
1656 | + |
1657 | + print ''' |
1658 | +<center> |
1659 | +[<a href='#assigned'>Assigned SRU</a>| <a href='#unassigned'>Unassigned SRU</a> | <a href='#verified_bugs'>Bugs need Verification</a> ] |
1660 | +</center> |
1661 | +''' |
1662 | + print ''' |
1663 | +<a name='assigned'></a> |
1664 | +<h2> Assigned Bugs </h2> |
1665 | +<table border='1' id='icons' class='sortable'> |
1666 | +<th><b>SRU Bug</b></th> |
1667 | +<th><b>Source Package</b></th> |
1668 | +<th><b>Series</b></th> |
1669 | +<th><b>Assignee</b></th> |
1670 | +<th><b>Title</b></th> |
1671 | +<th><b>Assigned Age</b></th> |
1672 | +<th><b>Upload Age</b></th> |
1673 | +<th><b>Verification Needed Age</b></th> |
1674 | +<th><b>Verification Success</b></th> |
1675 | +<th><b>Fixed Released</b></th> |
1676 | +''' |
1677 | + |
1678 | + for row in table: |
1679 | + id = row[0] |
1680 | + package = row[1] |
1681 | + release = row[2] |
1682 | + title = row[3] |
1683 | + assignee = row[4] |
1684 | + verification_status = row[5] |
1685 | + upload_status = row[6] |
1686 | + bug_status = row[7] |
1687 | + date_assigned = row[8] |
1688 | + date_uploaded = row[9] |
1689 | + date_verification_needed = row[10] |
1690 | + date_verification_done = row[11] |
1691 | + |
1692 | + |
1693 | + if date_assigned is not None and assignee != "None" and upload_status is not None: |
1694 | + print "<tr>\n" |
1695 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, id) |
1696 | + print "<td><a href='https://bugs.launchpad.net/ubuntu/+source/%s'>%s</a></td>\n" %(package, package) |
1697 | + print "<td>%s</td>\n" %(release) |
1698 | + print "<td><a href='http://launchpad.net/~%s'>%s</a></td>\n" %(assignee, assignee) |
1699 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, title) |
1700 | + |
1701 | + assigned_information = "" |
1702 | + assigned_information = processAssignedInformation(date_assigned) |
1703 | + print assigned_information |
1704 | + |
1705 | + upload_information = "" |
1706 | + upload_information = processUploadInformation(upload_status, date_uploaded) |
1707 | + print upload_information |
1708 | + |
1709 | + verification_need_information = "" |
1710 | + verification_need_information = processDateVerification(verification_status, date_verification_needed) |
1711 | + print verification_need_information |
1712 | + |
1713 | + verification_done_information = "" |
1714 | + verification_done_information = processDateVerificationDone(verification_status, date_verification_done) |
1715 | + print verification_done_information |
1716 | + |
1717 | + print "<td></td>\n" |
1718 | + print "<td></td>\n" |
1719 | + |
1720 | + print "</tr>\n" |
1721 | + |
1722 | + print "</table>\n" |
1723 | + |
1724 | + |
1725 | +def processAssignedInformation(date_assigned): |
1726 | + assigned_age = calculateDays(str(date_assigned)) |
1727 | + if assigned_age == 0: |
1728 | + new_assignee = calculateHours(str(date_assigned)) |
1729 | + assigned_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_assignee) |
1730 | + else: |
1731 | + status_color = ageCheck(assigned_age) |
1732 | + assigned_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, assigned_age) |
1733 | + |
1734 | + return assigned_info |
1735 | + |
1736 | +def processUploadInformation(upload_status, date_uploaded): |
1737 | + if upload_status == "SRU Uploaded": |
1738 | + upload_age = calculateDays(str(date_uploaded)) |
1739 | + if upload_age == 0: |
1740 | + new_upload = calculateHours(str(date_uploaded)) |
1741 | + upload_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_upload) |
1742 | + else: |
1743 | + status_color = ageCheck(upload_age) |
1744 | + upload_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, upload_age) |
1745 | + return upload_info |
1746 | + elif upload_status == "No Upload SRU Status": |
1747 | + upload_info = "<td><p align='center'>-</p></td>\n" |
1748 | + return upload_info |
1749 | + |
1750 | +def processDateVerification(verification_status, date_verification_needed): |
1751 | + if verification_status == "Verification Needed": |
1752 | + verification_age = calculateDays(str(date_verification_needed)) |
1753 | + if verification_age == 0: |
1754 | + new_verification = calculateHours(str(date_verification_needed)) |
1755 | + verification_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_verification) |
1756 | + else: |
1757 | + status_color = ageCheck(verification_age) |
1758 | + verification_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_age) |
1759 | + else: |
1760 | + verification_info = "<td><p align='center'>-</p></td>\n" |
1761 | + return verification_info |
1762 | + |
1763 | +def processDateVerificationDone(verificaton_status, date_verification_done): |
1764 | + if verificaton_status== "Verfication Done": |
1765 | + verification_done_age = calculateDays(str(date_verification_done)) |
1766 | + if verification_done_age == 0: |
1767 | + new_done_verification = calculateHours(str(date_verification_done)) |
1768 | + verification_done_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_done_verification) |
1769 | + else: |
1770 | + status_color = ageCheck(verification_done_age) |
1771 | + verification_done_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_done_age) |
1772 | + else: |
1773 | + verification_done_info = "<td><p align='center'>-</p></td>\n" |
1774 | + return verification_done_info |
1775 | + |
1776 | +def ageCheck(age): |
1777 | + if age >= 1 and age <= 10: |
1778 | + color = "green" |
1779 | + return color |
1780 | + elif age >= 10 and age <= 20: |
1781 | + color = "orange" |
1782 | + return color |
1783 | + elif age >= 20 and age <= 50: |
1784 | + color = "yellow" |
1785 | + return color |
1786 | + elif age >= 50: |
1787 | + color = "red" |
1788 | + return color |
1789 | + |
1790 | +def write_unassigned_table(table): |
1791 | + print ''' |
1792 | +<a name='unassigned'></a> |
1793 | +<h2> Unassigned Bugs </h2></a> |
1794 | +<table border='1' id='icons' class='sortable'> |
1795 | +<th><b>Bug</b></th> |
1796 | +<th><b>Package</b></th> |
1797 | +<th><b>Assigned Age</b></th> |
1798 | +<th><b>Release</b></th> |
1799 | +<th><b>Title</b></th> |
1800 | +''' |
1801 | + for row in table: |
1802 | + id = row[0] |
1803 | + package = row[1] |
1804 | + release = row[2] |
1805 | + title = row[3] |
1806 | + assignee = row[4] |
1807 | + verification_status = row[5] |
1808 | + upload_status = row[6] |
1809 | + bug_status = row[7] |
1810 | + date_assigned = row[8] |
1811 | + date_uploaded = row[9] |
1812 | + date_verification_needed = row[10] |
1813 | + date_verification_done = row[11] |
1814 | + |
1815 | + if assignee == "None" and date_assigned is not None: |
1816 | + assigned_age = calculateDays(str(date_assigned)) |
1817 | + print "<tr>" |
1818 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>" %(id, id) |
1819 | + print "<td>%s</td>" %package |
1820 | + print "<td>%s</td>" %(assigned_age) |
1821 | + print "<td>%s</td>" %release |
1822 | + print "<td>%s</td>" %title |
1823 | + print "</tr>" |
1824 | + print '</table>' |
1825 | + |
1826 | +def write_bugs_verified(table): |
1827 | + print ''' |
1828 | +<a name='verified_bugs'></a> |
1829 | +<h2>Bugs Needed Verification</h2> |
1830 | +<table border='1' id='icons' class='sortable'> |
1831 | +<th><b>Bug</b></th> |
1832 | +<th><b>Package</b></th> |
1833 | +<th><b>Assigned Age</b></th> |
1834 | +<th><b>Release</b></th> |
1835 | +<th><b>Title</b></th> |
1836 | +''' |
1837 | + |
1838 | + for row in table: |
1839 | + id = row[0] |
1840 | + package = row[1] |
1841 | + release = row[2] |
1842 | + title = row[3] |
1843 | + assignee = row[4] |
1844 | + verification_status = row[5] |
1845 | + upload_status = row[6] |
1846 | + bug_status = row[7] |
1847 | + date_assigned = row[8] |
1848 | + date_uploaded = row[9] |
1849 | + date_verification_needed = row[10] |
1850 | + date_verification_done = row[11] |
1851 | + |
1852 | + if verification_status == "Verification Needed": |
1853 | + assigned_age = calculateDays(str(date_assigned)) |
1854 | + print "<tr>" |
1855 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>" %(id, id) |
1856 | + print "<td>%s</td>" %package |
1857 | + print "<td>%s</td>" %(assigned_age) |
1858 | + print "<td>%s</td>" %release |
1859 | + print "<td>%s</td>" %title |
1860 | + print "</tr>" |
1861 | + print "</table>" |
1862 | + |
1863 | +def calculateDays(date): |
1864 | + t = datetime.datetime.now() |
1865 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
1866 | + age = t - now |
1867 | + days = int(age.days) |
1868 | + return days |
1869 | + |
1870 | +def calculateHours(date): |
1871 | + t = datetime.datetime.now() |
1872 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
1873 | + age = t - now |
1874 | + seconds = int(age.seconds) |
1875 | + hours = seconds/3600 |
1876 | + return int(hours) |
1877 | + |
1878 | +if __name__ == '__main__': |
1879 | + optparser = optparse.OptionParser() |
1880 | + optparser.add_option('-d', '--database', |
1881 | + help='Path to database', dest='database', metavar='PATH') |
1882 | + optparser.add_option('-s', '--series', |
1883 | + help='series for sru', dest='series', metavar='SERIES') |
1884 | + |
1885 | + (opts, args) = optparser.parse_args() |
1886 | + |
1887 | + if not opts.database: |
1888 | + optparser.error("Database not specified") |
1889 | + if not opts.series: |
1890 | + optparser.error("series not specified") |
1891 | + |
1892 | + series = opts.series |
1893 | + |
1894 | + db = get_db(opts.database) |
1895 | + |
1896 | + print ''' |
1897 | +<html> |
1898 | +<head> |
1899 | + <style type='text/css' media='screen'>@import url(launchpad.css);</style> |
1900 | + <script type='text/javascript' src='sorttable.js'></script> |
1901 | + <title> Server SRU Tracker </title> |
1902 | +</head> |
1903 | +<body> |
1904 | +''' |
1905 | + table = get_query(db, series) |
1906 | + write_assignee_table(table) |
1907 | + table = get_query(db, series) |
1908 | + write_bugs_verified(table) |
1909 | + table = get_query(db, series) |
1910 | + write_unassigned_table(table) |
1911 | + |
1912 | + print ''' |
1913 | +</body> |
1914 | +</html> |
1915 | +''' |
1916 | |
1917 | === added file 'server/sru/sru-report.py' |
1918 | --- server/sru/sru-report.py 1970-01-01 00:00:00 +0000 |
1919 | +++ server/sru/sru-report.py 2012-05-30 18:11:22 +0000 |
1920 | @@ -0,0 +1,279 @@ |
1921 | +#!/usr/bin/python |
1922 | + |
1923 | +import sqlite3 as dbapi2 |
1924 | +import optparse |
1925 | +import datetime, time |
1926 | + |
1927 | +def get_db(dbpath): |
1928 | + db = dbapi2.connect(dbpath) |
1929 | + cur = db.cursor() |
1930 | + return db |
1931 | + |
1932 | +def get_query(db): |
1933 | + cursor = db.cursor() |
1934 | + cursor.execute('select * from sru_tracker') |
1935 | + return cursor |
1936 | + |
1937 | +def write_assignee_table(table): |
1938 | + print ''' |
1939 | +<h2>Server SRU Tracker</h2> |
1940 | +''' |
1941 | + print "<p>Generated on: %s </p>" %time.strftime('%x %X UTC', time.gmtime()) |
1942 | + print "<block>Colors in green, the age is between 1 and 10 days old." |
1943 | + print "Colors in orange, the age is between 10 and 20 days old." |
1944 | + print "Colors in yellow, the age is between 21 and 50 days old." |
1945 | + print "Colors in red, the age is 50 days and greater days old.</block>" |
1946 | + |
1947 | + print ''' |
1948 | +<center> |
1949 | +[<a href='#assigned'>Assigned SRU</a>| <a href='#unassigned'>Unassigned SRU</a> | <a href='#verified_bugs'>Bugs need Verification</a> ] |
1950 | +</center> |
1951 | +''' |
1952 | + print ''' |
1953 | +<a name='assigned'></a> |
1954 | +<h2> Assigned Bugs </h2> |
1955 | +<table border='1' id='icons' class='sortable'> |
1956 | +<th><b>SRU Bug</b></th> |
1957 | +<th><b>Source Package</b></th> |
1958 | +<th><b>Series</b></th> |
1959 | +<th><b>Assignee</b></th> |
1960 | +<th><b>Title</b></th> |
1961 | +<th><b>Assigned Age</b></th> |
1962 | +<th><b>Upload Age</b></th> |
1963 | +<th><b>Verification Needed Age</b></th> |
1964 | +<th><b>Verification Success</b></th> |
1965 | +<th><b>Fixed Released</b></th> |
1966 | +''' |
1967 | + |
1968 | + for row in table: |
1969 | + id = row[0] |
1970 | + package = row[1] |
1971 | + release = row[2] |
1972 | + title = row[3] |
1973 | + assignee = row[4] |
1974 | + verification_status = row[5] |
1975 | + upload_status = row[6] |
1976 | + bug_status = row[7] |
1977 | + date_assigned = row[8] |
1978 | + date_uploaded = row[9] |
1979 | + date_verification_needed = row[10] |
1980 | + date_verification_done = row[11] |
1981 | + |
1982 | + |
1983 | + if date_assigned is not None and assignee != "None" and upload_status is not None: |
1984 | + print "<tr>\n" |
1985 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, id) |
1986 | + print "<td><a href='https://bugs.launchpad.net/ubuntu/+source/%s'>%s</a></td>\n" %(package, package) |
1987 | + print "<td>%s</td>\n" %(release) |
1988 | + print "<td><a href='http://launchpad.net/~%s'>%s</a></td>\n" %(assignee, assignee) |
1989 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>\n" %(id, title) |
1990 | + |
1991 | + assigned_information = "" |
1992 | + assigned_information = processAssignedInformation(date_assigned) |
1993 | + print assigned_information |
1994 | + |
1995 | + upload_information = "" |
1996 | + upload_information = processUploadInformation(upload_status, date_uploaded) |
1997 | + print upload_information |
1998 | + |
1999 | + verification_need_information = "" |
2000 | + verification_need_information = processDateVerification(verification_status, date_verification_needed) |
2001 | + print verification_need_information |
2002 | + |
2003 | + verification_done_information = "" |
2004 | + verification_done_information = processDateVerificationDone(verification_status, date_verification_done) |
2005 | + print verification_done_information |
2006 | + |
2007 | + print "<td></td>\n" |
2008 | + print "<td></td>\n" |
2009 | + |
2010 | + print "</tr>\n" |
2011 | + |
2012 | + print "</table>\n" |
2013 | + |
2014 | + |
2015 | +def processAssignedInformation(date_assigned): |
2016 | + assigned_age = calculateDays(str(date_assigned)) |
2017 | + if assigned_age == 0: |
2018 | + new_assignee = calculateHours(str(date_assigned)) |
2019 | + assigned_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_assignee) |
2020 | + else: |
2021 | + status_color = ageCheck(assigned_age) |
2022 | + assigned_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, assigned_age) |
2023 | + |
2024 | + return assigned_info |
2025 | + |
2026 | +def processUploadInformation(upload_status, date_uploaded): |
2027 | + if upload_status == "SRU Uploaded": |
2028 | + upload_age = calculateDays(str(date_uploaded)) |
2029 | + if upload_age == 0: |
2030 | + new_upload = calculateHours(str(date_uploaded)) |
2031 | + upload_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_upload) |
2032 | + else: |
2033 | + status_color = ageCheck(upload_age) |
2034 | + upload_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, upload_age) |
2035 | + return upload_info |
2036 | + elif upload_status == "No Upload SRU Status": |
2037 | + upload_info = "<td><p align='center'>-</p></td>\n" |
2038 | + return upload_info |
2039 | + |
2040 | +def processDateVerification(verification_status, date_verification_needed): |
2041 | + if verification_status == "Verification Needed": |
2042 | + verification_age = calculateDays(str(date_verification_needed)) |
2043 | + if verification_age == 0: |
2044 | + new_verification = calculateHours(str(date_verification_needed)) |
2045 | + verification_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_verification) |
2046 | + else: |
2047 | + status_color = ageCheck(verification_age) |
2048 | + verification_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_age) |
2049 | + else: |
2050 | + verification_info = "<td><p align='center'>-</p></td>\n" |
2051 | + return verification_info |
2052 | + |
2053 | +def processDateVerificationDone(verificaton_status, date_verification_done): |
2054 | + if verificaton_status== "Verfication Done": |
2055 | + verification_done_age = calculateDays(str(date_verification_done)) |
2056 | + if verification_done_age == 0: |
2057 | + new_done_verification = calculateHours(str(date_verification_done)) |
2058 | + verification_done_info = "<td bgcolor='blue><p>align='center'>%s</p></td>\n" %(new_done_verification) |
2059 | + else: |
2060 | + status_color = ageCheck(verification_done_age) |
2061 | + verification_done_info = "<td bgcolor='%s'><p align='center'>%s</p></td>\n" %(status_color, verification_done_age) |
2062 | + else: |
2063 | + verification_done_info = "<td><p align='center'>-</p></td>\n" |
2064 | + return verification_done_info |
2065 | + |
2066 | +def ageCheck(age): |
2067 | + if age >= 1 and age <= 10: |
2068 | + color = "green" |
2069 | + return color |
2070 | + elif age >= 10 and age <= 20: |
2071 | + color = "orange" |
2072 | + return color |
2073 | + elif age >= 20 and age <= 50: |
2074 | + color = "yellow" |
2075 | + return color |
2076 | + elif age >= 50: |
2077 | + color = "red" |
2078 | + return color |
2079 | + |
2080 | +def write_unassigned_table(table): |
2081 | + print ''' |
2082 | +<a name='unassigned'></a> |
2083 | +<h2> Unassigned Bugs </h2></a> |
2084 | +<table border='1' id='icons' class='sortable'> |
2085 | +<th><b>Bug</b></th> |
2086 | +<th><b>Package</b></th> |
2087 | +<th><b>Assigned Age</b></th> |
2088 | +<th><b>Release</b></th> |
2089 | +<th><b>Title</b></th> |
2090 | +''' |
2091 | + for row in table: |
2092 | + id = row[0] |
2093 | + package = row[1] |
2094 | + release = row[2] |
2095 | + title = row[3] |
2096 | + assignee = row[4] |
2097 | + verification_status = row[5] |
2098 | + upload_status = row[6] |
2099 | + bug_status = row[7] |
2100 | + date_assigned = row[8] |
2101 | + date_uploaded = row[9] |
2102 | + date_verification_needed = row[10] |
2103 | + date_verification_done = row[11] |
2104 | + |
2105 | + if assignee == "None" and date_assigned is not None: |
2106 | + assigned_age = calculateDays(str(date_assigned)) |
2107 | + print "<tr>" |
2108 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>" %(id, id) |
2109 | + print "<td>%s</td>" %package |
2110 | + print "<td>%s</td>" %(assigned_age) |
2111 | + print "<td>%s</td>" %release |
2112 | + print "<td>%s</td>" %title |
2113 | + print "</tr>" |
2114 | + print '</table>' |
2115 | + |
2116 | +def write_bugs_verified(table): |
2117 | + print ''' |
2118 | +<a name='verified_bugs'></a> |
2119 | +<h2>Bugs Needed Verification</h2> |
2120 | +<table border='1' id='icons' class='sortable'> |
2121 | +<th><b>Bug</b></th> |
2122 | +<th><b>Package</b></th> |
2123 | +<th><b>Assigned Age</b></th> |
2124 | +<th><b>Release</b></th> |
2125 | +<th><b>Title</b></th> |
2126 | +''' |
2127 | + |
2128 | + for row in table: |
2129 | + id = row[0] |
2130 | + package = row[1] |
2131 | + release = row[2] |
2132 | + title = row[3] |
2133 | + assignee = row[4] |
2134 | + verification_status = row[5] |
2135 | + upload_status = row[6] |
2136 | + bug_status = row[7] |
2137 | + date_assigned = row[8] |
2138 | + date_uploaded = row[9] |
2139 | + date_verification_needed = row[10] |
2140 | + date_verification_done = row[11] |
2141 | + |
2142 | + if verification_status == "Verification Needed": |
2143 | + assigned_age = calculateDays(str(date_assigned)) |
2144 | + print "<tr>" |
2145 | + print "<td><a href='http://bugs.launchpad.net/bugs/%s'>%s</a></td>" %(id, id) |
2146 | + print "<td>%s</td>" %package |
2147 | + print "<td>%s</td>" %(assigned_age) |
2148 | + print "<td>%s</td>" %release |
2149 | + print "<td>%s</td>" %title |
2150 | + print "</tr>" |
2151 | + print "</table>" |
2152 | + |
2153 | +def calculateDays(date): |
2154 | + t = datetime.datetime.now() |
2155 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
2156 | + age = t - now |
2157 | + days = int(age.days) |
2158 | + return days |
2159 | + |
2160 | +def calculateHours(date): |
2161 | + t = datetime.datetime.now() |
2162 | + now = datetime.datetime.strptime(date.split(".")[0], "%Y-%m-%d %H:%M:%S") |
2163 | + age = t - now |
2164 | + seconds = int(age.seconds) |
2165 | + hours = seconds/3600 |
2166 | + return int(hours) |
2167 | + |
2168 | +if __name__ == '__main__': |
2169 | + optparser = optparse.OptionParser() |
2170 | + optparser.add_option('-d', '--database', |
2171 | + help='Path to database', dest='database', metavar='PATH') |
2172 | + |
2173 | + (opts, args) = optparser.parse_args() |
2174 | + |
2175 | + if not opts.database: |
2176 | + optparser.error("Database not specified") |
2177 | + |
2178 | + db = get_db(opts.database) |
2179 | + |
2180 | + print ''' |
2181 | +<html> |
2182 | +<head> |
2183 | + <style type='text/css' media='screen'>@import url(launchpad.css);</style> |
2184 | + <script type='text/javascript' src='sorttable.js'></script> |
2185 | + <title> Server SRU Tracker </title> |
2186 | +</head> |
2187 | +<body> |
2188 | +''' |
2189 | + table = get_query(db) |
2190 | + write_assignee_table(table) |
2191 | + table = get_query(db) |
2192 | + write_bugs_verified(table) |
2193 | + table = get_query(db) |
2194 | + write_unassigned_table(table) |
2195 | + |
2196 | + print ''' |
2197 | +</body> |
2198 | +</html> |
2199 | +''' |
The Launchpad edge server has been deprecated for some time now and EDGE_SERVICE_ROOT should not be used.