diff --git a/trello/__init__.py b/trello/__init__.py index fe720a88..1fca89a2 100644 --- a/trello/__init__.py +++ b/trello/__init__.py @@ -1,1285 +1,13 @@ -from datetime import datetime -import json -from dateutil import parser as dateparser -import requests -from requests_oauthlib import OAuth1 - - -class ResourceUnavailable(Exception): - """Exception representing a failed request to a resource""" - - def __init__(self, msg, http_response): - Exception.__init__(self) - self._msg = msg - self._status = http_response.status_code - - def __str__(self): - return "%s (HTTP status: %s)" % ( - self._msg, self._status) - - -class Unauthorized(ResourceUnavailable): - pass - - -class TokenError(Exception): - pass - - -class TrelloClient(object): - """ Base class for Trello API access """ - - def __init__(self, api_key, api_secret=None, token=None, token_secret=None): - """ - Constructor - - :api_key: API key generated at https://trello.com/1/appKey/generate - :api_secret: the secret component of api_key - :token_key: OAuth token generated by the user in - trello.util.create_oauth_token - :token_secret: the OAuth client secret for the given OAuth token - """ - - # client key and secret for oauth1 session - if api_key or token: - self.oauth = OAuth1(client_key=api_key, client_secret=api_secret, - resource_owner_key=token, resource_owner_secret=token_secret) - else: - self.oauth = None - - self.public_only = token is None - self.api_key = api_key - self.api_secret = api_secret - self.resource_owner_key = token - self.resource_owner_secret = token_secret - - def info_for_all_boards(self, actions): - """ - Use this if you want to retrieve info for all your boards in one swoop - """ - if self.public_only: - return None - else: - json_obj = self.fetch_json( - '/members/me/boards/all', - query_params={'actions': actions}) - self.all_info = json_obj - - def logout(self): - """Log out of Trello.""" - #TODO: This function. - - raise NotImplementedError() - - def list_boards(self): - """ - Returns all boards for your Trello user - - :return: a list of Python objects representing the Trello boards. - :rtype: Board - - Each board has the following noteworthy attributes: - - id: the board's identifier - - name: Name of the board - - desc: Description of the board (optional - may be missing from the - returned JSON) - - closed: Boolean representing whether this board is closed or not - - url: URL to the board - """ - json_obj = self.fetch_json('/members/me/boards') - return [Board.from_json(self, json_obj=obj) for obj in json_obj] - - def list_organizations(self): - """ - Returns all organizations for your Trello user - - :return: a list of Python objects representing the Trello organizations. - :rtype: Organization - - Each organization has the following noteworthy attributes: - - id: the organization's identifier - - name: Name of the organization - - desc: Description of the organization (optional - may be missing from the - returned JSON) - - closed: Boolean representing whether this organization is closed or not - - url: URL to the organization - """ - json_obj = self.fetch_json('members/me/organizations') - return [Organization.from_json(self, obj) for obj in json_obj] - - def get_organization(self, organization_id): - '''Get organization - - :rtype: Organization - ''' - obj = self.fetch_json('/organizations/' + organization_id) - - return Organization.from_json(self, obj) - - def get_board(self, board_id): - '''Get board - - :rtype: Board - ''' - obj = self.fetch_json('/boards/' + board_id) - return Board.from_json(self, json_obj=obj) - - def add_board(self, board_name): - '''Create board - - :rtype: Board - ''' - obj = self.fetch_json('/boards', http_method='POST', - post_args={'name': board_name}) - return Board.from_json(self, json_obj=obj) - - def get_member(self, member_id): - '''Get member - - :rtype: Member - ''' - return Member(self, member_id).fetch() - - def get_card(self, card_id): - '''Get card - - :rtype: Card - ''' - card_json = self.fetch_json('/cards/' + card_id) - list_json = self.fetch_json('/lists/' + card_json['idList']) - board = self.get_board(card_json['idBoard']) - return Card.from_json(List.from_json(board, list_json), card_json) - - def fetch_json( - self, - uri_path, - http_method='GET', - headers=None, - query_params=None, - post_args=None, - files=None): - """ Fetch some JSON from Trello """ - - # explicit values here to avoid mutable default values - if headers is None: - headers = {} - if query_params is None: - query_params = {} - if post_args is None: - post_args = {} - - # if files specified, we don't want any data - data = None - if files is None: - data = json.dumps(post_args) - - # set content type and accept headers to handle JSON - if http_method in ("POST", "PUT", "DELETE") and not files: - headers['Content-Type'] = 'application/json; charset=utf-8' - - headers['Accept'] = 'application/json' - - # construct the full URL without query parameters - if uri_path[0] == '/': - uri_path = uri_path[1:] - url = 'https://api.trello.com/1/%s' % uri_path - - # perform the HTTP requests, if possible uses OAuth authentication - response = requests.request(http_method, url, params=query_params, - headers=headers, data=data, - auth=self.oauth, files=files) - - if response.status_code == 401: - raise Unauthorized("%s at %s" % (response.text, url), response) - if response.status_code != 200: - raise ResourceUnavailable("%s at %s" % (response.text, url), response) - - return response.json() - - def list_hooks(self, token=None): - """ - Returns a list of all hooks associated with a specific token. If you don't pass in a token, - it tries to use the token associated with the TrelloClient object (if it exists) - """ - token = token or self.resource_owner_key - - if token is None: - raise TokenError("You need to pass an auth token in to list hooks.") - else: - url = "/tokens/%s/webhooks" % token - return self._existing_hook_objs(self.fetch_json(url), token) - - def _existing_hook_objs(self, hooks, token): - """ - Given a list of hook dicts passed from list_hooks, creates - the hook objects - """ - all_hooks = [] - for hook in hooks: - new_hook = WebHook(self, token, hook['id'], hook['description'], - hook['idModel'], - hook['callbackURL'], hook['active']) - all_hooks.append(new_hook) - return all_hooks - - def create_hook(self, callback_url, id_model, desc=None, token=None): - """ - Creates a new webhook. Returns the WebHook object created. - - There seems to be some sort of bug that makes you unable to create a - hook using httplib2, so I'm using urllib2 for that instead. - """ - token = token or self.resource_owner_key - - if token is None: - raise TokenError("You need to pass an auth token in to create a hook.") - - url = "https://trello.com/1/tokens/%s/webhooks/" % token - data = {'callbackURL': callback_url, 'idModel': id_model, - 'description': desc} - - response = requests.post(url, data=data, auth=self.oauth) - - if response.status_code == 200: - hook_id = response.json()['id'] - return WebHook(self, token, hook_id, desc, id_model, callback_url, True) - else: - return False - -class Organization(object): - - """ - Class representing an organization - """ - def __init__(self, client, organization_id, name=''): - self.client = client - self.id = organization_id - self.name = name - - @classmethod - def from_json(cls, trello_client, json_obj): - """ - Deserialize the board json object to a Organization object - - :trello_client: the trello client - :json_obj: the board json object - """ - organization = Organization(trello_client, json_obj['id'], name=json_obj['name'].encode('utf-8')) - organization.description = json_obj.get('desc', '').encode('utf-8') - # cannot close an organization - #organization.closed = json_obj['closed'] - organization.url = json_obj['url'] - return organization - - def __repr__(self): - return '' % self.name - - def fetch(self): - """Fetch all attributes for this organization""" - json_obj = self.client.fetch_json('/organizations/' + self.id) - self.name = json_obj['name'] - self.description = json_obj.get('desc', '') - self.closed = json_obj['closed'] - self.url = json_obj['url'] - - def all_boards(self): - """Returns all boards on this organization""" - return self.get_boards('all') - - def get_boards(self, list_filter): - '''Get boards using filter - - :rtype: Board - ''' - # error checking - json_obj = self.client.fetch_json( - '/organizations/' + self.id + '/boards', - query_params={'lists': 'none', 'filter': list_filter}) - return [Board.from_json(organization=self, json_obj=obj) for obj in json_obj] - - def get_board(self, field_name): - '''Get board - - :rtype: Board - ''' - # error checking - json_obj = self.client.fetch_json( - '/organizations/' + self.id + '/boards', - query_params={'filter': 'open','fields':field_name}) - return [Board.from_json(organization=self, json_obj=obj) for obj in json_obj] - - def get_members(self): - json_obj = self.client.fetch_json( - '/organizations/' + self.id + '/members', - query_params={'filter': 'all'}) - return [Member.from_json(trello_client=self.client, json_obj=obj) for obj in json_obj] - -class Board(object): - """ - Class representing a Trello board. Board attributes are stored as normal - Python attributes; access to all sub-objects, however, is always - an API call (Lists, Cards). - """ - - def __init__(self, client=None, board_id=None, organization=None, name=''): - """ - :trello: Reference to a Trello object - :board_id: ID for the board - - Alternative Constructor - - :organization: reference to the parent organization - :board_id: ID for this board - - """ - if organization is None: - self.client = client - else: - self.organization = organization - self.client = organization.client - self.id = board_id - self.name = name - - - @classmethod - def from_json(cls, trello_client=None, organization = None, json_obj=None): - """ - Deserialize the board json object to a Board object - - :trello_client: the trello client - :json_obj: the board json object - - Alternative contrustraction: - - Deserialize the board json object to a board object - - :organization: the organization object that the board belongs to - :json_obj: the json board object - """ - if organization is None: - board = Board(client=trello_client, board_id=json_obj['id'], name=json_obj['name'].encode('utf-8')) - else: - board = Board(organization=organization, board_id=json_obj['id'], name=json_obj['name'].encode('utf-8')) - - board.description = json_obj.get('desc', '').encode('utf-8') - board.closed = json_obj['closed'] - board.url = json_obj['url'] - return board - - def __repr__(self): - return '' % self.name - - def fetch(self): - """Fetch all attributes for this board""" - json_obj = self.client.fetch_json('/boards/' + self.id) - self.name = json_obj['name'] - self.description = json_obj.get('desc', '') - self.closed = json_obj['closed'] - self.url = json_obj['url'] - - def save(self): - pass - - def close(self): - self.client.fetch_json( - '/boards/' + self.id + '/closed', - http_method='PUT', - post_args={'value': 'true', }, ) - self.closed = True - - def open(self): - self.client.fetch_json( - '/boards/' + self.id + '/closed', - http_method='PUT', - post_args={'value': 'false', }, ) - self.closed = False - - def get_list(self, list_id): - '''Get list - - :rtype: List - ''' - obj = self.client.fetch_json('/lists/' + list_id) - return List.from_json(board=self, json_obj=obj) - - def all_lists(self): - """Returns all lists on this board - - :rtype: List - """ - return self.get_lists('all') - - def open_lists(self): - """Returns all open lists on this board - - :rtype: List - """ - return self.get_lists('open') - - def closed_lists(self): - """Returns all closed lists on this board - - :rtype: List - """ - return self.get_lists('closed') - - def get_lists(self, list_filter): - '''Get lists from filter - - :rtype: List - ''' - # error checking - json_obj = self.client.fetch_json( - '/boards/' + self.id + '/lists', - query_params={'cards': 'none', 'filter': list_filter}) - return [List.from_json(board=self, json_obj=obj) for obj in json_obj] - - def get_labels(self, fields='all', limit=50): - '''Get label - - :rtype: Label - ''' - json_obj = self.client.fetch_json( - '/boards/' + self.id + '/labels', - query_params={'fields': fields, 'limit': limit}) - return Label.from_json_list(self, json_obj) - - def add_list(self, name): - """Add a list to this board - - :name: name for the list - :return: the list - :rtype: List - """ - obj = self.client.fetch_json( - '/lists', - http_method='POST', - post_args={'name': name, 'idBoard': self.id}, ) - return List.from_json(board=self, json_obj=obj) - - def add_label(self, name, color): - """Add a label to this board - - :name: name of the label - :color: the color, either green, yellow, orange - red, purple, blue, sky, lime, pink, or black - :return: the label - :rtype: Label - """ - obj = self.client.fetch_json( - '/labels', - http_method='POST', - post_args={'name': name, 'idBoard': self.id, 'color': color},) - return Label.from_json(board=self, json_obj=obj) - - def all_cards(self): - """Returns all cards on this board - - :rtype: Card - """ - filters = { - 'filter': 'all', - 'fields': 'all' - } - return self.get_cards(filters) - - def open_cards(self): - """Returns all open cards on this board - - :rtype: Card - """ - filters = { - 'filter': 'open', - 'fields': 'all' - } - return self.get_cards(filters) - - def closed_cards(self): - """Returns all closed cards on this board - - :rtype: Card - """ - filters = { - 'filter': 'closed', - 'fields': 'all' - } - return self.get_cards(filters) - - def get_cards(self, filters=None): - """ - :card_filter: filters on card status ('open', 'closed', 'all') - :query_params: dict containing query parameters. Eg. {'fields': 'all'} - - More info on card queries: - https://trello.com/docs/api/board/index.html#get-1-boards-board-id-cards - - :rtype: Card - """ - json_obj = self.client.fetch_json( - '/boards/' + self.id + '/cards', - query_params=filters - ) - - return list([Card.from_json(self, json) for json in json_obj]) - - def all_members(self): - """Returns all members on this board - - :rtype: Member - """ - filters = { - 'filter': 'all', - 'fields': 'all' - } - return self.get_members(filters) - - def normal_members(self): - """Returns all normal members on this board - - :rtype: Member - """ - filters = { - 'filter': 'normal', - 'fields': 'all' - } - return self.get_members(filters) - - def admin_members(self): - """Returns all admin members on this board - - :rtype: Member - """ - filters = { - 'filter': 'admins', - 'fields': 'all' - } - return self.get_members(filters) - - def owner_members(self): - """Returns all owner members on this board - - :rtype: Member - """ - filters = { - 'filter': 'owners', - 'fields': 'all' - } - return self.get_members(filters) - - def get_members(self, filters=None): - """Get members with filter - - :rtype: Member - """ - json_obj = self.client.fetch_json( - '/boards/' + self.id + '/members', - query_params=filters) - members = list() - for obj in json_obj: - m = Member(self.client, obj['id']) - m.status = obj.get('status', '').encode('utf-8') - m.id = obj.get('id', '') - m.bio = obj.get('bio', '') - m.url = obj.get('url', '') - m.username = obj['username'].encode('utf-8') - m.full_name = obj['fullName'].encode('utf-8') - m.initials = obj.get('initials', '').encode('utf-8') - members.append(m) - - return members - - def fetch_actions(self, action_filter): - json_obj = self.client.fetch_json( - '/boards/' + self.id + '/actions', - query_params={'filter': action_filter}) - self.actions = json_obj - - -class List(object): - """ - Class representing a Trello list. List attributes are stored on the object, - but access to sub-objects (Cards) require an API call - """ - - def __init__(self, board, list_id, name=''): - """Constructor - - :board: reference to the parent board - :list_id: ID for this list - """ - self.board = board - self.client = board.client - self.id = list_id - self.name = name - - @classmethod - def from_json(cls, board, json_obj): - """ - Deserialize the list json object to a List object - - :board: the board object that the list belongs to - :json_obj: the json list object - """ - list = List(board, json_obj['id'], name=json_obj['name'].encode('utf-8')) - list.closed = json_obj['closed'] - return list - - def __repr__(self): - return '' % self.name - - def fetch(self): - """Fetch all attributes for this list""" - json_obj = self.client.fetch_json('/lists/' + self.id) - self.name = json_obj['name'] - self.closed = json_obj['closed'] - - def list_cards(self): - """Lists all cards in this list""" - json_obj = self.client.fetch_json('/lists/' + self.id + '/cards') - return [Card.from_json(self, c) for c in json_obj] - - def add_card(self, name, desc=None, labels=[], due="null"): - """Add a card to this list - - :name: name for the card - :desc: the description of the card - :labels: a list of label IDs to be added - :return: the card - """ - labels_str = "" - for label in labels: - labels_str += label.id + "," - json_obj = self.client.fetch_json( - '/cards', - http_method='POST', - post_args={'name': name, 'idList': self.id, 'desc': desc, 'idLabels': labels_str[:-1], 'due': due}) - return Card.from_json(self, json_obj) - - def archive_all_cards(self): - self.client.fetch_json( - '/lists/' + self.id + '/archiveAllCards', - http_method='POST') - - def fetch_actions(self, action_filter): - """ - Fetch actions for this list can give more argv to action_filter, - split for ',' json_obj is list - """ - json_obj = self.client.fetch_json( - '/lists/' + self.id + '/actions', - query_params={'filter': action_filter}) - self.actions = json_obj - - def _set_remote_attribute(self, attribute, value): - self.client.fetch_json( - '/lists/' + self.id + '/' + attribute, - http_method='PUT', - post_args={'value': value, }, ) - - def close(self): - self.client.fetch_json( - '/lists/' + self.id + '/closed', - http_method='PUT', - post_args={'value': 'true', }, ) - self.closed = True - - def open(self): - self.client.fetch_json( - '/lists/' + self.id + '/closed', - http_method='PUT', - post_args={'value': 'false', }, ) - self.closed = False - - def cardsCnt(self): - return len(self.list_cards()) - -class Card(object): - """ - Class representing a Trello card. Card attributes are stored on - the object - """ - - @property - def member_id(self): - return self.idMembers - - @property - def short_id(self): - return self.idShort - - @property - def list_id(self): - return self.idList - - @property - def board_id(self): - return self.idBoard - - @property - def description(self): - return self.desc - - @property - def date_last_activity(self): - return self.dateLastActivity - - @description.setter - def description(self, value): - self.desc = value - - @property - def idLabels(self): - return self.label_ids - - @idLabels.setter - def idLabels(self, values): - self.label_ids = values - - @property - def list_labels(self): - if self.labels: - return self.labels - return None - - @property - def comments(self): - """ - Lazily loads and returns the comments - """ - if self._comments is None: - self._comments = self.fetch_comments() - return self._comments - - @property - def checklists(self): - """ - Lazily loads and returns the checklists - """ - if self._checklists is None: - self._checklists = self.fetch_checklists() - return self._checklists - - def __init__(self, parent, card_id, name=''): - """ - :trello_list: reference to the parent list - :card_id: ID for this card - """ - if isinstance(parent, List): - self.trello_list = parent - self.board = parent.board - else: - self.board = parent - - self.client = parent.client - self.id = card_id - self.name = name - - @classmethod - def from_json(cls, parent, json_obj): - """ - Deserialize the card json object to a Card object - - :trello_list: the list object that the card belongs to - :json_obj: json object - """ - if 'id' not in json_obj: - raise Exception("key 'id' is not in json_obj") - card = cls(parent, - json_obj['id'], - name=json_obj['name'].encode('utf-8')) - card.desc = json_obj.get('desc', '') - card.closed = json_obj['closed'] - card.url = json_obj['url'] - card.member_ids = json_obj['idMembers'] - card.idLabels = json_obj['idLabels'] - card.idList = json_obj['idList'] - card.labels = Label.from_json_list(card.board, json_obj['labels']) - return card - - def __repr__(self): - return '' % self.name - - def fetch(self, eager=True): - """ - Fetch all attributes for this card - :param eager: If eager is true comments and checklists will be fetched immediately, otherwise on demand - """ - json_obj = self.client.fetch_json( - '/cards/' + self.id, - query_params={'badges': False}) - self.id = json_obj['id'] - self.name = json_obj['name'].encode('utf-8') - self.desc = json_obj.get('desc', '') - self.closed = json_obj['closed'] - self.url = json_obj['url'] - self.idMembers = json_obj['idMembers'] - self.idShort = json_obj['idShort'] - self.idList = json_obj['idList'] - self.idBoard = json_obj['idBoard'] - self.idLabels = json_obj['idLabels'] - self.labels = Label.from_json_list(self.board, json_obj['labels']) - self.badges = json_obj['badges'] - self.pos = json_obj['pos'] - # For consistency, due date is in YYYY-MM-DD format - if json_obj.get('due', ''): - self.due = json_obj.get('due', '')[:10] - else: - self.due = '' - self.checked = json_obj['checkItemStates'] - self.dateLastActivity = dateparser.parse(json_obj['dateLastActivity']) - - self._checklists = self.fetch_checklists() if eager else None - self._comments = self.fetch_comments() if eager else None - - def fetch_comments(self): - comments = [] - - if self.badges['comments'] > 0: - comments = self.client.fetch_json( - '/cards/' + self.id + '/actions', - query_params={'filter': 'commentCard'}) - - return comments - - def get_list(self): - obj = self.client.fetch_json('/lists/' + self.idList) - return List.from_json(board=self, json_obj=obj) - - def get_comments(self): - comments = [] - comments = self.client.fetch_json( - '/cards/' + self.id + '/actions', - query_params={'filter': 'commentCard'}) - return comments - - def fetch_checklists(self): - checklists = [] - json_obj = self.client.fetch_json( - '/cards/' + self.id + '/checklists', ) - for cl in json_obj: - checklists.append(Checklist(self.client, self.checked, cl, - trello_card=self.id)) - return checklists - - def fetch_actions(self, action_filter='createCard'): - """ - Fetch actions for this card can give more argv to action_filter, - split for ',' json_obj is list - """ - json_obj = self.client.fetch_json( - '/cards/' + self.id + '/actions', - query_params={'filter': action_filter}) - self.actions = json_obj - - - def attriExp(self, multiple): - """ - Provides the option to explore what comes from trello - :multiple is one of the attributes of GET /1/cards/[card id or shortlink]/actions - """ - self.fetch_actions(multiple) - return self.actions - - def listCardMove_date(self): - """ - Will return the history of transitions of a card from one list to another - The lower the index the more resent the historical item - - It returns a list of lists. The sublists are triplates of - starting list, ending list and when the transition occured. - """ - self.fetch_actions('updateCard:idList') - res =[] - for idx in self.actions: - date_str = idx['date'] - dateDate = dateparser.parse(date_str) - strLst = idx['data']['listBefore']['name'] - endLst = idx['data']['listAfter']['name'] - res.append([strLst,endLst,dateDate]) - return res - - @property - def latestCardMove_date(self): - """ - returns the date of the last card transition - - """ - self.fetch_actions('updateCard:idList') - date_str = self.actions[0]['date'] - return dateparser.parse(date_str) - - @property - def create_date(self): - """Will return the creation date of the card. - - WARNING: if the card was create via convertion of a checklist item - it fails. attriExp('convertToCardFromCheckItem') allows to - test for the condition. - """ - self.fetch_actions() - date_str = self.actions[0]['date'] - return dateparser.parse(date_str) - - @property - def due_date(self): - return dateparser.parse(self.due) if self.due else '' - - def set_name(self, new_name): - """ - Update the name on the card to :new_name: - """ - self._set_remote_attribute('name', new_name) - self.name = new_name - - def set_description(self, description): - self._set_remote_attribute('desc', description) - self.desc = description - - def set_due(self, due): - """Set the due time for the card - - :title: due a datetime object - """ - datestr = due.strftime('%Y-%m-%dT%H:%M:%S') - self._set_remote_attribute('due', datestr) - self.due = datestr - - def set_pos(self, pos): - """ - Update card position in list - - :pos: 'top', 'bottom' or int - """ - self._set_remote_attribute('pos', pos) - self.pos = pos - - def set_closed(self, closed): - self._set_remote_attribute('closed', closed) - self.closed = closed - - def delete(self): - # Delete this card permanently - self.client.fetch_json( - '/cards/' + self.id, - http_method='DELETE', ) - - def assign(self, member_id): - self.client.fetch_json( - '/cards/' + self.id + '/members', - http_method='POST', - post_args={'value': member_id, }) - - def subscribe(self): - self.client.fetch_json( - '/cards/' + self.id + '/subscribed', - http_method='PUT', - post_args={'value': True, }) - - def comment(self, comment_text): - """Add a comment to a card.""" - self.client.fetch_json( - '/cards/' + self.id + '/actions/comments', - http_method='POST', - post_args={'text': comment_text, }) - - def add_label(self, label): - self.client.fetch_json( - '/cards/' + self.id +'/idLabels', - http_method='POST', - post_args={'value': label.id}) - - def attach(self, name=None, mimeType=None, file=None, url=None): - """ - Add an attachment to the card. The attachment can be either a - file or a url. Setting the name and/or mime type is optional. - :param name: The name of the attachment - :param mimeType: mime type for the attachement - :param file: a file-like, binary object that supports read() - :param url: a URL pointing to the resource to be attached - """ - if (file and url) or (not file and not url): - raise Exception('Please provide either a file or url, and not both!') - - kwargs = {} - if file: - kwargs['files'] = dict(file=(name, file, mimeType)) - else: - kwargs['name'] = name - kwargs['mimeType'] = mimeType - kwargs['url'] = url - - self._post_remote_data( - 'attachments', **kwargs - ) - - def change_list(self, list_id): - self.client.fetch_json( - '/cards/' + self.id + '/idList', - http_method='PUT', - post_args={'value': list_id, }) - - def change_board(self, board_id, list_id=None): - args = {'value': board_id, } - if list_id is not None: - args['idList'] = list_id - self.client.fetch_json( - '/cards/' + self.id + '/idBoard', - http_method='PUT', - post_args=args) - - def add_checklist(self, title, items, itemstates=None): - - """Add a checklist to this card - - :title: title of the checklist - :items: a list of the item names - :itemstates: a list of the state (True/False) of each item - :return: the checklist - """ - if itemstates is None: - itemstates = [] - - json_obj = self.client.fetch_json( - '/cards/' + self.id + '/checklists', - http_method='POST', - post_args={'name': title}, ) - - cl = Checklist(self.client, [], json_obj, trello_card=self.id) - for i, name in enumerate(items): - try: - checked = itemstates[i] - except IndexError: - checked = False - cl.add_checklist_item(name, checked) - - self.fetch() - return cl - - def _set_remote_attribute(self, attribute, value): - self.client.fetch_json( - '/cards/' + self.id + '/' + attribute, - http_method='PUT', - post_args={'value': value, }, ) - - def _post_remote_data(self, attribute, files=None, **kwargs): - self.client.fetch_json( - '/cards/' + self.id + '/' + attribute, - http_method='POST', - files=files, - post_args=kwargs ) - -class Label(object): - """ - Class representing a Trello Label. - """ - def __init__(self, client, label_id, name, color=""): - self.client = client - self.id = label_id - self.name = name - self.color = color - - @classmethod - def from_json(cls, board, json_obj): - """ - Deserialize the label json object to a Label object - - :board: the parent board the label is on - :json_obj: the label json object - """ - label = Label(board.client, label_id=json_obj['id'], name=json_obj['name'].encode('utf-8'), color=json_obj['color']) - return label - - @classmethod - def from_json_list(cls, board, json_objs): - return [cls.from_json(board, obj) for obj in json_objs] - - def __repr__(self): - return '