Changeset 01d7b6e898bf…
Parent 3f812d376aa5…
by Tyler G. Hicks-Wright <tghw@fogcreek.com>
Changes to 3 files · Browse files at 01d7b6e898bf Showing diff from parent 3f812d376aa5 Diff from another changeset...
|
|
|
@@ -1,0 +1,33 @@ + Python FogBugz API Wrapper
+
+This Python API is simply a wrapper around the FogBugz API, with some help from Leonard Richardson's BeautifulSoup (http://www.crummy.com/software/BeautifulSoup/) and the magic of Python's __getattr__().
+
+A quick example:
+
+>>> from fogbugz import FogBugz
+>>> fb = FogBugz("http://example.fogbugz.com/") # URL is to your FogBugz install
+>>> fb.logon("logon@example.com", "password")
+>>> resp = fb.search(q="assignedto:tyler") # All calls take named parameters, per the API
+>>> resp # Responses are BeautifulSoup objects of the response XML
+<response><cases count="2"><case ixbug="1" operations="edit,assign,resolve,email,remind"></case><case ixbug="2" operations="edit,spam,assign,resolve,reply,forward,remind"></case></cases></response>
+>>> for case in resp.cases.childGenerator(): # One way to access the cases
+... print case['ixbug']
+...
+1
+2
+>>> for case in resp.findAll('case'):
+... print case['operations']
+...
+edit,assign,resolve,email,remind
+edit,spam,assign,resolve,reply,forward,remind
+>>> resp = fb.edit(ixbug=1, sEvent="Edit from the API")
+>>> resp
+<response><case ixbug="1" operations="edit,assign,resolve,email,remind"></case></response>
+
+For more info on the API
+http://our.fogbugz.com/help/topics/advanced/API.html
+
+Known Issues:
+* File uploading is currently unsupported for both cases and emails.
+
+Much of the API has not been thoroughly tested. Please assign bugs to Tyler Hicks-Wright.
\ No newline at end of file |
+ import urllib
import urllib2
from BeautifulSoup import BeautifulSoup
class FogBugzAPIError(Exception):
- def __init__(self, response):
- self.response = response
- self.message = response.error.string
- self.error_code = int(response.error['code'])
+ pass
class FogBugzLogonError(FogBugzAPIError):
pass
-class FogBugzConnectionError(Exception):
+class FogBugzConnectionError(FogBugzAPIError):
pass
class FogBugz:
def __init__(self, url):
self.__handlerCache = {}
if not url.endswith('/'):
url += '/'
self._token = None
self._opener = urllib2.build_opener()
try:
soup = BeautifulSoup(self._opener.open(url + 'api.xml'))
except URLError:
raise FogBugzConnectionError("Library could not connect to the FogBugz API. Either this installation of FogBugz does not support the API, or the url, %s, is incorrect." % (self._url,))
self._url = url + soup.response.url.string
self.currentFilter = None
def logon(self, username, password):
"""
Logs the user on to FogBugz.
Returns None for a successful login, otherwise returns a list
of logins to choose from if the username provided is
ambiguous.
"""
if self._token:
logoff()
try:
response = self.__makerequest('logon', email=username, password=password)
except FogBugzAPIError, e:
raise FogBugzLogonError(e)
self._token = response.token.string
def logoff(self):
"""
Logs off the current user.
"""
self.__makerequest('logoff')
self._token = None
def __makerequest(self, cmd, **kwargs):
- data = 'cmd=%s' % (cmd,)
- for k in kwargs.keys():
- data += '&%s=%s' % (k, kwargs[k],)
+ kwargs["cmd"] = cmd
if self._token:
- data += '&token=%s' % (self._token,)
+ kwargs["token"] = self._token
try:
- response = BeautifulSoup(self._opener.open(self._url+data)).response
+ response = BeautifulSoup(self._opener.open(self._url+urllib.urlencode(kwargs))).response
except urllib2.URLError, e:
raise FogBugzConnectionError(e)
if response.error:
- print response
- raise FogBugzAPIError(response)
- # TODO: Remove print for Release
- print response
+ raise FogBugzAPIError('Error Code %s: %s' % (response.error['code'], response.error.string,))
return response
def __getattr__(self, name):
"""
Handle all FogBugz API calls.
>>> fb.logon(email@example.com, password)
>>> response = fb.search(q="assignedto:email")
"""
# Let's leave the private stuff to Python
if name.startswith("__"):
raise AttributeError("No such attribute '%s'" % name)
if not self.__handlerCache.has_key(name):
def handler(**kwargs):
return self.__makerequest(name, **kwargs)
self.__handlerCache[name] = handler
return self.__handlerCache[name]
|
Loading...