Subversion Repositories Scribus

Compare Revisions

Ignore whitespace Rev 12493 → Rev 12494

/tools/mantisctl/mantisctl.py
18,91 → 18,50
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###
# Changelog:
# Version:
# 0.1: Initial release - bug reporting to Mantis is working.
# 0.2:
# - Added a confirmation message and an option to override it.
# - Fixed a bug in the option parsing method that would not correctly
# return options when script is run with no arguments.
###
 
import re, sys
import mechanize
import logging
import re, sys, logging
from mechanize import Browser
from mechanize import CookieJar
from mechanize import LWPCookieJar
from BeautifulSoup import BeautifulSoup
 
##############################################################################
# CONFIGURATION
# USER CONFIGURATION - edit for your site if necessary. #
##############################################################################
# USER CONFIGURATION - edit for your site.
#username = "xxxxxxxx"
#password = "xxxxxxxx"
baseurl = "http://bugs.scribus.net/main_page.php"
title = "Mantis Issue Tracker for Scribus"
filename = "mantisctl.cookies"
handlers = { '' : 0, 'ale' : 20, 'alexandre' : 362, 'avox' : 569, 'cbradney' : 2,
'christoph_s' : 387, 'deejay1' : 12, 'fschmid' : 3, 'h_a_j_s' : 525,
'Herm' : 1552, 'jghali' : 649, 'jo-hannes' : 114, 'ltning' : 651,
'malex' : 73, 'mhanski' : 72, 'nimda' : 6, 'pierremarchand' : 1260,
'plinnell' : 4, 'psmedley' : 1408, 'rajeev_jsv' : 665, 'ringerc' : 212,
'sjc' : 28, 'subik' : 8, 'Tsoots' : 22 }
basetitle = "^Mantis Issue Tracker"
#baseurl = None
#username = None
#password = None
##############################################################################
# SYSTEM CONFIGURATION - you do not usually need to edit this.
severities = { 'feature' : '10', 'trivial': '20', 'text' : '30', 'tweak' : '40', 'minor'
: '50', 'major' : '60', 'crash' : '70', 'block' : '80'}
##############################################################################
 
class Mantisctl:
"""This class handles Mantis Bug Tracking system data retrieval, bug
report submission, and control tasks."""
"""This class handles command line Mantis Bug Tracking System bug report
submission. a mechanize.Browser() instance is initialized and used to
control the site navigation and bug report submission. dorun is
the only public method you really need to run to submit a bug report."""
def __init__(self):
self.br = Browser()
self.br.set_handle_redirect(True)
self.br.set_handle_refresh(True, honor_time=False)
self.br.set_handle_refresh(True, max_time=30.0, honor_time=False)
self.br.set_handle_referer(True)
(self.opts, self.args) = self._getopts()
assert self._checkbts()
(self.opts, self.args) = self.parseopts()
 
def _askconfirm(self):
print "\nYou will submit a bug report with the following data:\n"
self._printdata()
yes = ['y', 'Y']
var = raw_input("Press 'y' to submit the report or any other key to abort and hit ENTER: ")
print var
if var in yes:
print "Submitting your bug report..."
else:
print "Exiting without submitting the report..."
sys.exit(0)
 
def _checkbts(self):
""" Checks if the URL for the front page could be opened."""
try:
self.br.open(self.baseurl)
if self.br.title() == self.title:
return True
else:
print "Main page does not correspond to the configuration."
sys.exit(1)
except:
print "Cannot open the chosen Mantis Bug Tracker main page."
sys.exit(1)
 
def _usage(self):
def usage(self):
"""Prints out a usage summary and exits."""
print "\n", 78*"#"
print """\n
Usage: mantisctl.py [Options], version = mantisctl.py 0.2
Usage: mantisctl.py [Options], version = mantisctl.py 0.3
 
Options:
--version show program's version number and exit
-h, --help show this help message and exit
ACTION:
-T, --test Test the script
-L, --list List the Mantis options
-r, --report Report a new bug
-h, --help show this help message and exit
 
OPTIONS:
--version show program's version number and exit
-n, --noconfirm Do not ask for confirmation before submitting a report
-b BASEURL, --baseurl=BASEURL
Base URL for a Mantis BTS instance [optional if you set
it in the script configuration section]. [default: None]
-u USERNAME, --username=USERNAME
User name [optional if you set it in the script
itself]. [default: None]
126,34 → 85,19
"""
sys.exit(1)
 
