diff options
author | Pawel Hajdan, Jr <phajdan.jr@gentoo.org> | 2011-05-21 18:37:53 +0200 |
---|---|---|
committer | Pawel Hajdan, Jr <phajdan.jr@gentoo.org> | 2011-05-21 18:37:53 +0200 |
commit | 32749bc6cb27bbf6da9361583d627a2faef33f51 (patch) | |
tree | 6478091761c4d91487f67ed81ef86fc2f4958107 | |
download | arch-tools-32749bc6cb27bbf6da9361583d627a2faef33f51.tar.gz arch-tools-32749bc6cb27bbf6da9361583d627a2faef33f51.tar.bz2 arch-tools-32749bc6cb27bbf6da9361583d627a2faef33f51.zip |
Initial commit, add a simple batch bugzilla viewer.
-rwxr-xr-x | bugzilla-viewer.py | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/bugzilla-viewer.py b/bugzilla-viewer.py new file mode 100755 index 0000000..bf034cc --- /dev/null +++ b/bugzilla-viewer.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# Copyright 2011 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +import curses.ascii +import itertools +import optparse +import os +import re +import subprocess +import sys +import textwrap +import xml.etree + +import bugz.bugzilla +import portage.versions + +CPV_REGEX = re.compile("[A-Za-z0-9+_.-]+/[A-Za-z0-9+_-]+-[0-9]+(?:\.[0-9]+)*[a-z0-9_]*(?:-r[0-9]+)?") + +class TermTooSmall(Exception): + pass + +# Main class (called with curses.wrapper later). +class MainWindow: + def __init__(self, screen, bugs, bugs_dict, packages_dict, related_bugs, repoman_dict): + self.bugs = bugs + self.bugs_dict = bugs_dict + self.packages_dict = packages_dict + self.related_bugs = related_bugs + self.repoman_dict = repoman_dict + + curses.curs_set(0) + self.screen = screen + + curses.use_default_colors() + self.init_screen() + + c = self.screen.getch() + while c not in ( ord("q"), curses.ascii.ESC): + if c == ord("j"): + self.scroll_bugs_pad(1) + elif c == ord("k"): + self.scroll_bugs_pad(-1) + elif c == curses.KEY_DOWN: + self.scroll_contents_pad(1) + elif c == curses.KEY_UP: + self.scroll_contents_pad(-1) + elif c == curses.KEY_RESIZE: + self.init_screen() + + c = self.screen.getch() + + def init_screen(self): + (self.height, self.width) = self.screen.getmaxyx() + + if self.height < 12 or self.width < 80: + raise TermTooSmall() + + self.screen.border() + self.screen.vline(1, self.width / 3, curses.ACS_VLINE, self.height - 2) + self.screen.refresh() + + self.fill_bugs_pad() + self.refresh_bugs_pad() + + self.fill_contents_pad() + self.refresh_contents_pad() + + def fill_bugs_pad(self): + self.bugs_pad = curses.newpad(len(self.bugs),self.width) + self.bugs_pad.erase() + + self.bugs_pad_pos = 0 + + for i in range(len(self.bugs)): + self.bugs_pad.addstr(i, 0, + " " + self.bugs[i].find('bug_id').text + " " + self.bugs[i].find('short_desc').text) + + def scroll_bugs_pad(self, amount): + height = len(self.bugs) + + self.bugs_pad_pos += amount + if self.bugs_pad_pos < 0: + self.bugs_pad_pos = 0 + if self.bugs_pad_pos >= height: + self.bugs_pad_pos = height - 1 + self.refresh_bugs_pad() + + self.fill_contents_pad() + self.refresh_contents_pad() + + def refresh_bugs_pad(self): + (height, width) = self.bugs_pad.getmaxyx() + for i in range(height): + self.bugs_pad.addch(i, 0, " ") + self.bugs_pad.addch(self.bugs_pad_pos, 0, "*") + pos = min(height - self.height + 2, max(0, self.bugs_pad_pos - (self.height / 2))) + self.bugs_pad.refresh( + pos, 0, + 1, 1, + self.height - 2, self.width / 3 - 1) + + def fill_contents_pad(self): + width = 2 * self.width / 3 + + bug = self.bugs[self.bugs_pad_pos] + + output = [] + output += textwrap.wrap(bug.find("short_desc").text, width=width-2) + output.append("-" * (width - 2)) + + cpvs = self.packages_dict[bug.find("bug_id").text] + if cpvs: + output += textwrap.wrap("Found package cpvs:", width=width-2) + for cpv in cpvs: + output += textwrap.wrap(cpv, width=width-2) + output.append("-" * (width - 2)) + + deps = bug.findall("dependson") + if deps: + output += textwrap.wrap("Depends on:", width=width-2) + for dep in deps: + dep_bug = self.bugs_dict[dep.text] + desc = dep.text + " " + dep_bug.find("bug_status").text + " " + dep_bug.find("short_desc").text + output += textwrap.wrap(desc, width=width-2) + output.append("-" * (width - 2)) + + related = self.related_bugs[bug.find("bug_id").text] + if related: + output += textwrap.wrap("Related bugs:", width=width-2) + for related_bug in related: + if related_bug['bugid'] == bug.find("bug_id").text: + continue + desc = related_bug['bugid'] + " " + related_bug['desc'] + output += textwrap.wrap(desc, width=width-2) + output.append("-" * (width - 2)) + + if bug.find("bug_id").text in repoman_dict and repoman_dict[bug.find("bug_id").text]: + output += textwrap.wrap("Repoman output:", width=width-2) + lines = repoman_dict[bug.find("bug_id").text].split("\n") + for line in lines: + output += textwrap.wrap(line, width=width-2) + output.append("-" * (width - 2)) + + for elem in bug.findall("long_desc"): + output += textwrap.wrap("%s:" % elem.find("who").text, width=width-2) + lines = elem.find("thetext").text.split("\n") + for line in lines: + output += textwrap.wrap(line, width=width-2) + output.append("-" * (width - 2)) + + self.contents_pad_length = len(output) + + self.contents_pad = curses.newpad(max(self.contents_pad_length, self.height), width) + self.contents_pad.erase() + + self.contents_pad_pos = 0 + + for i in range(len(output)): + if type(output[i]) == unicode: + real_output = output[i] + else: + real_output = unicode(output[i], errors='replace') + self.contents_pad.addstr(i, 0, real_output.encode("utf-8")) + + def scroll_contents_pad(self, amount): + height = self.contents_pad_length - self.height + 5 + + self.contents_pad_pos += amount + if self.contents_pad_pos < 0: + self.contents_pad_pos = 0 + if self.contents_pad_pos >= height: + self.contents_pad_pos = height - 1 + self.refresh_contents_pad() + + def refresh_contents_pad(self): + self.contents_pad.refresh( + self.contents_pad_pos, 0, + 1, self.width / 3 + 1, + self.height - 2, self.width - 2) + self.screen.refresh() + +if __name__ == "__main__": + parser = optparse.OptionParser() + parser.add_option("--arch", dest="arch", help="Gentoo arch to use, e.g. x86, amd64, ...") + parser.add_option("--repo", dest="repo", help="Path to portage CVS repository") + parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False, help="Include more output, e.g. related bugs") + + (options, args) = parser.parse_args() + if not options.arch: + parser.error("--arch option is required") + if args: + parser.error("unrecognized command-line args") + + bugzilla = bugz.bugzilla.Bugz('http://bugs.gentoo.org', skip_auth=True) + + print "Searching for arch bugs..." + raw_bugs = bugzilla.search("", cc="%s@gentoo.org" % options.arch, keywords="STABLEREQ") + bugs = bugzilla.get([bug['bugid'] for bug in raw_bugs]).findall("bug") + + dep_bug_ids = [] + + bugs_dict = {} + packages_dict = {} + related_bugs = {} + repoman_dict = {} + for bug in bugs: + print "Processing bug %s: %s" % (bug.find("bug_id").text, bug.find("short_desc").text) + bugs_dict[bug.find("bug_id").text] = bug + packages_dict[bug.find("bug_id").text] = [] + related_bugs[bug.find("bug_id").text] = [] + repoman_dict[bug.find("bug_id").text] = "" + for cpv_candidate in CPV_REGEX.findall(bug.find("short_desc").text): + if portage.db["/"]["porttree"].dbapi.cpv_exists(cpv_candidate): + packages_dict[bug.find("bug_id").text].append(cpv_candidate) + pv = portage.versions.cpv_getkey(cpv_candidate) + if options.verbose: + related_bugs[bug.find("bug_id").text] += bugzilla.search(pv) + + if options.repo: + cvs_path = os.path.join(options.repo, pv) + ebuild_name = portage.versions.catsplit(cpv_candidate)[1] + ".ebuild" + ebuild_path = os.path.join(cvs_path, ebuild_name) + manifest_path = os.path.join(cvs_path, 'Manifest') + if os.path.exists(ebuild_path): + original_contents = open(ebuild_path).read() + manifest_contents = open(manifest_path).read() + try: + output = repoman_dict[bug.find("bug_id").text] + output += subprocess.Popen(["ekeyword", options.arch, ebuild_name], cwd=cvs_path, stdout=subprocess.PIPE).communicate()[0] + subprocess.check_call(["repoman", "manifest"], cwd=cvs_path) + output += subprocess.Popen(["repoman", "full"], cwd=cvs_path, stdout=subprocess.PIPE).communicate()[0] + repoman_dict[bug.find("bug_id").text] = output + finally: + f = open(ebuild_path, "w") + f.write(original_contents) + f.close() + f = open(manifest_path, "w") + f.write(manifest_contents) + f.close() + dep_bug_ids += [dep.text for dep in bug.findall("dependson")] + + dep_bug_ids = list(set(dep_bug_ids)) + dep_bugs = bugzilla.get(dep_bug_ids).findall("bug") + for bug in dep_bugs: + bugs_dict[bug.find("bug_id").text] = bug + + try: + curses.wrapper(MainWindow, bugs=bugs, bugs_dict=bugs_dict, packages_dict=packages_dict, related_bugs=related_bugs, repoman_dict=repoman_dict) + except TermTooSmall: + print "Your terminal window is too small, please try to enlarge it" + sys.exit(1) |