class Department(object): """ Represents a new trac department """ _param_defaults = {"uri": "www.example.com"} _field_defaults = {"department_from": "", "department_to": "", "department_name": ""} def __init__(self, **kw): """ Set up a department and connection Possible arguments (with defaults) are: uri 'www.example.com' """ self.params = self._param_defaults for k, v in kw.iteritems(): if not k in self._param_defaults.keys(): raise ValueError, "Unexpected keyword '%s=%s'" % (str(k), str(v)) if k in self.params: self.params[k] = v else: raise ValueError, "WTF... '%s=%s'" % (str(k), str(v)) # Setup our connection self.br = TwillBrowser() self.br._browser.addheaders.append( ( "User-agent", "Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.7 Safari/533.2", ) ) # Test our connection url = "http://%s" % self.params["uri"] self.br.go(url) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, url) def update(self, **kw): """ Fill in department values. Possible arguments (with defaults) are: department_from department_to department_name """ self.fields = self._field_defaults for k, v in kw.iteritems(): if not k in self._field_defaults.keys(): raise ValueError, "Unexpected keyword '%s=%s'" % (str(k), str(v)) if k in self.fields: if self.fields[k] == v: pass else: self.fields[k] = v else: raise ValueError, "WTF... '%s=%s'" % (str(k), str(v)) def submit(self): """ Submit a new department. Returns the HTTP status code. """ url = "https://%s" % self.params["uri"] self.br.go(url) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, url) form = self.br.get_form("thisForm") for k, v in self.fields.iteritems(): control = form.find_control(k) if control.is_of_kind("text"): form[k] = str(v) elif control.is_of_kind("singlelist"): def get_text(item): if len(item.get_labels()) == 0: return "" return item.get_labels()[0].text possible = [get_text(item) for item in control.get_items()] if v not in possible: raise ValueError, '"%s" not a valid option for %s (%s)' % (v, k, str(possible)) form[k] = [v] else: raise ValueError, "Unimplemented '%s'." % k self.br.clicked(form, form.find_control("submit")) self.br.submit() code = self.br.get_code() if code != 200: raise ValueException, "failure with code %i" % code soup = BeautifulSoup(self.br.get_html()) table = soup.findAll(name="table")[0] cols = table.findAll(name="td") code, name = cols[:2] return str(name.text)
class Ticket(object): """ Represents a new trac ticket """ _param_defaults = { 'user' : 'user', 'passwd' : 'password', 'realm' : 'example realm', 'uri' : 'trac.example.com' } _field_defaults = { 'summary' : 'new ticket', 'description' : '(no description provided)', 'keywords' : '', 'type' : 'task', 'priority' : 'major', 'milestone' : '', 'component' : '', 'cc' : '', 'owner' : '', } def __init__(self, **kw): """ Set up a ticket and connection Possible arguments (with defaults) are: user 'user' passwd 'password' realm 'example realm' uri 'trac.example.com' """ self.params = self._param_defaults for k, v in kw.iteritems(): if not k in self._param_defaults.keys(): raise ValueError, "Unexpected keyword '%s=%s'"%(str(k),str(v)) if k in self.params: self.params[k] = v else: raise ValueError, "WTF... '%s=%s'" % (str(k), str(v)) # Setup our connection self.br = TwillBrowser() self.br.creds.add_password(*[ self.params[k] for k in ['realm', 'uri', 'user', 'passwd'] ]) # Test our connection url = 'https://%s/' % self.params['uri'] self.br.go(url) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, url) # signifies that this ticket is in sync with what's in the trac db self._dirty = False # signifies that this ticket DNE in the trac db as far as we know self.id = None def update(self, **kw): """ Fill in ticket values. Possible arguments (with defaults) are: summary 'new ticket' description '(no description provided)' keywords '' type 'task' priority 'major' milestone '' component '' cc '' owner '' """ self.fields = self._field_defaults for k, v in kw.iteritems(): if not k in self._field_defaults.keys(): raise ValueError, "Unexpected keyword '%s=%s'"%(str(k),str(v)) if k in self.fields: if self.fields[k] == v: pass else: self.fields[k] = v self._dirty = True else: raise ValueError, "WTF... '%s=%s'" % (str(k), str(v)) def search(self, term, unique=True): """ Retrieve a ticket from trac by ID and populate my fields """ url = 'https://%s/search' % self.params['uri'] self.br.go(url) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, url) form = self.br.get_form('fullsearch') for checkbox in ['wiki', 'milestone', 'changeset']: form.find_control(checkbox).checked = False form['q'] = term self.br.clicked(form, form.find_control(type='submit')) self.br.submit() code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to search." % code soup = BeautifulSoup(self.br.get_html()) results = soup.findAll(id='results')[0] results = soup.findAll(id='results')[0].findAll(name='dt') if len(results) < 1: raise Exception, "No results for term '%s'" % term if unique and len(results) > 1: raise Exception, "More than one results for '%s' found." % term loc = results[0].findAll(name='a')[0]['href'] tid = loc.split('/')[-1] return int(tid) def flush(self): """ Flush changes to this ticket to the trac DB via http POSTs """ if not self._dirty: raise ValueError, "No changes to ticket. Can't flush." if not self.id: raise ValueError, "Ticket DNE in trac db yet. Use submit." raise NotImplementedError, "Gotta write this method first." def submit(self): """ Submit a new ticket. Returns the HTTP status code. """ if not self._dirty: raise ValueError, "Ticket has not been modified." if self.id: raise ValueError, "Cannot submit already submitted ticket. Use flush to push modifications to trac." url = 'https://%s/newticket' % self.params['uri'] self.br.go(url) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, url) form = self.br.get_form('propertyform') for k, v in self.fields.iteritems(): k = 'field_%s' % k try: control = form.find_control(k) except Exception: print "Failed on", k, v continue if control.is_of_kind('text'): form[k] = v elif control.is_of_kind('list'): def get_text(item): if len(item.get_labels()) == 0: return '' return item.get_labels()[0].text possible = [ get_text(item) for item in control.get_items() ] if v not in possible: raise ValueError, '"%s" not a valid option for %s (%s)' % ( v, k, str(possible)) form[k] = [v] else: raise ValueError, "Unimplemented '%s'." % k self.br.clicked(form, form.find_control('submit')) self.br.submit() code = self.br.get_code() if code == 200: self._dirty = False # TODO -- get the ticket id and save it!!!!!!! print "TODO -- get the ticket id and save it!!!!" return code
class ListManipulator(object): def __init__(self, uri, adminpw): self.uri = uri if self.uri.startswith('http://'): self.uri = self.uri.replace('http://', 'https://', 1) if not self.uri.startswith('https://'): self.uri = 'https://' + self.uri if not self.uri.endswith('/'): self.uri += '/' self.br = TwillBrowser() self.br._browser.addheaders.append(('User-agent', 'Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.7 Safari/533.2')) self.br.go(self.uri) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s."%(code,self.uri) # Now login... form = self.br.get_all_forms()[0] form['adminpw'] = adminpw self.br.clicked(form, form.find_control('admlogin')) self.br.submit() code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to login @ %s."%(code,self.uri) def has_subscriber(self, addr): if not isinstance(addr, basestring): raise ValueError, '`addr` must be type "str" not %s' % str(type(addr)) loc = "%s%s" % ( self.uri, 'members/list') self.br.go(loc) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, loc) form = self.br.get_all_forms()[0] form['findmember'] = addr self.br.clicked(form, form.find_control('findmember_btn')) self.br.submit() code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to search %s." % (code,loc) form = self.br.get_all_forms()[0] try: field = form.find_control('user') found = unquote(field.attrs['value']) return found == addr except Exception: # control not found return False def subscribe(self, addrs): if not isinstance(addrs, list): raise ValueError, '`addrs` must be a list of email addresses' loc = "%s%s" % ( self.uri, 'members/add') self.br.go(loc) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, loc) form = self.br.get_all_forms()[0] form['subscribees'] = '\n'.join(addrs) self.br.clicked(form, form.find_control('setmemberopts_btn')) self.br.submit() code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to add @ %s." % (code,loc) def unsubscribe(self, addrs): if not isinstance(addrs, list): raise ValueError, '`addrs` must be a list of email addresses' loc = "%s%s" % ( self.uri, 'members/remove') self.br.go(loc) code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to access %s." % (code, loc) form = self.br.get_all_forms()[0] form['unsubscribees'] = '\n'.join(addrs) self.br.clicked(form, form.find_control('setmemberopts_btn')) self.br.submit() code = self.br.get_code() if code != 200: raise Exception, "(Code: %i) Failed to add @ %s." % (code,loc)