summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2016-03-25 12:37:30 +0200
committerLars Wirzenius <liw@liw.fi>2016-03-25 14:33:22 +0200
commit196113608892c3f873250827dcbec487dc2b5b1d (patch)
tree4460dc42bf6615a349b4bc53f281aa2b077c3b17
parent506ba31a1486210e163ce63322d83cb4ae0bb6aa (diff)
downloaddistix-196113608892c3f873250827dcbec487dc2b5b1d.tar.gz
Implement "distix html"
-rw-r--r--distixlib/__init__.py1
-rw-r--r--distixlib/distix.css35
-rw-r--r--distixlib/html_renderer.py45
-rw-r--r--distixlib/plugins/html_plugin.py167
-rw-r--r--distixlib/templates/closed.html.j227
-rw-r--r--distixlib/templates/index.html.j226
-rw-r--r--distixlib/templates/ticket.html.j226
-rw-r--r--without-tests2
8 files changed, 329 insertions, 0 deletions
diff --git a/distixlib/__init__.py b/distixlib/__init__.py
index 6661f31..8f0c20d 100644
--- a/distixlib/__init__.py
+++ b/distixlib/__init__.py
@@ -41,6 +41,7 @@ from .message_renderer import MessageRenderer
from .repo import Repository
from .git import Git
from .text_renderer import TextRenderer
+from .html_renderer import HtmlRenderer
from .util import get_ticket_ids
diff --git a/distixlib/distix.css b/distixlib/distix.css
new file mode 100644
index 0000000..0faf8ae
--- /dev/null
+++ b/distixlib/distix.css
@@ -0,0 +1,35 @@
+table {
+ border: 0;
+}
+th {
+ border-bottom: solid 1px;
+}
+th, td {
+ font-family: monospace;
+ text-align: left;
+ vertical-align: top;
+ padding-right: 2em;
+}
+td {
+ padding-bottom: 1em;
+}
+th.duration, td.duration {
+ width: 15em;
+}
+td.date {
+// min-width: 30em;
+}
+td.commitid {
+// width: 7em;
+}
+td.commitmsg {
+// max-width: 30em;
+}
+
+span.links {
+ font-size: 70%;
+}
+
+ul.ticketlist a.ticketinlist {
+ font-family: monospace;
+}
diff --git a/distixlib/html_renderer.py b/distixlib/html_renderer.py
new file mode 100644
index 0000000..509f2e3
--- /dev/null
+++ b/distixlib/html_renderer.py
@@ -0,0 +1,45 @@
+# Copyright 2016 Lars Wirzenius
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+import time
+
+import jinja2
+
+
+class HtmlRenderer(object):
+
+ def render(self, template_name, variables):
+ env = self._get_env()
+ self._add_custom_filters(env)
+ template = env.get_template(template_name)
+ return template.render(**variables)
+
+ def _get_env(self):
+ loader = jinja2.PackageLoader('distixlib')
+ return jinja2.Environment(
+ loader=loader,
+ autoescape=lambda _: True,
+ extensions=['jinja2.ext.autoescape'],
+ )
+
+ def _add_custom_filters(self, env):
+ env.filters['date'] = self._date_filter
+
+ def _date_filter(self, value): # pragma: no cover
+ tm = time.gmtime(value)
+ return time.strftime('%Y-%m-%d', tm)
diff --git a/distixlib/plugins/html_plugin.py b/distixlib/plugins/html_plugin.py
new file mode 100644
index 0000000..59f82a8
--- /dev/null
+++ b/distixlib/plugins/html_plugin.py
@@ -0,0 +1,167 @@
+# coding: UTF8
+# Copyright 2016 Lars Wirzenius
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+# =*= License: GPL-3+ =*=
+
+
+import locale
+import markdown
+import os
+import uuid
+
+import cliapp
+
+import distixlib
+
+
+class HtmlPlugin(cliapp.Plugin):
+
+ def enable(self):
+ self.app.add_subcommand(
+ 'html', self.html, arg_synopsis='OUTPUT-DIR')
+
+ def html(self, args):
+ '''Generate static HTML web pages of the repository and its tickets.'''
+
+ dirname = self.parse_args(args)
+ self.create_output(dirname)
+
+ repo = distixlib.Repository('.')
+
+ site = StaticSite()
+ site.set_output_dir(dirname)
+ site.set_repository(repo)
+ site.render()
+
+ def parse_args(self, args):
+ if len(args) != 1:
+ raise UsageError()
+ return args[0]
+
+ def create_output(self, dirname):
+ if os.path.isdir(dirname):
+ if os.listdir(dirname):
+ raise OutputNotEmptyError(dirname=dirname)
+ elif os.path.exists(dirname):
+ raise OutputExistsError(dirname=dirname)
+ else:
+ os.mkdir(dirname)
+
+
+class StaticSite(object):
+
+ def __init__(self):
+ self._dirname = None
+ self._repo = None
+ self._ticket_store = None
+ self._renderer = distixlib.HtmlRenderer()
+
+ def set_repository(self, repo):
+ self._repo = repo
+ self._ticket_store = repo.open_ticket_store(distixlib.tickets_dir_name)
+
+ def set_output_dir(self, dirname):
+ self._dirname = dirname
+
+ def render(self):
+ self._render_ticket_list(
+ 'index.html.j2', 'index.html', self._ticket_is_open)
+ self._render_ticket_list(
+ 'closed.html.j2', 'closed.html', self._ticket_is_closed)
+ for ticket in self._ticket_store.get_tickets():
+ self._render_ticket(ticket)
+ self._copy_static_files()
+
+ def _render_ticket_list(self, template, filename, condition):
+ variables = {
+ 'description_html': self._make_html(self._repo.get_description()),
+ 'tickets': self._get_tickets(condition),
+ }
+ html = self._renderer.render(template, variables)
+ self._write_file(filename, html)
+
+ def _render_ticket(self, ticket):
+ thread = distixlib.MessageThread()
+ for msg in ticket.get_messages():
+ thread.add_message(msg)
+
+ msg_renderer = distixlib.MessageRenderer()
+
+ metadata = ticket.get_ticket_metadata()
+
+ variables = {
+ 'ticket': ticket,
+ 'metadata': metadata,
+ 'msgs': [
+ msg_renderer.summary(msg)
+ for msg in thread.get_messages_in_date_order()
+ ],
+ }
+
+ html = self._renderer.render('ticket.html.j2', variables)
+ self._write_file('{}.html'.format(ticket.get_ticket_id()), html)
+
+ def _make_html(self, text):
+ return markdown.markdown(text)
+
+ def _get_tickets(self, condition):
+ return [
+ ticket
+ for ticket in self._ticket_store.get_tickets()
+ if condition(ticket)
+ ]
+
+ def _ticket_is_open(self, ticket):
+ return not self._ticket_is_closed(ticket)
+
+ def _ticket_is_closed(self, ticket):
+ return self._ticket_has_key_value(ticket, 'status', 'closed')
+
+ def _ticket_has_key_value(self, ticket, key, value):
+ metadata = ticket.get_ticket_metadata()
+ return key in metadata and value in metadata.get_all_values(key)
+
+ def _copy_static_files(self):
+ filenames = ['distix.css']
+ for filename in filenames:
+ data = self._read_file(filename)
+ self._write_file(filename, data)
+
+ def _read_file(self, filename):
+ pathname = os.path.join(
+ os.path.dirname(distixlib.__file__),
+ filename)
+ with open(pathname) as f:
+ return f.read()
+
+ def _write_file(self, filename, text):
+ with open(os.path.join(self._dirname, filename), 'w') as f:
+ f.write(text.encode('UTF8'))
+
+
+class UsageError(distixlib.StructuredError):
+
+ msg = '"html" command must get exactly one argument: the output directory'
+
+
+class OutputNotEmptyError(distixlib.StructuredError):
+
+ msg = 'Output directory {dirname} is not empty'
+
+
+class OutputExistsError(distixlib.StructuredError):
+
+ msg = 'Output directory {dirname} exists, but is not an empty directory'
diff --git a/distixlib/templates/closed.html.j2 b/distixlib/templates/closed.html.j2
new file mode 100644
index 0000000..ab76ddd
--- /dev/null
+++ b/distixlib/templates/closed.html.j2
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <title>distix: closed tickets</title>
+ <link rel="stylesheet" href="distix.css" type="text/css" />
+ <meta charset="utf-8" />
+ </head>
+ <body>
+
+ <h1>distix closed tickets</h1>
+
+ {% autoescape false %}
+ {{ description_html }}
+ {% endautoescape %}
+
+ <p><a href="index.html">Front page</a></p>
+
+ <ul class="ticketlist">
+ {% for ticket in tickets %}
+ <li>
+ <a class="ticketinlist" href="{{ ticket.get_ticket_id() }}.html"
+ >{{ ticket.get_ticket_id() }}
+ {{ ticket.get_title() }}</a></li>
+ {% endfor %}
+ </ul>
+
+ </body>
+</html>
diff --git a/distixlib/templates/index.html.j2 b/distixlib/templates/index.html.j2
new file mode 100644
index 0000000..4c7105c
--- /dev/null
+++ b/distixlib/templates/index.html.j2
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <title>distix</title>
+ <link rel="stylesheet" href="distix.css" type="text/css" />
+ <meta charset="utf-8" />
+ </head>
+ <body>
+
+ <h1>distix</h1>
+ {% autoescape false %}
+ {{ description_html }}
+ {% endautoescape %}
+
+ <h2>Open tickets</h2>
+ <ul class="ticketlist">
+ {% for ticket in tickets %}
+ <li>
+ <a class="ticketinlist" href="{{ ticket.get_ticket_id() }}.html"
+ >{{ ticket.get_title() }}</a></li>
+ {% endfor %}
+ </ul>
+
+ <p><a href="closed.html">Closed tickets</a></p>
+
+ </body>
+</html>
diff --git a/distixlib/templates/ticket.html.j2 b/distixlib/templates/ticket.html.j2
new file mode 100644
index 0000000..9d19b37
--- /dev/null
+++ b/distixlib/templates/ticket.html.j2
@@ -0,0 +1,26 @@
+<html>
+ <head>
+ <title>distix: {{ ticket.get_ticket_id() }} {{ ticket.get_title() }}</title>
+ <link rel="stylesheet" href="distix.css" type="text/css" />
+ <meta charset="utf-8" />
+ </head>
+ <body>
+
+ <h1>{{ ticket.get_title() }}</h1>
+
+ <p><a href="index.html">Front page</a></p>
+
+ <ul>
+ {% for key in metadata.keys()|sort %}
+ {% for value in metadata.get_all_values(key) %}
+ <li> <code>{{key}}</code>: <code>{{value}}</code></li>
+ {% endfor %}
+ {% endfor %}
+ </ul>
+
+ {% for msg in msgs %}
+ <pre>{{ msg }}</pre>
+ {% endfor %}
+
+ </body>
+</html>
diff --git a/without-tests b/without-tests
index 12f58cb..3966e3f 100644
--- a/without-tests
+++ b/without-tests
@@ -2,6 +2,7 @@ setup.py
distixlib/app.py
distixlib/__init__.py
distixlib/plugins/global_settings_plugin.py
+distixlib/plugins/html_plugin.py
distixlib/plugins/import_mail_plugin.py
distixlib/plugins/init_plugin.py
distixlib/plugins/list_plugin.py
@@ -15,3 +16,4 @@ distixlib/git.py
distixlib/version.py
distixlib/util.py
distixlib/message_renderer.py
+distixlib/html_renderer.py