Merge lp:~krytarik/ubuntu-bots/bugtracker-output into lp:ubuntu-bots

Proposed by Krytarik Raido
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
Reviewer Review Type Date Requested Status
Ubuntu IRC Bots Pending
Benjamin Rubin Pending
Review via email: mp+311899@code.launchpad.net

Description of the change

Bugtracker: Make sure output fits maximum length of IRC messages, improve its formatting.

To post a comment you must log in.
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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

Subscribers

People subscribed via source and target branches