summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Woods <wwoods@redhat.com>2007-09-06 18:26:57 -0400
committerWill Woods <wwoods@redhat.com>2007-09-06 18:26:57 -0400
commit9ee1856c171867976b7a7b987baa62b24d14fd5c (patch)
treeb3dbbae8f7491b7f38bb570dacb7e6fde16f36c1
parentda6ca0d553e0c3dd3da698e46b215df23369eee2 (diff)
downloadpython-bugzilla-9ee1856c171867976b7a7b987baa62b24d14fd5c.tar.gz
python-bugzilla-9ee1856c171867976b7a7b987baa62b24d14fd5c.tar.xz
python-bugzilla-9ee1856c171867976b7a7b987baa62b24d14fd5c.zip
add Bug object, which has methods for modifying bugs
-rw-r--r--bugzilla.py124
1 files changed, 112 insertions, 12 deletions
diff --git a/bugzilla.py b/bugzilla.py
index 05215a3..6326649 100644
--- a/bugzilla.py
+++ b/bugzilla.py
@@ -102,9 +102,9 @@ class Bugzilla(object):
# Set querydata and querydefaults as properties so they auto-create
# themselves when touched by a user. This bit was lifted from YumBase,
# because skvidal is much smarter than I am.
- querydata = property(fget=lambda self: self.__get_queryinfo()[0],
+ querydata = property(fget=lambda self: self._get_queryinfo()[0],
fdel=lambda self: setattr(self,"_querydata",None))
- querydefaults = property(fget=lambda self: self.__get_queryinfo()[1],
+ querydefaults = property(fget=lambda self: self._get_queryinfo()[1],
fdel=lambda self: setattr(self,"_querydefaults",None))
def getproducts(self,force_refresh=False):
@@ -127,12 +127,20 @@ class Bugzilla(object):
return self._components[product]
# TODO - add a .components property that acts like a dict?
- #---- Methods for reading bugs
+ #---- Methods for reading bugs and bug info
- def getbug(self,id):
+ # Return raw dicts
+ def _getbug(self,id):
'''Return a dict of full bug info for the given bug id'''
return self._proxy.bugzilla.getBug(id, self.user, self.password)
+ def _getbugsimple(self,id):
+ '''Return a short dict of simple bug info for the given bug id'''
+ return self._proxy.bugzilla.getBugSimple(id, self.user, self.password)
+ # TODO return a Bug object instead
+ def getbug(self,id):
+ '''Return a dict of full bug info for the given bug id'''
+ return self._proxy.bugzilla.getBug(id, self.user, self.password)
def getbugsimple(self,id):
'''Return a short dict of simple bug info for the given bug id'''
return self._proxy.bugzilla.getBugSimple(id, self.user, self.password)
@@ -154,10 +162,18 @@ class Bugzilla(object):
'''
return self._proxy.bugzilla.runQuery(query,self.user,self.password)
- def getwhiteboard(self,id,which):
- '''Get the current value of the whiteboard specified by 'which' on bug
- with the the given id.'''
- raise NotImplementedError
+ def query_comments(self,product,version,component,string,matchtype='allwordssubstr'):
+ '''Convenience method - query for bugs filed against the given
+ product, version, and component whose comments match the given string.
+ matchtype specifies the type of match to be done. matchtype may be
+ any of the types listed in querydefaults['long_desc_type_list'], e.g.:
+ ['allwordssubstr','anywordssubstr','substring','casesubstring',
+ 'allwords','anywords','regexp','notregexp']
+ Return value is the same as with query().
+ '''
+ q = {'product':product,'version':version,'component':component,
+ 'long_desc':string,'long_desc_type':matchtype}
+ return self.query(q)
#---- Methods for modifying existing bugs
@@ -178,9 +194,6 @@ class Bugzilla(object):
def closebug(self,id):
raise NotImplementedError
-
- def setwhiteboard(self,id,type,data):
- raise NotImplmementedError
def setassignee(self,id,assignee):
raise NotImplementedError
@@ -191,6 +204,13 @@ class Bugzilla(object):
def updatecc(self,id,cclist):
raise NotImplementedError
+ def _updatewhiteboard(self,id,text,which,action):
+ '''Update the whiteboard given by 'which' for the given bug.
+ performs the given action (which may be 'append',' prepend', or
+ 'overwrite') using the given text.'''
+ data = {'type':which,'text':text,'action':action}
+ self._proxy.bugzilla.updateWhiteboard(id,data,self.user,self.password)
+
#---- Methods for working with attachments
def __attachment_encode(self,fh):
@@ -265,7 +285,6 @@ class Bugzilla(object):
'''Create a bug with the given info. Returns the bug ID.'''
raise NotImplementedError
- # TODO: allow simple 'tagging' by adding/removing text to whiteboard(s)
# TODO: flag handling?
class CookieTransport(xmlrpclib.Transport):
@@ -340,3 +359,84 @@ class SafeCookieTransport(xmlrpclib.SafeTransport,CookieTransport):
'''SafeTransport subclass that supports cookies.'''
scheme = 'https'
request = CookieTransport.request
+
+class Bug(object):
+ '''A container object for a bug report. Requires a Bugzilla instance -
+ every Bug is on a Bugzilla, obviously.
+ Optional keyword args:
+ dict=DICT - populate attributes with the result of a getBug() call
+ bug_id=ID - if dict does not contain bug_id, this is required before
+ you can read any attributes or make modifications to this
+ bug.
+ '''
+ def __init__(self,bugzilla,**kwargs):
+ self.bugzilla = bugzilla
+ if 'dict' in kwargs:
+ self.__dict__.update(kwargs['dict'])
+ if 'bug_id' in kwargs:
+ setattr(self,'bug_id',kwargs['bug_id'])
+
+ def __getattr__(self,name):
+ if name not in ('__members__','__methods__','trait_names',
+ '_getAttributeNames') and not name.endswith(')'):
+ # FIXME: that .endswith hack is an extremely stupid way to figure
+ # out if we're checking on a method call. Find a smarter one!
+ r = self.bugzilla._getbug(self.bug_id)
+ self.__dict__.update(r)
+ if name in self.__dict__:
+ return self.__dict__[name]
+ raise AttributeError
+
+ def refreshattr(self,name):
+ delattr(self,name)
+ r = self.bugzilla.query({'bug_id':self.bug_id,'column_list':[name]})
+ self.__dict__.update(r['bugs'][0])
+ return self.__dict__[name]
+
+ def getwhiteboard(self,which='status'):
+ '''Get the current value of the whiteboard specified by 'which'.
+ Known whiteboard names: 'status','internal','devel','qa'.
+ Defaults to the 'status' whiteboard.
+ '''
+ return getattr(self,"%s_whiteboard" % which)
+ def _dowhiteboard(self,text,which,action):
+ '''Actually does the updateWhiteboard call to perform the given action
+ (append,prepend,overwrite) with the given text on the given whiteboard
+ for the given bug.'''
+ self.bugzilla._updatewhiteboard(self.bug_id,text,which,action)
+ # NOTE: right now we don't get to the refreshattr here, because the
+ # server is throwing an XMLRPC fault on _updatewhiteboard.
+ # It still *works* - you can manually refresh and see the change - but
+ # for some reason it's complaining. Might be a bugzilla bug.
+ self.refreshattr("%s_whiteboard" % which)
+ def appendwhiteboard(self,text,which='status'):
+ '''Append the given text (with a space before it) to the given
+ whiteboard. Defaults to using status_whiteboard.'''
+ self._dowhiteboard(text,which,'append')
+ def prependwhiteboard(self,text,which='status'):
+ '''Prepend the given text (with a space following it) to the given
+ whiteboard. Defaults to using status_whiteboard.'''
+ self._dowhiteboard(text,which,'prepend')
+ def setwhiteboard(self,text,which='status'):
+ '''Overwrites the contents of the given whiteboard with the given text.
+ Defaults to using status_whiteboard.'''
+ self._dowhiteboard(text,which,'overwrite')
+ def addtag(self,tag,which='status'):
+ '''Adds the given tag to the given bug.'''
+ whiteboard = self.getwhiteboard(which)
+ if whiteboard:
+ self.appendwhiteboard(tag,which)
+ else:
+ self.setwhiteboard(tag,which)
+ def tags(self,which='status'):
+ '''Get a list of tags (basically just whitespace-split the given
+ whiteboard)'''
+ return self.getwhiteboard(which).split()
+ def deltag(self,tag,which='status'):
+ '''Removes the given tag from the given bug.'''
+ tags = self.gettags(which)
+ tags.remove(tag)
+ self.setwhiteboard(' '.join(tags),which)
+
+# TODO: add a sync() method that writes the changed data in the Bug object
+# back to Bugzilla. Someday.