diff options
Diffstat (limited to 'webapp.py')
-rw-r--r-- | webapp.py | 105 |
1 files changed, 72 insertions, 33 deletions
@@ -35,7 +35,8 @@ index_template = """<!DOCTYPE html> <tr> <th>source</th> <th>version</th> - <th>architecture</th> + <th>build architecture</th> + <th>host architecture</th> <th>started</th> <th>result log</th> <th>bugs</th> @@ -50,7 +51,8 @@ index_template = """<!DOCTYPE html> </a> </td> <td>{{ build.version|e }}</td> - <td>{{ build.architecture|e }}</td> + <td>{{ build.buildarch|e }}</td> + <td>{{ build.hostarch|e }}</td> <td> {{- build.starttime|sqltimestamp|formatts -}} </td> @@ -107,13 +109,13 @@ tr.dep.tempbad td:nth-child(1) { tr.dep.good td:nth-child(1) { background-color: #afa; } -tr.build.bad td:nth-child(4) { +tr.build.bad td:nth-child(5) { background-color: #faa; } -tr.build.tempbad td:nth-child(4) { +tr.build.tempbad td:nth-child(5) { background-color: #ffa; } -tr.build.good td:nth-child(4) { +tr.build.good td:nth-child(5) { background-color: #afa; } th { @@ -177,13 +179,13 @@ footer { {%- for reason, archs in depresult.items()|sort -%} <tr class="dep {{ "tempbad" if reason.startswith("skew") else "bad" }}"> <td>{{ reason|e }}</td> - <td>{{ archs|arch_format }}</td> + <td>{{ archs|archpairs_format }}</td> </tr> {%- endfor -%} {%- if okarchs -%} <tr class="dep good"> <td>ok</td> - <td>{{ okarchs|arch_format }}</td> + <td>{{ okarchs|archpairs_format }}</td> </tr> {%- endif -%} </tbody> @@ -220,7 +222,8 @@ footer { <tr> <th>started</th> <th>version</th> - <th>architecture</th> + <th>build architecture</th> + <th>host architecture</th> <th>result log</th> </tr> </thead> @@ -231,7 +234,8 @@ footer { {{- build.starttime|sqltimestamp|formatts -}} </td> <td>{{ build.version|e }}</td> - <td>{{ build.architecture|e }}</td> + <td>{{ build.buildarch|e }}</td> + <td>{{ build.hostarch|e }}</td> <td> <a href="{{ url_for("show_log", filename=build.filename[:-3]) }}"> {{- "ok" if build.success else "failed" -}} @@ -249,10 +253,12 @@ footer { <input type="submit" name="schedule" value="cross build" /> {{ sourcepackage|e }} for <input type="hidden" name="source" value="{{ sourcepackage|e }}" /> - <select name="architecture"> - <option value="any">any</option> - {%- for architecture in architectures|sort -%} - <option value="{{ architecture|e }}">{{ architecture|e }}</option> + <select name="archpair"> + <option value="any_any">{{ ("any", "any")|archpair_format }}</option> + {%- for buildarch, hostarch in architectures|sort -%} + <option value="{{ buildarch|e }}_{{ hostarch|e }}"> + {{- (buildarch, hostarch)|archpair_format -}} + </option> {%- endfor -%} </select> </form> @@ -308,36 +314,64 @@ def formatts_filter(ts): return jinja2.Markup('<time title="%s" datetime="%s">%s</time>' % (ts, ts, formatts(ts))) -@app.template_filter('arch_format') -@jinja2.contextfilter -def arch_format_filter(context, some_archs): - if context["architectures"] == some_archs: +@app.template_filter("archpair_format") +def archpair_format_filter(archpair): + return jinja2.Markup("%s → %s" % tuple(map(jinja2.escape, archpair))) + +def group_pairs(pairs): + result = {} + for v, w in pairs: + result.setdefault(v, set()).add(w) + return result + +def render_archset(subset, all_archs): + if len(subset) == 1: + return next(iter(subset)) + if subset == all_archs: return "any" - return ", ".join(sorted(some_archs)) + return "{%s}" % ", ".join(map(jinja2.escape, sorted(subset))) + +@app.template_filter('archpairs_format') +@jinja2.contextfilter +def archpairs_format_filter(context, some_archs): + architectures = group_pairs(context["architectures"]) + fwdmap = {} # build architecture -> host architecture set representation + for buildarch, hostarchs in group_pairs(some_archs).items(): + fwdmap[buildarch] = render_archset(hostarchs, architectures[buildarch]) + allbuildarchs = set(architectures.keys()) + # host architecture set representation -> build architecture set + flippedit = group_pairs((v, k) for (k, v) in fwdmap.items()).items() + maps = ("%s → %s" % (render_archset(buildarchs, allbuildarchs), + hostarchrep) + for hostarchrep, buildarchs in flippedit) + return jinja2.Markup("; ".join(sorted(maps))) def collect_depstate(conn, source): version = None depstate = None query = sqlalchemy.text(""" - SELECT version, architecture, satisfiable, reason + SELECT version, 'amd64' AS buildarch, architecture AS hostarch, + satisfiable, reason FROM depstate WHERE source = :source;""") for row in conn.execute(query, source=source): if version is None or version_compare(version, row.version) > 0: version = row.version depstate = {} - depstate[row.architecture] = None if row.satisfiable else row.reason + depstate[row.buildarch, row.hostarch] = \ + None if row.satisfiable else row.reason if version is None: raise werkzeug.exceptions.NotFound() depresult = {} - for arch, reason in depstate.items(): - depresult.setdefault(reason, set()).add(arch) + for archpair, reason in depstate.items(): + depresult.setdefault(reason, set()).add(archpair) return version, depresult @app.route("/") def show_index(): with db.engine.connect() as conn: builds = list(conn.execute(""" - SELECT source, version, architecture, starttime, filename, + SELECT source, version, 'amd64' AS buildarch, + architecture AS hostarch, starttime, filename, ifnull((SELECT max(patched + 1) FROM bugs WHERE affects = source), 0) AS buglvl @@ -351,12 +385,15 @@ def show_index(): def show_source(source): context = dict(sourcepackage=source) with db.engine.connect() as conn: - query = sqlalchemy.text("SELECT architecture FROM depcheck;") - context["architectures"] = set(row[0] for row in conn.execute(query)) + query = sqlalchemy.text("""SELECT 'amd64' AS buildarch, + architecture AS hostarch + FROM depcheck;""") + context["architectures"] = set(map(tuple, conn.execute(query))) context["version"], context["depresult"] = collect_depstate(conn, source) query = sqlalchemy.text(""" - SELECT version, architecture, success, starttime, filename + SELECT version, 'amd64' AS buildarch, architecture AS hostarch, + success, starttime, filename FROM builds WHERE source = :source;""") context["builds"] = list(conn.execute(query, source=source)) query = sqlalchemy.text(""" @@ -388,21 +425,23 @@ def show_log(filename): @app.route("/schedule", methods=["POST"]) def request_schedule(): source = flask.request.form["source"] - architecture = flask.request.form["architecture"] + buildarch, hostarch = flask.request.form["archpair"].split("_") + if buildarch not in ("any", "amd64"): + raise werkzeug.exceptions.BadRequest() with db.engine.connect() as conn: query = sqlalchemy.text(""" SELECT 1 FROM depstate WHERE source = :source;""") if not conn.execute(query, source=source).first(): raise werkzeug.exceptions.BadRequest() - if architecture == "any": - architecture = None + if hostarch == "any": + hostarch = None else: query = sqlalchemy.text(""" - SELECT 1 FROM depcheck WHERE architecture = :architecture;""") - if not conn.execute(query, architecture=architecture).first(): + SELECT 1 FROM depcheck WHERE architecture = :hostarch;""") + if not conn.execute(query, hostarch=hostarch).first(): raise werkzeug.exceptions.BadRequest() query = sqlalchemy.text(""" INSERT INTO buildrequests (source, architecture, requesttime) - VALUES (:source, :architecture, datetime('now'));""") - conn.execute(query, source=source, architecture=architecture) + VALUES (:source, :hostarch, datetime('now'));""") + conn.execute(query, source=source, hostarch=hostarch) return flask.render_template_string(schedule_template) |