| 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 |
“<b>Package</b>” is the source package's name.<br>
|
| 353 |
“<b>Orphaned and NMU</b>” is the orphaned and NMU status.
|
| 354 |
“QA” indicates that the maintainer is set to QA.
|
| 355 |
If there exists a WNPP bug for this package, it will be shown here.
|
| 356 |
“NMU” indicates that the last upload was a non-maintainer
|
| 357 |
upload.<br>
|
| 358 |
“<b>RC bugs</b>” indicates how many release critical bugs
|
| 359 |
the package has.<br>
|
| 360 |
“<b>Bugs</b>” is the number of non-RC bugs.<br>
|
| 361 |
“<b>Inst</b>” 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 |
“<b>Votes</b>” 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 |
“<b>Last upload</b>” shows when the last upload was made.<br>
|
| 370 |
“<b>Freshness</b>” indicates when the control bot last
|
| 371 |
processed an update for the package.<br>
|
| 372 |
“<b>Outdated archs</b>” 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&data="+source.package+"&archive=no&pend-exc=pending-fixed&pend-exc=fixed&pend-exc=done&sev-inc=critical&sev-inc=grave&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&data="+source.package+"&archive=no&pend-exc=pending-fixed&pend-exc=fixed&pend-exc=done&sev-exc=critical&sev-exc=grave&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+"\">✎</a>")
|
| 491 |
else:
|
| 492 |
f.write("<a href=\"modifyinfo.php?packagename="+source.package+"\">✎</a>")
|
| 493 |
f.write("</td>")
|
| 494 |
|
| 495 |
f.write("</tr>\n")
|
| 496 |
|
| 497 |
f.write("</table>")
|
| 498 |
|
| 499 |
f.write("<hr><p>Copyright © 2004-2005 Göran Weinholt.<br>\n")
|
| 500 |
f.write("Copyright © 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 © 2004-2005 Gö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)
|