diff options
author | Lars Wirzenius <liw@liw.fi> | 2016-03-25 12:37:30 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2016-03-25 14:33:22 +0200 |
commit | 196113608892c3f873250827dcbec487dc2b5b1d (patch) | |
tree | 4460dc42bf6615a349b4bc53f281aa2b077c3b17 | |
parent | 506ba31a1486210e163ce63322d83cb4ae0bb6aa (diff) | |
download | distix-196113608892c3f873250827dcbec487dc2b5b1d.tar.gz |
Implement "distix html"
-rw-r--r-- | distixlib/__init__.py | 1 | ||||
-rw-r--r-- | distixlib/distix.css | 35 | ||||
-rw-r--r-- | distixlib/html_renderer.py | 45 | ||||
-rw-r--r-- | distixlib/plugins/html_plugin.py | 167 | ||||
-rw-r--r-- | distixlib/templates/closed.html.j2 | 27 | ||||
-rw-r--r-- | distixlib/templates/index.html.j2 | 26 | ||||
-rw-r--r-- | distixlib/templates/ticket.html.j2 | 26 | ||||
-rw-r--r-- | without-tests | 2 |
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 |