# HG changeset patch # User Bernhard Herzog # Date 1173810741 -3600 # Node ID 52a1d74084a63b61f8d47d5e3a4e5856c945dbd2 # Parent 467013d9d627a0d605fbfedea6dd4a47a0ca458f# Parent 6ced445aa0904ea1dccfb7ab164734a9b6135952 merge diff -r 6ced445aa090 -r 52a1d74084a6 reportstatus.py --- a/reportstatus.py Mon Mar 12 15:24:18 2007 +0100 +++ b/reportstatus.py Tue Mar 13 19:32:21 2007 +0100 @@ -11,8 +11,7 @@ import os from optparse import OptionParser -from treepkg.packager import create_package_line, PackagerGroup -from treepkg.readconfig import read_config +from treepkg.report import get_packager_group, prepare_report def parse_commandline(): parser = OptionParser() @@ -21,17 +20,22 @@ parser.add_option("--config-file") return parser.parse_args() + +def report_text(group): + report = prepare_report(group) + for revno, row in report.revisions: + for col in row: + if col: + print "%s %s: %s" % (col.name, revno, col.status.desc) + if col.status.start: + print " Start:", col.status.start + print " Stop:", col.status.stop + print + def main(): options, args = parse_commandline() + group = get_packager_group(options.config_file) + report_text(group) - treepkg_opts, packager_opts = read_config(options.config_file) - group = PackagerGroup([create_package_line(**opts) - for opts in packager_opts], - **treepkg_opts) - for line in group.get_package_lines(): - for revision in line.get_revisions(): - print line.name, revision.revision, revision.status.status - print " start:", revision.status.start - print " stop:", revision.status.stop main() diff -r 6ced445aa090 -r 52a1d74084a6 starttreepkgweb.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/starttreepkgweb.py Tue Mar 13 19:32:21 2007 +0100 @@ -0,0 +1,33 @@ +#! /usr/bin/python2.4 +# Copyright (C) 2007 by Intevation GmbH +# Authors: +# Bernhard Herzog +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +"""Starts the tree packager webinterface""" + +import os +from optparse import OptionParser + +from treepkg.web import runserver + +def parse_commandline(): + parser = OptionParser() + dirname = os.path.dirname(__file__) + parser.set_defaults(config_file=os.path.join(dirname, "treepkg.cfg"), + cherrypy_config=os.path.join(dirname, "cherrypy.cfg")) + parser.add_option("--config-file", + help=("The tree packager config file." + " Default treepkg.cfg")) + parser.add_option("--cherrypy-config", + help=("The cherrypy config file for the web interface." + " Default cherrypy.cfg")) + return parser.parse_args() + +def main(): + options, args = parse_commandline() + runserver(options.config_file, options.cherrypy_config) + +main() diff -r 6ced445aa090 -r 52a1d74084a6 treepkg/packager.py --- a/treepkg/packager.py Mon Mar 12 15:24:18 2007 +0100 +++ b/treepkg/packager.py Tue Mar 13 19:32:21 2007 +0100 @@ -205,6 +205,7 @@ work_dir = _filenameproperty("work") binary_dir = _filenameproperty("binary") src_dir = _filenameproperty("src") + build_log = _filenameproperty("build.log") def find_dsc_file(self): for filename in os.listdir(self.src_dir): @@ -212,6 +213,9 @@ return os.path.join(self.src_dir, filename) return None + def has_build_log(self): + return os.path.exists(self.build_log) + def package(self): try: util.ensure_directory(self.work_dir) @@ -227,8 +231,7 @@ bin_packager = self.binary_packager_cls(self.pkg_line, self.status, self.binary_dir, dsc_file, - os.path.join(self.base_dir, - "build.log")) + self.build_log) bin_packager.package() self.status.stop = datetime.datetime.utcnow() except: diff -r 6ced445aa090 -r 52a1d74084a6 treepkg/report.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/report.py Tue Mar 13 19:32:21 2007 +0100 @@ -0,0 +1,97 @@ +# Copyright (C) 2007 by Intevation GmbH +# Authors: +# Bernhard Herzog +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +"""Support for status reports""" + +from packager import create_package_line, PackagerGroup +from readconfig import read_config + +class struct(object): + + """Class to create simple struct like objects + + All keyword arguments passed to the constructor are available as + instance variables. + """ + + def __init__(self, **kw): + self.__dict__.update(kw) + + def __repr__(self): + fields = ["%s=%r" % item for item in self.__dict__.items()] + return "struct(" + ", ".join(fields) + ")" + + +def get_packager_group(config_file): + treepkg_opts, packager_opts = read_config(config_file) + return PackagerGroup([create_package_line(**opts) + for opts in packager_opts], + **treepkg_opts) + +status_expansions = dict( + binary_package_created="build successful", + creating_binary_package="building binary packages", + source_package_created="source package created", + creating_source_package="creating source package", + error="error") + +def status_finished(status): + return status.status == "binary_package_created" + +def status_error(status): + return status.status == "error" + +def status_class(status): + """Returns the CSS class for a status""" + if status_finished(status): + return "finished" + elif status_error(status): + return "error" + else: + return "inprogress" + +def format_time(timestamp): + """Formats a datetime object for a status report + + if the argument is true, the return value is simply str applied to + the argument, which for datetime objects is a string with the format + 'YYYY-MM-DD hh:mm:ss'. If the argument is false, the return value + is ''. + """ + if timestamp: + return str(timestamp) + else: + return "" + + +def prepare_status(status): + return struct(desc=status_expansions.get(status.status, + status.status), + start=format_time(status.start), + stop=format_time(status.stop), + cls=status_class(status)) + +def prepare_report(group): + revisions = [] + columns = [] + pkglines = group.get_package_lines() + num_columns = len(pkglines) + for column, line in enumerate(pkglines): + columns.append((column, line.name)) + for revision in line.get_revisions(): + row = [None] * num_columns + row[column] = struct(revno=revision.revision, + revision=revision, + column=column, + name=line.name, + status=prepare_status(revision.status)) + revisions.append((revision.revision, row)) + revisions.sort() + revisions.reverse() + + return struct(columns=columns, + revisions=revisions) diff -r 6ced445aa090 -r 52a1d74084a6 treepkg/web-status.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/web-status.html Tue Mar 13 19:32:21 2007 +0100 @@ -0,0 +1,53 @@ + + + Tree Packager Status + + + +

