#!/usr/bin/env python # # Copyright 2008 Rakesh Pandit # Addions by Brennan Ashton # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with translate; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys import os import time # For debugging pickly result dictionary to file as soon as things break # Need to remove it later, once script is stable import pickle import datetime import xmlrpclib from optparse import OptionParser from bugzilla import Bugzilla from ConfigParser import ConfigParser def checkdateInBetween(date, start_date, end_date): """ """ date_string = date.__str__() date = datetime.datetime(*(time.strptime(date_string, "%Y%m%dT%H:%M:%S")[0:5])) if date >= start_date and date < end_date: return True return False def getDateTime(date_string): """ """ try: return datetime.datetime(*time.strptime(date_string, "%d/%m/%Y")[0:5]) except ValueError: print "Error: Date string", date_string, "is wrong. Please check." exit(1) def getBugInfo(bugzilla,bug_id,flag,start,end): """ """ result = {} bug_info = bugzilla._proxy.Bug.get({'ids':[bug_id]}) result['summary'] = bug_info['bugs'][0]['internals']['short_desc'] if flag == "new": result['who'] = bug_info['bugs'][0]['internals']['reporter'] result['date'] = True else: history = bugzilla._proxy.Bug.get_history({'ids':[bug_id]}) dict_changes = history['bugs'][0][str(bug_id)] for record in dict_changes: when = record['when'] who = record['who'] for each_change in record['changes']: added = each_change['added'] if flag in added: result['who'] = who result['date'] = checkdateInBetween(when, start, end) return result return result def getReportDict(bz,bug_list,flag,start,end): """ """ result = {} rr = 0 mr = 0 try: try: if verbose: print "Computing data for completed reviews against reviewers." number = 1 total_number = len(bug_list) for bug in bug_list: if verbose: print "Getting bug number:", number," report -", round((float(number)*100.0/float(total_number)), 3), " %" number = number + 1 report_data = [] return_data = getBugInfo(bz,bug.bug_id,flag,start,end) if return_data['date']: if "Review Request" in return_data['summary']: rr = rr + 1 elif "Merge Review" in return_data['summary']: mr = mr + 1 if (result.has_key(return_data['who'])): result[return_data['who']] = result[return_data['who']] + 1 else: result[return_data['who']] = 1 print str(return_data['who']) + ' - ' + str(bug.bug_id) print "Results in a dictionary:" return result, rr, mr except KeyboardInterrupt: print print "Interrupting ...." if len(result.keys()) == 0: print "No computation done.. Exiting." exit(0) print "Results till now in a dictionary:" return result, rr, mr except: print print "Unexpected error:", sys.exc_info()[0] print "Results till now in a dictionary:" return result, rr, mr usage = "usage: %prog [options]" parser = OptionParser(usage=usage) # Verbose progress parser.add_option("-v", action="store_true", dest="verbose", default=False, \ help="Make a lot of noise [false by default]") # Report parser.add_option("-r", "--report", action="store", dest="report", default=False, \ help="Values can be - 'rev-com' - Review complete (against reviewer), 'rev-incom' - Review incomplete - (against reviewer), \ 'cvs-com' - CVS complete (against reviewer), 'cvs-incom' - CVS incomplete - (against reviewer), \ 'new' - New packages - (against reporter)") # Bugzilla username parser.add_option("-u", "--username", action="store", dest="username", \ help="Username for Bugzilla authentication.") # Bugzilla password parser.add_option("-p", "--password", action="store", dest="password", \ help="Password for Bugzilla authentication.") # Start date parser.add_option("-s", "--start", action="store", dest="start_date", \ help="Start date for report. Format: d/m/Y") # End date parser.add_option("-e", "--end", action="store", dest="end_date", \ help="End date for report generation. Format: d/m/Y") options = {} args = [] (options, args) = parser.parse_args() username = options.username password = options.password verbose = options.verbose report = options.report if not report or (report not in ['rev-com', 'rev-incom', 'cvs-com', 'cvs-incom', 'new']): print "Check ./bugzillaReport -h or --help for details." print "Error: Either wrong report arg value or not supplied." print "Values can be: 'rev-com', 'rev-incom', 'new'" exit(1) if not options.start_date and options.end_date: print "Error: No start date specified." exit(1) # Decide on timespan for report if options.start_date and not options.end_date: start = getDateTime(options.start_date) end = datetime.datetime.now() difference = end - start print "Report for ", difference.days, " days." elif not options.start_date and not options.end_date: end = datetime.datetime.now() # Default: 1 week timespan interval = datetime.timedelta(seconds=0, minutes=0, hours=0, days=7) start = end - interval print "Report for ", interval.days, " days." elif options.start_date and options.end_date: start = getDateTime(options.start_date) end = getDateTime(options.end_date) difference = end - start print "Report for ", difference.days, " days." # Find bugzilla username and password if not (username and password): try: conf_file = open('bugzilla.config') except IOError: print "Neither username and password supplied" print "nor conf file for bugzilla username and password." print "Make a conf file if you want with name 'bugzilla.config'" print ", with following contents:" print "[Details]" print "username=" exit(1) # Using config file cf = ConfigParser() try: cf.readfp(conf_file) except NameError: print "Neither username and password supplied" print "nor conf file for bugzilla username and password." print ", with following contents:" print "[Details]" print "username=" exit(1) username = cf.get("Details",'username') password = cf.get("Details",'password') # Get bug list bug_list = [] url = 'https://bugzilla.redhat.com/xmlrpc.cgi' query_dict = {'product':'Fedora', 'component':'Package Review', 'chfieldfrom':str(start)} q_new = {'bug_status':'NEW'} q_new.update(query_dict) if verbose: print "Bugzilla Query: ", q_new q_revq = {'field0-0-0':'flagtypes.name','type0-0-0':'equals','value0-0-0':'fedora-review?'} q_revq.update(query_dict) q_revp = {'field0-0-0':'flagtypes.name','type0-0-0':'equals','value0-0-0':'fedora-review+'} q_revp.update(query_dict) q_cvsq = {'field0-0-0':'flagtypes.name','type0-0-0':'equals','value0-0-0':'fedora-cvs?'} q_cvsq.update(query_dict) q_cvsp = {'field0-0-0':'flagtypes.name','type0-0-0':'equals','value0-0-0':'fedora-cvs+'} q_cvsp.update(query_dict) bz = Bugzilla(url=url, username=username, password=password) if verbose: print "Getting all package review bugs (be patient, this may take a while) ...." if report == 'new': flag = 'new' bug_list = bz.query(q_new) elif report == 'rev-com': flag = 'fedora-review+' bug_list = bz.query(q_revp) elif report == 'rev-incom': flag = 'fedora-review?' bug_list = bz.query(q_revq) elif report == 'cvs-com': flag = 'fedora-cvs+' bug_list = bz.query(q_cvsp) elif report == 'cvs-incom': flag = 'fedora-cvs?' bug_list = bz.query(q_cvsq) if verbose: print "Total number of Package Reviews:", len(bug_list) result = {} rr = 0 mr = 0 # Result will have name as keys and number of bugs as value. result, rr, mr = getReportDict(bz,bug_list,flag,start,end) print result print "Merge Reviews: ", mr print "Review Requests: ", rr print "Calculated results are in file output.txt" # pickle to a file for debugging, need to reomve it later # once script is stable file_pickle = open("pickle.pck", "w") pickle.dump(result, file_pickle) list_of_names = result.keys() # Fox comparing based on value of result def cmp(x, y): if result[x] == result[y]: return 0 elif result[x] > result[y]: return -1 else: return 1 # To sort based on alphabets list_of_names.sort() # To sort based on names list_of_names.sort(cmp) file = open('output.txt', 'w') file.write("Start Date: " + str(start) + "\n") file.write("End Date: " + str(end) + "\n") file.write('\n') # Variable to hold total number of reviews total_reviews = 0 for i in range(0, list_of_names.__len__()): file.write(list_of_names[i].encode('utf-8') + " - " + str(result[list_of_names[i]])) file.write('\n') total_reviews = total_reviews + result[list_of_names[i]] file.write("\nTotal reviews modified: " + str(total_reviews)) file.write("\nMerge Reviews: " + str(mr)) file.write("\nReview Requests: " + str(rr)) file.write("\n This report by generated by %s, the source can be found at %s. Please submit patches or bug reports using the issue tracker at %s.\n" % (os.path.basename(sys.argv[0]), "https://fedorahosted.org/triage/browser/scripts/bzReviewReport.py", "https://fedorahosted.org/triage/") ) file.close() # File ends here