/[nonfree]/nfts/generate.py
ViewVC logotype

Contents of /nfts/generate.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.38 - (show annotations) (download) (as text)
Sat Apr 12 11:39:33 2008 UTC (5 years, 1 month ago) by paulliu-guest
Branch: MAIN
CVS Tags: HEAD
Changes since 1.37: +3 -2 lines
File MIME type: text/x-python
fix bugs for attribute ID
1 #!/usr/bin/python
2 # "Non-free tracking system for Debian" or "My first python program"
3 #
4 # Copyright (C) 2004-2005 Goran Weinholt <weinholt@debian.org>.
5 # Copyright (C) 2007-2008 Ying-Chun Liu (PaulLiu) <grandpaul@gmail.com>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software Foundation,
19 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20 #
21 # $Id: generate.py,v 1.38 2008/04/12 11:39:33 paulliu-guest Exp $
22
23 # We parse Sources and *.status with this
24 import rfc822
25 import os, sys, time, re, fcntl, string
26 import SOAPpy
27 import xmlrpclib
28 import codecs
29
30 nonfree_mirror = "./mirror"
31 packages = {} # a map of binary -> source
32 sources = {} # a dictionary of Package's
33 removed = {} # removed packages
34 debbugssoap = SOAPpy.SOAPProxy('http://bugs.debian.org/cgi-bin/soap.cgi','Debbugs/SOAP')
35 debianwiki = xmlrpclib.ServerProxy("http://wiki.debian.org/?action=xmlrpc2")
36
37 class Package:
38 def __init__(self, package, binaries, version, directory, orphaned, nmu):
39 self.package = package
40 self.binaries = binaries
41 self.version = version
42 self.directory = directory
43 self.orphaned = orphaned
44 self.nmu = nmu
45 self.archs = {}
46 self.popcon = (0,0,0,0,0)
47 self.last_upload = 0
48 self.reason = ""
49 self.resolution = ""
50 self.replacements = ""
51 self.wnpp = []
52 self.rcbugs = 0
53 self.nonrcbugs = 0
54 self.ftpbugs = {}
55 self.freshness = 0
56 self.wikipage = ""
57 self.wikipagedata = ""
58
59 # Remove the file that indicates to the crontab that we need to update
60 try: os.unlink("needs_update")
61 except: pass
62
63 # Parse Sources
64 f = open("Sources")
65 # Match versions of the form 0.1.2-0.1
66 regex = re.compile('-[^.]+\.[^-]+$')
67 while True:
68 # Parse one package
69 hdr = rfc822.Message(f)
70 if not hdr.has_key('Package'):
71 break
72 # Build a map of Binary->Source
73 for binary in hdr['Binary'].split(', '):
74 packages[binary] = hdr['Package']
75 if hdr['Maintainer'].find('packages@qa.debian.org') > 0:
76 orphaned = True
77 else:
78 orphaned = False
79 nmu = bool(regex.search(hdr['Version']))
80 sources[hdr['Package']] = Package(hdr['Package'],
81 hdr['Binary'].split(', '),
82 hdr['Version'],
83 hdr['Directory'], orphaned, nmu)
84 f.close()
85
86 # Parse the list of outdated packages
87 f = open("unstable_outdate.txt")
88 while True:
89 line = f.readline()
90 if line == "" or line.startswith("Columns: "):
91 break
92 line = line.split()
93 package = line[0]
94 arch = line[2].split('(')[1].split(')')[0]
95 if package in sources:
96 sources[package].archs[arch] = None
97
98 # Parse the popcon data
99 f = open("popcon-non-free-by_inst")
100 while True:
101 line = f.readline()
102 if line == "" or line.startswith('-'):
103 break
104 if line.startswith('#'):
105 continue
106 line = line.split()
107 package = line[1]
108 # Map binary package name to source
109 if package in packages:
110 source = packages[package]
111 else:
112 continue # package exists in popcon but not in the archive
113 # Add the numbers to the source package
114 sources[source].popcon = map(lambda a, b: a + int(b),
115 sources[source].popcon,
116 (line[2],line[3],line[4],line[5],line[6]))
117 # Divide the popcon numbers by the number of binary packages the
118 # source package produces
119 for source in sources:
120 sources[source].popcon = map(lambda a:
121 int(round(float(a) /
122 len(sources[source].binaries))),
123 sources[source].popcon)
124
125 # Find out when each package was last uploaded
126 for source in sources:
127 if sources[source].version.find(':') != -1:
128 ourversion = sources[source].version.split(':')[1]
129 else:
130 ourversion = sources[source].version
131 try:
132 st = os.stat(nonfree_mirror + '/' + sources[source].directory + '/' +
133 source + "_" + ourversion + ".dsc")
134 except OSError, e:
135 dir = os.getcwd()
136 os.system("mkdir -p " + nonfree_mirror + '/' + sources[source].directory)
137 os.chdir(nonfree_mirror + '/' + sources[source].directory)
138 os.system('wget --timestamping http://ftp.debian.org/debian/'+ sources[source].directory + '/' + source + "_" + ourversion + ".dsc")
139 os.chdir(dir)
140 print "Couldn't find " + source + "_" + ourversion + ".dsc"
141 try:
142 st = os.stat(nonfree_mirror + '/' + sources[source].directory + '/' + source + "_" + ourversion + ".dsc")
143 except OSError, e:
144 print "Couldn't download " + source + "_" + ourversion + ".dsc"
145 continue
146 sources[source].last_upload = st.st_mtime
147
148 # Get WNPP data
149 #lines = open("wnpp-data.txt").readlines()
150 #regex = re.compile('.* #(\d+): (\w+): ([^ ]+) --')
151 #bugs = [ regex.match(line) for line in lines ]
152 #bugs = [ m.groups() for m in bugs if m ]
153 lines = debbugssoap.get_bugs("package","wnpp","status",["open","forwarded","pending","fixed"])
154 lines = debbugssoap.get_status(lines)
155 if (lines):
156 lines = lines.item
157 else:
158 lines = []
159 lines = [ l for l in lines if l.value.pending != "done" ]
160 bugs = [ (str(m.value.id),m.value.subject) for m in lines ]
161 regex = re.compile('(\w+): ([^ ]+) --')
162 bugs = [ (bugid, regex.match(bugsubject)) for bugid,bugsubject in bugs ]
163 bugs = [ (bugid, m.groups()[0], m.groups()[1]) for bugid,m in bugs if m ]
164 for bug, kind, pkg in bugs:
165 if pkg in sources:
166 sources[pkg].wnpp.append((bug, kind))
167
168 # Parse the page with the bugs
169 #lines = open("bugs.txt").readlines()
170 #regex = re.compile('\s+Package(?:s)*: ([^;]+); (Severity: ([^;]+); )?Reported by:')
171 #pkgs = [ regex.match(s) for s in lines ]
172 #pkgs = [ m.groups() for m in pkgs if m ]
173 lines = debbugssoap.get_bugs("src", [ pkg for pkg in sources ],"status",["open","forwarded","pending","fixed"])
174 lines = debbugssoap.get_status(lines)
175 if (lines):
176 lines = lines.item
177 else:
178 lines = []
179 lines = [ l for l in lines if (l.value.pending != "done" and l.value.pending != "pending-fixed" and l.value.pending != "fixed") ]
180 pkgs = [ (m.value.package, m.value.found_versions, m.value.severity) for m in lines ]
181 for pkg, tmp, severity in pkgs:
182 if pkg in packages:
183 p = sources[packages[pkg]] # bug filed against a binary pacakge
184 elif pkg in sources:
185 p = sources[pkg] # bug filed against a source pacakge
186 else:
187 continue # bug filed against a package we don't have
188 if severity == 'critical' or severity == 'grave' or severity == 'serious':
189 p.rcbugs += 1
190 else:
191 p.nonrcbugs += 1
192
193 # Parse the ftp.debian.org bug data
194 #lines = open("ftp-bugs.txt").readlines()
195 #regex = re.compile('.* #(\d+): (.*)')
196 #bugdesc = [ regex.match(s) for s in lines ]
197 #bugdesc = [ m.groups() for m in bugdesc if m ]
198 #regex = re.compile('\s+(Package(?:s)*: .*)')
199 #bugdata = [ regex.match(s) for s in lines ]
200 #bugdata = [ m.groups() for m in bugdata if m ]
201 lines = debbugssoap.get_bugs("package", "ftp.debian.org","status",["open","forwarded","pending","fixed"])
202 lines = debbugssoap.get_status(lines)
203 if (lines):
204 lines = lines.item
205 else:
206 lines = []
207 lines = [ l for l in lines if l.value.pending != "done" ]
208 bugdesc = [ (str(line.value.id), line.value.subject) for line in lines ]
209 bugdata = [ (line.value.package, line.value.tags) for line in lines ]
210 #regex = re.compile('Tags: ([^;]+);')
211 for (num, desc), data in zip(bugdesc, bugdata):
212 #tags = regex.search(data[0])
213 tags = data[1]
214 if tags:
215 #tags = tags.groups()[0].split(', ')
216 tags = tags.split(' ')
217 # Skip all bugs already fixed in sid
218 if ('woody' in tags or 'sarge' in tags or 'potato' in tags) \
219 and not 'sid' in tags:
220 continue
221 words = map(lambda s: s.strip(':"').lower(), desc.split())
222 for pkg in sources.keys()+packages.keys():
223 if pkg in words:
224 if pkg in sources:
225 package = sources[pkg]
226 else:
227 package = sources[packages[pkg]]
228 package.ftpbugs[num] = desc
229
230 # Get the metadata that applies to packages
231 debianwikipackages = debianwiki.getAllPages()
232 regex = re.compile('^NonFreeTrackingSystem/SourcePackage/(.*)')
233 debianwikipackages = [ regex.match (s) for s in debianwikipackages ]
234 debianwikipackages = [ m.groups()[0] for m in debianwikipackages if m ]
235 for source in sources:
236 try:
237 f = open("db/"+source+".status")
238 except IOError, e:
239 #open("db/"+source+".status", "w+").close()
240 sources[source].wikipage = "NonFreeTrackingSystem/SourcePackage/"+source
241 if (source in debianwikipackages):
242 wikipageinfo = debianwiki.getPageInfo(sources[source].wikipage);
243 sources[source].wikipagedata = debianwiki.getPageHTML(sources[source].wikipage);
244 if (wikipageinfo.has_key('lastModified')):
245 sources[source].freshness = time.mktime(time.strptime(wikipageinfo['lastModified'].value,"%Y%m%dT%H:%M:%S"))
246 pass
247 continue
248 fcntl.flock(f, fcntl.LOCK_SH)
249 metadata = rfc822.Message(f)
250 if metadata.has_key('Reason'):
251 sources[source].reason = metadata['Reason']
252 if metadata.has_key('Resolution'):
253 sources[source].resolution = metadata['Resolution']
254 if metadata.has_key('Replacements'):
255 sources[source].replacements = metadata['Replacements']
256 fcntl.flock(f, fcntl.LOCK_UN)
257 f.close()
258 try:
259 s = os.stat("db/"+source+".mbox")
260 sources[source].freshness = s.st_mtime
261 s1 = os.stat("db/"+source+".status")
262 if (s1.st_mtime > s.st_mtime):
263 sources[source].freshness = s1.st_mtime
264 except OSError, e:
265 pass
266
267 # Try to find orphaned .status files
268 try:
269 dbdirlist = os.listdir("db/")
270 except:
271 dbdirlist = []
272 for f in filter(lambda d: d.endswith(".status"), dbdirlist):
273 pkg = f[0:f.rfind('.')]
274 if pkg in sources:
275 continue
276 # Found an orphaned .status file
277 try: f = open("db/"+f)
278 except: continue
279 removed[pkg] = (dict(rfc822.Message(f)), [])
280 f.close()
281 for f in debianwikipackages:
282 pkg = f;
283 if pkg in sources:
284 continue
285 # Found an orphaned wiki page
286 f = debianwiki.getPageHTML("NonFreeTrackingSystem/SourcePackage/"+pkg)
287 if ((type(f) is str or type(f) is unicode) and f):
288 removed[pkg] = ({'wikipagedata': f},[])
289
290 # Parse removals.txt and the add data to "removed"
291 f = open("removals.txt")
292 # Skip to the first entry
293 while f.readline() not in ('', "="*73+'\n'): continue
294 while line:
295 lines = []
296 # Read a whole entry
297 while True:
298 line = f.readline()
299 if line == "="*44+'\n': # silly error in removals.txt
300 continue
301 if line == "="*73+'\n' and lines == []:
302 continue
303 if line in ('', "="*73+'\n'):
304 break
305 lines.append(line.rstrip())
306 if lines == []:
307 break
308 # Ignore entries that don't remove something from unstable
309 if not lines[1].endswith("unstable:"):
310 continue
311 rems = [ l.split(" | ") for l in lines ]
312 rems = [ map(lambda e: e.strip(), r) for r in rems if len(r)==3 ]
313 for rem in rems:
314 pkg = rem[0]
315 archs = rem[2].replace(',', '').split()
316 if 'source' not in archs:
317 continue
318 if pkg not in removed:
319 continue
320 removed[pkg][1].append(lines)
321
322 def fixWikiHTMLPage(data,padding):
323 cpadding = padding.replace('+','_plus_')
324 ret = data.replace('<br />','<br>')
325 ret = re.sub(r'([<]p class=\"line867\"[>])',"",ret)
326 ret = re.sub(r'([<][^>]*id=["])([^>]*[>])',r'\1'+cpadding+r'\2',ret)
327 return ret
328
329 # The function that actually outputs the index* pages
330 def output_page(filename, srcs):
331 f = codecs.open(filename + ".new", "w", "utf-8")
332
333 f.write(r"""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
334 <html>
335 <head>
336 <title>Non-free tracking system</title>
337 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
338 <link rel="stylesheet" href="nfts.css">
339 </head>
340 <body>
341 <h1>Non-free tracking system</h1>
342 <p>
343 Go <a href="removed.html">here</a> to see a list of removed packages.<br>
344 """)
345 f.write("There are %d source packages in non-free.<br>" % len(srcs))
346 f.write(r"""
347 The interface to the <a href="control.html">control bot</a>.
348 </p>
349 <hr>
350 <p>
351 Legend:<br>
352 &ldquo;<b>Package</b>&rdquo; is the source package's name.<br>
353 &ldquo;<b>Orphaned and NMU</b>&rdquo; is the orphaned and NMU status.
354 &ldquo;QA&rdquo; indicates that the maintainer is set to QA.
355 If there exists a WNPP bug for this package, it will be shown here.
356 &ldquo;NMU&rdquo; indicates that the last upload was a non-maintainer
357 upload.<br>
358 &ldquo;<b>RC bugs</b>&rdquo; indicates how many release critical bugs
359 the package has.<br>
360 &ldquo;<b>Bugs</b>&rdquo; is the number of non-RC bugs.<br>
361 &ldquo;<b>Inst</b>&rdquo; is the number of installations
362 for all the binary packages as reported by
363 <a href="http://popcon.debian.org/">popcon</a>, divided by the number
364 of binary packages.<br>
365 &ldquo;<b>Votes</b>&rdquo; is the number of
366 <a href="http://popcon.debian.org/">popcon</a> votes for the binary
367 packages the source package produces, divided by the number of packages.
368 <br>
369 &ldquo;<b>Last upload</b>&rdquo; shows when the last upload was made.<br>
370 &ldquo;<b>Freshness</b>&rdquo; indicates when the control bot last
371 processed an update for the package.<br>
372 &ldquo;<b>Outdated archs</b>&rdquo; shows which archs have an older version
373 and therefore are keeping the package from entering testing.
374 </p>
375 <table class="lefttable">
376 <tr class="titlerow">
377 <td class="titlecell" width="10%">
378 <a class="titlelink" href="index.html">Package</a>
379 </td>
380 <td class="titlecell" width="7%">Orphaned and NMU</td>
381 <td class="titlecell" width="3%">RC bugs</td>
382 <td class="titlecell" width="3%">
383 <a class="titlelink" href="index_bugs.html">Bugs</a>
384 </td>
385 <td class="titlecell" width="3%">
386 <a class="titlelink" href="index_inst.html">Inst.</a>
387 </td>
388 <td class="titlecell" width="4%">
389 <a class="titlelink" href="index_votes.html">Votes</a>
390 </td>
391 <td class="titlecell" width="5%">
392 <a class="titlelink" href="index_last_upload.html">Last upload</a>
393 </td>
394 <td class="titlecell" width="5%">
395 <a class="titlelink" href="index_freshness.html">Freshness</a>
396 </td>
397 <td class="titlecell" width="9%">Outdated archs</td>
398 <td class="titlecell">Reason, replacements, resolution</td>
399 </tr>
400 """)
401 for source in srcs:
402 f.write("<tr class=\"normalrow\">\n")
403 f.write("<td class=\"contentcell\">")
404 f.write("<a href=\"http://packages.qa.debian.org/" + source.package +
405 "\">" + source.package + "</a> ")
406 f.write("(<a href=\"http://packages.debian.org/changelogs/"+
407 source.directory+"\">copyright</a>)")
408 f.write("</td>")
409 # Orphaned
410 f.write("<td class=\"contentcell\">")
411 if source.orphaned:
412 f.write("QA ")
413 for bug, kind in source.wnpp:
414 f.write("<a href=\"http://bugs.debian.org/" + bug + "\">" +
415 kind + ': #' + bug + "</a> ")
416 if source.nmu:
417 f.write("NMU")
418 f.write("</td>")
419 # RC bugs
420 f.write("<td class=\"contentcell\">")
421 if source.rcbugs > 0:
422 f.write("<a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=src&amp;data="+source.package+"&amp;archive=no&amp;pend-exc=pending-fixed&amp;pend-exc=fixed&amp;pend-exc=done&amp;sev-inc=critical&amp;sev-inc=grave&amp;sev-inc=serious\">" +
423 str(source.rcbugs) + "</a> ")
424 f.write("</td>")
425 # non-RC Bugs
426 f.write("<td class=\"contentcell\">")
427 if source.nonrcbugs > 0:
428 f.write("<a href=\"http://bugs.debian.org/cgi-bin/pkgreport.cgi?which=src&amp;data="+source.package+"&amp;archive=no&amp;pend-exc=pending-fixed&amp;pend-exc=fixed&amp;pend-exc=done&amp;sev-exc=critical&amp;sev-exc=grave&amp;sev-exc=serious\">" +
429 str(source.nonrcbugs) + "</a> ")
430 f.write("</td>")
431 # popcon
432 f.write("<td class=\"contentcell\">" + str(source.popcon[0]) + "</td>")
433 f.write("<td class=\"contentcell\">" + str(source.popcon[1]) + "</td>")
434 # Last upload, in full color
435 f.write("<td class=\"contentcell\"><div class=\"")
436 if source.last_upload < int(time.time()) - 3*86400*365:
437 f.write("ancient")
438 elif source.last_upload < int(time.time()) - 2*86400*365:
439 f.write("old")
440 elif source.last_upload < int(time.time()) - 1*86400*365:
441 f.write("dusty")
442 else:
443 f.write("normal")
444 f.write("\">")
445 if source.last_upload != 0:
446 f.write(time.strftime("%Y-%m-%d",
447 time.gmtime(source.last_upload)))
448 f.write("</div></td>")
449 # Freshness of our data
450 f.write("<td class=\"contentcell\"><div class=\"")
451 if source.freshness < int(time.time()) - 3*86400*365:
452 f.write("ancient")
453 elif source.freshness < int(time.time()) - 2*86400*365:
454 f.write("old")
455 elif source.freshness < int(time.time()) - 1*86400*365:
456 f.write("dusty")
457 else:
458 f.write("normal")
459 f.write("\">")
460 if source.freshness != 0:
461 f.write(time.strftime("%Y-%m-%d",time.gmtime(source.freshness)))
462 f.write("</div></td>")
463 # Outdated
464 f.write("<td class=\"contentcell\">")
465 archs = source.archs.keys()
466 archs.sort()
467 f.write(string.join(archs, ' '))
468 f.write("</td>")
469 # Information about the package
470 f.write("<td class=\"contentcell\">")
471 if source.wikipage:
472 wikipagedata = source.wikipagedata
473 if ((type(wikipagedata) is str or type(wikipagedata) is unicode) and wikipagedata):
474 f.write(fixWikiHTMLPage(wikipagedata,source.package+"_"))
475 else:
476 if source.reason:
477 f.write("<b>Reason for being non-free</b>: " +
478 source.reason + "<br>")
479 if source.replacements:
480 f.write("<b>Replacements</b>: " + source.replacements
481 + "<br>")
482 if source.resolution:
483 f.write("<b>Resolution</b>:<br>" + source.resolution + "<br>")
484 if source.ftpbugs:
485 f.write("<b>Possibly related ftp.debian.org bugs</b>:")
486 for bug in source.ftpbugs:
487 f.write('<br><a href="http://bugs.debian.org/%s">#%s</a>: %s' %
488 (bug,bug,source.ftpbugs[bug]))
489 if source.wikipage:
490 f.write("<a href=\"http://wiki.debian.org/"+source.wikipage+"\">&#9998;</a>")
491 else:
492 f.write("<a href=\"modifyinfo.php?packagename="+source.package+"\">&#9998;</a>")
493 f.write("</td>")
494
495 f.write("</tr>\n")
496
497 f.write("</table>")
498
499 f.write("<hr><p>Copyright &copy; 2004-2005 G&ouml;ran Weinholt.<br>\n")
500 f.write("Copyright &copy; 2007-2008 Ying-Chun Liu (PaulLiu)<br>\n")
501 f.write("Last modified: " +
502 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) +
503 "</p>\n</body></html>\n")
504 f.close()
505
506 # Atomically rename the new page
507 os.rename(filename + ".new", filename)
508
509 def output_removed(filename, removed):
510 f = codecs.open(filename + ".new", "w", "utf-8")
511
512 f.write(r"""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
513 <html>
514 <head>
515 <title>Non-free tracking system</title>
516 <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
517 <link rel="stylesheet" href="nfts.css">
518 </head>
519 <body>
520 <h1>Non-free tracking system</h1>
521 <p>
522 This page lists the source packages that have been removed from
523 non-free and for which there is a record in the database.<br>
524 Click <a href="index.html">here</a> to go back to the main page.
525 </p>
526 """)
527
528 f.write(r"""
529 <table class="lefttable" width="100%">
530 <tr class="titlerow">
531 <td class="titlecell" width="6%">Package</td>
532 <td class="titlecell">Reason, replacements, resolution</td>
533 <td class="titlecell">Removal</td>
534 """)
535
536 pkgs = list(removed)
537 pkgs.sort()
538 for pkg in pkgs:
539 info, removal = removed[pkg]
540 f.write("<tr class=\"normalrow\">\n")
541 # Package name
542 f.write("<td class=\"contentcell\">" + pkg + "</td>")
543 # Our information about the package
544 f.write("<td class=\"contentcell\">")
545 if 'reason' in info:
546 f.write("<b>Reason for being non-free</b>: " +
547 info['reason'] + "<br>")
548 if 'replacements' in info:
549 f.write("<b>Replacements</b>: " + info['replacements']
550 + "<br>")
551 if 'resolution' in info:
552 f.write("<b>Resolution</b>:<br>" + info['resolution'])
553 if 'wikipagedata' in info:
554 f.write(fixWikiHTMLPage(info['wikipagedata'],pkg+"_"))
555 f.write("</td>")
556 # Cause for removal
557 f.write("<td class=\"contentcell\">")
558 for r in removal:
559 f.write("<tt>")
560 lines = []
561 for line in r:
562 # Add links to the BTS
563 if line.startswith("Closed bugs:"):
564 line = re.sub("(\d+)",
565 r'<a href="http://bugs.debian.org/\1">\1</a>',
566 line)
567 lines.append(line)
568 f.write("<br>\n".join(lines))
569 f.write("</tt>")
570 f.write("</td>")
571 f.write("</tr>\n")
572
573 f.write("</table>\n")
574
575 f.write("<hr><p>Copyright &copy; 2004-2005 G&ouml;ran Weinholt.<br>\n")
576 f.write("Last modified: " +
577 time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) +
578 "</p>\n</body></html>\n")
579 f.close()
580
581 # Atomically rename the new page
582 os.rename(filename + ".new", filename)
583
584 ## Output our pages
585 # Sort by package name
586 srcs = sources.values()
587 srcs.sort(lambda p1, p2: cmp(p1.package, p2.package))
588 output_page("index.html", srcs)
589 # Sort by popcon installations, package name
590 srcs.sort(lambda p1, p2: cmp(p2.popcon[0], p1.popcon[0]) or
591 cmp(p1.package, p2.package))
592 output_page("index_inst.html", srcs)
593 # Sort by popcon votes, package name
594 srcs.sort(lambda p1, p2: cmp(p2.popcon[1], p1.popcon[1]) or
595 cmp(p1.package, p2.package))
596 output_page("index_votes.html", srcs)
597 # Sort by last upload, package name
598 srcs.sort(lambda p1, p2: cmp(p1.last_upload, p2.last_upload) or
599 cmp(p1.package, p2.package))
600 output_page("index_last_upload.html", srcs)
601 # Sort by freshness of our data
602 srcs.sort(lambda p1, p2: cmp(p1.freshness, p2.freshness) or
603 cmp(p1.package, p2.package))
604 output_page("index_freshness.html", srcs)
605 # Sort by bugs, package name
606 srcs.sort(lambda p1, p2: cmp(p2.nonrcbugs, p1.nonrcbugs) or
607 cmp(p1.package, p2.package))
608 output_page("index_bugs.html", srcs)
609 # The page with removed packages
610 output_removed("removed.html", removed)

  ViewVC Help
Powered by ViewVC 1.1.5