def _getopts(self):
def parseopts(self):
"""Parses options and arguments passed to the script
and returns a tuple of a dictionary (opts) and a list (args)."""
from optparse import OptionParser
"""This procedure parses options and arguments passed to the program
and stores them in a dictionary (opts) and a list (args)"""
try:
self.title = title
except:
sys.exit("You must set the title of the base Mantis BTS page in the configuration section of this script.")
try:
self.baseurl = baseurl
except:
sys.exit("You must set base URL for Mantis BTS in the configuration section of this script.")
try:
self.username = username
except:
self.username = "None"
try:
self.password = password
except:
self.password = "None"
usage = "%prog [Options], version = %prog 0.2"
op = OptionParser(usage=usage, version='%prog 0.2')
usage = "%prog [Options], version = %prog 0.3"
op = OptionParser(usage=usage, version='%prog 0.3')
op.add_option("-T", "--test", action="store_true", dest="test", default=False, help="Test the script")
op.add_option("-L", "--list", action="store_true", dest="list", default=False, help="List the Mantis options")
op.add_option("-r", "--report", action="store_true", dest="report", default=False, help="Report a new bug")
op.add_option("-n", "--noconfirm", action="store_true", dest="noconfirm", default=False, help="Do not ask for confirmation before submitting a report")
op.add_option("-u", "--username", dest="username", default=self.username, help="User name [optional if you set it in the script itself]. [default: %default]")
op.add_option("-w", "--password", dest="password", default=self.password, help="User password [optional if you set it in the script itself]. [default: %default]")
op.add_option("-b", "--baseurl", dest="baseurl", default=baseurl, help="Base URL for a Mantis BTS instance [optional if you set it in the script configuration section]. [default: %default]")
op.add_option("-u", "--username", dest="username", default=username, help="User name [optional if you set it in the script itself]. [default: %default]")
op.add_option("-w", "--password", dest="password", default=password, help="User password [optional if you set it in the script itself]. [default: %default]")
op.add_option("-a", "--assignto", dest="assignto", help="Assign to [optional]")
op.add_option("-c", "--category", dest="category", help="Bug category <mandatory>")
op.add_option("-y", "--severity", dest="severity", help="Bug severity <mandatory>")
163,189 → 107,286
op.add_option("-t", "--target", dest="tversion", help="Target version [optional]")
return op.parse_args()
 
def _printcookies(self):
for i in self.cj:
print i
def _getbaseurl(self):
"""Returns the Base URL for a Mantis BTS instance from options or
configuration. Will exit if basurl is not provided either as an option
or in the confguration section."""
if not self.opts.baseurl:
try:
url = baseurl
except:
sys.exit("Base URL is not provided either as an option or in the confguration section.")
else:
url = self.opts.baseurl
return baseurl
 
def _setupcookies(self):
policy = mechanize.DefaultCookiePolicy(rfc2965=True)
cj = LWPCookieJar(policy=policy)
self.br.set_cookiejar(cj)
self.cj = cj
def checkbts(self):
"""Checks if the URL for the Mantis BTS instance front page could be
opened and if the title of main page corresponds to the configuration
option 'title'."""
# Get the base url from options or configuration section
url = self._getbaseurl()
try:
self.br.open(url)
mainpagetitle = self.br.title()
except:
print "Cannot open a Mantis BTS instance main page."
sys.exit(1)
# basetitle is defined in the configuration section
if re.search(basetitle, mainpagetitle):
return True
else:
print "This doesn't seem to be a Mantis BTS instance main page."
sys.exit(1)
 
