view contrib/sawmill/web/details.py @ 490:73a2e603a23b

sawmill: Prepared detail view for syndication: Added anchors for each build and added render rel link if RSS file exists next to treepkg.xml.
author Sascha Teichmann <teichmann@intevation.de>
date Wed, 29 Sep 2010 19:25:52 +0000
parents f208b139190b
children
line wrap: on
line source
# -*- coding: UTF-8 -*-
#
# Copyright (C) 2010 by Intevation GmbH
# Authors:
# Sascha L. Teichmann <sascha.teichmann@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with the software for details.

from mod_python import apache, psp, util

import os
import re
import datetime
import time

from lxml import etree

BASE_DIR = "treepkgs"

TREEPKG_DIR = os.path.join(os.path.dirname(__file__), BASE_DIR)

STATUS_LINE = re.compile(r"^([^:]+):(.+)")

UNDER_SCORE = re.compile(r"_+(\w)")

INVALID_LABEL = re.compile(r"[^a-zA-Z0-9_]")

def _create_time(s, format="%Y-%m-%d %H:%M:%S"):
    return datetime.datetime(*(time.strptime(s, format)[0:6]))

def _pretty_log_name(log):
    log = log.replace(".txt", "").replace(".gz", "").capitalize()
    return UNDER_SCORE.sub(lambda x: " %s" % x.group(1).upper(), log)

def _make_valid_label(label):
    return INVALID_LABEL.sub("_", label)

class TrackItem(object):

    def __init__(self, treepkg, track, revision, status_file):
        self.treepkg     = treepkg
        self.track       = track
        self.revision    = revision
        self.status_file = status_file
        self.loaded      = False
        self.status      = None
        self.start       = None
        self.stop        = None
        self.logs        = None
        self.tags        = None

    def check_loaded(self):
        if not self.loaded:
            f = open(self.status_file)
            try:
                for line in f:
                    m = STATUS_LINE.match(line)
                    if not m: continue
                    key, value = [x.strip() for x in m.groups()]

                    if   key == 'status': self.status = value
                    elif key == 'start':  self.start  = _create_time(value)
                    elif key == 'stop':   self.stop   = _create_time(value)
                    elif key == 'tags':   self.tags   = value
            finally:
                f.close()
            self.loaded = True

    def get_build_status(self):
        self.check_loaded()
        return self.status

    def get_build_start(self):
        self.check_loaded()
        return self.start

    def get_build_stop(self):
        self.check_loaded()
        return self.stop

    def get_tags(self):
        self.check_loaded()
        return self.tags

    def get_label(self):
        self.check_loaded()
        out = [
            _make_valid_label(self.track),
            _make_valid_label(self.revision) ]
        if self.start:
            out.append(self.start.strftime("%Y%m%d%H%M%S"))
        return ''.join(out)

    def log_path(self, log):
        return "%s/tracks/%s/pkg/%s/log/%s" % (
            self.treepkg, self.track, self.revision, log)

    def get_build_logs(self):
        oj = os.path.join
        if self.logs is None:
            log_dir = oj(os.path.dirname(self.status_file), "log")
            if not os.path.isdir(log_dir):
                self.logs = []
            else:
                self.logs =[(_pretty_log_name(f), self.log_path(f))
                    for f in os.listdir(log_dir)
                    if os.path.isfile(oj(log_dir, f)) and f.find("txt") >= 0]
        return self.logs

    build_status = property(get_build_status)
    build_start  = property(get_build_start)
    build_stop   = property(get_build_stop)
    build_logs   = property(get_build_logs)
    build_tags   = property(get_tags)
    build_label  = property(get_label)


def __scan_track_items(treepkg, path):
    items = []

    tracks_path = os.path.join(path, "tracks")
    if os.path.isdir(tracks_path):
        for track in os.listdir(tracks_path):
            track_path = os.path.join(tracks_path, track)
            if not os.path.isdir(track_path): continue
            revisions_path = os.path.join(track_path, "pkg")
            if not os.path.isdir(revisions_path): continue
            for revision in os.listdir(revisions_path):
                revision_path = os.path.join(revisions_path, revision)
                if not os.path.isdir(revision_path): continue
                status_file = os.path.join(revision_path, "status")
                if not os.path.isfile(status_file): continue
                items.append(TrackItem(treepkg, track, revision, status_file))

    return items

def __description_header(treepkg):
    treepkg_xml = os.path.join(treepkg, "treepkg.xml")
    if os.path.isfile(treepkg_xml):
        xml = None
        try:
            xml = open(treepkg_xml, "rb")
            dom = etree.parse(xml)
        finally:
            if xml: xml.close()

        description = ''.join(dom.xpath("//description/text()"))
        header = ''.join([etree.tostring(x, encoding="UTF-8", method="html")
            for x in dom.xpath("//header/*")])
        return description, header
    return "unknown", ""

def index(req, treepkg=''):
    if not treepkg: util.redirect(req, "index.py")

    package_dir = None
    for d in os.listdir(TREEPKG_DIR):
        dp = os.path.join(TREEPKG_DIR, d)
        if d == treepkg and os.path.isdir(dp):
            package_dir = dp
            break

    if not package_dir:
        req.status = apache.HTTP_NOT_FOUND
        return "requested TreePkg not found"

    description, header = __description_header(package_dir)

    track_items = __scan_track_items(treepkg, package_dir)

    parameters = {
        'page_title' : description,
        'back_link'  : 'index.py',
        'base_dir'   : BASE_DIR,
        'description': description,
        'header'     : header,
        'track_items': track_items
    }

    if os.path.isfile(os.path.join(package_dir, "rss.xml")):
        parameters['syndicate'] = (
            'Build error feed',
            'treepkgs/%s/rss.xml' % treepkg)

    req.content_type = 'text/html;charset=utf-8'
    template = psp.PSP(req, filename='templates/details.html')
    template.run(parameters)
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)