Merged Gianluca's html output branch.
authorW. Trevor King <wking@drexel.edu>
Fri, 7 Aug 2009 18:19:30 +0000 (14:19 -0400)
committerW. Trevor King <wking@drexel.edu>
Fri, 7 Aug 2009 18:19:30 +0000 (14:19 -0400)
Changes to merge with my branch:

  * Added some reference to "be html" or "becommands/html.py" in his
    new bugs' summaries.  Bug titles should make clear to which aspect
    of our growing repository they refer.
  * Fixed unittests in becommands/html.py.
  * execute() kwarg "test"->!"manipulate_encodings
  * bugdir.simple_bug_dir() -> bugdir.SimpleBugDir()
  * bd.cleanup() at end of unittests

1  2 
.be/bugs/2b81b428-fc43-4970-9469-b442385b9c0d/values
.be/bugs/8385a1fb-63df-4ca6-81cd-28ede83bb0c2/values
.be/bugs/9b1a0e71-4f7d-40b1-ab32-18496bf19a3f/values
.be/bugs/c271a802-d324-48a6-b01d-63e4a72aa43e/values
.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/body
.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/values
.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/values
.be/bugs/da2b09ff-af24-40f3-9b8d-6ffaa5f41164/values
.be/bugs/f77fc673-c852-4c81-bfa2-1d59de2661c8/values
becommands/html.py

index 0000000000000000000000000000000000000000,0a47ab5bd5c59de2439ea8160c795c8a608a0bfc..c2861d09197608d0bf2d88453a5b79552c7bf3d0
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -status: closed
+ creator: gianluca <gian@galactica>
+ reporter: gianluca <gian@galactica>
+ severity: minor
 -summary: Use the get_parser
++status: fixed
++summary: Use the get_parser in becommands/html.py
+ time: Wed, 08 Jul 2009 21:27:37 +0000
index 0000000000000000000000000000000000000000,918f6be7bdb2edde7090d2a6f002975ba1b7de25..5d80e706f008c8b8b0f33facec0e83f9de1fa683
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -summary: Add the html files for the status detail
+ creator: gianluca <gian@galactica>
+ reporter: gianluca <gian@galactica>
+ severity: minor
+ status: wontfix
++summary: Add the html files for the status detail to "be html" output
+ time: Fri, 03 Jul 2009 22:56:09 +0000
index 0000000000000000000000000000000000000000,600f2c3e1bda28784b6119f4a281a8786f4f188f..4b6bb4f7d75bde5117ca04a55d56ff98c0a50cc5
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -summary: Add the html files for the severity detail
+ creator: gianluca <gian@galactica>
+ reporter: gianluca <gian@galactica>
+ severity: minor
+ status: wontfix
++summary: Add the html files for the severity detail to "be html" output
+ time: Fri, 03 Jul 2009 22:56:19 +0000
index 0000000000000000000000000000000000000000,8b58566ab6c50b23e532474f3e7d9ab77d39ec17..b85936499f666f66ee7081941bbe94a5dfafcb76
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -summary: Add a verbose option?
+ creator: gianluca <gian@galactica>
+ reporter: gianluca <gian@galactica>
+ severity: wishlist
+ status: open
++summary: Add a verbose option to "be html"?
+ time: Fri, 03 Jul 2009 21:17:41 +0000
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1090ace37188472bb67d578c7b14e1cfeeae7b10
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,2 @@@
++Available with
++  be -d DIR html
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9ac4884bfd3dd7e30b3a6d82cff2c5ff6d76980d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++Author: W. Trevor King <wking@drexel.edu>
++
++
++Content-type: text/plain
++
++
++Date: Fri, 07 Aug 2009 17:58:58 +0000
++
index 0000000000000000000000000000000000000000,a5777b95d54d5e405cec39d1fd7317c263f7c1d8..ce4bc921bd83dd66adb9d7b794088f8fd5ace661
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -status: closed
+ creator: gianluca <gian@galactica>
+ reporter: gianluca <gian@galactica>
+ severity: wishlist
 -summary: Add the possibility to specify the repository Directory ?
