Merge lp:~zulcss/ubuntu-reports/server-sru-reports into lp:ubuntu-reports

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
Reviewer Review Type Date Requested Status
Ubuntu Reports Dev Team Pending
Review via email: mp+108029@code.launchpad.net

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 :

The Launchpad edge server has been deprecated for some time now and EDGE_SERVICE_ROOT should not be used.

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.distributions['ubuntu']
        for series in ubuntu.series:
            release_tags.append(series.name)

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+'''

Subscribers

People subscribed via source and target branches