]>
Commit | Line | Data |
---|---|---|
c3e3cdd9 | 1 | #!/usr/bin/python |
c3e3cdd9 AK |
2 | import calendar |
3 | import feedparser | |
4 | import formatter | |
6a32ccab | 5 | import htmlentitydefs |
c3e3cdd9 | 6 | import htmllib |
a41187bd | 7 | import mechanize |
c3e3cdd9 AK |
8 | import os |
9 | import random | |
10 | import string | |
6a32ccab | 11 | import StringIO |
c3e3cdd9 AK |
12 | import time |
13 | import traceback | |
14 | import urlparse | |
15 | import zephyr | |
16 | ||
17 | zephyr_sender = 'jira' | |
18 | zephyr_class = 'andersk-test' | |
19 | time_file = 'jirabot.time' | |
a41187bd | 20 | |
6a32ccab AK |
21 | class UnicodeHTMLParser(htmllib.HTMLParser): |
22 | entitydefs = dict((k, unichr(v)) for (k, v) in htmlentitydefs.name2codepoint.items()) | |
23 | ||
24 | def convert_charref(self, name): | |
25 | try: | |
26 | n = int(name) | |
27 | except ValueError: | |
28 | return | |
29 | return self.convert_codepoint(n) | |
30 | ||
31 | def convert_codepoint(self, codepoint): | |
32 | return unichr(codepoint) | |
33 | ||
8b8cecc4 AK |
34 | def jira_init(): |
35 | b = mechanize.Browser() | |
36 | b.set_handle_robots(False) | |
37 | b.add_client_certificate("https://idp.mit.edu:9443", "cert.pem", "cert.pem") | |
38 | b.addheaders = [("Accept-Language", "en-us,en;q=0.5"),] | |
39 | return b | |
40 | ||
41 | def jira_login(b): | |
42 | b.open("https://jira.mit.edu/jira/secure/Dashboard.jspa") | |
0ebeacca AK |
43 | try: |
44 | b.follow_link(text="MIT Touchstone") | |
45 | except mechanize.LinkNotFoundError: | |
46 | return | |
47 | if (urlparse.urlparse(b.geturl())[1] == "jira.mit.edu"): | |
48 | return | |
8b8cecc4 AK |
49 | b.select_form("wayfForm1") |
50 | b.submit() | |
51 | b.select_form(predicate=lambda f: any(c.name == 'login_certificate' | |
52 | for c in f.controls)) | |
53 | b.submit() | |
54 | b.select_form(nr=0) | |
55 | b.submit() | |
c3e3cdd9 | 56 | |
0344134a AK |
57 | def feed_to_zephyrs(thing, rss, parse): |
58 | zephyrs = [] | |
59 | try: | |
60 | feed = feedparser.parse(rss) | |
61 | for e in feed.entries: | |
62 | global old_time, new_time | |
63 | t = int(calendar.timegm(e.date_parsed)) | |
64 | if t <= old_time: | |
65 | continue | |
66 | if t > new_time: | |
67 | new_time = t | |
68 | try: | |
69 | z = parse(e) | |
70 | except: | |
71 | z = zerror("Error parsing " + thing + ":\n" + e.id + "\n" + traceback.format_exc()) | |
72 | zephyrs.append((t, z)) | |
73 | except: | |
74 | zephyrs.append((0, zerror("Error parsing " + thing + "s feed:\n" + traceback.format_exc()))) | |
75 | return zephyrs | |
76 | ||
c3e3cdd9 AK |
77 | def parse_issue(e): |
78 | issue = urlparse.urlparse(e.id)[2].rsplit('/', 1)[1] | |
79 | url = e.id | |
80 | msg = e.id + "\nThis issue was updated." | |
81 | ||
82 | return zephyr.ZNotice( | |
83 | sender=zephyr_sender, | |
84 | auth=False, | |
278c5c28 | 85 | opcode='auto', |
c3e3cdd9 AK |
86 | cls=zephyr_class, |
87 | instance=issue, | |
7f50f108 | 88 | fields=[e.title, msg], |
c3e3cdd9 AK |
89 | ) |
90 | ||
91 | def parse_comment(e): | |
92 | url = urlparse.urlunsplit(urlparse.urlparse(e.id)[0:3] + (None,None)) | |
93 | issue = url.rsplit('/', 1)[1] | |
94 | ||
6a32ccab AK |
95 | s = StringIO.StringIO() |
96 | parser = UnicodeHTMLParser(formatter.AbstractFormatter(formatter.DumbWriter(s))) | |
c3e3cdd9 AK |
97 | parser.feed(e.summary.rsplit('<table>', 1)[0]) |
98 | parser.close() | |
6a32ccab | 99 | comment = s.getvalue() |
c3e3cdd9 AK |
100 | |
101 | msg = e.author + " added a comment:\n" + comment.rstrip() | |
102 | ||
103 | return zephyr.ZNotice( | |
104 | sender=zephyr_sender, | |
105 | auth=False, | |
278c5c28 | 106 | opcode='auto', |
c3e3cdd9 AK |
107 | cls=zephyr_class, |
108 | instance=issue, | |
7f50f108 | 109 | fields=[e.title, msg], |
c3e3cdd9 AK |
110 | ) |
111 | ||
112 | def zerror(msg): | |
113 | return zephyr.ZNotice( | |
114 | sender=zephyr_sender, | |
115 | auth=False, | |
278c5c28 | 116 | opcode='auto', |
c3e3cdd9 AK |
117 | cls=zephyr_class, |
118 | instance='jira-error', | |
7f50f108 | 119 | fields=['Jira bot error', msg] |
c3e3cdd9 AK |
120 | ) |
121 | ||
8b8cecc4 | 122 | b = jira_init() |
8bf2b029 | 123 | zephyr.init() |
8b8cecc4 | 124 | |
a75c8c0f AK |
125 | count = 0 |
126 | ||
8bf2b029 | 127 | while True: |
8bf2b029 | 128 | time_file_new = time_file + '.' + ''.join(random.sample(string.letters, 8)) |
c3e3cdd9 | 129 | |
8bf2b029 AK |
130 | try: |
131 | os.rename(time_file, time_file_new) | |
132 | except OSError: | |
4311bd26 AK |
133 | print "warning: could not acquire timestamp lock" |
134 | time.sleep(17) | |
135 | continue | |
c3e3cdd9 | 136 | |
a75c8c0f AK |
137 | if (count >= 200): |
138 | b = jira_init() | |
139 | count = 0 | |
140 | count += 1 | |
141 | ||
f719e433 AK |
142 | jira_login(b) |
143 | b.open("https://jira.mit.edu/jira/sr/jira.issueviews:searchrequest-rss/temp/SearchRequest.xml?&pid=10185&updated%3Aprevious=-1w&sorter/field=updated&sorter/order=DESC&tempMax=1000") | |
144 | issues_rss = b.response().read() | |
145 | b.open("https://jira.mit.edu/jira/sr/jira.issueviews:searchrequest-comments-rss/temp/SearchRequest.xml?&pid=10185&updated%3Aprevious=-1w&sorter/field=updated&sorter/order=DESC&tempMax=1000") | |
146 | comments_rss = b.response().read() | |
a75c8c0f | 147 | b.clear_history() |
f719e433 | 148 | |
8bf2b029 AK |
149 | old_time = int(open(time_file_new).read()) |
150 | new_time = old_time | |
c3e3cdd9 | 151 | |
8bf2b029 AK |
152 | zephyrs = (feed_to_zephyrs('issue', issues_rss, parse_issue) + |
153 | feed_to_zephyrs('comment', comments_rss, parse_comment)) | |
8bf2b029 AK |
154 | zephyrs.sort(key=lambda tz: tz[0]) |
155 | for (t, z) in zephyrs: | |
156 | z.send() | |
f719e433 | 157 | |
776fe303 | 158 | open(time_file_new, 'w').write(str(new_time)) |
f719e433 AK |
159 | os.rename(time_file_new, time_file) |
160 | ||
8a49ed8a | 161 | time.sleep(20) |