++status: fixed
++summary: Add the possibility to specify the repository directory to "be html"?
+ time: Fri, 03 Jul 2009 21:18:13 +0000
index 0000000000000000000000000000000000000000,4d784ad920f32e760a0d9edd428151f2172cbc23..2832bb36168ac4a0da3abeda88e6df8bb4b60b81
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -summary: Add an icon near the status string
+ creator: Gianluca Montecchi <gian@grys.it>
+ reporter: Gianluca Montecchi <gian@grys.it>
+ severity: wishlist
+ status: open
++summary: Add an icon near the status string in "be html" output
+ time: Tue, 04 Aug 2009 21:15:52 +0000
index 0000000000000000000000000000000000000000,5a7261bb635f20da8a37c7724264528793506b5f..72c2839e71aa49d228ebfcadbe1106a7f96c28ee
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -summary: Comment should be threaded in the html output
+ creator: Gianluca Montecchi <gian@grys.it>
+ reporter: Gianluca Montecchi <gian@grys.it>
+ severity: minor
+ status: fixed
++summary: Comment should be threaded in the "be html" output
+ time: Tue, 21 Jul 2009 21:39:52 +0000
index 0000000000000000000000000000000000000000,4bf43f439844205c37ea7c56aee09b7d8e73bea0..3acf973e33eb025df3d8fba66fbdc8a6c96ebe19
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,581 +1,587 @@@
 -# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
 -#                         Marien Zwart <marienz@gentoo.org>
 -#                         Thomas Gerigk <tgerigk@gmx.de>
 -#                         W. Trevor King <wking@drexel.edu>
 -# <abentley@panoramicfeedback.com>
++# Copyright (C) 2005-2009
+ #
+ #    This program is free software; you can redistribute it and/or modify
+ #    it under the terms of the GNU General Public License as published by
+ #    the Free Software Foundation; either version 2 of the License, or
+ #    (at your option) any later version.
+ #
+ #    This program is distributed in the hope that it will be useful,
+ #    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ #    GNU General Public License for more details.
+ #
+ #    You should have received a copy of the GNU General Public License
+ #    along with this program; if not, write to the Free Software
+ #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ """Generate a static HTML dump of the current repository status"""
 -from libbe import cmdutil, bugdir, bug, settings_object
++from libbe import cmdutil, bugdir, bug
+ #from html_data import *
+ import codecs, os, re, string, time
+ import xml.sax.saxutils, htmlentitydefs
+ __desc__ = __doc__
 -def execute(args, test=False):
++def execute(args, manipulate_encodings=True):
+     """
+     >>> import os
 -    >>> bd = bugdir.simple_bug_dir()
++    >>> bd = bugdir.SimpleBugDir()
+     >>> os.chdir(bd.root)
 -    >>> print bd.bug_from_shortname("b").status
 -    closed
 -    >>> execute(["b"], test=True)
 -    >>> bd._clear_bugs()
 -    >>> print bd.bug_from_shortname("b").status
 -    open
++    >>> execute([], manipulate_encodings=False)
++    Creating the html output in html_export
++    >>> os.path.exists("./html_export")
++    True
++    >>> os.path.exists("./html_export/index.html")
++    True
++    >>> os.path.exists("./html_export/index_inactive.html")
++    True
++    >>> os.path.exists("./html_export/bugs")
++    True
++    >>> os.path.exists("./html_export/bugs/a.html")
++    True
++    >>> os.path.exists("./html_export/bugs/b.html")
++    True
++    >>> bd.cleanup()
+     """
+     parser = get_parser()
+     options, args = parser.parse_args(args)
+     complete(options, args, parser)
+     cmdutil.default_complete(options, args, parser,
+                              bugid_args={0: lambda bug : bug.active==False})
+     
+     if len(args) == 0:
+         out_dir = options.outdir
+         print "Creating the html output in %s"%out_dir
+     else:
+         out_dir = args[0]
+     if len(args) > 0:
+         raise cmdutil.UsageError, "Too many arguments."
+     
 -    bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test)
++    bd = bugdir.BugDir(from_disk=True,
++                       manipulate_encodings=manipulate_encodings)
+     bd.load_all_bugs()
+     status_list = bug.status_values
+     severity_list = bug.severity_values
+     st = {}
+     se = {}
+     stime = {}
+     bugs_active = []
+     bugs_inactive = []
+     for s in status_list:
+         st[s] = 0
+     for b in sorted(bd, reverse=True):
+         stime[b.uuid]  = b.time
+         if b.status in ["open", "test", "unconfirmed", "assigned"]:
+             bugs_active.append(b)
+         else:
+             bugs_inactive.append(b)
+         st[b.status] += 1
+     ordered_bug_list = sorted([(value,key) for (key,value) in stime.items()])
+     ordered_bug_list_in = sorted([(value,key) for (key,value) in stime.items()])
+     #open_bug_list = sorted([(value,key) for (key,value) in bugs.items()])
+     
+     html_gen = BEHTMLGen(bd)
+     html_gen.create_index_file(out_dir,  st, bugs_active, ordered_bug_list, "active", bd.encoding)
+     html_gen.create_index_file(out_dir,  st, bugs_inactive, ordered_bug_list, "inactive", bd.encoding)
+     
+ def get_parser():
+     parser = cmdutil.CmdOptionParser("be open OUTPUT_DIR")
+     parser.add_option("-o", "--output", metavar="export_dir", dest="outdir",
+         help="Set the output path, default is ./html_export", default="html_export")    
+     return parser
+ longhelp="""
+ Generate a set of html pages representing the current state of the bug
+ directory.
+ """
+ def help():
+     return get_parser().help_str() + longhelp
+ def complete(options, args, parser):
+     for option, value in cmdutil.option_value_pairs(options, parser):
+         if "--complete" in args:
+             raise cmdutil.GetCompletions() # no positional arguments for list
+         
+ def escape(string):
 -    if string == settings_object.EMPTY:
++    if string == None:
+         return ""
+     chars = []
+     for char in xml.sax.saxutils.escape(string):
+         codepoint = ord(char)
+         if codepoint in htmlentitydefs.codepoint2name:
+             char = "&%s;" % htmlentitydefs.codepoint2name[codepoint]
+         chars.append(char)
+     return "".join(chars)
+ class BEHTMLGen():
+     def __init__(self, bd):
+         self.index_value = ""    
+         self.bd = bd
+         
+         self.css_file = """
+         body {
+         font-family: "lucida grande", "sans serif";
+         color: #333;
+         width: auto;
+         margin: auto;
+         }
+         
+         
+         div.main {
+         padding: 20px;
+         margin: auto;
+         padding-top: 0;
+         margin-top: 1em;
+         background-color: #fcfcfc;
+         }
+         
+         .comment {
+         padding: 20px;
+         margin: auto;
+         padding-top: 20px;
+         margin-top: 0;
+         }
+         
+         .commentF {
+         padding: 0px;
+         margin: auto;
+         padding-top: 0px;
+         paddin-bottom: 20px;
+         margin-top: 0;
+         }
+         
+         tb {
+         border = 1;
+         }
+         
+         .wishlist-row {
+         background-color: #B4FF9B;
+         width: auto;
+         }
+         
+         .minor-row {
+         background-color: #FCFF98;
+         width: auto;
+         }
+         
+         
+         .serious-row {
+         background-color: #FFB648;
+         width: auto;
+         }
+         
+         .critical-row {
+         background-color: #FF752A;
+         width: auto;
+         }
+         
+         .fatal-row {
+         background-color: #FF3300;
+         width: auto;
+         }
+                 
+         .person {
+         font-family: courier;
+         }
+         
+         a, a:visited {
+         background: inherit;
+         text-decoration: none;
+         }
+         
+         a {
+         color: #003d41;
+         }
+         
+         a:visited {
+         color: #553d41;
+         }
+         
+         ul {
+         list-style-type: none;
+         padding: 0;
+         }
+         
+         p {
+         width: auto;
+         }
+         
+         .inline-status-image {
+         position: relative;
+         top: 0.2em;
+         }
+         
+         .dimmed {
+         color: #bbb;
+         }
+         
+         table {
+         border-style: 10px solid #313131;
+         border-spacing: 0;
+         width: auto;
+         }
+         
+         table.log {
+         }
+         
+         td {
+         border-width: 0;
+         border-style: none;
+         padding-right: 0.5em;
+         padding-left: 0.5em;
+         width: auto;
+         }
+         
+         .td_sel {
+         background-color: #afafaf;
+         border: 1px solid #afafaf;
+         font-weight:bold;
+         padding-right: 1em;
+         padding-left: 1em;
+         
+         }
+         
+         .td_nsel {
+         border: 0px;
+         padding-right: 1em;
+         padding-left: 1em;
+         }
+         
+         tr {
+         vertical-align: top;
+         width: auto;
+         }
+         
+         h1 {
+         padding: 0.5em;
+         background-color: #305275;
+         margin-top: 0;
+         margin-bottom: 0;
+         color: #fff;
+         margin-left: -20px;
+         margin-right: -20px;  
+         }
+         
+         wid {
+         text-transform: uppercase;
+         font-size: smaller;
+         margin-top: 1em;
+         margin-left: -0.5em;  
+         /*background: #fffbce;*/
+         /*background: #628a0d;*/
+         padding: 5px;
+         color: #305275;
+         }
+         
+         .attrname {
+         text-align: right;
+         font-size: smaller;
+         }
+         
+         .attrval {
+         color: #222;
+         }
+         
+         .issue-closed-fixed {
+         background-image: "green-check.png";
+         }
+         
+         .issue-closed-wontfix {
+         background-image: "red-check.png";
+         }
+         
+         .issue-closed-reorg {
+         background-image: "blue-check.png";
+         }
+         
+         .inline-issue-link {
+         text-decoration: underline;
+         }
+         
+         img {
+         border: 0;
+         }
+         
+         
+         div.footer {
+         font-size: small;
+         padding-left: 20px;
+         padding-right: 20px;
+         padding-top: 5px;
+         padding-bottom: 5px;
+         margin: auto;
+         background: #305275;
+         color: #fffee7;
+         }
+         
+         .footer a {
+         color: #508d91;
+         }
+         
+         
+         .header {
+         font-family: "lucida grande", "sans serif";
+         font-size: smaller;
+         background-color: #a9a9a9;
+         text-align: left;
+         
+         padding-right: 0.5em;
+         padding-left: 0.5em;
+         
+         }
+         
+         
+         .selected-cell {
+         background-color: #e9e9e2;
+         }
+         
+         .plain-cell {
+         background-color: #f9f9f9;
+         }
+         
+         
+         .logcomment {
+         padding-left: 4em;
+         font-size: smaller;
+         }
+         
+         .id {
+         font-family: courier;
+         }
+         
+         .table_bug {
+         background-color: #afafaf;
+         border: 2px solid #afafaf;
+         }
+         
+         .message {
+         }
+         
+         .progress-meter-done {
+         background-color: #03af00;
+         }
+         
+         .progress-meter-undone {
+         background-color: #ddd;
+         }
+         
+         .progress-meter {
+         }
+         
+         """
+         
+         self.index_first = """
+         <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+         <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+         <head>
+         <title>BugsEverywhere Issue Tracker</title>
+         <meta http-equiv="Content-Type" content="text/html; charset=%s" />
+         <link rel="stylesheet" href="style.css" type="text/css" />
+         </head>
+         <body>
+         
+         
+         <div class="main">
+         <h1>BugsEverywhere Bug List</h1>
+         <p></p>
+         <table>
+         
+         <tr>
+         <td class="%%s"><a href="index.html">Active Bugs</a></td>
+         <td class="%%s"><a href="index_inactive.html">Inactive Bugs</a></td>
+         </tr>
+         
+         </table>
+         <table class="table_bug">
+         <tbody>
+         """ % self.bd.encoding
+         
+         self.bug_line ="""
+         <tr class="%s-row">
+         <td ><a href="bugs/%s.html">%s</a></td>
+         <td ><a href="bugs/%s.html">%s</a></td>
+         <td><a href="bugs/%s.html">%s</a></td>
+         <td><a href="bugs/%s.html">%s</a></td>
+         <td><a href="bugs/%s.html">%s</a></td>
+         </tr>
+         """
+         
+         self.detail_first = """
+         <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+           "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+         <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+         <head>
+         <title>BugsEverywhere Issue Tracker</title>
+         <meta http-equiv="Content-Type" content="text/html; charset=%s" />
+         <link rel="stylesheet" href="../style.css" type="text/css" />
+         </head>
+         <body>
+         
+         
+         <div class="main">
+         <h1>BugsEverywhere Bug List</h1>
+         <h5><a href="%%s">Back to Index</a></h5>
+         <h2>Bug: _bug_id_</h2>
+         <table >
+         <tbody>
+         """ % self.bd.encoding
+         
+         
+         
+         self.detail_line ="""
+         <tr>
+         <td align="right">%s</td><td>%s</td>
+         </tr>
+         """
+         
+         self.index_last = """
+         </tbody>
+         </table>
+         
+         </div>
+         
+         <div class="footer">Generated by <a href="http://www.bugseverywhere.org/">BugsEverywhere</a> on %s</div>
+         
+         </body>
+         </html>
+         """
+         
+         self.comment_section = """
+         """
+         
+         self.begin_comment_section ="""
+         <tr>
+         <td align="right">Comments:
+         </td>
+         <td>
+         """
+         
+         
+         self.end_comment_section ="""
+         </td>
+         </tr>
+         """
+         
+         self.detail_last = """
+         </tbody>
+         </table>
+         </div>
+         <h5><a href="%s">Back to Index</a></h5>
+         <div class="footer">Generated by <a href="http://www.bugseverywhere.org/">BugsEverywhere</a>.</div>
+         </body>
+         </html>
+         """   
+         
+         
+     def create_index_file(self, out_dir_path,  summary,  bugs, ordered_bug, fileid, encoding):
+         try:
+             os.stat(out_dir_path)
+         except:
+             try:
+                 os.mkdir(out_dir_path)
+             except:
+                 raise  cmdutil.UsageError, "Cannot create output directory."
+         try:
+             FO = codecs.open(out_dir_path+"/style.css", "w", encoding)
+             FO.write(self.css_file)
+             FO.close()
+         except:
+             raise  cmdutil.UsageError, "Cannot create the style.css file."
+         
+         try:
+             os.mkdir(out_dir_path+"/bugs")
+         except:
+             pass
+         
+         try:
+             if fileid == "active":
+                 FO = codecs.open(out_dir_path+"/index.html", "w", encoding)
+                 FO.write(self.index_first%('td_sel','td_nsel'))
+             if fileid == "inactive":
+                 FO = codecs.open(out_dir_path+"/index_inactive.html", "w", encoding)
+                 FO.write(self.index_first%('td_nsel','td_sel'))
+         except:
+             raise  cmdutil.UsageError, "Cannot create the index.html file."
+         
+         c = 0
+         t = len(bugs) - 1
+         for l in range(t,  -1,  -1):
+             line = self.bug_line%(escape(bugs[l].severity),
+                                   escape(bugs[l].uuid), escape(bugs[l].uuid[0:3]),
+                                   escape(bugs[l].uuid), escape(bugs[l].status),
+                                   escape(bugs[l].uuid), escape(bugs[l].severity),
+                                   escape(bugs[l].uuid), escape(bugs[l].summary),
+                                   escape(bugs[l].uuid), escape(bugs[l].time_string)
+                                   )
+             FO.write(line)
+             c += 1
+             self.create_detail_file(bugs[l], out_dir_path, fileid, encoding)
+         when = time.ctime()
+         FO.write(self.index_last%when)
+     def create_detail_file(self, bug, out_dir_path, fileid, encoding):
+         f = "%s.html"%bug.uuid
+         p = out_dir_path+"/bugs/"+f
+         try:
+             FD = codecs.open(p, "w", encoding)
+         except:
+             raise  cmdutil.UsageError, "Cannot create the detail html file."
+         detail_first_ = re.sub('_bug_id_', bug.uuid[0:3], self.detail_first)
+         if fileid == "active":
+             FD.write(detail_first_%"../index.html")
+         if fileid == "inactive":
+             FD.write(detail_first_%"../index_inactive.html")
+             
+         
+          
+         bug_ = self.bd.bug_from_shortname(bug.uuid)
+         bug_.load_comments(load_full=True)
+         
+         FD.write(self.detail_line%("ID : ", bug.uuid))
+         FD.write(self.detail_line%("Short name : ", escape(bug.uuid[0:3])))
+         FD.write(self.detail_line%("Severity : ", escape(bug.severity)))
+         FD.write(self.detail_line%("Status : ", escape(bug.status)))
+         FD.write(self.detail_line%("Assigned : ", escape(bug.assigned)))
+         FD.write(self.detail_line%("Target : ", escape(bug.target)))
+         FD.write(self.detail_line%("Reporter : ", escape(bug.reporter)))
+         FD.write(self.detail_line%("Creator : ", escape(bug.creator)))
+         FD.write(self.detail_line%("Created : ", escape(bug.time_string)))
+         FD.write(self.detail_line%("Summary : ", escape(bug.summary)))
+         FD.write("<tr><td colspan=\"2\"><hr /></td></tr>")
+         FD.write(self.begin_comment_section)
+         tr = []
+         b = ''
+         level = 0
+         stack = []
+         for depth,comment in bug_.comment_root.thread(flatten=False):
+             while len(stack) > depth:
+                 stack.pop(-1)      # pop non-parents off the stack
+                 FD.write("</div>\n") # close non-parent <div class="comment...
+             assert len(stack) == depth
+             stack.append(comment)
+             lines = ["--------- Comment ---------",
+                      "Name: %s" % comment.uuid,
+                      "From: %s" % escape(comment.From),
+                      "Date: %s" % escape(comment.time_string),
+                      ""]
+             lines.extend(escape(comment.body).splitlines())
+             if depth == 0:
+                 FD.write('<div class="commentF">')
+             else:
+                 FD.write('<div class="comment">')
+             FD.write("<br />\n".join(lines)+"<br />\n")
+         while len(stack) > 0:
+             stack.pop(-1)
+             FD.write("</div>\n") # close every remaining <div class="comment...
+         FD.write(self.end_comment_section)
+         if fileid == "active":
+             FD.write(self.detail_last%"../index.html")
+         if fileid == "inactive":
+             FD.write(self.detail_last%"../index_inactive.html")
+         FD.close()
+         
+