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