#!/usr/bin/python
# -*- coding: utf8 -*-
# Make sure tabs expand to 8 spaces in vim
# vim: expandtab
# Copyright 2002 Raphaƫl Hertzog
# Copyright 2005 Jeroen van Wolffelaar
# Copyright 2007-2008 Stefano Zacchiroli
# This file is distributed under the terms of the General Public License
# version 2 or (at your option) any later version.
import os.path, rfc822, sys, string, re, email, common, cPickle, syck
from xml.dom import implementation, ext
from config import dir, odir, root
# Create the source <-> binaries correspondances
source2binaries = {} # maps a source package to its binaries
binary2source = {} # maps a binary package to its source
f = open(dir + "/sources.map", "r")
while 1:
line = f.readline();
if not line: break #eof
line = line.strip()
(binary, source) = line.split(None, 1)
if not source2binaries.has_key(source):
source2binaries[source] = []
source2binaries[source].append(binary)
binary2source[binary] = source
f.close()
def read_shortdesc(fname):
global binary2source
source_descs = {} # source package -> (binary package -> short description)
for line in open(fname):
pkg, shortdesc = line.strip().split("\t", 1)
if not binary2source.has_key(pkg):
continue
src = binary2source[pkg]
if not source_descs.has_key(src):
source_descs[src] = {}
source_descs[src][pkg] = shortdesc
return source_descs
def read_bug_summary(fname):
global binary2source
summary = {} # source package -> bug count
for line in open(fname):
pkg, count = line.split()
if not binary2source.has_key(pkg):
continue
src = binary2source[pkg]
if not summary.has_key(src):
summary[src] = 0
summary[src] += int(count)
return summary
# Read all the bugs stats
bugs = {}
f = open(dir + "/bugs.txt")
while 1:
line = f.readline()
if not line: break #eof
line = line.strip()
(binary, stats) = line.split(None, 1)
bugs[binary] = [ string.atoi(i) for i in stats.split() ]
f.close()
srcbugs = {}
f = open(dir + "/bugs-src.txt")
while 1:
line = f.readline()
if not line: break #eof
line = line.strip()
(pkg, stats) = line.split(":", 1)
try:
srcbugs[pkg] = [ string.atoi(i) for i in stats.replace("(", " ").replace(")", " ").split() ]
except:
sys.stderr.write("Failed to parse bugs-src.txt stats for %s: %s" % (pkg, stats))
f.close()
gift_bugs = read_bug_summary(os.path.join(dir, 'bugs.gift.txt'))
help_bugs = read_bug_summary(os.path.join(dir, 'bugs.help.txt'))
# Read all the PTS stats
pts = {}
f = open(dir + "/count.txt")
while 1:
line = f.readline()
if not line: break #eof
line = line.strip()
(binary, stats) = line.split(None, 1)
pts[binary] = stats
f.close()
# Read the lisf of packages with debcheck problems
debcheck = {}
for dist in ("stable", "testing", "unstable"):
debcheck[dist] = {}
f = open(dir + "/debcheck-" + dist)
while 1:
line = f.readline()
if not line: break #eof
debcheck[dist][line.strip()] = 1
f.close()
# Read the list of packages with override disparities
override = {}
for dist in ("unstable", "experimental"):
override[dist] = {}
f = open(dir + "/override-disparities." + dist)
for line in f:
if line[0] != '-':
source = line.strip()[:-1]
override[dist][source] = []
else:
override[dist][source].append(line.strip()[2:])
f.close()
# Read the list of source packages with debconf templates
debconf = {}
# DISABLED until ddtp.debian.org is setup again
#f = open(dir + "/debconf-list")
#while 1:
# line = f.readline()
# if not line: break #eof
# line = line.strip()
# debconf[line] = 1;
#f.close()
# Read the current signature of other.xml files
sigs = {}
if os.path.exists(odir + "/other.sigs"):
f = open(odir + "/other.sigs", "r")
sigs = cPickle.load(f)
f.close()
# Read the wnpp information. [PvR]
wnpp = {}
if os.path.exists(dir + "/wnpp_rm"):
f = open(dir + "/wnpp_rm")
while 1:
line = f.readline()
if not line: break # eof
line = line.strip()
try:
(package, type, number) = line.split("|")[0].split()
except:
#too many badly formatted ITP... disable warning. --RH
#sys.stderr.write("Ignoring bad line '%s' in wnpp_rm\n" % line)
pass
wnpp[package[:-1]] = (type, number)
f.close()
# Read patches information [FG]
ubuntu_patches = {}
# this can be easily inserted into a new update_patches.py if it becomes too
# heavy to parse the file
if os.path.exists(dir + "/patches.ubuntu"):
f = open(dir + "/patches.ubuntu")
for line in f.readlines():
(package, rel_url) = line.split(' ', 2)
rel_url = rel_url.strip()
r = re.search("_(\S+).patch", line)
if not r:
continue
version = r.group(1)
ubuntu_patches[package] = (version, "http://patches.ubuntu.com/" + rel_url)
f.close()
ubuntu_versions = {}
if os.path.exists(dir + "/versions.ubuntu"):
f = open(dir + "/versions.ubuntu")
for line in f.readlines():
(package, version) = line.split(' ', 2)
version = version.strip()
ubuntu_versions[package] = (version, "https://launchpad.net/ubuntu/+source/" + package)
f.close()
ubuntu_bugs = {}
if os.path.exists(dir + "/bugs.ubuntu"):
f = open(dir + "/bugs.ubuntu")
for line in f.readlines():
(package, count) = line.split('|', 2)
count = count.strip()
ubuntu_bugs[package] = (count, "https://bugs.launchpad.net/ubuntu/+source/" + package)
f.close()
# read low threshold NMU infos
def read_low_threshold_nmu(fname):
emails = []
if os.path.exists(fname):
f = open(fname)
devel_php_RE = \
re.compile(r'http://qa\.debian\.org/developer\.php\?login=([^\s&]+)')
word_RE = re.compile(r'^\w+$')
for line in f.readlines():
match = devel_php_RE.search(line)
while match: # look for several matches on the same line
email = None
login = match.group(1)
if word_RE.match(login):
email = login + '@debian.org'
elif login.find('@') >= 0:
email = login
if email:
emails.append(email)
line = line[match.end():]
match = devel_php_RE.search(line)
f.close()
return emails
# write lowThresholdNmu info to a (global, i.e. not per-package) file
#
# XXX this is sub-optimal, as the XSLT rendering of each package page will have
# to read the whole XML file each time. However, this can't be fixed here
# unless we have somewhere an additional map (in the spirit of sources.map)
# mapping source package names to maintainer emails
low_nmu_emails = read_low_threshold_nmu(dir + "/low_threshold_nmu.txt")
f = open(odir + "/low_threshold_nmu.emails.xml", 'w')
f.write("\n");
f.writelines(map(lambda s: " %s\n" % s, low_nmu_emails))
f.write("\n");
f.close()
def read_transitions(fname):
f = open(fname)
markup = f.read()
f.close()
yaml = syck.load(markup)
packages = {} # maps pkg to the _list_ of transitions they are involved in
for id, transition in yaml.iteritems():
for pkg in transition['packages']:
if not packages.has_key(pkg):
packages[pkg] = []
packages[pkg].append(id)
return packages
# read the list of packages involved in transitions
transitions = read_transitions(os.path.join(dir, "transitions.yaml"))
def read_piuparts(fname):
failures = {}
if os.path.exists(fname):
f = open(fname)
for line in f.readlines():
try:
src, outcome, log = line.rstrip().split(None, 2)
if outcome == "fail":
failures[src] = log
except ValueError:
pass
f.close()
return failures
piuparts = read_piuparts(os.path.join(dir, "piuparts.txt"))
def read_lintian_info(fname):
lintian = {} # maps source pkg names to pairs
for line in open(fname).readlines():
try:
(pkg, errors_no, warnings_no) = line.split()
lintian[pkg] = (int(errors_no), int(warnings_no))
except ValueError:
continue
return lintian
# read QA lintian info
lintian = read_lintian_info(os.path.join(dir, "lintian.qa-list.txt"))
# read the list of packages indexed by svnbuildstat.debian.net
svnbuildstat = {}
f = open(os.path.join(dir, "svnbuildstat_list.txt"))
for pkgname in map(string.rstrip, f.readlines()):
svnbuildstat[pkgname] = True
f.close()
# DEHS textual file are line oriented with lines like "field: value"
def read_dehs(fname):
f = open(fname)
for line in f.readlines():
yield map(string.strip, line.split(':'))
f.close()
# read info gathered from dehs.alioth.debian.org
dehs = {}
for pkgname, version in read_dehs(os.path.join(dir, "dehs_out_of_date.txt")):
if not dehs.has_key(pkgname):
dehs[pkgname] = {}
dehs[pkgname]['newer'] = version
for pkgname, msg in read_dehs(os.path.join(dir, "dehs_error.txt")):
if not dehs.has_key(pkgname):
dehs[pkgname] = {}
dehs[pkgname]['error'] = msg
# read short descriptions
shortdescs = read_shortdesc(os.path.join(dir, "shortdesc.txt"))
# Create the XML documents
while 1:
line = sys.stdin.readline()
if not line: break #eof
pkg = line.strip()
doc = implementation.createDocument(None, "other", None)
root_elt = doc.documentElement
hash = pkg[0]
if pkg[0:3] == "lib":
hash = pkg[0:4]
# Add debcheck availability info
dc_sig = ""
elt = doc.createElement("debcheck")
for dist in ("stable", "testing", "unstable"):
if debcheck[dist].has_key(pkg):
elt.setAttribute(dist, "yes")
dc_sig += "y"
else:
elt.setAttribute(dist, "no")
dc_sig += "n"
root_elt.appendChild(elt)
# Add debconf templates availibilty info
if debconf.has_key(pkg):
root_elt.setAttribute("debconf", "yes")
dc_sig += "y"
else:
root_elt.setAttribute("debconf", "no")
dc_sig += "n"
# Get PTS stats
elt = doc.createElement("pts")
elt.setAttribute("count", pts.get(pkg, "0"))
root_elt.appendChild(elt)
# Get BTS stats
elt = doc.createElement("bugs")
(s_rc, s_rc_m, s_normal, s_normal_m, s_wishlist, s_wishlist_m, s_fixed,
s_fixed_m, s_patch, s_patch_m) = \
srcbugs.get(pkg, [0,0,0,0,0,0,0,0,0,0])
binlist = source2binaries.get(pkg, [])
binlist.sort()
subsig = ""
for binary in binlist:
sub_elt = doc.createElement("item")
sub_elt.setAttribute("name", binary)
(rc, normal, wishlist, fixed, patch) = bugs.get(binary, [0,0,0,0,0])
sub_elt.setAttribute("rc", "%d" % rc)
sub_elt.setAttribute("normal", "%d" % normal)
sub_elt.setAttribute("wishlist", "%d" % wishlist)
sub_elt.setAttribute("fixed", "%d" % fixed)
sub_elt.setAttribute("patch", "%d" % patch)
all = rc + normal + wishlist + fixed
sub_elt.setAttribute("all", "%d" % all)
elt.appendChild(sub_elt)
if len(subsig):
subsig = "%s|%d|%d" % (subsig, all, patch)
else:
subsig = "%d|%d" % (all, patch)
elt.setAttribute("rc", "%d" % s_rc)
if s_rc != s_rc_m:
elt.setAttribute("rc_m", "%d" % s_rc_m)
elt.setAttribute("normal", "%d" % s_normal)
if s_normal != s_normal_m:
elt.setAttribute("normal_m", "%d" % s_normal_m)
elt.setAttribute("wishlist", "%d" % s_wishlist)
if s_wishlist != s_wishlist_m:
elt.setAttribute("wishlist_m", "%d" % s_wishlist_m)
elt.setAttribute("fixed", "%d" % s_fixed)
if s_fixed != s_fixed_m:
elt.setAttribute("fixed_m", "%d" % s_fixed_m)
elt.setAttribute("patch", "%d" % s_patch)
if s_patch != s_patch_m:
elt.setAttribute("patch_m", "%d" % s_patch_m)
s_all = s_fixed + s_wishlist + s_normal + s_rc
s_all_m = s_fixed_m + s_wishlist_m + s_normal_m + s_rc_m
elt.setAttribute("all", "%d" % s_all)
if s_all != s_all_m:
elt.setAttribute("all_m", "%d" % s_all_m)
root_elt.appendChild(elt)
if gift_bugs.has_key(pkg):
s_gift = gift_bugs[pkg]
else:
s_gift = 0
elt.setAttribute("gift", str(s_gift))
if help_bugs.has_key(pkg):
s_help = help_bugs[pkg]
else:
s_help = 0
elt.setAttribute("help", str(s_help))
# Get WNPP information. [PvR]
if wnpp.has_key(pkg):
(type, number) = wnpp[pkg]
elt = doc.createElement("wnpp")
elt.setAttribute("type", type)
elt.setAttribute("bugnumber", number)
root_elt.appendChild(elt)
root_elt.setAttribute("wnpp", "yes")
wnpp_sig = "%s%s" % (type,number)
else:
root_elt.setAttribute("wnpp", "no")
wnpp_sig = "n"
# Get override info [JvW]
override_elt = None
override_sig = []
for dist in [ 'unstable', 'experimental' ]:
if override[dist].has_key(pkg):
if not override_elt: override_elt = doc.createElement("override")
disparities = override[dist][pkg]
override_sig.append(disparities)
elt_g = doc.createElement("group")
elt_g.setAttribute("suite", dist)
for disp in disparities:
elt = doc.createTextNode(disp)
elt_disp = doc.createElement("disparity")
elt_disp.appendChild(elt)
elt_g.appendChild(elt_disp)
override_elt.appendChild(elt_g)
if override_elt:
root_elt.appendChild(override_elt)
root_elt.setAttribute("override", "yes")
else:
root_elt.setAttribute("override", "no")
# Add Ubuntu information
if ubuntu_versions.has_key(pkg):
elt = doc.createElement("ubuntu")
(version, url) = ubuntu_versions[pkg]
ubuntu_sig = "ubuntu/ " + version
elt.setAttribute("version", unicode(version, 'UTF8', 'replace'))
elt.setAttribute("url", unicode(url, 'UTF8', 'replace'))
if ubuntu_bugs.has_key(pkg):
elt.setAttribute("bugs", "yes")
elt_bugs = doc.createElement("bugs")
(count, url) = ubuntu_bugs[pkg]
ubuntu_sig += "ubuntubugs/" + count
elt_bugs.setAttribute("count", unicode(count, 'UTF8', 'replace'))
elt_bugs.setAttribute("url", unicode(url, 'UTF8', 'replace'))
elt.appendChild(elt_bugs)
if ubuntu_patches.has_key(pkg):
elt.setAttribute("patch", "yes")
elt_patch = doc.createElement("patch")
(version, url) = ubuntu_patches[pkg]
ubuntu_sig += "ubuntupatch/" + version
elt_patch.setAttribute("version", unicode(version, 'UTF8', 'replace'))
elt_patch.setAttribute("url", unicode(url, 'UTF8', 'replace'))
elt.appendChild(elt_patch)
root_elt.appendChild(elt)
root_elt.setAttribute("ubuntu", "yes")
else:
root_elt.setAttribute("ubuntu", "no")
ubuntu_sig = "n"
# Get DEHS information
if dehs.has_key(pkg):
elt = doc.createElement('dehs')
root_elt.appendChild(elt)
root_elt.setAttribute('dehs', 'yes')
if dehs[pkg].has_key('newer'):
elt.setAttribute('newer', dehs[pkg]['newer'])
if dehs[pkg].has_key('error'):
elt.setAttribute('error', 'yes')
dehs_sig = 'y'
else:
root_elt.setAttribute('dehs', 'no')
dehs_sig = 'n'
# add svnbuildstat info
if svnbuildstat.has_key(pkg):
elt = doc.createElement("svnbuildstat")
root_elt.setAttribute("svnbuildstat", "yes")
root_elt.appendChild(elt)
svnbuildstat_sig = "y"
else:
root_elt.setAttribute("svnbuildstat", "no")
svnbuildstat_sig = "n"
# add piuparts info
if piuparts.has_key(pkg):
#elt = doc.createElement("piuparts")
#root_elt.appendChild(elt)
root_elt.setAttribute("piuparts", "yes")
piuparts_sig = "y"
else:
root_elt.setAttribute("piuparts", "no")
piuparts_sig = "n"
# add transitions info
if transitions.has_key(pkg):
elt = doc.createElement("transitions")
for id in transitions[pkg]:
trans_elt = doc.createElement("transition")
trans_elt.setAttribute("name", id)
elt.appendChild(trans_elt)
root_elt.setAttribute("transitions", "yes")
root_elt.appendChild(elt)
transitions_sig = "y"
else:
root_elt.setAttribute("transitions", "no")
transitions_sig = "n"
# add lintian QA info
if lintian.has_key(pkg):
(errs, warns) = lintian[pkg]
elt = doc.createElement("lintian")
elt.setAttribute("errors", str(errs))
elt.setAttribute("warnings", str(warns))
root_elt.appendChild(elt)
root_elt.setAttribute("lintian", "yes")
lintian_sig = (errs, warns)
else:
root_elt.setAttribute("lintian", "no")
lintian_sig = (0, 0)
# add short descriptions
elt = doc.createElement("descriptions")
root_elt.appendChild(elt)
if shortdescs.has_key(pkg):
for package, shortdesc in shortdescs[pkg].iteritems():
desc_elt = doc.createElement("shortdesc")
elt.appendChild(desc_elt)
desc_elt.setAttribute("package", package)
desc_text = doc.createTextNode(shortdesc)
desc_elt.appendChild(desc_text)
shortdesc_sig = str(shortdescs[pkg]).__hash__()
# XXX hash(str(...)) does not work: WTF?
else:
shortdesc_sig = ''.__hash__()
# TODO: try to do that signature checking before the creation of XML DOM
# Build the sig and check if anything changed
sig = (pts.get(pkg, "0"), dc_sig, wnpp_sig, override_sig, dehs_sig,
ubuntu_sig, s_rc, s_normal, s_wishlist, s_fixed, s_gift, s_help,
subsig, svnbuildstat_sig, transitions_sig, lintian_sig,
shortdesc_sig, piuparts_sig)
if sigs.has_key(pkg) and sig == sigs[pkg] and \
os.path.isfile("%s/%s/%s/other.xml" % (odir, hash, pkg)):
continue
sigs[pkg] = sig
# Output the data to the XML file
try:
f = open("%s/%s/%s/other.xml" % (odir, hash, pkg), "w")
ext.PrettyPrint(doc, f, "utf-8")
f.close()
except Exception, msg:
sys.stderr.write("Output problem for " + pkg + "/other.xml (%s)\n" %
msg);
# Store the signatures
f = open(odir + "/other.sigs", "w")
cPickle.dump(sigs, f, 0)
f.close()