| #!/usr/bin/python |
| |
| import xmllib; |
| import sys; |
| import string |
| import re |
| |
| def html_subst(s): |
| if s.group(1) != None: |
| return s.group(0) |
| elif s.group(2) != None: |
| return '<a href="' + s.group(0) + '">' + s.group(0) + '</a>' |
| elif s.group(3) != None: |
| return '<a href="mailto:' + s.group(0) + '">' + s.group(0) + '</a>' |
| |
| def htmlify(str): |
| return re.sub ("(<[^>]*>)|(http://[~.:/\w-]+)|([\w._!-]+@[\w_-]+).[\w_-]+", html_subst, str) |
| |
| def bug_subst(s): |
| if s.group(1) != None: |
| return s.group(0) |
| else: |
| n = s.group(2) |
| return '<a href="http://bugs.gnome.org/db/%s/%s.html">#%s</a>' % (n[0:2], n, n) |
| |
| def bugify(str): |
| str = re.sub ("(<[^>]*>)|#(\d+)", bug_subst, str) |
| return htmlify(str) |
| |
| def make_id(str): |
| return re.sub ("[^a-z]","-", string.lower(str)) |
| |
| class ParseError (Exception): |
| pass |
| |
| class Entry: |
| def __init__(self): |
| self.description = None |
| self.title = None |
| self.url = None |
| self.contact = None |
| self.bugs = None |
| |
| def set_size(self, size): |
| size = string.lower(size) |
| if size == "small": |
| self.size = "Small" |
| elif size == "medium": |
| self.size = "Medium" |
| elif size == "big": |
| self.size = "Big" |
| else: |
| raise ParseError, 'size must be "small", "medium", or "big"' |
| |
| def output(self): |
| if self.size == "Big": |
| bgcolor = "#88bb88" |
| elif self.size == "Medium": |
| bgcolor = "#b4d4b4" |
| else: |
| bgcolor = "#d0e0d0" |
| |
| print '''<table cellspacing="0" cellpadding="2" width="97%%" border="0" bgcolor="#000000"> |
| <tbody><tr><td colspan=2> |
| <table cellspacing="0" cellpadding="5" width="100%%" border="0" bgcolor="#ffffff"> |
| <tbody> |
| <tr bgcolor="%s"> |
| <td align="left"><font size="+1">%s</font></font></td> |
| <td align="left" width="20%%"><b>Size</b>: %s</td> |
| <td align="center" width="20%%"><b>Status</b>: %s</td> |
| <td align="right" width="20%%"><b>Target Version</b>: %s</td> |
| </tr> |
| <tr> |
| <td colspan=4> |
| %s |
| <table cellspacing="0" cellpadding="0"> |
| <tbody>''' % (bgcolor, self.title, self.size, self.status, self.target, htmlify(self.description)) |
| |
| if self.url != None: |
| print '''<tr><td width="0"><b>More Info</b>:</td> |
| <td>%s</td> |
| </tr>''' % htmlify (self.url) |
| |
| if self.bugs != None: |
| print '''<tr><td width="0"><b>Bug Reports</b>:</td> |
| <td>%s</td> |
| </tr>''' % bugify (self.bugs) |
| |
| if self.contact != None: |
| print '''<tr><td width="0"><b>Contact</b>:</td> |
| <td>%s</td> |
| </tr>''' % htmlify (self.contact) |
| |
| print '''</tbody> |
| </table> |
| </td> |
| </tr> |
| </tbody></table> |
| </td></tr></tbody></table> |
| ''' |
| |
| class Section: |
| def __init__(self): |
| self.title = None |
| self.entries = [] |
| |
| def output(self): |
| |
| print '<h2><a name="%s">%s</a></h2>' % (make_id(self.title), self.title) |
| |
| first = 1 |
| for entry in self.entries: |
| if not first: |
| print "<br>" |
| first = 0 |
| entry.output() |
| |
| class TodoParser (xmllib.XMLParser): |
| def __init__(self): |
| xmllib.XMLParser.__init__(self) |
| |
| self.in_todo = 0 |
| self.in_data = 0 |
| self.data = "" |
| self.section = None |
| self.entry = None |
| self.logourl = None |
| self.title = None |
| self.sections = [] |
| |
| self.entitydefs = {} |
| |
| def start_todo(self,attributes): |
| if self.in_todo: |
| raise ParseError, "<todo> tags may not be nested" |
| if attributes.has_key ("logourl"): |
| self.logourl = attributes["logourl"] |
| self.in_todo = 1 |
| |
| def end_todo(self): |
| self.in_todo = 0 |
| |
| def start_section(self,attributes): |
| if self.section: |
| raise ParseError, "<section> tags may not be nested" |
| |
| self.section = Section() |
| |
| def end_section(self): |
| if self.section.title == None: |
| raise ParseError, "<section> requires <title>" |
| |
| self.sections.append(self.section) |
| self.section = None |
| |
| def start_title(self,attributes): |
| if not self.in_todo: |
| raise ParseError, "<title> tag must be in <todo>, <section> or <entry>" |
| if self.in_data: |
| raise ParseError, "Unexpected <title> tag in content" |
| self.in_data = 1 |
| |
| def end_title(self): |
| self.in_data = 0 |
| if self.entry: |
| self.entry.title = self.data |
| elif self.section: |
| self.section.title = self.data |
| else: |
| self.title = self.data |
| self.data = "" |
| |
| def start_description(self,attributes): |
| if not self.entry: |
| raise ParseError, "<description> tag must be in <entry>" |
| if self.in_data: |
| raise ParseError, "Unexpected <description> tag in content" |
| self.in_data = 1 |
| |
| def end_description(self): |
| self.in_data = 0 |
| self.entry.description = self.data |
| self.data = "" |
| |
| def start_url(self,attributes): |
| if not self.entry: |
| raise ParseError, "<url> tag must be in <entry>" |
| if self.in_data: |
| raise ParseError, "Unexpected <url> tag in content" |
| self.in_data = 1 |
| |
| def end_url(self): |
| self.in_data = 0 |
| self.entry.url = self.data |
| self.data = "" |
| |
| def start_contact(self,attributes): |
| if not self.entry: |
| raise ParseError, "<contact> tag must be in <entry>" |
| if self.in_data: |
| raise ParseError, "Unexpected <contact> tag in content" |
| self.in_data = 1 |
| |
| def end_contact(self): |
| self.in_data = 0 |
| self.entry.contact = self.data |
| self.data = "" |
| |
| def start_bugs(self,attributes): |
| if not self.entry: |
| raise ParseError, "<bugs> tag must be in <bugs>" |
| if self.in_data: |
| raise ParseError, "Unexpected <bugs> tag in content" |
| self.in_data = 1 |
| |
| def end_bugs(self): |
| self.in_data = 0 |
| self.entry.bugs = self.data |
| self.data = "" |
| |
| def start_entry(self,attributes): |
| if not self.section: |
| raise ParseError, "<entry> tag must be in <section>" |
| if self.entry: |
| raise ParseError, "<entry> tags may not be nested" |
| |
| self.entry = Entry() |
| |
| if not attributes.has_key("size"): |
| raise ParseError, '"size" attribute required for entry' |
| self.entry.set_size(attributes["size"]) |
| |
| if not attributes.has_key("status"): |
| raise ParseError, '"status" attribute (completion percentage) required for entry' |
| self.entry.status=attributes["status"] |
| |
| if not attributes.has_key("target"): |
| raise ParseError, '"target" attribute (target version) required for entry' |
| self.entry.target=attributes["target"] |
| |
| def end_entry(self): |
| if self.entry.title == None: |
| raise ParseError, "<entry> requires <title>" |
| |
| if self.entry.description == None: |
| raise ParseError, "<entry> requires <description>" |
| |
| self.section.entries.append(self.entry) |
| self.entry = None |
| |
| def handle_data(self,data): |
| if self.in_data: |
| self.data = self.data + data |
| |
| def unknown_starttag(self,tag,attributes): |
| if not self.in_data: |
| raise ParseError, "Unexpected start tag: " + tag |
| else: |
| self.data = self.data + "<" + tag |
| for (key,val) in attributes.items(): |
| self.data = self.data + ' %s="%s"' % (key,val) |
| self.data = self.data + ">" |
| |
| def unknown_endtag(self,tag): |
| if not self.in_data: |
| raise ParseError, "Unexpected end tag: " + tag |
| else: |
| self.data = self.data + "</%s>" % tag |
| |
| def syntax_error(self, err): |
| if re.match("reference to unknown entity", err): |
| pass |
| else: |
| xmllib.XMLParser.syntax_error (self, err) |
| |
| def unknown_entityref(self,ref): |
| if not self.in_data: |
| raise ParseError, "Unknown entity &" + ref + ";" |
| else: |
| self.data = self.data + "&" + ref + ";" |
| |
| file = open(sys.argv[1]) |
| parser = TodoParser() |
| |
| lineno = 1 |
| while 1: |
| line = file.readline() |
| if line == "": |
| break |
| |
| try: |
| parser.feed(line) |
| except ParseError, err: |
| sys.stderr.write("Parse error at line " + `lineno` + ": " + err.__str__() + "\n") |
| sys.exit(1) |
| except RuntimeError, err: |
| sys.stderr.write(err.__str__() + "\n") |
| sys.exit(1) |
| |
| lineno = lineno + 1 |
| |
| parser.close() |
| if parser.title == None: |
| sys.stderr.write ("<todo> Document must have a <title>\n") |
| sys.exit (1) |
| |
| print '''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> |
| <html> |
| <head> |
| <title>%s</title> |
| </head> |
| <body bgcolor="#ffffff"> |
| <table width="100%%" cellspacing="0" cellpadding="0" border="0"> |
| <tbody> |
| <tr valign="top"> |
| <td> |
| <h1>%s</h1>''' % (parser.title, parser.title) |
| |
| |
| for section in parser.sections: |
| ntasks = len(section.entries) |
| id = make_id (section.title) |
| if ntasks == 1: |
| print '<a href="#%s">%s</a> (1 item)<br>' % (id,section.title) |
| else: |
| print '<a href="#%s">%s</a> (%d items)<br>' % (id,section.title,ntasks) |
| |
| print ''' |
| </td>''' |
| if parser.logourl != None: |
| print ''' <td align="right"> |
| <img src="%s" alt="Logo"></img> |
| </td>''' % parser.logourl |
| print ''' |
| </tr> |
| </tbody> |
| </table> |
| ''' |
| |
| first = 1 |
| for section in parser.sections: |
| if not first: |
| print "<br><br>" |
| first = 0 |
| section.output() |
| |
| print '''</body> |
| </html>''' |
| |
| |