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