# dependencies
# - python 2.6 for json
# - argparse; either python 2.7, or install separately
-# - collections.OrderedDict; python 2.7
from __future__ import print_function
from __future__ import unicode_literals
import json
import argparse
import os
+import re
import sys
import subprocess
_PAGES = {}
+if not hasattr(collections, 'OrderedDict'): # Python 2.6 or earlier
+ class _OrderedDict (dict):
+ "Just enough of a stub to get through Page._get_threads"
+ def __init__(self, *args, **kwargs):
+ super(_OrderedDict, self).__init__(*args, **kwargs)
+ self._keys = [] # record key order
+
+ def __setitem__(self, key, value):
+ super(_OrderedDict, self).__setitem__(key, value)
+ self._keys.append(key)
+
+ def __values__(self):
+ for key in self._keys:
+ yield self[key]
+
+
+ collections.OrderedDict = _OrderedDict
+
+
def read_config(path=None, encoding=None):
"Read config from json file"
if not encoding:
class HtmlPage (Page):
+ _slug_regexp = re.compile('\W+')
+
def _write_header(self, views, stream):
super(HtmlPage, self)._write_header(views=views, stream=stream)
stream.write('<ul>\n')
for view in views:
+ if 'id' not in view:
+ view['id'] = self._slug(view['title'])
stream.write(
- '<li><a href="#{title}">{title}</a></li>\n'.format(**view))
+ '<li><a href="#{id}">{title}</a></li>\n'.format(**view))
stream.write('</ul>\n')
def _write_view_header(self, view, stream):
- stream.write('<h3><a name="{title}" />{title}</h3>\n'.format(**view))
+ stream.write('<h3 id="{id}">{title}</h3>\n'.format(**view))
+ stream.write('<p>\n')
if 'comment' in view:
stream.write(view['comment'])
stream.write('\n')
for line in [
'The view is generated from the following query:',
- '<blockquote>',
+ '</p>',
+ '<p>',
+ ' <code>',
view['query-string'],
- '</blockquote>',
+ ' </code>',
+ '</p>',
]:
stream.write(line)
stream.write('\n')
for thread in threads:
for message_display_data in thread:
stream.write((
- '<tr><td>{date}\n'
- '</td><td>{message-id-term}\n'
- '</td></tr>\n'
- '<tr><td>{from}\n'
- '</td><td>{subject}\n'
- '</td></tr>\n'
+ '<tr>\n'
+ ' <td>{date}</td>\n'
+ ' <td><code>{message-id-term}</code></td>\n'
+ '</tr>\n'
+ '<tr>\n'
+ ' <td>{from}</td>\n'
+ ' <td>{subject}</td>\n'
+ '</tr>\n'
).format(**message_display_data))
if thread != threads[-1]:
stream.write('<tr><td colspan="2"><br /></td></tr>\n')
).format(**d)
return (running_data, display_data)
+ def _slug(self, string):
+ return self._slug_regexp.sub('-', string)
+
_PAGES['text'] = Page()
_PAGES['html'] = HtmlPage(
- header='''<?xml version="1.0" encoding="utf-8" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ header='''<!DOCTYPE html>
+<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Notmuch Patches</title>
</head>
<body>
<h2>Notmuch Patches</h2>
+<p>
Generated: {date}<br />
For more infomation see <a href="http://notmuchmail.org/nmbug">nmbug</a>
+</p>
<h3>Views</h3>
'''.format(date=datetime.datetime.utcnow().date()),
footer='</body>\n</html>\n',