def _savecookies(self, filename):
self.cj.save(filename, ignore_discard = True, ignore_expires = True)
def askconfirm(self):
"""Asks for a confirmation to submit a bug report. Exits without
submitting a bug report if 'y' is not provided as an answer."""
msg = "\nYou are about to submit a bug report with the following data:\n"
self.printdata(msg)
yes = ['y', 'Y']
var = raw_input("Press 'y' to submit the report or any other key to abort and hit ENTER: ")
print var
if var in yes:
print "Submitting your bug report..."
else:
print "Exiting without submitting the report..."
sys.exit(0)
 
def _loadcookies(self, filename):
self.cj.load(filename, ignore_discard = True, ignore_expires = True)
def _printwarning(self, msg, list):
print "\nYou must specify a %s from the following list: \n" % (msg)
print list
sys.exit(1)
 
def _dologin(self):
def printdata(self, msg):
"""Prints the bug report data either for verification before and after
submission."""
print msg
print "\tCategory: \t\t%s" % self.category
print "\tSeverity: \t\t%s" % self.opts.severity
print "\tAssigned To: \t\t%s" % self.username
print "\tProduct Version: \t%s" % self.pversion
print "\tTarget Version: \t%s" % self.tversion
print "\tSummary: \t\t%s" % self.summary
print "\tDescription: \t\t%s" % self.description
 
def _checkcredentials(self):
if self.opts.username:
self.username = self.opts.username
elif username:
self.username = username
else:
print "\nUsername is not specified either in the script configuration or on the command line."
self._usage()
if self.opts.password:
self.password = self.opts.password
elif password:
self.password = password
else:
print "\nPassword is not specified either in the script or on the command line."
self._usage()
 
def login(self):
"""Performs logging into a Mantis BTS instance as an authenticated
reporter."""
# Check if we have the credentials to login into a Mantis BTS instance.
self._checkcredentials()
self.loginlink = self.br.links(text_regex=re.compile("Login")).next()
self.br.follow_link(self.loginlink)
self.br.select_form(name="login_form")
self.br.form["username"] = self.opts.username
self.br.form["password"] = self.opts.password
self.br.form["username"] = self.username
self.br.form["password"] = self.password
self.br.submit()
 
def _login(self):
self.filename = filename
try:
self._loadcookies(self.filename)
if not self.cj.as_lwp_str():
self._dologin()
except IOError:
self._dologin()
self.cj.save(self.filename)
def _gethandlers(self, response):
"""Returns a dictionary of 'Assign To' names and ids directly from the bug report page."""
response.seek(0)
handlersoup = BeautifulSoup(response)
selects = handlersoup.findAll('select', { "name" : "handler_id" })[0].findAll("option")
handlers = {}
for select in selects:
handler_id = select.attrs[0][1].strip().encode('utf8')
if len (select.contents) > 0:
handler_login = select.contents[0].strip().encode('utf8')
else:
handler_login = ""
handlers[handler_login] = handler_id
return handlers
 
def _getmantisopts(self):
self.handlers = handlers
self.severities = severities
self.category_control = self.br.form.find_control("category")
self.category_list = []
for i in self.category_control.items:
self.category_list.append(i.name)
self.severity_control = self.br.form.find_control("severity")
self.severity_list = []
for i in self.severity_control.items:
self.severity_list.append(i.name)
self.handler_control = self.br.form.find_control("handler_id")
self.handler_list = []
for i in self.handler_control.items:
self.handler_list.append(int(i.name))
self.pversion_control = self.br.form.find_control("product_version")
self.pversion_list = []
for i in self.pversion_control.items:
self.pversion_list.append(i.name)
self.tversion_control = self.br.form.find_control("target_version")
self.tversion_list = []
for i in self.tversion_control.items:
self.tversion_list.append(i.name)
def _getseverities(self, response):
"""Returns a dictionary of 'Severity' names and ids directly from the bug report page."""
response.seek(0)
severitysoup = BeautifulSoup(response)
selects = severitysoup.findAll('select', { "name" : "severity" })[0].findAll("option")
severities = {}
for select in selects:
severity_id = select.attrs[0][1].strip().encode('utf8')
if len (select.contents) > 0:
severity_name = select.contents[0].strip().encode('utf8')
else:
severity_name = ""
severities[severity_name] = severity_id
return severities
 
