Merge lp:~krytarik/ubuntu-bots/bugtracker-output into lp:ubuntu-bots
- bugtracker-output
- Merge into devel
Status: | Needs review |
---|---|
Proposed branch: | lp:~krytarik/ubuntu-bots/bugtracker-output |
Merge into: | lp:ubuntu-bots |
Diff against target: |
662 lines (+169/-171) 1 file modified
Bugtracker/plugin.py (+169/-171) |
To merge this branch: | bzr merge lp:~krytarik/ubuntu-bots/bugtracker-output |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu IRC Bots | Pending | ||
Benjamin Rubin | Pending | ||
Review via email: mp+311899@code.launchpad.net |
Commit message
Description of the change
Bugtracker: Make sure output fits maximum length of IRC messages, improve its formatting.
- 321. By Krytarik Raido
-
Bugtracker: Strip any leading or trailing spaces off bug report title.
- 322. By Krytarik Raido
-
Bugtracker: Ensure consistent casing of bug severity and status.
- 323. By Krytarik Raido
-
Bugtracker: Improve formatting of duplicate bug reports.
- 324. By Krytarik Raido
-
Bugtracker: Improve handling of bug assignee and extended info.
- 325. By Krytarik Raido
-
Bugtracker: Backport from Git.
<https://git.launchpad. net/~krytarik/ ubuntu- bots/+git/ ubuntu- bots>
Unmerged revisions
- 325. By Krytarik Raido
-
Bugtracker: Backport from Git.
<https://git.launchpad. net/~krytarik/ ubuntu- bots/+git/ ubuntu- bots> - 324. By Krytarik Raido
-
Bugtracker: Improve handling of bug assignee and extended info.
- 323. By Krytarik Raido
-
Bugtracker: Improve formatting of duplicate bug reports.
- 322. By Krytarik Raido
-
Bugtracker: Ensure consistent casing of bug severity and status.
- 321. By Krytarik Raido
-
Bugtracker: Strip any leading or trailing spaces off bug report title.
- 320. By Krytarik Raido
-
Bugtracker: Make sure output fits maximum length
of IRC messages, improve its formatting.
Preview Diff
1 | === modified file 'Bugtracker/plugin.py' |
2 | --- Bugtracker/plugin.py 2014-08-12 16:47:38 +0000 |
3 | +++ Bugtracker/plugin.py 2018-05-13 02:56:20 +0000 |
4 | @@ -2,6 +2,7 @@ |
5 | ### |
6 | # Copyright (c) 2005-2007 Dennis Kaarsemaker |
7 | # Copyright (c) 2008-2010 Terence Simpson |
8 | +# Copyright (c) 2017- Krytarik Raido |
9 | # |
10 | # This program is free software; you can redistribute it and/or modify |
11 | # it under the terms of version 2 of the GNU General Public License as |
12 | @@ -25,11 +26,11 @@ |
13 | import supybot.log as supylog |
14 | |
15 | #import imaplib |
16 | -import re, os, time, commands |
17 | +import re, os, time, subprocess |
18 | import xml.dom.minidom as minidom |
19 | from htmlentitydefs import entitydefs as entities |
20 | -import email.FeedParser |
21 | -import SOAPpy |
22 | +from email.parser import FeedParser |
23 | +from SOAPpy.Client import SOAPProxy |
24 | |
25 | # All the words below will be censored when reporting bug information |
26 | bad_words = set(["fuck","fuk","fucking","fuking","fukin","fuckin","fucked","fuked","fucker","shit","cunt","bastard","nazi","nigger","nigga","cock","bitches","bitch"]) |
27 | @@ -52,7 +53,7 @@ |
28 | if description: |
29 | DESC.setValue(description) |
30 | if trackertype: |
31 | - if defined_bugtrackers.has_key(trackertype.lower()): |
32 | + if trackertype.lower() in defined_bugtrackers: |
33 | TRACKERTYPE.setValue(trackertype.lower()) |
34 | else: |
35 | raise BugtrackerError("Unknown trackertype: %s" % trackertype) |
36 | @@ -81,6 +82,19 @@ |
37 | val = entre.sub('?', val) |
38 | return val |
39 | |
40 | +def _getnodeattr(node, attr): |
41 | + if node.hasAttribute(attr): |
42 | + val = node.getAttribute(attr) |
43 | + else: |
44 | + raise ValueError, "No such attribute" |
45 | + while entre.search(val): |
46 | + entity = entre.search(val).group(1) |
47 | + if entity in entities: |
48 | + val = entre.sub(entities[entity], val) |
49 | + else: |
50 | + val = entre.sub('?', val) |
51 | + return val |
52 | + |
53 | class BugtrackerError(Exception): |
54 | """A bugtracker error""" |
55 | pass |
56 | @@ -103,7 +117,7 @@ |
57 | for name in self.registryValue('bugtrackers'): |
58 | registerBugtracker(name) |
59 | group = self.registryValue('bugtrackers.%s' % name.replace('.','\\.'), value=False) |
60 | - if group.trackertype() in defined_bugtrackers.keys(): |
61 | + if group.trackertype() in defined_bugtrackers: |
62 | self.db[name] = defined_bugtrackers[group.trackertype()](name, group.url(), group.description()) |
63 | else: |
64 | self.log.warning("Bugtracker: Unknown trackertype: %s (%s)" % (group.trackertype(), name)) |
65 | @@ -171,7 +185,7 @@ |
66 | # # Read all new mail |
67 | # for m in new_mail: |
68 | # msg = sc.fetch(m, 'RFC822')[1][0][1] |
69 | -# fp = email.FeedParser.FeedParser() |
70 | +# fp = FeedParser() |
71 | # sc.store(m, '+FLAGS', "(\Deleted)") # Mark message deleted so we don't have to process it again |
72 | # fp.feed(msg) |
73 | # bug = fp.close() |
74 | @@ -275,7 +289,7 @@ |
75 | self.shorthand = utils.abbrev(self.db.keys()) |
76 | irc.replySuccess() |
77 | except KeyError: |
78 | - s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None) |
79 | + s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None) |
80 | irc.error(s % name) |
81 | remove = wrap(remove, [('checkCapability', 'admin'), 'text']) |
82 | |
83 | @@ -297,7 +311,7 @@ |
84 | self.shorthand = utils.abbrev(self.db.keys()) |
85 | irc.replySuccess() |
86 | except KeyError: |
87 | - s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None) |
88 | + s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None) |
89 | irc.error(s % name) |
90 | rename = wrap(rename, [('checkCapability', 'admin'), 'something','something', additional('text')]) |
91 | |
92 | @@ -315,7 +329,7 @@ |
93 | self.db[name].__class__.__name__) |
94 | irc.reply('%s: %s, %s [%s]' % (name, description, url, type)) |
95 | except KeyError: |
96 | - s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None) |
97 | + s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None) |
98 | irc.error(s % name) |
99 | else: |
100 | if self.db: |
101 | @@ -328,7 +342,7 @@ |
102 | |
103 | def bugSnarfer(self, irc, msg, match): |
104 | r"""\b(?P<bt>(([a-z0-9]+)?\s+bugs?|[a-z0-9]+)):?\s+#?(?P<bug>\d+(?!\d*[\-\.]\d+)((,|\s*(and|en|et|und|ir))\s*#?\d+(?!\d*[\-\.]\d+))*)""" |
105 | - channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None |
106 | + channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None |
107 | if not self.registryValue('bugSnarfer', channel): |
108 | return |
109 | nbugs = msg.tagged('nbugs') |
110 | @@ -403,13 +417,14 @@ |
111 | for bugid in bugids: |
112 | bugid = int(bugid) |
113 | try: |
114 | - report = self.get_bug(channel,tracker,bugid,self.registryValue('showassignee', channel), show_tracker=showTracker) |
115 | + report = self.get_bug(channel or msg.nick, tracker, bugid, self.registryValue('showassignee', channel), |
116 | + self.registryValue('extended', channel), do_tracker=showTracker) |
117 | except BugNotFoundError: |
118 | if self.registryValue('replyWhenNotFound'): |
119 | irc.error("%s bug %d could not be found" % (tracker.description, bugid)) |
120 | except BugtrackerError, e: |
121 | # if 'private' in str(e): |
122 | -# irc.reply("Bug %d on http://launchpad.net/bugs/%d is private" % (bugid, bugid)) |
123 | +# irc.reply("Bug %d on https://launchpad.net/bugs/%d is private" % (bugid, bugid)) |
124 | # return |
125 | if not sure_bug and bugid < 30: |
126 | return |
127 | @@ -420,7 +435,7 @@ |
128 | |
129 | def turlSnarfer(self, irc, msg, match): |
130 | r"(?P<tracker>https?://\S*?)/(?:Bugs/0*|str.php\?L|show_bug.cgi\?id=|bugreport.cgi\?bug=|(?:bugs|\+bug)/|ticket/|tracker/|\S*aid=|bug=)?(?P<bug>\d+)(?P<sfurl>&group_id=\d+&at_id=\d+)?" |
131 | - channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None |
132 | + channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None |
133 | if not self.registryValue('bugSnarfer', channel): |
134 | return |
135 | nbugs = msg.tagged('nbugs') |
136 | @@ -432,7 +447,8 @@ |
137 | tracker = self.get_tracker(match.group(0),match.group('sfurl')) |
138 | if not tracker: |
139 | return |
140 | - report = self.get_bug(channel, tracker, int(match.group('bug')), self.registryValue('showassignee', channel), do_url = False) |
141 | + report = self.get_bug(channel or msg.nick, tracker, int(match.group('bug')), self.registryValue('showassignee', channel), |
142 | + self.registryValue('extended', channel), do_url=False) |
143 | except BugtrackerError, e: |
144 | irc.error(str(e)) |
145 | except BugNotFoundError, e: |
146 | @@ -444,32 +460,32 @@ |
147 | # Only useful for launchpad developers |
148 | def oopsSnarfer(self, irc, msg, match): |
149 | r"(?:https?://pad.lv/){0}?OOPS-(?P<oopsid>\d*[\dA-Z]{3,})" |
150 | - channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None |
151 | + channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None |
152 | if not self.registryValue('bugSnarfer', channel) or not self.registryValue('oopsSnarfer', channel): |
153 | return |
154 | oopsid = match.group(1) |
155 | if oopsid.lower() == "tools": |
156 | return |
157 | - if not self.is_ok(channel, 'lpoops', oopsid): |
158 | + if not self.is_ok(channel or msg.nick, 'lpoops', oopsid): |
159 | return |
160 | - irc.reply('https://oops.canonical.com/?oopsid=OOPS-' + oopsid, prefixNick=False) |
161 | + irc.reply('https://oops.canonical.com/?oopsid=OOPS-%s' % oopsid, prefixNick=False) |
162 | |
163 | def cveSnarfer(self, irc, msg, match): |
164 | r"(cve[- ]\d{4}[- ]\d{4,})" |
165 | - channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None |
166 | + channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None |
167 | if not self.registryValue('bugSnarfer', channel) or not self.registryValue('cveSnarfer', channel): |
168 | return |
169 | cve = match.group(1).replace(' ','-').upper() |
170 | - if not self.is_ok(channel, 'cve', cve): |
171 | + if not self.is_ok(channel or msg.nick, 'cve', cve): |
172 | return |
173 | - url = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' % cve |
174 | + url = 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' % cve |
175 | cvedata = utils.web.getUrl(url) |
176 | m = cvere.search(cvedata) |
177 | if m: |
178 | cve = m.group(1).replace('\n', ' ') |
179 | if len(cve) > 380: |
180 | cve = cve[:380] + '...' |
181 | - irc.reply("%s (%s)" % (cve,url), prefixNick=False) |
182 | + irc.reply("%s <%s>" % (cve, url), prefixNick=False) |
183 | |
184 | #TODO: as we will depend on launchpadlib, we should consider using lazr.uri.URI to do URL parsing |
185 | def get_tracker(self, snarfurl, sfdata): |
186 | @@ -529,39 +545,57 @@ |
187 | return tracker |
188 | return None |
189 | |
190 | - def get_bug(self, channel, tracker, id, do_assignee, do_url = True, show_tracker = True): |
191 | + def get_bug(self, channel, tracker, id, do_assignee, do_extinfo, do_url=True, do_tracker=True): |
192 | reports = [] |
193 | + message_max = 450 - len(channel) |
194 | + |
195 | if not self.is_ok(channel, tracker, id): |
196 | return [] |
197 | + |
198 | for r in tracker.get_bug(id): |
199 | - showext = self.registryValue('extended', channel) |
200 | - extinfo = '' |
201 | - if len(r) == 8: |
202 | - (bid, product, title, severity, status, assignee, url, extinfo) = r |
203 | + (bid, product, title, severity, status, assignee, url, extinfo, duplicate) = r |
204 | + |
205 | + if duplicate and not self.is_ok(channel, tracker, bid): |
206 | + continue |
207 | + |
208 | + if do_tracker: |
209 | + report = '%s bug %s' % (tracker.description, bid) |
210 | else: |
211 | - (bid, product, title, severity, status, assignee, url) = r |
212 | + report = 'Bug %s' % bid |
213 | |
214 | - severity = severity[0].upper() + severity[1:].lower() |
215 | - status = status[0].upper() + status[1:].lower() |
216 | - tracker_name = tracker.description + ' ' |
217 | - if not do_url: |
218 | - url = '' |
219 | - if not show_tracker: |
220 | - tracker_name = '' |
221 | if product: |
222 | - if showext: |
223 | - reports.append("%sbug %s in %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, product, |
224 | - title, extinfo, severity, status, url)) |
225 | - else: |
226 | - reports.append("%sbug %s in %s \"%s\" [%s,%s] %s" % (tracker_name, bid, product, |
227 | - title, severity, status, url)) |
228 | - else: |
229 | - if showext: |
230 | - reports.append("%sbug %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, title, extinfo, severity, status, url)) |
231 | - else: |
232 | - reports.append("%sbug %s \"%s\" [%s,%s] %s" % (tracker_name, bid, title, severity, status, url)) |
233 | + report += ' in %s' % product |
234 | + |
235 | + report += ' "%s"' % title.replace('"', "'").strip() |
236 | + |
237 | + if do_extinfo and extinfo: |
238 | + report += ' (%s)' % ', '.join(extinfo) |
239 | + |
240 | if do_assignee and assignee: |
241 | - reports[-1] = reports[-1] + (" - Assigned to %s" % assignee) |
242 | + report += ' (assigned: %s)' % assignee |
243 | + |
244 | + severity_status = [] |
245 | + if severity: |
246 | + severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in severity.split())) |
247 | + severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in status.split())) |
248 | + report += ' [%s]' % ', '.join(severity_status) |
249 | + |
250 | + if duplicate: |
251 | + report += ' [duplicate: %s]' % duplicate[0] |
252 | + |
253 | + if do_url: |
254 | + report += ' %s' % url |
255 | + |
256 | + if len(report) > message_max: |
257 | + report_parts = report.split('"') |
258 | + report_start = report_parts[0] |
259 | + report_end = report_parts[-1] |
260 | + report_title = '"'.join(report_parts[1:-1]) |
261 | + title_max = message_max - len(report_start) - len(report_end) - 5 |
262 | + report_title_cut = report_title[:title_max].rsplit(None, 1)[0] + '...' |
263 | + report = '%s"%s"%s' % (report_start, report_title_cut, report_end) |
264 | + |
265 | + reports.append(report) |
266 | return reports |
267 | |
268 | # Define all bugtrackers |
269 | @@ -608,6 +642,7 @@ |
270 | return tracker |
271 | except: |
272 | return None |
273 | + |
274 | def get_bug(self, id): |
275 | url = "%s/show_bug.cgi?id=%d&ctype=xml" % (self.url,id) |
276 | try: |
277 | @@ -630,49 +665,22 @@ |
278 | status = "%s: %s" % (status, _getnodetxt(bug_n.getElementsByTagName('resolution')[0])) |
279 | except: |
280 | pass |
281 | - component = _getnodetxt(bug_n.getElementsByTagName('component')[0]) |
282 | + product = _getnodetxt(bug_n.getElementsByTagName('product')[0]) |
283 | severity = _getnodetxt(bug_n.getElementsByTagName('bug_severity')[0]) |
284 | - assignee = '(unavailable)' |
285 | try: |
286 | - assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0]) |
287 | + assignee = _getnodeattr(bug_n.getElementsByTagName('assigned_to')[0], 'name') |
288 | except: |
289 | - pass |
290 | + try: |
291 | + assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0]) |
292 | + except: |
293 | + assignee = '' |
294 | except Exception, e: |
295 | s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url) |
296 | raise BugtrackerError, s |
297 | - return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))] |
298 | + return [(id, product, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id), [], [])] |
299 | |
300 | class Issuezilla(Bugzilla): |
301 | pass |
302 | -#class Issuezilla(IBugtracker): |
303 | -# def get_bug(self, id): |
304 | -# url = "%s/show_bug.cgi?id=%d&ctype=xml" % (self.url,id) |
305 | -# try: |
306 | -# bugxml = utils.web.getUrl(url) |
307 | -# zilladom = minidom.parseString(bugxml) |
308 | -# except Exception, e: |
309 | -# s = 'Could not parse XML returned by %s: %s (%s)' % (self.description, e, url) |
310 | -# raise BugtrackerError, s |
311 | -# bug_n = zilladom.getElementsByTagName('issue')[0] |
312 | -# if not (bug_n.getAttribute('status_code') == '200'): |
313 | -# if bug_n.getAttribute('status_message') == 'NotFound': |
314 | -# raise BugNotFoundError |
315 | -# s = 'Error getting %s bug #%s: %s' % (self.description, id, bug_n.getAttribute('status_message')) |
316 | -# raise BugtrackerError, s |
317 | -# try: |
318 | -# title = _getnodetxt(bug_n.getElementsByTagName('short_desc')[0]) |
319 | -# status = _getnodetxt(bug_n.getElementsByTagName('issue_status')[0]) |
320 | -# try: |
321 | -# status = "%s: %s" % (status, _getnodetxt(bug_n.getElementsByTagName('resolution')[0])) |
322 | -# except: |
323 | -# pass |
324 | -# component = _getnodetxt(bug_n.getElementsByTagName('component')[0]) |
325 | -# severity = _getnodetxt(bug_n.getElementsByTagName('issue_type')[0]) |
326 | -# assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0]) |
327 | -# except Exception, e: |
328 | -# s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url) |
329 | -# raise BugtrackerError, s |
330 | -# return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))] |
331 | |
332 | class Launchpad(IBugtracker): |
333 | statuses = ["Unknown", "Invalid", "Opinion", "Won't Fix", "Fix Released", "Fix Committed", "New", "Incomplete", "Confirmed", "Triaged", "In Progress"] |
334 | @@ -705,7 +713,7 @@ |
335 | supylog.exception("Unknown exception while accessing the Launchpad API") |
336 | |
337 | def _parse(self, task): #Depricated |
338 | - parser = email.FeedParser.FeedParser() |
339 | + parser = FeedParser() |
340 | parser.feed(task) |
341 | return parser.close() |
342 | |
343 | @@ -770,15 +778,15 @@ |
344 | bugdata = self.lp.bugs[id] |
345 | if bugdata.private: |
346 | raise BugtrackerError, "This bug is private" |
347 | + duplicate = [] |
348 | dup = bugdata.duplicate_of |
349 | - summary_prefix = '' # Used to made dups easier |
350 | while dup: |
351 | - summary_prefix = 'duplicate for #%d ' % id |
352 | + duplicate.append(str(bugdata.id)) |
353 | bugdata = dup |
354 | dup = bugdata.duplicate_of |
355 | |
356 | - affected = bugdata.users_affected_count_with_dupes |
357 | - heat = bugdata.heat |
358 | + extinfo = ['affected: %d' % bugdata.users_affected_count_with_dupes] |
359 | + extinfo.append('heat: %d' % bugdata.heat) |
360 | tasks = bugdata.bug_tasks |
361 | |
362 | if tasks.total_size != 1: |
363 | @@ -787,7 +795,7 @@ |
364 | tasks.sort(self._sort) |
365 | taskdata = tasks[-1] |
366 | except ValueError: |
367 | - tasks = [_ for _ in tasks if _.bug_target_name.endswith(u'(Ubuntu)')] |
368 | + tasks = [_ for _ in tasks if _.bug_target_name.endswith('(Ubuntu)')] |
369 | if tasks: |
370 | if len(tasks) != 1: |
371 | try: |
372 | @@ -802,18 +810,15 @@ |
373 | else: |
374 | taskdata = tasks[0] |
375 | |
376 | - assignee = taskdata.assignee |
377 | - t = taskdata.bug_target_display_name #task name |
378 | - |
379 | - if assignee: # "Diaplay Name (Launchpad ID)" |
380 | - assignee = u"%s (%s)" % (assignee.display_name, assignee.name) |
381 | + if taskdata.assignee: |
382 | + assignee = taskdata.assignee.display_name |
383 | else: |
384 | assignee = '' |
385 | |
386 | except Exception, e: |
387 | if type(e).__name__ == 'HTTPError': # messy, but saves trying to import lazr.restfulclient.errors.HTPError |
388 | if e.response.status == 404: |
389 | - bugNo = e.content.split(None)[-1][2:-1] # extract the real bug number |
390 | + bugNo = e.content.split()[-1][2:-1] # extract the real bug number |
391 | if bugNo != str(id): # A duplicate of a private bug, at least we know it exists |
392 | raise BugtrackerError, 'Bug #%s is a duplicate of bug #%s, but it is private (%s/bugs/%s)' % (id, bugNo, self.url, bugNo) |
393 | raise BugtrackerError, "Bug #%s (%s/bugs/%d) is private or doesn't exist" % (id, self.url, id) # Could be private, could just not exist |
394 | @@ -825,58 +830,52 @@ |
395 | supylog.exception("Error gathering bug data for %s bug %d" % (self.description, id)) |
396 | raise BugtrackerError, "Could not gather data from %s for bug #%s (%s/bugs/%s). The error has been logged" % (self.description, id, self.url, id) |
397 | |
398 | - extinfo = "(affected: %d, heat: %d)" % (affected, heat) |
399 | - |
400 | - return [(bugdata.id, t, summary_prefix + bugdata.title, taskdata.importance, taskdata.status, |
401 | - assignee, "%s/bugs/%s" % (self.url, bugdata.id), extinfo)] |
402 | - |
403 | - def get_bug_old(self, id): #Depricated |
404 | + return [(bugdata.id, taskdata.bug_target_display_name, bugdata.title, taskdata.importance, taskdata.status, |
405 | + assignee, "%s/bugs/%s" % (self.url, bugdata.id), extinfo, duplicate)] |
406 | + |
407 | + def get_bug_old(self, id, duplicate=None): #Depricated |
408 | if id == 1: |
409 | raise BugtrackerError, "https://bugs.launchpad.net/ubuntu/+bug/1 (Not reporting large bug)" |
410 | |
411 | try: |
412 | - bugdata = utils.web.getUrl("%s/bugs/%d/+text" % (self.url,id)) |
413 | + bugdata = utils.web.getUrl("%s/bugs/%d/+text" % (self.url, id)) |
414 | except Exception, e: |
415 | if '404' in str(e): |
416 | - raise BugNotFoundError |
417 | + if duplicate: |
418 | + raise BugtrackerError, 'Bug #%s is a duplicate of bug #%s, but it is private (%s/bugs/%s)' % (duplicate, id, self.url, id) |
419 | + else: |
420 | + raise BugNotFoundError |
421 | s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id) |
422 | raise BugtrackerError, s |
423 | - summary = {} |
424 | + |
425 | # Trap private bugs |
426 | if "<!-- 4a. didn't try to log in last time: -->" in bugdata: |
427 | raise BugtrackerError, "This bug is private" |
428 | try: |
429 | # Split bug data into separate pieces (bug data, task data) |
430 | - data = bugdata.split('\n\n') |
431 | - bugdata = data[0] |
432 | - taskdata = data[1:] |
433 | - parser = email.FeedParser.FeedParser() |
434 | - parser.feed(bugdata) |
435 | - bugdata = parser.close() |
436 | - taskdata = map(self._parse, taskdata) |
437 | - taskdata.sort(self._old_sort) |
438 | - taskdata = taskdata[-1] |
439 | - |
440 | + data = bugdata.split('\n\nContent-Type:', 1)[0].split('\n\n') |
441 | + bugdata = self._parse(data[0]) |
442 | + if not bugdata['duplicate-of']: |
443 | + taskdata = map(self._parse, data[1:]) |
444 | + taskdata.sort(self._old_sort) |
445 | + taskdata = taskdata[-1] |
446 | + if taskdata['assignee']: |
447 | + assignee = re.sub(r' \([^)]*\)$', '', taskdata['assignee']) |
448 | + else: |
449 | + assignee = '' |
450 | except Exception, e: |
451 | s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id) |
452 | raise BugtrackerError, s |
453 | + |
454 | # Try and find duplicates |
455 | - t = taskdata['task'] |
456 | - if '(' in t: |
457 | - t = t[:t.rfind('(') -1] |
458 | - if bugdata['duplicate-of']: # This will suck if for dup of dups..., but +text is pure suck anyway |
459 | - bugNo = bugdata['duplicate-of'] |
460 | - try: |
461 | - data = self.get_bug(int(bugdata['duplicate-of'])) |
462 | - except Exception, e: |
463 | - if '404' in str(e): |
464 | - raise BugtrackerError, 'Bug #%s is a duplicate of Bug #%s, but it is private. (%s/bugs/%s)' % (id, bugNo, self.url, bugNo) |
465 | - data = list(data[0]) |
466 | - data[2] = ('duplicate for #%d ' % id) + data[2] |
467 | - return [tuple(data)] |
468 | - return [(id, t, bugdata['title'], taskdata['importance'], |
469 | - taskdata['status'], taskdata['assignee'], "%s/bugs/%s" % (self.url, id))] |
470 | - |
471 | + if bugdata['duplicate-of']: |
472 | + data = self.get_bug_old(int(bugdata['duplicate-of']), duplicate or id)[0] |
473 | + data[8].append(bugdata['bug']) |
474 | + return [data] |
475 | + |
476 | + return [(id, taskdata['task'], bugdata['title'], taskdata['importance'], taskdata['status'], |
477 | + assignee, "%s/bugs/%s" % (self.url, id), [], [])] |
478 | + |
479 | # <rant> |
480 | # Debbugs sucks donkeyballs |
481 | # * HTML pages are inconsistent |
482 | @@ -890,11 +889,10 @@ |
483 | class Debbugs(IBugtracker): |
484 | def __init__(self, *args, **kwargs): |
485 | IBugtracker.__init__(self, *args, **kwargs) |
486 | - self.soap_proxy = SOAPpy.SOAPProxy("bugs.debian.org/cgi-bin/soap.cgi", "Debbugs/SOAP/Status") |
487 | - self.soap_proxy.soapaction = "Debbugs/SOAP/Status#get_status" |
488 | + self.soap_proxy = SOAPProxy("%s/cgi-bin/soap.cgi" % self.url, namespace="Debbugs/SOAP") |
489 | |
490 | def get_bug(self, id): |
491 | - bug_url = "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%d" % id |
492 | + bug_url = "%s/cgi-bin/bugreport.cgi?bug=%d" % (self.url, id) |
493 | try: |
494 | raw = self.soap_proxy.get_status(id) |
495 | except Exception, e: |
496 | @@ -902,13 +900,13 @@ |
497 | raise BugtrackerError, s |
498 | if not raw: |
499 | raise BugNotFoundError |
500 | - raw = raw['item']['value'] |
501 | try: |
502 | - if len(raw['fixed_versions']): |
503 | + raw = raw['item']['value'] |
504 | + if raw['fixed_versions']: |
505 | status = 'Fixed' |
506 | else: |
507 | status = 'Open' |
508 | - return [(id, raw['package'], raw['subject'], raw['severity'], status, '', "%s/%s" % (self.url, id))] |
509 | + return [(id, raw['package'], raw['subject'], raw['severity'], status, '', "%s/%s" % (self.url, id), [], [])] |
510 | except Exception, e: |
511 | s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, bug_url) |
512 | raise BugtrackerError, s |
513 | @@ -916,11 +914,10 @@ |
514 | class Mantis(IBugtracker): |
515 | def __init__(self, *args, **kwargs): |
516 | IBugtracker.__init__(self, *args, **kwargs) |
517 | - self.soap_proxy = SOAPpy.SOAPProxy(self.url + "/api/soap/mantisconnect.php", "http://futureware.biz/mantisconnect") |
518 | - self.soap_proxy.soapaction = "http://futureware.biz/mantisconnect#mc_issue_get" |
519 | + self.soap_proxy = SOAPProxy("%s/api/soap/mantisconnect.php" % self.url, namespace="http://futureware.biz/mantisconnect") |
520 | |
521 | def get_bug(self, id): |
522 | - url = self.url + "/view.php?id=%i" % id |
523 | + url = "%s/view.php?id=%d" % (self.url, id) |
524 | try: |
525 | raw = self.soap_proxy.mc_issue_get('', "", id) |
526 | except Exception, e: |
527 | @@ -929,7 +926,7 @@ |
528 | if not raw: |
529 | raise BugNotFoundError |
530 | try: |
531 | - return [(id, raw['project']['name'], raw['summary'], raw['priority']['name'], raw['resolution']['name'], '', url)] |
532 | + return [(id, raw['project']['name'], raw['summary'], raw['severity']['name'], raw['resolution']['name'], '', url, [], [])] |
533 | except Exception, e: |
534 | s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, url) |
535 | raise BugtrackerError, s |
536 | @@ -952,7 +949,8 @@ |
537 | (headers, rest) = raw.split('\n', 1) |
538 | headers = headers.strip().split('\t') |
539 | rest = rest.strip().split('\t') |
540 | - title = status = package = severity = assignee = "Unknown" |
541 | + |
542 | + title = status = package = severity = assignee = "" |
543 | if "summary" in headers: |
544 | title = rest[headers.index("summary")] |
545 | if "status" in headers: |
546 | @@ -961,13 +959,12 @@ |
547 | package = rest[headers.index("component")] |
548 | if "severity" in headers: |
549 | severity = rest[headers.index("severity")] |
550 | + elif "priority" in headers: |
551 | + severity = rest[headers.index("priority")] |
552 | if "owner" in headers: |
553 | - assingee = rest[headers.index("owner")] |
554 | - if severity == "Unknown" and "priority" in headers: |
555 | - severity = rest[headers.index("priority")] |
556 | + assignee = rest[headers.index("owner")] |
557 | + return [(id, package, title, severity, status, assignee, bug_url, [], [])] |
558 | |
559 | - return [(id, package, title, severity, status, assignee, bug_url)] |
560 | - |
561 | class WikiForms(IBugtracker): |
562 | def get_bug(self, id): |
563 | def strip_tags(s): |
564 | @@ -986,14 +983,14 @@ |
565 | for l in bugdata.split("\n"): |
566 | l2 = l.lower() |
567 | if '<dt>importance</dt>' in l2: |
568 | - severity = 'Importance ' + strip_tags(l[l.find('<dd>')+4:]) |
569 | + severity = strip_tags(l[l.find('<dd>')+4:]) |
570 | if '<dt>summary</dt>' in l2: |
571 | title = strip_tags(l[l.find('<dd>')+4:]) |
572 | if '<dt>status</dt>' in l2: |
573 | status = strip_tags(l[l.find('<dd>')+4:]) |
574 | if '<dt>category</dt>' in l2: |
575 | package = strip_tags(l[l.find('<dd>')+4:]) |
576 | - return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id))] |
577 | + return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id), [], [])] |
578 | |
579 | class Str(IBugtracker): |
580 | def get_bug(self, id): |
581 | @@ -1010,7 +1007,7 @@ |
582 | for l in bugdata.split("\n"): |
583 | l2 = l.lower() |
584 | if 'nowrap>priority:</th>' in l2: |
585 | - severity = 'Priority ' + l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))] |
586 | + severity = l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))] |
587 | if '>application:</th>' in l2: |
588 | package = l[l.find('<td>')+4:l.find('</td>')] |
589 | if 'nowrap>status:</th>' in l2: |
590 | @@ -1020,9 +1017,8 @@ |
591 | if 'nowrap>assigned to:</th>' in l2: |
592 | assignee = strip_tags(l[l.find('<td>')+4:l.find('</td>')]) |
593 | if assignee == 'Unassigned': |
594 | - assignee = 'nobody' |
595 | - return [(id, package, title, severity, status, assignee, "%s?L%d" % (self.url, id))] |
596 | - |
597 | + assignee = '' |
598 | + return [(id, package, title, severity, status, assignee, "%s?L%d" % (self.url, id), [], [])] |
599 | |
600 | sfre = re.compile(r""" |
601 | .*? |
602 | @@ -1051,9 +1047,12 @@ |
603 | reo = sfre.search(bugdata) |
604 | status = reo.group('status') |
605 | resolution = reo.group('resolution') |
606 | - if not (resolution.lower() == 'none'): |
607 | - status += ' ' + resolution |
608 | - return [(id, None, reo.group('title'), "Pri: %s" % reo.group('priority'), status, reo.group('assignee'),self._sf_url % id)] |
609 | + if resolution.lower() != 'none': |
610 | + status += ': %s' % resolution |
611 | + assignee = reo.group('assignee') |
612 | + if assignee.lower() == 'nobody': |
613 | + assignee = '' |
614 | + return [(id, '', reo.group('title'), "Pri: %s" % reo.group('priority'), status, assignee, self._sf_url % id, [], [])] |
615 | except: |
616 | raise BugNotFoundError |
617 | |
618 | @@ -1064,26 +1063,25 @@ |
619 | if type(v[k]) == type(IBugtracker) and issubclass(v[k], IBugtracker) and not (v[k] == IBugtracker): |
620 | defined_bugtrackers[k.lower()] = v[k] |
621 | |
622 | -registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla') |
623 | +registerBugtracker('mozilla', 'https://bugzilla.mozilla.org', 'Mozilla', 'bugzilla') |
624 | +registerBugtracker('gnome', 'https://bugzilla.gnome.org', 'Gnome', 'bugzilla') |
625 | +registerBugtracker('gnome2', 'https://bugs.gnome.org', 'Gnome', 'bugzilla') |
626 | +registerBugtracker('kde', 'https://bugs.kde.org', 'KDE', 'bugzilla') |
627 | +registerBugtracker('xfce', 'https://bugzilla.xfce.org', 'Xfce', 'bugzilla') |
628 | +registerBugtracker('freedesktop', 'https://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla') |
629 | +registerBugtracker('freedesktop2', 'https://bugs.freedesktop.org', 'Freedesktop', 'bugzilla') |
630 | +registerBugtracker('openoffice', 'https://bz.apache.org/ooo', 'OpenOffice', 'bugzilla') |
631 | registerBugtracker('ubuntu', 'https://launchpad.net', 'Ubuntu', 'launchpad') |
632 | -registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', 'bugzilla') |
633 | -registerBugtracker('gnome2', 'http://bugs.gnome.org', 'Gnome', 'bugzilla') |
634 | -registerBugtracker('kde', 'http://bugs.kde.org', 'KDE', 'bugzilla') |
635 | -registerBugtracker('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla') |
636 | -registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla') |
637 | -registerBugtracker('freedesktop2', 'http://bugs.freedesktop.org', 'Freedesktop', 'bugzilla') |
638 | -registerBugtracker('openoffice', 'http://openoffice.org/issues', 'OpenOffice.org', 'issuezilla') |
639 | +registerBugtracker('ubottu', 'https://launchpad.net', 'Ubottu', 'launchpad') |
640 | registerBugtracker('launchpad', 'https://launchpad.net', 'Launchpad', 'launchpad') |
641 | registerBugtracker('lp', 'https://launchpad.net', 'Launchpad', 'launchpad') |
642 | -registerBugtracker('malone', 'https://launchpad.net', 'Launchpad', 'launchpad') |
643 | -registerBugtracker('debian', 'http://bugs.debian.org', 'Debian', 'debbugs') |
644 | -registerBugtracker('trac', 'http://trac.edgewall.org/ticket', 'Trac', 'trac') |
645 | -registerBugtracker('django', 'http://code.djangoproject.com/ticket', 'Django', 'trac') |
646 | -registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str') |
647 | -registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms') |
648 | -registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge') |
649 | -registerBugtracker('mantis', "http://www.mantisbt.org/bugs", "Mantis", 'mantis') |
650 | -registerBugtracker('ubottu', 'https://launchpad.net', 'Ubottu', 'launchpad') |
651 | -# Don't delete this one |
652 | -registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge') |
653 | +registerBugtracker('debian', 'https://bugs.debian.org', 'Debian', 'debbugs') |
654 | +registerBugtracker('mantis', 'https://www.mantisbt.org/bugs', 'Mantis', 'mantis') |
655 | +registerBugtracker('trac', 'https://trac.edgewall.org/ticket', 'Trac', 'trac') |
656 | +registerBugtracker('django', 'https://code.djangoproject.com/ticket', 'Django', 'trac') |
657 | +# Outdated |
658 | +#registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str') |
659 | +#registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms') |
660 | +#registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge') |
661 | +#registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge') |
662 | Class = Bugtracker |