aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rcynic/rcynic.py211
1 files changed, 211 insertions, 0 deletions
diff --git a/rcynic/rcynic.py b/rcynic/rcynic.py
new file mode 100644
index 00000000..e371edcf
--- /dev/null
+++ b/rcynic/rcynic.py
@@ -0,0 +1,211 @@
+"""
+Python translation of rcynic.xsl, which has gotten too slow and complex.
+
+Copyright (C) 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+"""
+
+import sys, urlparse, os
+
+from xml.etree.cElementTree import (ElementTree, Element, SubElement, Comment)
+
+refresh = 1800
+suppress_zero_columns = True
+show_total = True
+use_colors = True
+show_detailed_status = True
+show_problems = False
+show_summary = True
+
+#input = ElementTree(file = sys.stdin)
+input = ElementTree(file = "rcynic.xml")
+
+html = Element("html")
+html.append(Comment(" Generators:\n " +
+ input.getroot().get("rcynic-version") +
+ "\n $Id$\n"))
+head = SubElement(html, "head")
+body = SubElement(html, "body")
+
+title = "rcynic summary %s" % input.getroot().get("date")
+SubElement(head, "title").text = title
+SubElement(body, "h1").text = title
+
+if refresh:
+ SubElement(head, "meta", { "http-equiv" : "Refresh", "content" : str(refresh) })
+
+css = '''
+ td { text-align: center; padding: 4px }
+ td.uri { text-align: left }
+ td.host { text-align: left }
+'''
+
+if use_colors:
+ css += '''\
+ tr.good,td.good { background-color: #77ff77 }
+ tr.warn,td.warn { background-color: yellow }
+ tr.bad,td.bad { background-color: #ff5500 }
+'''
+
+SubElement(head, "style", type = "text/css").text = css
+
+class Label(object):
+
+ def __init__(self, elt):
+ self.tag = elt.tag
+ self.kind = elt.get("kind")
+ self.text = elt.text.strip()
+
+class Host_Datum(object):
+
+ def __init__(self, elt):
+ self.uri = elt.text.strip()
+ self.status = elt.get("status")
+ self.timestamp = elt.get("timestamp")
+ self.generation = elt.get("generation")
+ self.hostname = urlparse.urlparse(self.uri).hostname
+ self.label = label_map[self.status]
+ self.mood = self.label.kind
+ self.fn2 = os.path.splitext(self.uri)[1] or None
+ if not self.generation:
+ self.fn2 = None
+
+ def __cmp__(self, other):
+ return cmp(self.uri, other.uri) or cmp(self.generation, other.generation)
+
+class Total(object):
+
+ # This probably should merge into the Label class, right now I'm
+ # just trying to do straight translation to keep from getting too
+ # confused by all the different attribute names.
+
+ def __init__(self, label, host_data):
+ self.label = label
+ self.name = label.tag
+ self.mood = label.kind
+ self.text = label.text
+ self.sum = sum(int(h.status == self.name) for h in host_data)
+ self.show = self.sum > 0 or not suppress_zero_columns
+
+labels = [Label(elt) for elt in input.find("labels")]
+label_map = dict((label.tag, label) for label in labels)
+
+if show_summary:
+
+ host_data = [Host_Datum(elt) for elt in input.findall("validation_status")]
+ #host_data.sort()
+
+ unique_hostnames = set(h.hostname for h in host_data)
+ unique_fn2s = set(h.fn2 for h in host_data)
+ unique_generations = set(h.generation for h in host_data)
+
+ totals = [Total(label, host_data) for label in labels]
+ columns = sum(total.sum for total in totals)
+
+ if show_summary:
+
+ SubElement(body, "br")
+ SubElement(body, "h2").text = "Grand Totals"
+ table = SubElement(body, "table", { "class" : "summary", "rules" : "all", "border" : "1"})
+ thead = SubElement(table, "thead")
+ tr = SubElement(thead, "tr")
+ SubElement(tr, "td")
+ for t in totals:
+ if t.show:
+ elt = SubElement(tr, "td")
+ elt = SubElement(elt, "b")
+ elt.text = t.text
+ tbody = SubElement(table, "tbody")
+ tr = SubElement(tbody, "tr")
+ elt = SubElement(tr, "td")
+ elt = SubElement(elt, "b")
+ elt.text = "Total"
+ for t in totals:
+ if t.show:
+ SubElement(tr, "td", { "class" : t.mood }).text = str(t.sum)
+
+ SubElement(body, "br")
+ SubElement(body, "h2").text = "Summaries by Repository Host"
+
+ for hostname in sorted(unique_hostnames):
+ SubElement(body, "br")
+ SubElement(body, "h3").text = hostname
+ table = SubElement(body, "table", { "class" : "summary", "rules" : "all", "border" : "1"})
+ thead = SubElement(table, "thead")
+ tr = SubElement(thead, "tr")
+ SubElement(tr, "td")
+ for t in totals:
+ if t.show:
+ elt = SubElement(tr, "td")
+ elt = SubElement(elt, "b")
+ elt.text = t.text
+ tbody = SubElement(table, "tbody")
+ for fn2 in sorted(unique_fn2s):
+ for generation in sorted(unique_generations):
+ if any(h.hostname == hostname and h.fn2 == fn2 and h.generation == generation for h in host_data):
+ tr = SubElement(tbody, "tr")
+ SubElement(tr, "td").text = (generation or "") + " " + (fn2 or "")
+ for t in totals:
+ if t.show:
+ value = sum(int(h.hostname == hostname and h.fn2 == fn2 and h.generation == generation and h.status == t.name) for h in host_data)
+ elt = SubElement(tr, "td")
+ if value > 0:
+ elt.set("class", t.mood)
+ elt.text = str(value)
+ tr = SubElement(tbody, "tr")
+ SubElement(tr, "td").text = "Total"
+ for t in totals:
+ if t.show:
+ value = sum(int(h.hostname == hostname and h.status == t.name) for h in host_data)
+ elt = SubElement(tr, "td")
+ if value > 0:
+ elt.set("class", t.mood)
+ elt.text = str(value)
+
+ if show_problems:
+
+ SubElement(body, "br")
+ SubElement(body, "h2").text = "Problems"
+ table = SubElement(body, "table", { "class" : "problems", "rules" : "all", "border" : "1"})
+ thead = SubElement(table, "thead")
+ tr = SubElement(thead, "tr")
+ for c in ("Status", "URI"):
+ elt = SubElement(tr, "td", { "class" : c.lower() })
+ SubElement(elt, "b").text = c
+ tbody = SubElement(table, "tbody")
+ for h in host_data:
+ if h.mood != "good":
+ tr = SubElement(tbody, "tr", { "class" : h.mood })
+ SubElement(tr, "td", { "class" : "status" }).text = h.label.text
+ SubElement(tr, "td", { "class" : "uri" }).text = h.uri
+
+ if show_detailed_status:
+
+ SubElement(body, "br")
+ SubElement(body, "h2").text = "Validation Status"
+ table = SubElement(body, "table", { "class" : "details", "rules" : "all", "border" : "1"})
+ thead = SubElement(table, "thead")
+ tr = SubElement(thead, "tr")
+ for c in ("Timestamp", "Generation", "Status", "URI"):
+ elt = SubElement(tr, "td", { "class" : c.lower() })
+ SubElement(elt, "b").text = c
+ tbody = SubElement(table, "tbody")
+ for h in host_data:
+ tr = SubElement(tbody, "tr", { "class" : h.mood })
+ SubElement(tr, "td", { "class" : "timestamp" }).text = h.timestamp
+ SubElement(tr, "td", { "class" : "generation" }).text = h.generation
+ SubElement(tr, "td", { "class" : "status" }).text = h.label.text
+ SubElement(tr, "td", { "class" : "uri" }).text = h.uri
+
+ElementTree(element = html).write(sys.stdout)