Tree Packager Status

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Revision${col[1]}
${row[0]} + ${col.status.desc}
+ Start: ${col.status.start}
+ Stop: ${col.status.stop}
+ + build_log + +
+ + All times are given in UTC. + + + diff -r 6ced445aa090 -r 52a1d74084a6 treepkg/web.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/web.py Tue Mar 13 19:32:21 2007 +0100 @@ -0,0 +1,71 @@ +# Copyright (C) 2007 by Intevation GmbH +# Authors: +# Bernhard Herzog +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +import os + +from genshi.template import TemplateLoader + +import cherrypy +from cherrypy import expose +from cherrypy.lib import cptools + +import report + + +class Status(object): + + """Implements the tree packager status pages""" + + def __init__(self, treepkg_config): + self.treepkg_config = treepkg_config + self.loader = TemplateLoader([os.path.dirname(__file__)]) + + @expose + def index(self): + group = report.get_packager_group(self.treepkg_config) + tmpl = self.loader.load('web-status.html') + stream = tmpl.generate(report=report.prepare_report(group)) + return stream.render('html') + + def build_log_filename(self, package_line_name, revno): + """Returns the name of the build log file of a revision if it exists""" + group = report.get_packager_group(self.treepkg_config) + for line in group.get_package_lines(): + if line.name == package_line_name: + for revision in line.get_revisions(): + if str(revision.revision) == revno: + if revision.has_build_log(): + return revision.build_log + + @expose + def default(self, *rest): + """Handles requests for .../pkg/revno/build.log""" + filename = None + if len(rest) == 3 and rest[2] == "build.log": + filename = self.build_log_filename(*rest[:2]) + if filename is not None: + return cptools.serveFile(filename, contentType="text/plain") + else: + raise cherrypy.HTTPError(status="404") + + + +class TreePKG(object): + + """Root object for the tree packager web interface""" + + @expose + def index(self): + raise cherrypy.HTTPRedirect('/status') + + +def runserver(treepkg_config, cherrypy_config): + cherrypy.root = TreePKG() + cherrypy.root.status = Status(treepkg_config=treepkg_config) + + cherrypy.config.update(file=cherrypy_config) + cherrypy.server.start()