From 166cd32fb089968d7a1c2ae12fe7bf09a611f5f6 Mon Sep 17 00:00:00 2001 From: Mathias Kahl Date: Thu, 7 Aug 2014 15:41:04 +0200 Subject: [PATCH] Unified initialization of cards from JSON data. Added optionally lazy properties for comments and checklists. --- test/test_trello.py | 43 +++++++++++++++--- trello/__init__.py | 103 ++++++++++++++++++++++++++------------------ 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/test/test_trello.py b/test/test_trello.py index 808376c3..bf1ad3fd 100644 --- a/test/test_trello.py +++ b/test/test_trello.py @@ -61,7 +61,7 @@ def test30_list_attrs(self): self.assertIsNotNone(l.closed, msg="closed not provided") break # only need to test one board's lists - def test40_list_cards(self): + def test50_list_cards(self): boards = self._trello.list_boards() for b in boards: for l in b.all_lists(): @@ -75,7 +75,7 @@ def test40_list_cards(self): break pass - def test41_fetch_cards(self): + def test51_fetch_cards(self): """ Tests fetching all attributes for all cards """ @@ -92,7 +92,7 @@ def test41_fetch_cards(self): pass - def test50_add_card(self): + def test40_add_card(self): boards = self._trello.list_boards() board_id = None for b in boards: @@ -117,7 +117,7 @@ def test50_add_card(self): if not card: self.fail("No card created") - def test51_add_card(self): + def test41_add_card(self): boards = self._trello.list_boards() board_id = None for b in boards: @@ -144,6 +144,37 @@ def test51_add_card(self): if not card: self.fail("No card created") + def test42_add_card_with_comments(self): + boards = self._trello.list_boards() + board_id = None + for b in boards: + if b.name != os.environ['TRELLO_TEST_BOARD_NAME']: + continue + + for l in b.open_lists(): + try: + name = "Card with comments" + comment = "Hello World!" + card = l.add_card(name) + card.comment(comment) + card.fetch(True) + except Exception as e: + print(str(e)) + self.fail("Caught Exception adding card") + + self.assertIsNotNone(card, msg="card is None") + self.assertIsNotNone(card.id, msg="id not provided") + self.assertEquals(card.name, name) + self.assertEquals(card.description, '') + self.assertIsNotNone(card.closed, msg="closed not provided") + self.assertIsNotNone(card.url, msg="url not provided") + self.assertEquals(len(card.comments), 1) + self.assertEquals(card.comments[0]['data']['text'], comment) + break + break + if not card: + self.fail("No card created") + def test52_get_cards(self): boards = [board for board in self._trello.list_boards() if board.name == os.environ['TRELLO_TEST_BOARD_NAME']] @@ -151,13 +182,15 @@ def test52_get_cards(self): board = boards[0] cards = board.get_cards() - self.assertEqual(len(cards), 2, msg="Unexpected number of cards in testboard") + self.assertEqual(len(cards), 3, msg="Unexpected number of cards in testboard") for card in cards: if card.name == 'Testing from Python': self.assertEqual(card.description, 'Description goes here') elif card.name == 'Testing from Python - no desc': self.assertEqual(card.description, '') + elif card.name == 'Card with comments': + self.assertEqual(card.description, '') else: self.fail(msg='Unexpected card found') diff --git a/trello/__init__.py b/trello/__init__.py index c925dc06..629808b4 100644 --- a/trello/__init__.py +++ b/trello/__init__.py @@ -322,20 +322,7 @@ def get_cards(self, filters=None): query_params=filters ) - cards = list() - for card_json in json_obj: - card = Card(self, card_json['id'], - name=card_json['name']) - - for card_key, card_val in card_json.items(): - if card_key in ['id', 'name']: - continue - - setattr(card, card_key, card_val) - - cards.append(card) - - return cards + return list([Card.from_json(json, self) for json in json_obj]) def fetch_actions(self, action_filter): json_obj = self.client.fetch_json( @@ -373,15 +360,7 @@ def fetch(self): def list_cards(self): """Lists all cards in this list""" json_obj = self.client.fetch_json('/lists/' + self.id + '/cards') - cards = list() - for c in json_obj: - card = Card(self, c['id'], name=c['name']) - card.description = c.get('desc', '') - card.closed = c['closed'] - card.url = c['url'] - card.member_ids = c['idMembers'] - cards.append(card) - return cards + return list([Card.from_json(json, self) for json in json_obj]) def add_card(self, name, desc=None): """Add a card to this list @@ -393,13 +372,7 @@ def add_card(self, name, desc=None): '/lists/' + self.id + '/cards', http_method='POST', post_args={'name': name, 'idList': self.id, 'desc': desc}, ) - card = Card(self, json_obj['id']) - card.name = json_obj['name'] - card.description = json_obj.get('desc', '') - card.closed = json_obj['closed'] - card.url = json_obj['url'] - card.member_ids = json_obj['idMembers'] - return card + return Card.from_json(json_obj, self) def fetch_actions(self, action_filter): """ @@ -453,12 +426,30 @@ def description(self): @property def date_last_activity(self) -> datetime: - return self._dateLastActivity + return self.dateLastActivity @description.setter def description(self, value): self.desc = value + @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, trello_list, card_id, name=''): """ :trello_list: reference to the parent list @@ -469,14 +460,34 @@ def __init__(self, trello_list, card_id, name=''): self.id = card_id self.name = name + @staticmethod + def from_json(json_obj, trello_list, eager=False): + card = Card(trello_list, json_obj['id']) + card.init(json_obj, eager) + return card + def __repr__(self): return '' % self.name - def fetch(self): - """Fetch all attributes for this card""" + 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.init(json_obj, eager) + + def init(self, json_obj, eager=True): + """ + Initializes the card from its JSON representation as obtained through the Trello API. + :param json_obj: + :param eager: If eager is true comments and checklists will be fetched immediately, otherwise on demand + :return: + """ + + self.id = json_obj['id'] self.name = json_obj['name'] self.description = json_obj.get('desc', '') self.closed = json_obj['closed'] @@ -489,21 +500,27 @@ def fetch(self): self.badges = json_obj['badges'] self.due = json_obj['due'] self.checked = json_obj['checkItemStates'] - self._dateLastActivity = dateparser.parse(json_obj['dateLastActivity']) + self.dateLastActivity = dateparser.parse(json_obj['dateLastActivity']) - self.checklists = [] - if self.badges['checkItems'] > 0: - json_obj = self.client.fetch_json( - '/cards/' + self.id + '/checklists', ) - for cl in json_obj: - self.checklists.append(Checklist(self.client, self.checked, cl, - trello_card=self.id)) + self._checklists = self.fetch_checklists() if eager else None + self._comments = self.fetch_comments() if eager else None - self.comments = [] + def fetch_comments(self): + comments = [] if self.badges['comments'] > 0: - self.comments = self.client.fetch_json( + comments = self.client.fetch_json( '/cards/' + self.id + '/actions', query_params={'filter': 'commentCard'}) + return comments + + def fetch_checklists(self): + checklists = [] + if self.badges['checkItems'] > 0: + 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'): """