def _checkopts(self):
def _getcategories(self):
"""Returns a list of bug categories directly from the bug reporting page."""
category_control = self.br.form.find_control("category")
category_list = []
for i in category_control.items:
category_list.append(i.name)
return category_list
 
def _getpversions(self):
"""Returns a list of product versions directly from the bug reporting page."""
pversion_control = self.br.form.find_control("product_version")
pversion_list = []
for i in pversion_control.items:
pversion_list.append(i.name)
return pversion_list
 
def _gettversions(self):
"""Returns a list of target versions directly from the bug reporting page."""
tversion_control = self.br.form.find_control("target_version")
tversion_list = []
for i in tversion_control.items:
tversion_list.append(i.name)
return tversion_list
 
def setreportdata(self):
"""Handles filling out the bug report form."""
reportlink = self.br.links(text_regex=re.compile("Report Issue")).next()
response = self.br.follow_link(reportlink)
self.handlers = self._gethandlers(response)
self.severities = self._getseverities(response)
self.br.select_form(name="report_bug_form")
self.categories = self._getcategories()
self.pversions = self._getpversions()
self.tversions = self._gettversions()
 
def checkreportopts(self):
"""Verifies the validity of options passed to the script."""
# summary
if not self.opts.summary:
sys.exit("You must provide a bug summary!")
if not self.opts.description:
self.opts.description = self.opts.summary
elif len(self.opts.description.strip()) < 1:
self.opts.description = self.opts.summary
for i in self.handler_list:
if i not in self.handlers.values():
sys.exit("Developer list at the top of this script is not complete.")
else:
self.summary = self.opts.summary
# description
if self.opts.description and len(self.opts.description.strip()) > 1:
self.description = self.opts.description
else:
self.description = self.opts.summary
# handler
if self.opts.assignto and len(self.opts.assignto) > 0:
if self.opts.assignto not in self.handlers.keys():
print "You must specify a developer to assign bug to from the following list: \n", self.handlers.keys()
sys.exit(1)
self._printwarning("developer to assign bug to", self.handlers.keys())
else:
self.handler = str(self.handlers[self.opts.assignto])
else:
self.handler = '0'
# category
if not self.opts.category:
print "You must specify a category from the following list: \n", self.category_list
sys.exit(1)
self._printwarning("category", self.categories)
else:
if self.opts.category in self.category_list:
pass
if self.opts.category in self.categories:
self.category = self.opts.category
else:
print "Your category is not in the list: \n", self.category_list
sys.exit(1)
for i in self.severity_list:
if i not in self.severities.values():
sys.exit("Severity list at the top of this script is not complete.")
if not self.opts.severity:
print "You must specify a severity from the following list: \n", self.severities.keys()
sys.exit(1)
else:
self._printwarning("category", self.categories)
# severity
if self.opts.severity and len(self.opts.severity) > 0:
if self.opts.severity in self.severities.keys():
self.severity = self.severities[self.opts.severity]
self.severity = str(self.severities[self.opts.severity])
else:
print "Your severity is not in the list: \n", self.severities.keys()
sys.exit(1)
self._printwarning("severity", self.severities.keys())
# product version
if not self.opts.pversion:
print "You must specify a product version from the following list: \n", self.pversion_list
sys.exit(1)
self._printwarning("product version", self.pversions)
else:
if self.opts.pversion in self.pversion_list:
pass
if self.opts.pversion in self.pversions:
self.pversion = self.opts.pversion
else:
print "Your product version is not in the list: \n", self.pversion_list
sys.exit(1)
if self.opts.tversion:
if self.opts.tversion in self.tversion_list:
pass
self._printwarning("product version", self.pversions)
# target version
if not self.opts.tversion:
self._printwarning("target version", self.tversions)
else:
if self.opts.tversion in self.tversions:
self.tversion = self.opts.tversion
else:
print "Your target version is not in the list: \n", self.tversion_list
sys.exit(1)
self._printwarning("target version", self.tversions)
 
