]> git.notmuchmail.org Git - notmuch/blob - devel/nmbug/nmbug-status
nmbug-status: Consolidate HTML header printing
[notmuch] / devel / nmbug / nmbug-status
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2011-2012 David Bremner <david@tethera.net>
4 # License: Same as notmuch
5 # dependencies
6 #       - python 2.6 for json
7 #       - argparse; either python 2.7, or install separately
8
9 from __future__ import print_function
10
11 import codecs
12 import datetime
13 import email.utils
14 import locale
15 import urllib
16 import json
17 import argparse
18 import os
19 import sys
20 import subprocess
21
22
23 _ENCODING = locale.getpreferredencoding() or sys.getdefaultencoding()
24
25
26 def read_config(path=None, encoding=None):
27     "Read config from json file"
28     if not encoding:
29         encoding = _ENCODING
30     if path:
31         fp = open(path)
32     else:
33         nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
34
35         # read only the first line from the pipe
36         sha1_bytes = subprocess.Popen(
37             ['git', '--git-dir', nmbhome, 'show-ref', '-s', 'config'],
38             stdout=subprocess.PIPE).stdout.readline()
39         sha1 = sha1_bytes.decode(encoding).rstrip()
40
41         fp_byte_stream = subprocess.Popen(
42             ['git', '--git-dir', nmbhome, 'cat-file', 'blob',
43              sha1+':status-config.json'],
44             stdout=subprocess.PIPE).stdout
45         fp = codecs.getreader(encoding=encoding)(stream=fp_byte_stream)
46
47     return json.load(fp)
48
49
50 class Thread:
51     def __init__(self, last, lines):
52         self.last = last
53         self.lines = lines
54
55     def join_utf8_with_newlines(self):
56         return '\n'.join( (line.encode('utf-8') for line in self.lines) )
57
58
59 def output_with_separator(threadlist, sep):
60     outputs = (thread.join_utf8_with_newlines() for thread in threadlist)
61     print(sep.join(outputs))
62
63
64 def print_view(database, title, query, comment,
65                headers=('date', 'from', 'subject')):
66
67     query_string = ' and '.join(query)
68     q_new = notmuch.Query(database, query_string)
69     q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
70
71     last_thread_id = ''
72     threads = {}
73     threadlist = []
74     out = {}
75     last = None
76     lines = None
77
78     if output_format == 'html':
79         print('<h3><a name="%s" />%s</h3>' % (title, title))
80         print(comment)
81         print('The view is generated from the following query:')
82         print('<blockquote>')
83         print(query_string)
84         print('</blockquote>')
85         print('<table>\n')
86
87     for m in q_new.search_messages():
88
89         thread_id = m.get_thread_id()
90
91         if thread_id != last_thread_id:
92             if threads.has_key(thread_id):
93                 last = threads[thread_id].last
94                 lines = threads[thread_id].lines
95             else:
96                 last = {}
97                 lines = []
98                 thread = Thread(last, lines)
99                 threads[thread_id] = thread
100                 for h in headers:
101                     last[h] = ''
102                 threadlist.append(thread)
103             last_thread_id = thread_id
104
105         for header in headers:
106             val = m.get_header(header)
107
108             if header == 'date':
109                 val = str.join(' ', val.split(None)[1:4])
110                 val = str(datetime.datetime.strptime(val, '%d %b %Y').date())
111             elif header == 'from':
112                 (val, addr) = email.utils.parseaddr(val)
113                 if val == '':
114                     val = addr.split('@')[0]
115
116             if header != 'subject' and last[header] == val:
117                 out[header] = ''
118             else:
119                 out[header] = val
120                 last[header] = val
121
122         mid = m.get_message_id()
123         out['id'] = 'id:"%s"' % mid
124
125         if output_format == 'html':
126
127             out['subject'] = '<a href="http://mid.gmane.org/%s">%s</a>' \
128                 % (urllib.quote(mid), out['subject'])
129
130             lines.append(' <tr><td>%s' % out['date'])
131             lines.append('</td><td>%s' % out['id'])
132             lines.append('</td></tr>')
133             lines.append(' <tr><td>%s' % out['from'])
134             lines.append('</td><td>%s' % out['subject'])
135             lines.append('</td></tr>')
136         else:
137             lines.append('%(date)-10.10s %(from)-20.20s %(subject)-40.40s\n%(id)72s' % out)
138
139     if output_format == 'html':
140         output_with_separator(threadlist,
141                               '\n<tr><td colspan="2"><br /></td></tr>\n')
142         print('</table>')
143     else:
144         output_with_separator(threadlist, '\n\n')
145
146
147 # parse command line arguments
148
149 parser = argparse.ArgumentParser()
150 parser.add_argument('--text', help='output plain text format',
151                     action='store_true')
152 parser.add_argument('--config', help='load config from given file',
153                     metavar='PATH')
154 parser.add_argument('--list-views', help='list views',
155                     action='store_true')
156 parser.add_argument('--get-query', help='get query for view',
157                     metavar='VIEW')
158
159 args = parser.parse_args()
160
161 config = read_config(path=args.config)
162
163 if args.list_views:
164     for view in config['views']:
165         print(view['title'])
166     sys.exit(0)
167 elif args.get_query != None:
168     for view in config['views']:
169         if args.get_query == view['title']:
170             print(' and '.join(view['query']))
171     sys.exit(0)
172 else:
173     # only import notmuch if needed
174     import notmuch
175
176 if args.text:
177     output_format = 'text'
178 else:
179     output_format = 'html'
180
181 # main program
182
183 db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
184
185 if output_format == 'html':
186     print('''<?xml version="1.0" encoding="utf-8" ?>
187 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
188 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
189 <head>
190 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
191 <title>Notmuch Patches</title>
192 </head>
193 <body>
194 <h2>Notmuch Patches</h2>
195 Generated: {date}<br />
196 For more infomation see <a href="http://notmuchmail.org/nmbug">nmbug</a>
197 <h3>Views</h3>
198 <ul>'''.format(date=datetime.datetime.utcnow().date()))
199     for view in config['views']:
200         print('<li><a href="#%(title)s">%(title)s</a></li>' % view)
201     print('</ul>')
202
203 for view in config['views']:
204     print_view(database=db, **view)
205
206 if output_format == 'html':
207     print('</body>\n</html>')