[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#855220: [PATCH] a new cgi script



On Wed, Feb 15, 2017 at 04:53:14PM +0100, Mattia Rizzolo wrote:
> (also re-attached the attachment).

... here.

-- 
regards,
                        Mattia Rizzolo

GPG Key: 66AE 2B4A FCCF 3F52 DA18  4D18 4B04 3FCD B944 4540      .''`.
more about me:  https://mapreri.org                             : :'  :
Launchpad user: https://launchpad.net/~mapreri                  `. `'`
Debian QA page: https://qa.debian.org/developer.php?login=mattia  `-
From 9cf994e38331f895fae15772225925e25d304391 Mon Sep 17 00:00:00 2001
From: Erwan Prioul <erwan@linux.vnet.ibm.com>
Date: Wed, 15 Feb 2017 15:43:32 +0100
Subject: [PATCH] add first version of ftbfs.cgi

script that displays the FTBFS packages on a given architecture, with related bugs if any.
---
 web/cgi-bin/ftbfs.cgi | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 247 insertions(+)
 create mode 100755 web/cgi-bin/ftbfs.cgi

diff --git a/web/cgi-bin/ftbfs.cgi b/web/cgi-bin/ftbfs.cgi
new file mode 100755
index 0000000..b340e59
--- /dev/null
+++ b/web/cgi-bin/ftbfs.cgi
@@ -0,0 +1,247 @@
+#!/usr/bin/env python
+# Display FTBFS packages on given arch
+# Copyright (C) 2017, Erwan Prioul <erwan@linux.vnet.ibm.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import datetime
+import psycopg2
+import cgi
+import cgitb
+
+DATABASE = {
+    'database': 'udd',
+    'port': 5452,
+    'host': 'localhost',
+    'user': 'guest'
+}
+
+class AttrDict(dict):
+    def __init__(self, **kwargs):
+        for key, value in kwargs.iteritems():
+            self[key] = value
+
+    def __getattr__(self, name):
+        try:
+            return self[name]
+        except KeyError, e:
+            raise AttributeError(e)
+
+def query(query, cols, *parameters):
+    try:
+        conn = psycopg2.connect(**DATABASE)
+        cursor = conn.cursor()
+        cursor.execute(query, parameters)
+    except:
+        exit(1)
+    for row in cursor.fetchall():
+        yield AttrDict(**dict(zip(cols, row)))
+    cursor.close()
+    conn.close()
+
+def pretty_time_delta(when):
+    seconds = (datetime.datetime.now() - when).total_seconds()
+    days, seconds = divmod(seconds, 86400)
+    hours, seconds = divmod(seconds, 3600)
+    minutes, seconds = divmod(seconds, 60)
+    if days > 0:
+        return '%dd' % (days)
+    elif hours > 0:
+        return '%dh' % (hours)
+    elif minutes > 0:
+        return '%dm' % (minutes)
+    else:
+        return '%ds' % (seconds)
+
+def packageLine(packages, package, pending = False):
+    u = package.replace('+', '%2B')
+    bugs = "&nbsp;"
+    what = 'nopatch'
+    if pending:
+        what = 'patch'
+    if len(packages[package][what]) > 0:
+        bugs = ''.join('<a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%d";>#%d</a> - %s<br>' % (x[0], x[0], x[1]) for x in sorted(packages[package][what]))
+    return \
+        '<tr><td class="package">%s_%s</td>' % (package, packages[package]['version']) + \
+        '<td class="links">' + \
+        '<a href="https://buildd.debian.org/status/package.php?p=%s&suite=sid"; title="Debian buildd status">%s</a> ' % (u, pretty_time_delta(packages[package]['state_change'])) + \
+        '[<a href="http://bugs.debian.org/cgi-bin/pkgreport.cgi?src=%s"; title="Debian bugs in source package">B</a>]' % (u) + \
+        '[<a href="http://buildd.debian.org/status/logs.php?pkg=%s"; title="Debian build logs">L</a>]' % (u) + \
+        '[<a href="https://tracker.debian.org/pkg/%s"; title="Debian Package Tracker">T</a>]</td>' % (u) + \
+        '<td>%s</td></tr>' % bugs
+
+def generatePending(packages, nb, arch):
+    return \
+        '<div class="claim" style="margin-top:20px;">%d FTBFS packages on <span class="arch">%s</span> with a patch pending</div><table>' % (nb, arch) + \
+        ''.join(packageLine(packages, x, True) for x in sorted(packages.keys()) if len(packages[x]['patch']) > 0) + \
+        '</table>'
+
+def generateFailing(packages, failing, nb, arch):
+    html = '<div class="claim">%d FTBFS packages on <span class="arch">%s</span> without patch</div>' % (nb, arch)
+    for c in sorted(failing.keys()):
+        if c > 2:
+	    txt = "and %d other architectures" % (c - 1)
+        elif c == 2:
+            txt = "and 1 other architecture"
+        else:
+            txt = ""
+        html += \
+            '<div class="banner">%d packages are failing on <span class="arch">%s</span> %s</div><table>' % (len(failing[c]), arch, txt) + \
+            ''.join(packageLine(packages, x) for x in sorted(failing[c])) + \
+            '</table>'
+    return html
+
+def getPackages(arch):
+    # Get all FTBFS packages on given architecture
+    q = """
+select distinct source, version, state_change
+from wannabuild
+where architecture = '%s' and
+      distribution = 'sid' and
+      state in ('Failed', 'Build-Attempted') and
+      vancouvered = 'f'
+;
+""" % arch
+    packages = {x['s']: { 'version': x['version'], 'state_change': x['state_change'], 'patch': [], 'nopatch': [] } for x in query(q, ('s', 'version', 'state_change'))}
+    # Get opened bugs with the given architecture as tag
+    q = """
+select distinct source, id, title
+from bugs inner join bugs_usertags using (id)
+where bugs_usertags.tag = '%s' and status != 'done' and source in (%s);
+""" % (arch, ", ".join("'"+x+"'" for x in packages.keys()))
+    bugs = {x['id']: [x['source'], x['title']] for x in query(q, ('source', 'id', 'title'))}
+    # Which bugs have a patch?
+    q = """
+select id
+from bugs_tags
+where tag = 'patch' and id in (%s);
+""" % ", ".join("'"+str(x)+"'" for x in bugs.keys())
+    patched = [x['i'] for x in query(q, ('id'))]
+    for b in bugs.keys():
+        where = 'nopatch'
+        if b in patched:
+            where = 'patch'
+        packages[bugs[b][0]][where].append([b, bugs[b][1]])
+    l = [x for x in packages.keys() if len(packages[x]['patch']) <= 0]
+    countFailing = len(l)
+    countPending = len(packages.keys()) - countFailing
+    # For a FTBFS package, get the number of architectures it also fails on
+    q = """
+select source, count(*) as c
+from wannabuild
+where distribution = 'sid' and state in ('Failed', 'Build-Attempted') and vancouvered = 'f' and source in (%s)
+group by source;
+""" % ", ".join("'"+x+"'" for x in l)
+    failing = {}
+    for r in query(q, ('source', 'c')):
+        if not failing.has_key(r['c']):
+            failing[r['c']] = []
+        failing[r['c']].append(r['source'])
+    return """
+<!doctype html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>FTBFS packages on %s</title>
+<style>
+table {
+    width: 100%%;
+    border-collapse: collapse;
+    table-layout: fixed;
+}
+tr:nth-child(2n+1) {
+    background-color: #fbfbfb;
+}
+td {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis !important;
+    vertical-align: top;
+    padding-left: 4px;
+}
+.package {
+    width: 375px;
+}
+.links {
+    width: 150px;
+    text-align: right;
+}
+.claim {
+    background-color: #dddddd;
+    padding: 10px;
+    padding-left: 4px !important;
+    font-size: 110%%;
+}
+.banner {
+    background-color: #eeeeee;
+    padding: 4px;
+}
+.arch {
+    font-style: oblique;
+}
+</style>
+</head>
+<body>
+""" % arch + generateFailing(packages, failing, countFailing, arch) + generatePending(packages, countPending, arch) + '</body></html>'
+
+def getArchs():
+    q = """
+select distinct(architecture) as a
+from wannabuild
+where distribution = 'sid' and
+      state in ('Failed', 'Build-Attempted') and
+      vancouvered = 'f'
+order by a;
+"""
+    return [x['a'] for x in query(q, ('a'))]
+
+def getForm(archs):
+    return """
+<!doctype html>
+<html>
+<head>
+<title>FTBFS packages on given architecture</title>
+<style>
+.claim {
+    background-color: #dddddd;
+    padding: 10px;
+    padding-left: 4px !important;
+    font-size: 110%%;
+    margin-bottom: 5px;
+}
+</style>
+</head>
+<body>
+<div class="claim">FTBFS packages</div>
+<form action="?" accept-charset="UTF-8">
+Architecture: <select name="arch" id="arch"><option value=""></option>%s</select> <input type="submit" value="Show packages">
+</form>
+</body>
+</html>
+""" % ''.join('<option value="%s">%s</option>' % (x, x) for x in archs)
+
+def main():
+    print 'Content-Type: text/html\n'
+    archs = getArchs()
+    cgitb.enable()
+    form = cgi.FieldStorage()
+    if 'arch' in form:
+        arch = form.getfirst('arch', '')
+        if arch in archs:
+            print getPackages(arch)
+        else:
+            print getForm(archs)
+    else:
+        print getForm(archs)
+
+if __name__ == '__main__':
+    main()
-- 
2.7.4

Attachment: signature.asc
Description: PGP signature


Reply to: