Bugtracker: Various improvements in output.

* Make sure output fits maximum length of IRC messages.
* Improve general formatting of output, including casing.
* Strip any leading or trailing spaces off bug report title.
* Improve handling of duplicates, assignee, and extended info.
This commit is contained in:
Krytarik Raido 2018-03-11 07:34:04 +01:00
parent 45ae48b53a
commit 9ab4725d49
2 changed files with 162 additions and 165 deletions

View File

@ -24,7 +24,7 @@ import supybot.world as world
from imp import reload
__version__ = "2.6.0"
__version__ = "2.7.0"
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
__contributors__ = {
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Author'],

View File

@ -33,7 +33,7 @@ import xml.dom.minidom as minidom
from html.entities import entitydefs as entities
from email.parser import FeedParser
if sys.version_info < (3,0):
import SOAPpy
from SOAPpy.Client import SOAPProxy
# All the words below will be censored when reporting bug information
bad_words = set(["fuck","fuk","fucking","fuking","fukin","fuckin","fucked","fuked","fucker","shit","cunt","bastard","nazi","nigger","nigga","cock","bitches","bitch"])
@ -85,6 +85,19 @@ def _getnodetxt(node):
val = entre.sub('?', val)
return val
def _getnodeattr(node, attr):
if node.hasAttribute(attr):
val = node.getAttribute(attr)
else:
raise ValueError("No such attribute")
while entre.search(val):
entity = entre.search(val).group(1)
if entity in entities:
val = entre.sub(entities[entity], val)
else:
val = entre.sub('?', val)
return val
class BugtrackerError(Exception):
"""A bugtracker error"""
pass
@ -279,7 +292,7 @@ class Bugtracker(callbacks.PluginRegexp):
self.shorthand = utils.abbrev(list(self.db.keys()))
irc.replySuccess()
except KeyError:
s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None)
s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None)
irc.error(s % name)
remove = wrap(remove, [('checkCapability', 'admin'), 'text'])
@ -301,7 +314,7 @@ class Bugtracker(callbacks.PluginRegexp):
self.shorthand = utils.abbrev(list(self.db.keys()))
irc.replySuccess()
except KeyError:
s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None)
s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None)
irc.error(s % name)
rename = wrap(rename, [('checkCapability', 'admin'), 'something','something', additional('text')])
@ -319,7 +332,7 @@ class Bugtracker(callbacks.PluginRegexp):
self.db[name].__class__.__name__)
irc.reply('%s: %s, %s [%s]' % (name, description, url, type))
except KeyError:
s = self.registryValue('replyNoBugtracker', ircutils.isChannel(msg.args[0]) and msg.args[0] or None)
s = self.registryValue('replyNoBugtracker', msg.args[0] if ircutils.isChannel(msg.args[0]) else None)
irc.error(s % name)
else:
if self.db:
@ -332,7 +345,7 @@ class Bugtracker(callbacks.PluginRegexp):
def bugSnarfer(self, irc, msg, match):
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+))*)"""
channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue('bugSnarfer', channel):
return
nbugs = msg.tagged('nbugs')
@ -407,13 +420,14 @@ class Bugtracker(callbacks.PluginRegexp):
for bugid in bugids:
bugid = int(bugid)
try:
report = self.get_bug(channel,tracker,bugid,self.registryValue('showassignee', channel), show_tracker=showTracker)
report = self.get_bug(channel or msg.nick, tracker, bugid, self.registryValue('showassignee', channel),
self.registryValue('extended', channel), do_tracker=showTracker)
except BugNotFoundError:
if self.registryValue('replyWhenNotFound'):
irc.error("%s bug %d could not be found" % (tracker.description, bugid))
except BugtrackerError as e:
# if 'private' in str(e):
# irc.reply("Bug %d on http://launchpad.net/bugs/%d is private" % (bugid, bugid))
# irc.reply("Bug %d on https://launchpad.net/bugs/%d is private" % (bugid, bugid))
# return
if not sure_bug and bugid < 30:
return
@ -424,7 +438,7 @@ class Bugtracker(callbacks.PluginRegexp):
def turlSnarfer(self, irc, msg, match):
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+)?"
channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue('bugSnarfer', channel):
return
nbugs = msg.tagged('nbugs')
@ -436,7 +450,8 @@ class Bugtracker(callbacks.PluginRegexp):
tracker = self.get_tracker(match.group(0),match.group('sfurl'))
if not tracker:
return
report = self.get_bug(channel, tracker, int(match.group('bug')), self.registryValue('showassignee', channel), do_url = False)
report = self.get_bug(channel or msg.nick, tracker, int(match.group('bug')), self.registryValue('showassignee', channel),
self.registryValue('extended', channel), do_url=False)
except BugtrackerError as e:
irc.error(str(e))
except BugNotFoundError as e:
@ -448,32 +463,32 @@ class Bugtracker(callbacks.PluginRegexp):
# Only useful for launchpad developers
def oopsSnarfer(self, irc, msg, match):
r"(?:https?://pad.lv/){0}?OOPS-(?P<oopsid>\d*[\dA-Z]{3,})"
channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue('bugSnarfer', channel) or not self.registryValue('oopsSnarfer', channel):
return
oopsid = match.group(1)
if oopsid.lower() == "tools":
return
if not self.is_ok(channel, 'lpoops', oopsid):
if not self.is_ok(channel or msg.nick, 'lpoops', oopsid):
return
irc.reply('https://oops.canonical.com/?oopsid=OOPS-' + oopsid, prefixNick=False)
irc.reply('https://oops.canonical.com/?oopsid=OOPS-%s' % oopsid, prefixNick=False)
def cveSnarfer(self, irc, msg, match):
r"(cve[- ]\d{4}[- ]\d{4,})"
channel = ircutils.isChannel(msg.args[0]) and msg.args[0] or None
channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue('bugSnarfer', channel) or not self.registryValue('cveSnarfer', channel):
return
cve = match.group(1).replace(' ','-').upper()
if not self.is_ok(channel, 'cve', cve):
if not self.is_ok(channel or msg.nick, 'cve', cve):
return
url = 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' % cve
url = 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=%s' % cve
cvedata = utils.web.getUrl(url).decode('utf-8')
m = cvere.search(cvedata)
if m:
cve = m.group(1).replace('\n', ' ')
if len(cve) > 380:
cve = cve[:380] + '...'
irc.reply("%s (%s)" % (cve,url), prefixNick=False)
irc.reply("%s <%s>" % (cve, url), prefixNick=False)
#TODO: as we will depend on launchpadlib, we should consider using lazr.uri.URI to do URL parsing
def get_tracker(self, snarfurl, sfdata):
@ -533,39 +548,57 @@ class Bugtracker(callbacks.PluginRegexp):
return tracker
return None
def get_bug(self, channel, tracker, id, do_assignee, do_url = True, show_tracker = True):
def get_bug(self, channel, tracker, id, do_assignee, do_extinfo, do_url=True, do_tracker=True):
reports = []
message_max = 450 - len(channel)
if not self.is_ok(channel, tracker, id):
return []
for r in tracker.get_bug(id):
showext = self.registryValue('extended', channel)
extinfo = ''
if len(r) == 8:
(bid, product, title, severity, status, assignee, url, extinfo) = r
else:
(bid, product, title, severity, status, assignee, url) = r
severity = severity[0].upper() + severity[1:].lower()
status = status[0].upper() + status[1:].lower()
tracker_name = tracker.description + ' '
if not do_url:
url = ''
if not show_tracker:
tracker_name = ''
if product:
if showext:
reports.append("%sbug %s in %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, product,
title, extinfo, severity, status, url))
else:
reports.append("%sbug %s in %s \"%s\" [%s,%s] %s" % (tracker_name, bid, product,
title, severity, status, url))
for r in tracker.get_bug(id):
(bid, product, title, severity, status, assignee, url, extinfo, duplicate) = r
if duplicate and not self.is_ok(channel, tracker, bid):
continue
if do_tracker:
report = '%s bug %s' % (tracker.description, bid)
else:
if showext:
reports.append("%sbug %s \"%s\" %s [%s,%s] %s" % (tracker_name, bid, title, extinfo, severity, status, url))
else:
reports.append("%sbug %s \"%s\" [%s,%s] %s" % (tracker_name, bid, title, severity, status, url))
report = 'Bug %s' % bid
if product:
report += ' in %s' % product
report += ' "%s"' % title.replace('"', "'").strip()
if do_extinfo and extinfo:
report += ' (%s)' % ', '.join(extinfo)
if do_assignee and assignee:
reports[-1] = reports[-1] + (" - Assigned to %s" % assignee)
report += ' (assigned: %s)' % assignee
severity_status = []
if severity:
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in severity.split()))
severity_status.append(' '.join(word[0].upper() + word[1:].lower() for word in status.split()))
report += ' [%s]' % ', '.join(severity_status)
if duplicate:
report += ' [duplicate: %s]' % duplicate[0]
if do_url:
report += ' %s' % url
if len(report) > message_max:
report_parts = report.split('"')
report_start = report_parts[0]
report_end = report_parts[-1]
report_title = '"'.join(report_parts[1:-1])
title_max = message_max - len(report_start) - len(report_end) - 5
report_title_cut = report_title[:title_max].rsplit(None, 1)[0] + '...'
report = '%s"%s"%s' % (report_start, report_title_cut, report_end)
reports.append(report)
return reports
# Define all bugtrackers
@ -612,6 +645,7 @@ class Bugzilla(IBugtracker):
return tracker
except:
return None
def get_bug(self, id):
url = "%s/show_bug.cgi?id=%d&ctype=xml" % (self.url,id)
try:
@ -634,49 +668,22 @@ class Bugzilla(IBugtracker):
status = "%s: %s" % (status, _getnodetxt(bug_n.getElementsByTagName('resolution')[0]))
except:
pass
component = _getnodetxt(bug_n.getElementsByTagName('component')[0])
product = _getnodetxt(bug_n.getElementsByTagName('product')[0])
severity = _getnodetxt(bug_n.getElementsByTagName('bug_severity')[0])
assignee = '(unavailable)'
try:
assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0])
assignee = _getnodeattr(bug_n.getElementsByTagName('assigned_to')[0], 'name')
except:
pass
try:
assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0])
except:
assignee = ''
except Exception as e:
s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url)
raise BugtrackerError(s)
return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))]
return [(id, product, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id), [], [])]
class Issuezilla(Bugzilla):
pass
#class Issuezilla(IBugtracker):
# def get_bug(self, id):
# url = "%s/show_bug.cgi?id=%d&ctype=xml" % (self.url,id)
# try:
# bugxml = utils.web.getUrl(url)
# zilladom = minidom.parseString(bugxml)
# except Exception, e:
# s = 'Could not parse XML returned by %s: %s (%s)' % (self.description, e, url)
# raise BugtrackerError, s
# bug_n = zilladom.getElementsByTagName('issue')[0]
# if not (bug_n.getAttribute('status_code') == '200'):
# if bug_n.getAttribute('status_message') == 'NotFound':
# raise BugNotFoundError
# s = 'Error getting %s bug #%s: %s' % (self.description, id, bug_n.getAttribute('status_message'))
# raise BugtrackerError, s
# try:
# title = _getnodetxt(bug_n.getElementsByTagName('short_desc')[0])
# status = _getnodetxt(bug_n.getElementsByTagName('issue_status')[0])
# try:
# status = "%s: %s" % (status, _getnodetxt(bug_n.getElementsByTagName('resolution')[0]))
# except:
# pass
# component = _getnodetxt(bug_n.getElementsByTagName('component')[0])
# severity = _getnodetxt(bug_n.getElementsByTagName('issue_type')[0])
# assignee = _getnodetxt(bug_n.getElementsByTagName('assigned_to')[0])
# except Exception, e:
# s = 'Could not parse XML returned by %s bugzilla: %s (%s)' % (self.description, e, url)
# raise BugtrackerError, s
# return [(id, component, title, severity, status, assignee, "%s/show_bug.cgi?id=%d" % (self.url, id))]
class Launchpad(IBugtracker):
statuses = ["Unknown", "Invalid", "Opinion", "Won't Fix", "Fix Released", "Fix Committed", "New", "Incomplete", "Confirmed", "Triaged", "In Progress"]
@ -774,15 +781,15 @@ class Launchpad(IBugtracker):
bugdata = self.lp.bugs[id]
if bugdata.private:
raise BugtrackerError("This bug is private")
duplicate = []
dup = bugdata.duplicate_of
summary_prefix = '' # Used to made dups easier
while dup:
summary_prefix = 'duplicate for #%d ' % id
duplicate.append(str(bugdata.id))
bugdata = dup
dup = bugdata.duplicate_of
affected = bugdata.users_affected_count_with_dupes
heat = bugdata.heat
extinfo = ['affected: %d' % bugdata.users_affected_count_with_dupes]
extinfo.append('heat: %d' % bugdata.heat)
tasks = bugdata.bug_tasks
if tasks.total_size != 1:
@ -806,18 +813,15 @@ class Launchpad(IBugtracker):
else:
taskdata = tasks[0]
assignee = taskdata.assignee
t = taskdata.bug_target_display_name #task name
if assignee: # "Diaplay Name (Launchpad ID)"
assignee = "%s (%s)" % (assignee.display_name, assignee.name)
if taskdata.assignee:
assignee = taskdata.assignee.display_name
else:
assignee = ''
except Exception as e:
if type(e).__name__ == 'HTTPError': # messy, but saves trying to import lazr.restfulclient.errors.HTPError
if e.response.status == 404:
bugNo = e.content.split(None)[-1][2:-1] # extract the real bug number
bugNo = e.content.split()[-1][2:-1] # extract the real bug number
if bugNo != str(id): # A duplicate of a private bug, at least we know it exists
raise BugtrackerError('Bug #%s is a duplicate of bug #%s, but it is private (%s/bugs/%s)' % (id, bugNo, self.url, bugNo))
raise BugtrackerError("Bug #%s (%s/bugs/%d) is private or doesn't exist" % (id, self.url, id)) # Could be private, could just not exist
@ -829,12 +833,10 @@ class Launchpad(IBugtracker):
supylog.exception("Error gathering bug data for %s bug %d" % (self.description, id))
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))
extinfo = "(affected: %d, heat: %d)" % (affected, heat)
return [(bugdata.id, taskdata.bug_target_display_name, bugdata.title, taskdata.importance, taskdata.status,
assignee, "%s/bugs/%s" % (self.url, bugdata.id), extinfo, duplicate)]
return [(bugdata.id, t, summary_prefix + bugdata.title, taskdata.importance, taskdata.status,
assignee, "%s/bugs/%s" % (self.url, bugdata.id), extinfo)]
def get_bug_old(self, id): #Depricated
def get_bug_old(self, id, duplicate=None): #Depricated
if id == 1:
raise BugtrackerError("https://bugs.launchpad.net/ubuntu/+bug/1 (Not reporting large bug)")
@ -842,45 +844,41 @@ class Launchpad(IBugtracker):
bugdata = utils.web.getUrl("%s/bugs/%d/+text" % (self.url,id)).decode('utf-8')
except Exception as e:
if '404' in str(e):
raise BugNotFoundError
if duplicate:
raise BugtrackerError('Bug #%s is a duplicate of bug #%s, but it is private (%s/bugs/%s)' % (duplicate, id, self.url, id))
else:
raise BugNotFoundError
s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id)
raise BugtrackerError(s)
summary = {}
# Trap private bugs
if "<!-- 4a. didn't try to log in last time: -->" in bugdata:
raise BugtrackerError("This bug is private")
try:
# Split bug data into separate pieces (bug data, task data)
data = bugdata.split('\n\n')
bugdata = data[0]
taskdata = data[1:]
parser = FeedParser()
parser.feed(bugdata)
bugdata = parser.close()
taskdata = list(map(self._parse, taskdata))
taskdata = sorted(taskdata, key=cmp_to_key(self._old_sort))
taskdata = taskdata[-1]
data = bugdata.split('\n\nContent-Type:', 1)[0].split('\n\n')
bugdata = self._parse(data[0])
if not bugdata['duplicate-of']:
taskdata = list(map(self._parse, data[1:]))
taskdata = sorted(taskdata, key=cmp_to_key(self._old_sort))
taskdata = taskdata[-1]
if taskdata['assignee']:
assignee = re.sub(r' \([^)]*\)$', '', taskdata['assignee'])
else:
assignee = ''
except Exception as e:
s = 'Could not parse data returned by %s: %s (%s/bugs/%d)' % (self.description, e, self.url, id)
raise BugtrackerError(s)
# Try and find duplicates
t = taskdata['task']
if '(' in t:
t = t[:t.rfind('(') -1]
if bugdata['duplicate-of']: # This will suck if for dup of dups..., but +text is pure suck anyway
bugNo = bugdata['duplicate-of']
try:
data = self.get_bug(int(bugdata['duplicate-of']))
except Exception as e:
if '404' in str(e):
raise BugtrackerError('Bug #%s is a duplicate of Bug #%s, but it is private. (%s/bugs/%s)' % (id, bugNo, self.url, bugNo))
data = list(data[0])
data[2] = ('duplicate for #%d ' % id) + data[2]
return [tuple(data)]
return [(id, t, bugdata['title'], taskdata['importance'],
taskdata['status'], taskdata['assignee'], "%s/bugs/%s" % (self.url, id))]
if bugdata['duplicate-of']:
data = self.get_bug_old(int(bugdata['duplicate-of']), duplicate or id)[0]
data[8].append(bugdata['bug'])
return [data]
return [(id, taskdata['task'], bugdata['title'], taskdata['importance'], taskdata['status'],
assignee, "%s/bugs/%s" % (self.url, id), [], [])]
# <rant>
# Debbugs sucks donkeyballs
# * HTML pages are inconsistent
@ -897,11 +895,10 @@ class Debbugs(IBugtracker):
# XXX python3 does not have SOAPpy, so just quit here (for now)
return
IBugtracker.__init__(self, *args, **kwargs)
self.soap_proxy = SOAPpy.SOAPProxy("bugs.debian.org/cgi-bin/soap.cgi", "Debbugs/SOAP/Status")
self.soap_proxy.soapaction = "Debbugs/SOAP/Status#get_status"
self.soap_proxy = SOAPProxy("%s/cgi-bin/soap.cgi" % self.url, namespace="Debbugs/SOAP")
def get_bug(self, id):
bug_url = "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%d" % id
bug_url = "%s/cgi-bin/bugreport.cgi?bug=%d" % (self.url, id)
try:
raw = self.soap_proxy.get_status(id)
except Exception as e:
@ -909,13 +906,13 @@ class Debbugs(IBugtracker):
raise BugtrackerError(s)
if not raw:
raise BugNotFoundError
raw = raw['item']['value']
try:
if len(raw['fixed_versions']):
raw = raw['item']['value']
if raw['fixed_versions']:
status = 'Fixed'
else:
status = 'Open'
return [(id, raw['package'], raw['subject'], raw['severity'], status, '', "%s/%s" % (self.url, id))]
return [(id, raw['package'], raw['subject'], raw['severity'], status, '', "%s/%s" % (self.url, id), [], [])]
except Exception as e:
s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, bug_url)
raise BugtrackerError(s)
@ -926,11 +923,10 @@ class Mantis(IBugtracker):
# XXX python3 does not have SOAPpy, so just quit here (for now)
return
IBugtracker.__init__(self, *args, **kwargs)
self.soap_proxy = SOAPpy.SOAPProxy(self.url + "/api/soap/mantisconnect.php", "http://futureware.biz/mantisconnect")
self.soap_proxy.soapaction = "http://futureware.biz/mantisconnect#mc_issue_get"
self.soap_proxy = SOAPProxy("%s/api/soap/mantisconnect.php" % self.url, namespace="http://futureware.biz/mantisconnect")
def get_bug(self, id):
url = self.url + "/view.php?id=%i" % id
url = "%s/view.php?id=%d" % (self.url, id)
try:
raw = self.soap_proxy.mc_issue_get('', "", id)
except Exception as e:
@ -939,7 +935,7 @@ class Mantis(IBugtracker):
if not raw:
raise BugNotFoundError
try:
return [(id, raw['project']['name'], raw['summary'], raw['priority']['name'], raw['resolution']['name'], '', url)]
return [(id, raw['project']['name'], raw['summary'], raw['severity']['name'], raw['resolution']['name'], '', url, [], [])]
except Exception as e:
s = 'Could not parse data returned by %s bugtracker: %s (%s)' % (self.description, e, url)
raise BugtrackerError(s)
@ -962,7 +958,8 @@ class Trac(IBugtracker):
(headers, rest) = raw.split('\n', 1)
headers = headers.strip().split('\t')
rest = rest.strip().split('\t')
title = status = package = severity = assignee = "Unknown"
title = status = package = severity = assignee = ""
if "summary" in headers:
title = rest[headers.index("summary")]
if "status" in headers:
@ -971,13 +968,12 @@ class Trac(IBugtracker):
package = rest[headers.index("component")]
if "severity" in headers:
severity = rest[headers.index("severity")]
if "owner" in headers:
assingee = rest[headers.index("owner")]
if severity == "Unknown" and "priority" in headers:
elif "priority" in headers:
severity = rest[headers.index("priority")]
if "owner" in headers:
assignee = rest[headers.index("owner")]
return [(id, package, title, severity, status, assignee, bug_url, [], [])]
return [(id, package, title, severity, status, assignee, bug_url)]
class WikiForms(IBugtracker):
def get_bug(self, id):
def strip_tags(s):
@ -996,14 +992,14 @@ class WikiForms(IBugtracker):
for l in bugdata.split("\n"):
l2 = l.lower()
if '<dt>importance</dt>' in l2:
severity = 'Importance ' + strip_tags(l[l.find('<dd>')+4:])
severity = strip_tags(l[l.find('<dd>')+4:])
if '<dt>summary</dt>' in l2:
title = strip_tags(l[l.find('<dd>')+4:])
if '<dt>status</dt>' in l2:
status = strip_tags(l[l.find('<dd>')+4:])
if '<dt>category</dt>' in l2:
package = strip_tags(l[l.find('<dd>')+4:])
return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id))]
return [(id, package, title, severity, status, '', "%s/%05d" % (self.url, id), [], [])]
class Str(IBugtracker):
def get_bug(self, id):
@ -1020,7 +1016,7 @@ class Str(IBugtracker):
for l in bugdata.split("\n"):
l2 = l.lower()
if 'nowrap>priority:</th>' in l2:
severity = 'Priority ' + l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))]
severity = l[l.find(' - ')+3:min(l.find(','),l.find('</td>'))]
if '>application:</th>' in l2:
package = l[l.find('<td>')+4:l.find('</td>')]
if 'nowrap>status:</th>' in l2:
@ -1030,9 +1026,8 @@ class Str(IBugtracker):
if 'nowrap>assigned to:</th>' in l2:
assignee = strip_tags(l[l.find('<td>')+4:l.find('</td>')])
if assignee == 'Unassigned':
assignee = 'nobody'
return [(id, package, title, severity, status, assignee, "%s?L%d" % (self.url, id))]
assignee = ''
return [(id, package, title, severity, status, assignee, "%s?L%d" % (self.url, id), [], [])]
sfre = re.compile(r"""
.*?
@ -1061,9 +1056,12 @@ class Sourceforge(IBugtracker):
reo = sfre.search(bugdata)
status = reo.group('status')
resolution = reo.group('resolution')
if not (resolution.lower() == 'none'):
status += ' ' + resolution
return [(id, None, reo.group('title'), "Pri: %s" % reo.group('priority'), status, reo.group('assignee'),self._sf_url % id)]
if resolution.lower() != 'none':
status += ': %s' % resolution
assignee = reo.group('assignee')
if assignee.lower() == 'nobody':
assignee = ''
return [(id, '', reo.group('title'), "Pri: %s" % reo.group('priority'), status, assignee, self._sf_url % id, [], [])]
except:
raise BugNotFoundError
@ -1074,26 +1072,25 @@ for k in list(v.keys()):
if type(v[k]) == type(IBugtracker) and issubclass(v[k], IBugtracker) and not (v[k] == IBugtracker):
defined_bugtrackers[k.lower()] = v[k]
registerBugtracker('mozilla', 'http://bugzilla.mozilla.org', 'Mozilla', 'bugzilla')
registerBugtracker('mozilla', 'https://bugzilla.mozilla.org', 'Mozilla', 'bugzilla')
registerBugtracker('gnome', 'https://bugzilla.gnome.org', 'Gnome', 'bugzilla')
registerBugtracker('gnome2', 'https://bugs.gnome.org', 'Gnome', 'bugzilla')
registerBugtracker('kde', 'https://bugs.kde.org', 'KDE', 'bugzilla')
registerBugtracker('xfce', 'https://bugzilla.xfce.org', 'Xfce', 'bugzilla')
registerBugtracker('freedesktop', 'https://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla')
registerBugtracker('freedesktop2', 'https://bugs.freedesktop.org', 'Freedesktop', 'bugzilla')
registerBugtracker('openoffice', 'https://bz.apache.org/ooo', 'OpenOffice', 'bugzilla')
registerBugtracker('ubuntu', 'https://launchpad.net', 'Ubuntu', 'launchpad')
registerBugtracker('gnome', 'http://bugzilla.gnome.org', 'Gnome', 'bugzilla')
registerBugtracker('gnome2', 'http://bugs.gnome.org', 'Gnome', 'bugzilla')
registerBugtracker('kde', 'http://bugs.kde.org', 'KDE', 'bugzilla')
registerBugtracker('ximian', 'http://bugzilla.ximian.com', 'Ximian', 'bugzilla')
registerBugtracker('freedesktop', 'http://bugzilla.freedesktop.org', 'Freedesktop', 'bugzilla')
registerBugtracker('freedesktop2', 'http://bugs.freedesktop.org', 'Freedesktop', 'bugzilla')
registerBugtracker('openoffice', 'http://openoffice.org/issues', 'OpenOffice.org', 'issuezilla')
registerBugtracker('ubottu', 'https://launchpad.net', 'Ubottu', 'launchpad')
registerBugtracker('launchpad', 'https://launchpad.net', 'Launchpad', 'launchpad')
registerBugtracker('lp', 'https://launchpad.net', 'Launchpad', 'launchpad')
registerBugtracker('malone', 'https://launchpad.net', 'Launchpad', 'launchpad')
registerBugtracker('debian', 'http://bugs.debian.org', 'Debian', 'debbugs')
registerBugtracker('trac', 'http://trac.edgewall.org/ticket', 'Trac', 'trac')
registerBugtracker('django', 'http://code.djangoproject.com/ticket', 'Django', 'trac')
registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str')
registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms')
registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge')
registerBugtracker('mantis', "http://www.mantisbt.org/bugs", "Mantis", 'mantis')
registerBugtracker('ubottu', 'https://launchpad.net', 'Ubottu', 'launchpad')
# Don't delete this one
registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge')
registerBugtracker('debian', 'https://bugs.debian.org', 'Debian', 'debbugs')
registerBugtracker('mantis', 'https://www.mantisbt.org/bugs', 'Mantis', 'mantis')
registerBugtracker('trac', 'https://trac.edgewall.org/ticket', 'Trac', 'trac')
registerBugtracker('django', 'https://code.djangoproject.com/ticket', 'Django', 'trac')
# Outdated
#registerBugtracker('cups', 'http://www.cups.org/str.php', 'CUPS', 'str')
#registerBugtracker('gnewsense', 'http://bugs.gnewsense.org/Bugs', 'gNewSense', 'wikiforms')
#registerBugtracker('sourceforge', 'http://sourceforge.net/tracker/', 'Sourceforge', 'sourceforge')
#registerBugtracker('supybot', 'http://sourceforge.net/tracker/?group_id=58965&atid=489447', 'Supybot', 'sourceforge')
Class = Bugtracker