class Committee(OpenStateObject): id = fields.Field() state = fields.Field() chamber = fields.Field() committee = fields.Field() subcommittee = fields.Field() members = fields.List(fields.Object(CommitteeMember)) sources = fields.List(fields.Object(Source)) @classmethod def get(cls, id): """ Get a committee. :param id: the committee's Open State ID (e.g. CAC000005) """ func = 'committees/%s' % id return super(Committee, cls).get(func) @classmethod def search(cls, **kwargs): """ Search comittees. Use keyword argmunents to filter by committee fields. For example, ``openstates.Committee.search(state='ca')``. """ return ListOf(cls).get('committees', kwargs).entries
class Legislator(OpenStateObject): id = fields.Field() leg_id = fields.Field() full_name = fields.Field() first_name = fields.Field() last_name = fields.Field() middle_name = fields.Field() suffixes = fields.Field() votesmart_id = fields.Field() nimsp_id = fields.Field() transparencydata_id = fields.Field() active = fields.Field() chamber = fields.Field() district = fields.Field() state = fields.Field() party = fields.Field() created_at = OpenStateDatetime() updated_at = OpenStateDatetime() roles = fields.List(fields.Object(Role)) sources = fields.List(fields.Object(Source)) # Deprecated nimsp_candidate_id = fields.Field() @classmethod def get(cls, id): """ Get a specific legislator. :param id: the legislator's Open State ID (e.g. 'TXL000139') """ func = 'legislators/%s' % id return super(Legislator, cls).get(func) @classmethod def search(cls, **kwargs): """ Search legislators. Use keyword arguments to filter by legislators fields. For example, `openstates.Legislator.search(last_name='Alesi')`. """ return ListOf(cls).get('legislators', kwargs).entries @classmethod def geo(cls, lat, long): """ Get all state legislators for a given lat/long pair :param lat: the latitude :param long: the longitude """ func = 'legislators/geo' params = dict(lat=lat, long=long) return ListOf(cls).get(func, params).entries def __str__(self): return self.full_name
class Event(OpenStateObject): id = fields.Field() state = fields.Field() description = fields.Field() when = OpenStateDatetime() end = OpenStateDatetime() location = fields.Field() type = fields.Field() session = fields.Field() participants = fields.List(fields.Object(EventParticipant)) sources = fields.List(fields.Object(Source)) @classmethod def search(cls, **kwargs): return ListOf(cls).get('events', kwargs).entries
class UserList(ListObject): entries = fields.List(fields.Object(User)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def get_friends(cls, http=None, **kwargs): return cls.get_related("friends", http=http, **kwargs) @classmethod def get_followers(cls, http=None, **kwargs): return cls.get_related("followers", http=http, **kwargs) @classmethod def get_related(cls, relation, http=None, **kwargs): url = '/statuses/%s' % relation if 'id' in kwargs: url += '/%s.json' % quote_plus(kwargs['id']) else: url += '.json' query = urlencode( filter(lambda x: x in ('screen_name', 'user_id', 'page'), kwargs)) url = urlunsplit((None, None, url, query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http)
class Attachment(RemoteObject): # Attachment data. id = fields.Field() attacher = fields.Object('User') creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS) last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS) description = fields.Field() bug_id = fields.Field() bug_ref = fields.Field() # File data. file_name = fields.Field() size = fields.Field() content_type = fields.Field() # Attachment metadata. flags = fields.List(fields.Object('Flag')) is_obsolete = StringBoolean() is_private = StringBoolean() is_patch = StringBoolean() # Used for submitting changes. token = fields.Field() ref = fields.Field() # Only with attachmentdata=1 data = fields.Field() encoding = fields.Field() def __repr__(self): return '<Attachment %s: "%s">' % (self.id, self.description) def __hash__(self): return self.id
class Labels(GitHubRemoteListObject): entries = fields.List(fields.Object(Label)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def by_repository(cls, user_name, repo_name, **kwargs): """Get labels by repository. `GET /repos/:user/:repo/labels` """ url = '/repos/%s/%s/labels' % (user_name, repo_name) return cls.get(urljoin(GitHub.endpoint, url), **kwargs) @classmethod def get_or_create_in_repository(cls, user_name, repo_name, label_name): labels = cls.by_repository(user_name, repo_name, per_page=100, all_pages=True) for label in labels: if label.name == label_name: return label label = Label() label.name = label_name labels.post(label) return label
class Issues(GitHubRemoteListObject): entries = fields.List(fields.Object(Issue)) @classmethod def by_repository(cls, user_name, repo_name, state='open', **kwargs): """Get issues by repository. `GET /repos/:user/:repo/issues` """ url = '/repos/%s/%s/issues?state=%s' % (user_name, repo_name, state) return cls.get(urljoin(GitHub.endpoint, url), **kwargs) @classmethod def by_repository_all(cls, user_name, repo_name, **kwargs): """Get all issues by repository (open and closed).""" open_issues = cls.by_repository(user_name, repo_name, state='open', **kwargs) closed_issues = cls.by_repository(user_name, repo_name, state='closed', **kwargs) open_issues.entries.extend(closed_issues.entries) return open_issues
class Vote(OpenStateObject): date = OpenStateDatetime() chamber = fields.Field() committee = fields.Field() motion = fields.Field() yes_count = fields.Field() no_count = fields.Field() other_count = fields.Field() passed = fields.Field() type = fields.Field() yes_votes = fields.List(fields.Object(SpecificVote)) no_votes = fields.List(fields.Object(SpecificVote)) other_votes = fields.List(fields.Object(SpecificVote)) def __str__(self): return "Vote on '%s'" % self.motion
class Term(OpenStateObject): start_year = fields.Field() end_year = fields.Field() name = fields.Field() sessions = fields.List(fields.Field()) def __str__(self): return self.name
class Changeset(RemoteObject): changer = fields.Object('User') changes = fields.List(fields.Object('Change')) change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS) def __repr__(self): return '<Changeset by %s on %s>' % ( self.changer, self.change_time.strftime(DATETIME_FORMAT))
class PageObject(SequenceProxy, PromiseObject): """A `RemoteObject` representing a set of other `RemoteObject` instances. Endpoints in APIs are often not objects themselves but lists of objects. As with regular `PromiseObject` instances, `PageObject` instances can be filtered by parameters that are then passed to your target API, such as a list of recent objects or a search. Filtering a `PageObject` instance by a parameter returns a new copy of that `PageObject` instance that includes the new parameter. The contents of regular `PageObject` instances will be decoded as with `Field` fields; that is, not decoded at all. To customize decoding of API contents, subclass `PageObject` and redefine the ``entries`` member with a `Field` instance that decodes the list content as necessary. As many API endpoints are sets of objects, to create a `PageObject` subclass for those endpoints, you can directly call its metaclass, `PageOf`, with the class reference you would use to construct an `Object` field. That is, these declarations are equivalent: >>> PageOfEntry = PageOf(Entry) >>> class PageOfEntry(PageObject): ... entries = fields.List(fields.Object(Entry)) For an ``Entry`` list you then fetch with the `PageOfEntry` class's `get()` method, all the entities in the list resource's `entries` member will be decoded into ``Entry`` instances. """ __metaclass__ = PageOf entries = fields.List(fields.Field()) def __getitem__(self, key): """Translates slice notation on a `ListObject` instance into ``limit`` and ``offset`` filter parameters.""" if isinstance(key, slice): args = dict() if key.start is not None: args['offset'] = key.start if key.stop is not None: args['limit'] = key.stop - key.start elif key.stop is not None: args['limit'] = key.stop return self.filter(**args) try: getitem = super(PageObject, self).__getitem__ except AttributeError: raise TypeError("'%s' object is unsubscriptable except by slices" % (type(self).__name__, )) else: return getitem(key)
class ViewResult(CouchObject, ListObject): total_rows = fields.Field() offset = fields.Field() entries = fields.List(fields.Object(ListItem), api_name='rows') def filter(self, **kwargs): for k, v in kwargs.iteritems(): if isinstance(v, list) or isinstance(v, dict) or isinstance( v, bool): kwargs[k] = json.dumps(v) return super(ViewResult, self).filter(**kwargs)
class Milestones(GitHubRemoteListObject): entries = fields.List(fields.Object(Milestone)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def by_repository(cls, user_name, repo_name, state='open', **kwargs): """Get milestones by repository. `GET /repos/:user/:repo/milestones` """ url = '/repos/%s/%s/milestones?state=%s' % (user_name, repo_name, state) return cls.get(urljoin(GitHub.endpoint, url), **kwargs) @classmethod def by_repository_all(cls, user_name, repo_name, state='open', **kwargs): """Get all milestones by repository (open and closed).""" open_milestones = cls.by_repository(user_name, repo_name, state='open', **kwargs) closed_milestones = cls.by_repository(user_name, repo_name, state='closed', **kwargs) open_milestones.entries.extend(closed_milestones.entries) return open_milestones @classmethod def get_or_create_in_repository(cls, user_name, repo_name, milestone_title): if milestone_title is None: return None milestones = cls.by_repository_all(user_name, repo_name, per_page=100, all_pages=True) for milestone in milestones: if milestone.title == milestone_title: return milestone milestone = Milestone() milestone.title = milestone_title milestones.post(milestone) return milestone
class Collaborators(GitHubRemoteListObject): entries = fields.List(fields.Object(Collaborator)) @classmethod def by_repository(cls, user_name, repo_name, **kwargs): """Get collaborators by repository. `GET /repos/:user/:repo/collaborators` """ url = '/repos/%s/%s/collaborators' % (user_name, repo_name) return cls.get(urljoin(GitHub.endpoint, url), **kwargs)
class GameResult(Bombject): status_code = fields.Field() error = fields.Field() total = fields.Field(api_name='number_of_total_results') count = fields.Field(api_name='number_of_page_results') limit = fields.Field() offset = fields.Field() results = fields.List(fields.Object(Game)) def update_from_dict(self, data): if not isinstance(data['results'], list): data = dict(data) data['results'] = [data['results']] super(GameResult, self).update_from_dict(data)
class Events(GitHubRemoteListObject): entries = fields.List(fields.Object(Event)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def by_repository(cls, repo_user, repo_name, **kwargs): """Get events by repository. `GET /repos/:user/:repo/events` """ url = '/repos/%s/%s/events' % (repo_user, repo_name) return cls.get(urljoin(GitHub.endpoint, url), **kwargs)
class Bill(OpenStateObject): title = fields.Field() state = fields.Field() session = fields.Field() chamber = fields.Field() bill_id = fields.Field() created_at = OpenStateDatetime() updated_at = OpenStateDatetime() alternate_titles = fields.List(fields.Field()) actions = fields.List(fields.Object(Action)) sponsors = fields.List(fields.Object(Sponsor)) votes = fields.List(fields.Object(Vote)) versions = fields.List(fields.Object(Version)) documents = fields.List(fields.Object(Document)) sources = fields.List(fields.Object(Source)) @classmethod def get(cls, state, session, chamber, bill_id): """ Get a specific bill. :param state: the two-letter abbreviation of the originating state :param session: the session identifier for the bill (see the state's metadata for legal values) :param chamber: which legislative chamber the bill originated in ('upper' or 'lower') :param bill_id: the bill's ID as assigned by the state """ func = "bills/%s/%s/%s/%s" % (state, session, chamber, bill_id) return super(Bill, cls).get(func) @classmethod def search(cls, query=None, **kwargs): """ Search bills. :param query: a query string which will be used to search bill titles Any additional keyword arguments will be used to further filter the results. """ if query: kwargs['q'] = query func = 'bills' return ListOf(cls).get(func, kwargs).entries def __str__(self): return '%s: %s' % (self.bill_id, self.title)
class Timeline(ListObject): entries = fields.List(fields.Object(Status)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def public(cls, http=None): return cls.get(urljoin(Twitter.endpoint, '/statuses/public_timeline.json'), http=http) @classmethod def friends(cls, http=None, **kwargs): query = urlencode( filter(lambda x: x in ('since_id', 'max_id', 'count', 'page'), kwargs)) url = urlunsplit( (None, None, '/statuses/friends_timeline.json', query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http) @classmethod def user(cls, http=None, **kwargs): url = '/statuses/user_timeline' if 'id' in kwargs: url += '/%s.json' % quote_plus(kwargs['id']) else: url += '.json' query = urlencode( filter( lambda x: x in ('screen_name', 'user_id', 'since_id', 'max_id', 'page'), kwargs)) url = urlunsplit((None, None, url, query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http) @classmethod def mentions(cls, http=None, **kwargs): query = urlencode( filter(lambda x: x in ('since_id', 'max_id', 'page'), kwargs)) url = urlunsplit((None, None, '/statuses/mentions.json', query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http)
def __new__(cls, name, bases=None, attr=None): """Creates a new `PageObject` subclass. If `bases` and `attr` are specified, as in a regular subclass declaration, a new class is created as per the specified settings. If only `name` is specified, that value is used as a reference to a `RemoteObject` class to which the new `PageObject` class is bound. The `name` parameter can be either a name or a `RemoteObject` class, as when declaring a `remoteobjects.fields.Object` field. """ direct = attr is None if direct: # Don't bother making a new subclass if we already made one for # this target. if name in cls._subclasses: return cls._subclasses[name] entryclass = name if callable(entryclass): name = cls.__name__ + entryclass.__name__ else: name = cls.__name__ + entryclass bases = (cls._basemodule, ) attr = { 'entries': fields.List(fields.Object(entryclass)), } newcls = super(PageOf, cls).__new__(cls, name, bases, attr) # Save the result for later direct invocations. if direct: cls._subclasses[entryclass] = newcls newcls.__module__ = cls._modulename setattr(sys.modules[cls._modulename], name, newcls) elif cls._basemodule is None: cls._basemodule = newcls return newcls
class Catalog(Flixject): results = fields.List(fields.Field()) total = fields.Field() offset = fields.Field() limit = fields.Field() decoder_ring = { 'results': lambda x: [ Title().update_from_tree(tree) for tree in x.findall('catalog_title') ], 'total': lambda x: int(x.find('number_of_results').text), 'offset': lambda x: int(x.find('start_index').text), 'limit': lambda x: int(x.find('results_per_page').text), }
class DirectMessageList(ListObject): entries = fields.List(fields.Object(DirectMessage)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def get_messages(cls, http=None, **kwargs): url = '/direct_messages.json' query = urlencode(filter(lambda x: x in ('since_id', 'page'), kwargs)) url = urlunsplit((None, None, url, query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http) @classmethod def get_sent_messages(cls, http=None, **kwargs): url = '/direct_messages/sent.json' query = urlencode(filter(lambda x: x in ('since_id', 'page'), kwargs)) url = urlunsplit((None, None, url, query, None)) return cls.get(urljoin(Twitter.endpoint, url), http=http)
class Comments(GitHubRemoteListObject): entries = fields.List(fields.Object(Comment)) def __getitem__(self, key): return self.entries.__getitem__(key) @classmethod def by_issue(cls, issue, **kwargs): """Get comments by issue. `GET /repos/:user/:repo/issues/:number/comments` """ # Don't request the list of comments if we know there should be 0. if issue.comments == 0: comments = cls(entries=[]) comments._location = '%s/comments' % issue.url return comments url = '%s/comments' % issue.url return cls.get(url, **kwargs)
class State(OpenStateObject): name = fields.Field() abbreviation = fields.Field() legislature_name = fields.Field() upper_chamber_name = fields.Field() lower_chamber_name = fields.Field() upper_chamber_term = fields.Field() lower_chamber_term = fields.Field() upper_chamber_title = fields.Field() lower_chamber_title = fields.Field() terms = fields.List(fields.Object(Term)) @classmethod def get(cls, abbrev): """ Get metadata about a state. :param abbrev: the state's two-letter abbreviation. """ return super(State, cls).get('metadata/%s' % abbrev) def __str__(self): return self.name
class Reflexive(self.cls): itself = fields.Object('Reflexive') themselves = fields.List(fields.Object('Reflexive'))
class Bug(RemoteObject): id = fields.Field() summary = fields.Field() assigned_to = fields.Object('User') creator = fields.Object('User') target_milestone = fields.Field() attachments = fields.List(fields.Object('Attachment')) comments = fields.List(fields.Object('Comment')) history = fields.List(fields.Object('Changeset')) keywords = fields.List(fields.Object('Keyword')) status = fields.Field() resolution = fields.Field() # TODO: These are Mozilla specific and should be generalized cf_blocking_20 = fields.Field() cf_blocking_fennec = fields.Field() cf_crash_signature = fields.Field() creation_time = Datetime(DATETIME_FORMAT_WITH_SECONDS) flags = fields.List(fields.Object('Flag')) blocks = fields.List(fields.Field()) depends_on = fields.List(fields.Field()) url = fields.Field() cc = fields.List(fields.Object('User')) keywords = fields.List(fields.Field()) whiteboard = fields.Field() op_sys = fields.Field() platform = fields.Field() priority = fields.Field() product = fields.Field() qa_contact = fields.Object('User') severity = fields.Field() see_also = fields.List(fields.Field()) version = fields.Field() alias = fields.Field() classification = fields.Field() component = fields.Field() is_cc_accessible = StringBoolean() is_everconfirmed = StringBoolean() is_creator_accessible = StringBoolean() last_change_time = Datetime(DATETIME_FORMAT_WITH_SECONDS) ref = fields.Field() # Needed for submitting changes. token = fields.Field(api_name='update_token') # Time tracking. actual_time = fields.Field() deadline = Datetime(DATETIME_FORMAT_WITH_SECONDS) estimated_time = fields.Field() # groups = fields.Field() # unimplemented percentage_complete = fields.Field() remaining_time = fields.Field() work_time = fields.Field() def __repr__(self): return '<Bug %s: "%s">' % (self.id, self.summary) def __str__(self): return "[Bug %s] - %s" % (self.id, self.summary) def __hash__(self): return self.id
class BugSearch(RemoteObject): bugs = fields.List(fields.Object('Bug'))
class Toy(self.cls): names = fields.List(fields.Field()) foo = fields.Field()
class Parentish(self.cls): name = fields.Field() children = fields.List(fields.Object(Childer))
class Twiddle(RemoteObject): kind = fields.Constant("tag:api.example.com,2009;Twiddle") name = fields.Field() frob = fields.Object(Frob, api_name="frobnitz") zotz = fields.List(fields.Object(Zot))
class Issue(GitHubRemoteObject): """A GitHub issue. `GET GET /repos/:user/:repo/issues/:number` { "url": "https://api.github.com/repos/octocat/Hello-World/issues/1", "html_url": "https://github.com/octocat/Hello-World/issues/1", "number": 1347, "state": "open", "title": "Found a bug", "body": "I'm having a problem with this.", "user": { "login": "******", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "labels": [ { "url": "https://api.github.com/repos/octocat/Hello-World/labels/bug", "name": "bug", "color": "f29513" } ], "assignee": { "login": "******", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "milestone": { "url": "https://api.github.com/repos/octocat/Hello-World/milestones/1", "number": 1, "state": "open", "title": "v1.0", "description": "", "creator": { "login": "******", "id": 1, "avatar_url": "https://github.com/images/error/octocat_happy.gif", "gravatar_id": "somehexcode", "url": "https://api.github.com/users/octocat" }, "open_issues": 4, "closed_issues": 8, "created_at": "2011-04-10T20:09:31Z", "due_on": null }, "comments": 0, "pull_request": { "html_url": "https://github.com/octocat/Hello-World/issues/1", "diff_url": "https://github.com/octocat/Hello-World/issues/1.diff", "patch_url": "https://github.com/octocat/Hello-World/issues/1.patch" }, "closed_at": null, "created_at": "2011-04-22T13:33:48Z", "updated_at": "2011-04-22T13:33:48Z" } """ url = fields.Field() html_url = fields.Field() number = fields.Field() id = fields.Field() state = fields.Field() title = fields.Field() body = fields.Field() user = fields.Object(User) labels = fields.List(fields.Object(Label)) assignee = fields.Object(User) milestone = fields.Object(Milestone) comments = fields.Field() pull_request = fields.Field() closed_at = fields.Field() created_at = fields.Field() updated_at = fields.Field() def __unicode__(self): return u"<Issue %d>" % self.number