def _setupreportform(self):
self._getmantisopts()
if self.opts.list:
print "Severities: \n", "\t", self.severities.keys(), "\n"
print "Categories: \n", "\t", self.category_list, "\n"
print "Developers: \n", "\t", self.handlers.keys(), "\n"
print "Product Versions: \n", "\t", self.pversion_list, "\n"
sys.exit(0)
self._checkopts()
self.br.form["category"] = [self.opts.category]
def filloutreportform(self):
"""Fills out required controls on the bug reporting form."""
self.br.form["category"] = [self.category]
self.br.form["severity"] = [self.severity]
self.br.form["handler_id"] = [self.handler]
self.br.form["product_version"] = [self.opts.pversion]
if self.opts.tversion:
self.br.form["target_version"] = [self.opts.tversion]
self.br.form["summary"] = self.opts.summary
self.br.form["description"] = self.opts.description
self.br.form["product_version"] = [self.pversion]
if self.tversion:
self.br.form["target_version"] = [self.tversion]
self.br.form["summary"] = self.summary
self.br.form["description"] = self.description
 
def reportbug(self):
reportlink = self.br.links(text_regex=re.compile("Report Issue")).next()
self.br.follow_link(reportlink)
self.br.select_form(name="report_bug_form")
self._setupreportform()
 
def _printdata(self):
print "\tCategory: \t\t%s" % self.opts.category
print "\tSeverity: \t\t%s" % self.opts.severity
print "\tAssigned To: \t\t%s" % self.opts.assignto
print "\tProduct Version: \t%s" % self.opts.pversion
print "\tTarget Version: \t%s" % self.opts.tversion
print "\tSummary: \t\t%s" % self.opts.summary
print "\tDescription: \t\t%s" % self.opts.description
 
def doreportbug(self):
self.reportbug()
"""Handles submission of a bug report."""
self.setreportdata()
self.checkreportopts()
# depends on self.checkreportopts as it sets all self.var(s)
self.filloutreportform()
if not self.opts.noconfirm:
self._askconfirm()
self.askconfirm()
try:
self.br.submit()
except:
pass
self.br.close()
print "\nBug report submission appears to be successful! The following data have been submitted:\n"
self._printdata()
msg = "\nBug report submission appears to be successful! The following data have been submitted:\n"
self.printdata(msg)
sys.exit(0)
 
def _checkcredentials(self):
if self.opts.username == 'None':
print "\nUsername is not specified either in the script configuration or on the command line."
self._usage()
if self.opts.password == 'None':
print "\nPassword is not specified either in the script or on the command line."
self._usage()
def listopts(self):
"""Lists options passed to the script and those obtained directly from
a Mantis BTS instance."""
self.setreportdata()
self.checkreportopts()
print "Severities: \n", "\t", self.severities.keys(), "\n"
print "Categories: \n", "\t", self.categories, "\n"
print "Developers: \n", "\t", self.handlers.keys(), "\n"
print "Product Versions: \n", "\t", self.pversions, "\n"
 
def dorun(self):
self._checkcredentials()
self._setupcookies()
self._login()
def runchoice(self):
"""Process a run choice such as list options, test the script, show
help, or submit a bug report or print usage summary and exit if no
action has been specified."""
if self.opts.list:
print "Listing the Mantis options...\n"
self.listopts()
elif self.opts.test:
print "\nTesting the script...\n",
self.setreportdata()
self.checkreportopts()
print "\nTest run completed without errors.\n"
sys.exit(0)
elif self.opts.report:
print "\nSubmitting a new bug report.\n"
self.reportbug()
if self.opts.test:
print "Testing the script...",
self.reportbug()
print "everything seems to be fine."
sys.exit(0)
if self.opts.report:
self.doreportbug()
else:
print """\nNo action such as -r (submit a bug report), -L (list
options), or -T (test run) has been specified. Exiting...\n"""
self._usage()
 
def dorun(self):
"""Main controller of the script. It will be called if the script is
run from a command line or can be run by an external script. No other
interface is available."""
# Check if we can access the main page of a Mantis BTS instance.
self.checkbts()
# Login into a Mantis BTS instance.
self.login()
# Select and run an action.
self.runchoice()
 
if __name__=='__main__':
"""Check if the script is run from a command line, initialize the main
class and either show usage summary or run the main controller method."""
if len(sys.argv) == 1:
sys.argv.append("--help")
browser = Mantisctl()