| | 1 | #!/usr/bin/python |
| | 2 | # -*- coding: utf-8 -*- |
| | 3 | |
| | 4 | import gtk, gobject |
| | 5 | import urllib, re, xml.dom.minidom, csv, urlparse |
| | 6 | import bug |
| | 7 | import BeautifulSoup |
| | 8 | |
| | 9 | Bug = bug.bug |
| | 10 | Comment = bug.comment |
| | 11 | |
| | 12 | class protocol: |
| | 13 | @staticmethod |
| | 14 | def Name(): |
| | 15 | return "Bugzilla" |
| | 16 | |
| | 17 | def __init__(self, account): |
| | 18 | self.account = account |
| | 19 | |
| | 20 | def getBugUrl(self,nbr): |
| | 21 | return self.__url('show_bug.cgi', id=nbr) |
| | 22 | |
| | 23 | def btsName(self): |
| | 24 | return urlparse.urlsplit(self.account.url)[1] |
| | 25 | |
| | 26 | def retrieveBug(self,nbr): |
| | 27 | """ Get bug #nbr """ |
| | 28 | def tagval(parent,tag): |
| | 29 | return parent.getElementsByTagName(tag)[0].firstChild.nodeValue |
| | 30 | |
| | 31 | fd = urllib.urlopen(self.__url('show_bug.cgi', id=nbr, ctype='xml')) |
| | 32 | xbug = xml.dom.minidom.parse(fd).getElementsByTagName('bug')[0] |
| | 33 | if xbug.getAttribute('error'): |
| | 34 | return Bug(-1) |
| | 35 | bug = Bug(nbr) |
| | 36 | |
| | 37 | comments = xbug.getElementsByTagName('long_desc') |
| | 38 | bug.setPackage(tagval(xbug, 'product')) |
| | 39 | bug.setTitle(tagval(xbug, 'short_desc')) |
| | 40 | bug.setDescription(tagval(comments[0], 'thetext')) |
| | 41 | bug.setStatus(tagval(xbug, 'bug_status')) |
| | 42 | bug.setImportance(tagval(xbug, 'bug_severity')) |
| | 43 | bug.setAssignee(tagval(xbug, 'assigned_to')) |
| | 44 | bug.setCommentable(False) # fixme ! |
| | 45 | |
| | 46 | for i, comment in enumerate(comments[1:]): |
| | 47 | c_content = tagval(comment, 'thetext') |
| | 48 | c_number = i+1 |
| | 49 | c_author = tagval(comment, 'who') |
| | 50 | c_date = tagval(comment, 'bug_when') |
| | 51 | |
| | 52 | bug.addComment(Comment(c_number, c_content, c_author, "", c_date)) |
| | 53 | |
| | 54 | return bug |
| | 55 | |
| | 56 | def genericSearch(self, string): |
| | 57 | """ Search for a string """ |
| | 58 | return self.__searchResults(query=string, quicksearch=string) |
| | 59 | |
| | 60 | def packageSearch(self, pkg, string): |
| | 61 | """ Search for a string, but only for bug of package "pkg" |
| | 62 | Well, sin't meaningful for Gentoo BTS ;) """ |
| | 63 | return self.__searchResults(product=pkg, content=string, bug_status='__open__') |
| | 64 | |
| | 65 | def packageExist(self, pkg): |
| | 66 | return False |
| | 67 | #fixme! |
| | 68 | potage = BeautifulSoup.BeautifulSoup(urllib.urlopen(self.__url('query.cgi', format="specific"))) |
| | 69 | products = potage.find('select', id='product').findAll('option') |
| | 70 | print [p.string.strip() for p in products] |
| | 71 | return pkg in products |
| | 72 | |
| | 73 | def advancedSearch(self, pkg): |
| | 74 | # fixme ! |
| | 75 | pass |
| | 76 | |
| | 77 | def __searchResults(self, **kwargs): |
| | 78 | url = self.__url('buglist.cgi', ctype='csv', **kwargs) |
| | 79 | data = urllib.urlopen(url) |
| | 80 | fmt = [x.replace('"', '').strip() for x in data.readline().split(',')] |
| | 81 | indexes = (fmt.index('bug_id'), fmt.index('short_short_desc'), fmt.index('bug_severity'), fmt.index('bug_status')) |
| | 82 | bugs = self.__newResult() |
| | 83 | for bug in csv.reader(data, quoting=csv.QUOTE_NONNUMERIC): |
| | 84 | bugs.append((int(bug[indexes[0]]), '', bug[indexes[1]], bug[indexes[2]], bug[indexes[3]])) |
| | 85 | return bugs |
| | 86 | |
| | 87 | def __newResult(self) : |
| | 88 | #private function to create a new result gtk.ListStore |
| | 89 | # Bug id, package, title, importance, status |
| | 90 | return gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) |
| | 91 | |
| | 92 | def __url(self, base, **qs): |
| | 93 | root = self.account.url |
| | 94 | if not root.startswith('http://') and not root.startswith('https://'): |
| | 95 | root = 'http://' + root |
| | 96 | if not root.endswith('/'): |
| | 97 | root += '/' |
| | 98 | s_qs = urllib.urlencode(qs) |
| | 99 | if s_qs: |
| | 100 | s_qs = '?' + s_qs |
| | 101 | return root + base + s_qs |
| | 102 | |
| | 103 | def availableStatus(self): |
| | 104 | return ('UNCONFIRMED', 'NEW', 'ASSIGNED', 'REOPENED', 'RESOLVED', 'VERIFIED', 'CLOSED') |
| | 105 | |
| | 106 | def availableImportance(self): |
| | 107 | return ('Blocker', 'Critical', 'Major', 'Minor', 'Trivial', 'Enhancement') |