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