teichmann@452: # -*- coding: UTF-8 -*- teichmann@452: # teichmann@452: # Copyright (C) 2010 by Intevation GmbH teichmann@452: # Authors: teichmann@452: # Sascha L. Teichmann teichmann@452: # teichmann@452: # This program is free software under the GPL (>=v2) teichmann@452: # Read the file COPYING coming with the software for details. teichmann@452: teichmann@452: from mod_python import apache, psp, util teichmann@452: teichmann@452: import os teichmann@452: import re teichmann@452: import datetime teichmann@452: import time teichmann@452: teichmann@452: from lxml import etree teichmann@452: teichmann@452: BASE_DIR = "treepkgs" teichmann@452: teichmann@452: TREEPKG_DIR = os.path.join(os.path.dirname(__file__), BASE_DIR) teichmann@452: teichmann@452: STATUS_LINE = re.compile(r"^([^:]+):(.+)") teichmann@452: teichmann@452: UNDER_SCORE = re.compile(r"_+(\w)") teichmann@452: teichmann@490: INVALID_LABEL = re.compile(r"[^a-zA-Z0-9_]") teichmann@490: teichmann@452: def _create_time(s, format="%Y-%m-%d %H:%M:%S"): teichmann@452: return datetime.datetime(*(time.strptime(s, format)[0:6])) teichmann@452: teichmann@452: def _pretty_log_name(log): teichmann@452: log = log.replace(".txt", "").replace(".gz", "").capitalize() teichmann@452: return UNDER_SCORE.sub(lambda x: " %s" % x.group(1).upper(), log) teichmann@452: teichmann@490: def _make_valid_label(label): teichmann@490: return INVALID_LABEL.sub("_", label) teichmann@490: teichmann@452: class TrackItem(object): teichmann@452: teichmann@452: def __init__(self, treepkg, track, revision, status_file): teichmann@452: self.treepkg = treepkg teichmann@452: self.track = track teichmann@452: self.revision = revision teichmann@452: self.status_file = status_file teichmann@452: self.loaded = False teichmann@452: self.status = None teichmann@452: self.start = None teichmann@452: self.stop = None teichmann@452: self.logs = None teichmann@474: self.tags = None teichmann@452: teichmann@452: def check_loaded(self): teichmann@452: if not self.loaded: teichmann@452: f = open(self.status_file) teichmann@452: try: teichmann@452: for line in f: teichmann@452: m = STATUS_LINE.match(line) teichmann@452: if not m: continue teichmann@452: key, value = [x.strip() for x in m.groups()] teichmann@452: teichmann@452: if key == 'status': self.status = value teichmann@452: elif key == 'start': self.start = _create_time(value) teichmann@452: elif key == 'stop': self.stop = _create_time(value) teichmann@474: elif key == 'tags': self.tags = value teichmann@452: finally: teichmann@452: f.close() teichmann@452: self.loaded = True teichmann@452: teichmann@452: def get_build_status(self): teichmann@452: self.check_loaded() teichmann@452: return self.status teichmann@452: teichmann@452: def get_build_start(self): teichmann@452: self.check_loaded() teichmann@452: return self.start teichmann@452: teichmann@452: def get_build_stop(self): teichmann@452: self.check_loaded() teichmann@452: return self.stop teichmann@452: teichmann@474: def get_tags(self): teichmann@474: self.check_loaded() teichmann@474: return self.tags teichmann@474: teichmann@490: def get_label(self): teichmann@490: self.check_loaded() teichmann@490: out = [ teichmann@490: _make_valid_label(self.track), teichmann@490: _make_valid_label(self.revision) ] teichmann@490: if self.start: teichmann@490: out.append(self.start.strftime("%Y%m%d%H%M%S")) teichmann@490: return ''.join(out) teichmann@490: teichmann@452: def log_path(self, log): teichmann@452: return "%s/tracks/%s/pkg/%s/log/%s" % ( teichmann@452: self.treepkg, self.track, self.revision, log) teichmann@452: teichmann@452: def get_build_logs(self): teichmann@452: oj = os.path.join teichmann@452: if self.logs is None: teichmann@452: log_dir = oj(os.path.dirname(self.status_file), "log") teichmann@452: if not os.path.isdir(log_dir): teichmann@452: self.logs = [] teichmann@452: else: teichmann@473: self.logs =[(_pretty_log_name(f), self.log_path(f)) teichmann@452: for f in os.listdir(log_dir) teichmann@452: if os.path.isfile(oj(log_dir, f)) and f.find("txt") >= 0] teichmann@452: return self.logs teichmann@452: teichmann@452: build_status = property(get_build_status) teichmann@452: build_start = property(get_build_start) teichmann@452: build_stop = property(get_build_stop) teichmann@452: build_logs = property(get_build_logs) teichmann@474: build_tags = property(get_tags) teichmann@490: build_label = property(get_label) teichmann@452: teichmann@452: teichmann@452: def __scan_track_items(treepkg, path): teichmann@452: items = [] teichmann@452: teichmann@452: tracks_path = os.path.join(path, "tracks") teichmann@474: if os.path.isdir(tracks_path): teichmann@467: for track in os.listdir(tracks_path): teichmann@467: track_path = os.path.join(tracks_path, track) teichmann@467: if not os.path.isdir(track_path): continue teichmann@467: revisions_path = os.path.join(track_path, "pkg") teichmann@467: if not os.path.isdir(revisions_path): continue teichmann@467: for revision in os.listdir(revisions_path): teichmann@467: revision_path = os.path.join(revisions_path, revision) teichmann@467: if not os.path.isdir(revision_path): continue teichmann@467: status_file = os.path.join(revision_path, "status") teichmann@467: if not os.path.isfile(status_file): continue teichmann@467: items.append(TrackItem(treepkg, track, revision, status_file)) teichmann@473: teichmann@452: return items teichmann@452: teichmann@452: def __description_header(treepkg): teichmann@452: treepkg_xml = os.path.join(treepkg, "treepkg.xml") teichmann@452: if os.path.isfile(treepkg_xml): teichmann@452: xml = None teichmann@452: try: teichmann@452: xml = open(treepkg_xml, "rb") teichmann@452: dom = etree.parse(xml) teichmann@452: finally: teichmann@452: if xml: xml.close() teichmann@452: teichmann@452: description = ''.join(dom.xpath("//description/text()")) teichmann@452: header = ''.join([etree.tostring(x, encoding="UTF-8", method="html") teichmann@452: for x in dom.xpath("//header/*")]) teichmann@452: return description, header teichmann@452: return "unknown", "" teichmann@452: teichmann@452: def index(req, treepkg=''): teichmann@452: if not treepkg: util.redirect(req, "index.py") teichmann@452: teichmann@490: package_dir = None teichmann@452: for d in os.listdir(TREEPKG_DIR): teichmann@452: dp = os.path.join(TREEPKG_DIR, d) teichmann@487: if d == treepkg and os.path.isdir(dp): teichmann@490: package_dir = dp teichmann@452: break teichmann@452: teichmann@490: if not package_dir: teichmann@452: req.status = apache.HTTP_NOT_FOUND teichmann@452: return "requested TreePkg not found" teichmann@452: teichmann@490: description, header = __description_header(package_dir) teichmann@452: teichmann@490: track_items = __scan_track_items(treepkg, package_dir) teichmann@473: teichmann@490: parameters = { teichmann@483: 'page_title' : description, teichmann@483: 'back_link' : 'index.py', teichmann@483: 'base_dir' : BASE_DIR, teichmann@452: 'description': description, teichmann@483: 'header' : header, teichmann@452: 'track_items': track_items teichmann@490: } teichmann@490: teichmann@490: if os.path.isfile(os.path.join(package_dir, "rss.xml")): teichmann@490: parameters['syndicate'] = ( teichmann@490: 'Build error feed', teichmann@490: 'treepkgs/%s/rss.xml' % treepkg) teichmann@490: teichmann@490: req.content_type = 'text/html;charset=utf-8' teichmann@490: template = psp.PSP(req, filename='templates/details.html') teichmann@490: template.run(parameters)