import sys, re
+makeToken_re = re.compile(r"[^a-zA-Z0-9@.+\-]")
+
class RemoteException(Exception):
def __init__(self, *args):
self.args = args
def failwith(s):
raise RemoteException(s)
+def maketoken(val):
+ if val and val.lower() == 'none': return None
+ return val and makeToken_re.sub('-', val)
+
class RemoteReport:
- _tokenre = re.compile("[^a-zA-Z0-9@.+\-]")
def __init__(self, uri, data, bts):
self._uri = uri
self.bts = bts
self.id = data.id
- self.status = self._makeToken(data.status)
- self.resolution = self._makeToken(data.resolution)
+ self.status = maketoken(data.status)
+ self.resolution = maketoken(data.resolution)
def uri(self):
return self.bts.getUri(self.id)
- def _makeToken(self, val):
- return val and RemoteReport._tokenre.sub('-', val)
-
def isClosed(self):
return self.bts.isClosing(self.status, self.resolution)
def _bugId(self, data):
return data.id
-import bugzilla, trac
+import bugzilla, trac, savane
--- /dev/null
+# vim:set encoding=utf-8:
+###############################################################################
+# Copyright:
+# © 2006 Sanghyeon Seo <sanxiyn@gmail.com>
+# © 2006 Pierre Habouzit <madcoder@debian.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+###############################################################################
+
+import urllib, urlparse, cgi
+
+from BeautifulSoup import BeautifulSoup
+from base import RemoteBts, failwith, maketoken
+
+def parse_uri(uri, key):
+ qs = urlparse.urlparse(uri)[4]
+ dic = cgi.parse_qs(qs)
+ return dic[key][0]
+
+def parse_table(soup, key):
+ cell = soup.firstText(key).findParent('td')
+ return maketoken(cell.nextSibling.string)
+
+class SavaneData:
+ def __init__(self, uri):
+ self.id = parse_uri(uri, 'item_id')
+
+ soup = BeautifulSoup(urllib.urlopen(uri))
+
+ self.status = parse_table(soup, 'Open/Closed:') or failwith("Savanah: no status")
+ self.resolution = parse_table(soup, 'Status:')
+
+ if self.resolution == 'Duplicate':
+ failwith("Savane: don't know how to follow duplicates")
+
+class RemoteSavane(RemoteBts):
+ def __init__(self, cnf):
+ bugre = r'^%(uri)s/\?func=detailitem&item_id=([0-9]+)$'
+ urifmt = '%(uri)s/?func=detailitem&item_id=%(id)s'
+ RemoteBts.__init__(self, cnf, bugre, urifmt, SavaneData)
+
+ def isClosing(self, status, resolution):
+ return status == 'Closed' and resolution != 'Wont-Fix'
+
+ def isWontfix(self, status, resolution):
+ return resolution == 'Wont-Fix'
+
+RemoteSavane.register('savane', RemoteSavane)
+