class BoardFetcher(Fetcher): # Date format of the actions and comments DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%f' # Create a fetcher from a board def __init__(self, board, debug=True): super(BoardFetcher, self).__init__(board) self.initializer = Initializer(self.creator, debug=debug) self.trello_client = self.initializer.trello_client self.trello_board = TrelloBoard(client=self.trello_client, board_id=self.board.uuid) self.initializer.fetch_members(board, self.trello_board) self.trello_board.fetch() self.debug = debug # Fetch data of this board def fetch(self, debug=False): self._start_fetch() try: with transaction.atomic(): self._truncate() self._fetch_labels() self._fetch_cards(debug=debug) self._create_card_reports() if self.board.url != self.trello_board.url: self.board.url = self.trello_board.url self.board.last_fetch_datetime = timezone.now() self.board.last_activity_datetime = self.board.last_fetch_datetime self.board.save() except Exception as e: raise finally: self._end_fetch() # Fetch the labels of this board def _fetch_labels(self): trello_labels = self.trello_board.get_labels() for trello_label in trello_labels: LabelUpdater.update(trello_label, self.board) self.labels = self.board.labels.all() # Return the Trello Cards def _fetch_cards(self, debug=False): trello_cards = self._fetch_trello_cards() trello_movements_by_card = self._fetch_trello_card_movements_by_card() trello_comments_by_card = self._fetch_trello_comments_by_card() card_fetcher = CardFetcher(self, trello_cards, trello_movements_by_card, trello_comments_by_card) self.cards = card_fetcher.fetch() # Fetch the card repots of this board def _create_card_reports(self, debug=False): workflows = self.board.workflows.all() # Card stats computation for card in self.cards: trello_card = card.trello_card # Label assignment to each card label_uuids = trello_card.idLabels card_labels = self.labels.filter(uuid__in=label_uuids) card.labels.clear() for card_label in card_labels: card.labels.add(card_label) # Workflow card reports for workflow in workflows: self._fetch_workflow(workflow, [card]) # Return the actions of the board grouped by the uuid of each card def _fetch_trello_cards(self): # Fetch as long as there is a result cards = [] must_retry = True # Limit of cards in each request limit = 1000 # We will be making request from the earliest to the oldest actions, so we will use before parameter # while since is always None filters = {'filter': 'all', 'fields': 'all', 'attachments': "true"} # While there are more than 1000 cards, make another request for the previous 1000 cards while must_retry: cards_i = self.trello_board.get_cards(filters=filters) cards += cards_i must_retry = len(cards_i) == limit if must_retry: # We get the maximum date of these cards and use it to paginate, # asking Trello to give us the actions since that date before = BoardFetcher._get_before_str_from_cards(cards_i) filters["before"] = before # There should be no need to assure uniqueness of the cards but it's better to be sure that # we have no repeated actions cards_dict = {card.id: card for card in cards} unique_cards = cards_dict.values() # Return the cards return unique_cards # Get the since parameter based on the actions we've got. That is, get the max date of the actions and prepare # the since parameter adding one microsecond to that date @staticmethod def _get_before_str_from_cards(cards): # Get max date and parse it from ISO format min_date_str = BoardFetcher._get_min_date_from_cards(cards) min_date = dateutil.parser.parse(min_date_str) # Get next possible date (max_date + 1 millisecond) before_date = min_date before_date_str = before_date.strftime( BoardFetcher.DATE_FORMAT)[:-3] + "Z" return before_date_str # Get the min date of a list of cards @staticmethod def _get_min_date_from_cards(cards): min_date = None for card in cards: if min_date is None or min_date > card.created_date: min_date = card.created_date return min_date # Return the comments of the board grouped by the uuid of each card def _fetch_trello_comments_by_card(self): comments_by_card = self._fetch_trello_actions_by_card( action_filter="commentCard", debug=True) return comments_by_card # Return the card movements of the board grouped by the uuid of each card def _fetch_trello_card_movements_by_card(self): comments_by_card = self._fetch_trello_actions_by_card( action_filter="updateCard:idList") return comments_by_card # Return the actions of the board grouped by the uuid of each card def _fetch_trello_actions_by_card(self, action_filter, limit=1000, debug=False): # Fetch as long as there is a result actions = [] must_retry = True # We will be making request from the earliest to the oldest actions, so we will use before parameter # while since is always None since = None before = None # While there are more than 1000 actions, make another request for the previous 1000 actions while must_retry: actions_i = self.trello_board.fetch_actions(action_filter, limit, before=before, since=since) actions += actions_i must_retry = len(actions_i) == limit if must_retry: # We get the maximum date of these actions and use it to paginate, # asking Trello to give us the actions since that date before = BoardFetcher._get_before_str_from_actions(actions_i) # There should be no need to assure uniqueness of the actions but it's better to be sure that # we have no repeated actions actions_dict = {action["id"]: action for action in actions} unique_actions = actions_dict.values() # Group actions by card actions_by_card = {} for action in unique_actions: card_uuid = action[u"data"][u"card"][u"id"] if card_uuid not in actions_by_card: actions_by_card[card_uuid] = [] actions_by_card[card_uuid].append(action) # Return the actions grouped by card return actions_by_card # Get the since parameter based on the actions we've got. That is, get the max date of the actions and prepare # the since parameter adding one microsecond to that date @staticmethod def _get_before_str_from_actions(actions): # Get max date and parse it from ISO format min_date_str = BoardFetcher._get_min_date_from_actions(actions) min_date = dateutil.parser.parse(min_date_str) # Get next possible date (max_date + 1 millisecond) before_date = min_date before_date_str = before_date.strftime( BoardFetcher.DATE_FORMAT)[:-3] + "Z" return before_date_str # Get the min date of a list of actions # We are not sure about the actions order, so each time we make a fetch of the actions of this board, we have to get # the max date to make other fetch since that max date @staticmethod def _get_min_date_from_actions(actions): min_date = None for action in actions: if min_date is None or min_date > action["date"]: min_date = action["date"] return min_date # Fetch data for this workflow, creating a workflow report def _fetch_workflow(self, workflow, cards): workflow_lists = workflow.workflow_lists.all() development_lists = { workflow_list.list.uuid: workflow_list.list for workflow_list in workflow.workflow_lists.filter( is_done_list=False) } done_lists = { workflow_list.list.uuid: workflow_list.list for workflow_list in workflow.workflow_lists.filter( is_done_list=True) } workflow_card_reports = [] for card in cards: trello_card = card.trello_card lead_time = None cycle_time = None # Lead time and cycle time only should be computed when the card is done if not card.is_closed and trello_card.idList in done_lists: # Lead time in this workflow for this card lead_time = sum([ list_stats["time"] for list_uuid, list_stats in trello_card.stats_by_list.items() ]) # Cycle time in this workflow for this card cycle_time = sum([ list_stats["time"] if list_uuid in development_lists else 0 for list_uuid, list_stats in trello_card.stats_by_list.items() ]) workflow_card_report = WorkflowCardReport( board=self.board, workflow=workflow, card=card, cycle_time=cycle_time, lead_time=lead_time) workflow_card_report.save() workflow_card_reports.append(workflow_card_report) return workflow_card_reports
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, source_board=None, organization_id=None, permission_level='private', default_lists=True): """Create board :param board_name: Name of the board to create :param source_board: Optional Board to copy :param permission_level: Permission level, defaults to private :rtype: Board """ post_args = { 'name': board_name, 'prefs_permissionLevel': permission_level } if source_board is not None: post_args['idBoardSource'] = source_board.id if organization_id is not None: post_args['idOrganization'] = organization_id if not default_lists: post_args['defaultLists'] = False obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=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 labels_according_to_legend(key, token, board_id): """ Coloring of existed cards according to legend """ client = TrelloClient(key, token) board = Board(client=client, board_id=board_id) list_of_lists = board.list_lists(list_filter='open') creators = my_get_members(key, token, board_id) legend_id = '' legend_labels = {} list_of_cards = [] for list in range(len(list_of_lists)): if list_of_lists[list].name == 'Legend': legend_id = list_of_lists[list].id list_of_lists.pop(list) break for card in my_get_cards(legend_id, key, token).keys(): for label in my_get_label(key, token, my_get_cards(legend_id, key, token)[card]): legend_labels[creators[card]] = my_get_label( key, token, my_get_cards(legend_id, key, token)[card])[label] for i in list_of_lists: for j in i.list_cards(): list_of_cards.append(j) for card_id in list_of_cards: id = card_id.id url = f"https://api.trello.com/1/cards/{id}/actions" params_key_and_token = { 'key': key, 'token': token, 'filter': ['updateCard', 'createCard'] } response = requests.get(url, params=params_key_and_token) client = Cards(key, token) try: client.new_idLabel( id, legend_labels[response.json()[0]['idMemberCreator']]) except: pass
def __init__(self, board, debug=True): super(BoardFetcher, self).__init__(board) self.initializer = Initializer(self.creator, debug=debug) self.trello_client = self.initializer.trello_client self.trello_board = TrelloBoard(client=self.trello_client, board_id=self.board.uuid) self.initializer.fetch_members(board, self.trello_board) self.trello_board.fetch() self.debug = debug
def create_board(self, board, lists=None): # Check if board exists if board.uuid: raise ValueError(u"This board already exists") # Connect to Trello and save the new board trello_board = TrelloBoard(client=self.trello_client) trello_board.name = board.name trello_board.description = board.description trello_board.save() # Trello id attribute assignment board.uuid = trello_board.id board.save() # Creation of lists if lists is not None and len(lists) > 0: for list_name in lists: self.create_list(board, list_name, list_position="bottom") # Fetch initial lists self.init(board.uuid)
def get_boards(self, list_filter): """Get boards using filter :rtype: list of Board """ from trello.board import Board 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 add_board(self, board_name, organization_id=None): '''Create board :rtype: Board ''' post_args = {'name': board_name} if organization_id: post_args['idOrganization'] = organization_id obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=obj)
def get_board(self, field_name): """Get board :rtype: list of Board """ from trello.board import Board 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_boards(self, list_filter): """Get boards using filter :rtype: list of 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: list of 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 list_boards(): boards_response = request.get('1/members/me/boards', {'filter': 'open'}) boards = [] for board_data in boards_response: boards.append(Board(board_data)) print('Open boards for {}{}{}:'.format( attr(1), request.get('1/members/me', {'fields': 'username'})['username'], attr(0))) for board in boards: with indent(): board.short_print()
def add_board(self, board_name, source_board=None): '''Create board :param board_name: Name of the board to create :param source_board: Optional Board to copy :rtype: Board ''' post_args = {'name': board_name} if source_board is not None: post_args['idBoardSource'] = source_board.id obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=obj)
def add_board(self, board_name, source_board=None, organization_id=None): '''Create board :param board_name: Name of the board to create :param source_board: Optional Board to copy :rtype: Board ''' post_args={'name': board_name} if source_board is not None: post_args['idBoardSource'] = source_board.id if organization_id is not None: post_args['idOrganization'] = organization_id obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=obj)
def add_board(self, board_name, source_board=None, **arguments): '''Create board :param board_name: Name of the board to create :param source_board: Optional Board to copy :param parameters: Arguments to the API call :rtype: Board ''' post_args={'name': board_name} post_args.update(arguments) if source_board is not None: post_args['idBoardSource'] = source_board.id obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=obj)
def post(self, request): try: data = request.data logging.info(data) board = Board.from_json(trello_client=g.trello_client, json_obj=request.data['model']) action_type_str = self._get_action_type(data) action_type_enum = self._get_enum(e.ActionType, action_type_str) if action_type_enum: self._map_n_act_n_save_log(action_type_enum, data, board=board) logging.info('<LogSaved> for {}'.format(action_type_str)) else: logging.info('<LogNOTSaved> for {}'.format(action_type_str)) return HttpResponse('OK') except: return h.error_response(500, "Internal server error")
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_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 add_board(self, board_name, source_board=None, organization_id=None, permission_level='private'): """Create board :param board_name: Name of the board to create :param source_board: Optional Board to copy :param permission_level: Permission level, defaults to private :rtype: Board """ post_args={'name': board_name, 'prefs_permissionLevel': permission_level} if source_board is not None: post_args['idBoardSource'] = source_board.id if organization_id is not None: post_args['idOrganization'] = organization_id obj = self.fetch_json('/boards', http_method='POST', post_args=post_args) return Board.from_json(self, json_obj=obj)
def list_boards(self, board_filter="all"): """ 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/?filter=%s' % board_filter) return [Board.from_json(self, json_obj=obj) for obj in json_obj]
def get_boards(self, list_filter): """Get boards using filter :rtype: list of Board """ from trello.board import Board from trello.organization import Organization json_obj = self.client.fetch_json('/members/' + self.id + '/boards', query_params={ 'lists': 'none', 'filter': list_filter }) organizations = { obj['idOrganization']: self.client.get_organization(obj['idOrganization']) for obj in json_obj if obj['idOrganization'] } return [ Board.from_json(trello_client=self.client, organization=organizations.get( obj['idOrganization']), json_obj=obj) for obj in json_obj ]
""" Created on Mon Jan 4 12:47:50 2021 @author: krish """ from trello import TrelloClient import random, requests from trello.board import Board from trello.base import TrelloBase from trello.compat import force_str api_key = '**************************************' api_secret = '**********************************************************' trelloboard = Board(name='python_trial') #print(trelloboard.__repr__.__str__) print(trelloboard.__repr__) class Boards(TrelloBase): def __init__(self, name): self.name = name def __repr__(self): return force_str(u'<Board %s>' % self.name) client = TrelloClient(api_key=api_key, api_secret=api_secret) all_boards = client.list_boards(board_filter='python_trial')
from trello.member import Member from trello.label import Label from trello.board import Board import trellovariables import re import sys import argparse #SETUP client = TrelloClient( api_key=trellovariables.trello_api['api_key'], api_secret=trellovariables.trello_api['api_secret'], ) #Get Board it_board = Board(client, trellovariables.ITBOARD) #Constants time position in card TIME_SPEND = 1 TIME_ESTIMATED = 2 def getCardTime(name, typetime): e = re.search("^.*?\((\d+)\/(\d+)\).*$", name) if e: return int(e.group(typetime)) return 0 def getMemberName(client, member_id): m = Member(client, member_id)
def search(self, query, partial_match=False, models=[], board_ids=[], org_ids=[], card_ids=[], cards_limit=10): """ Search trello given a query string. :param str query: A query string up to 16K characters :param bool partial_match: True means that trello will look for content that starts with any of the words in your query. :param list models: Comma-separated list of types of objects to search. This can be 'actions', 'boards', 'cards', 'members', or 'organizations'. The default is 'all' models. :param list board_ids: Comma-separated list of boards to limit search :param org_ids: Comma-separated list of organizations to limit search :param card_ids: Comma-separated list of cards to limit search :param cards_limit: The maximum number of cards to return (up to 1000) :return: All objects matching the search criterial. These can be Cards, Boards, Organizations, and Members. The attributes of the objects in the results are minimal; the user must call the fetch method on the resulting objects to get a full set of attributes populated. :rtype list: """ query_params = {'query': query} if partial_match: query_params['partial'] = 'true' # Limit search to one or more object types if models: query_params['modelTypes'] = models # Limit search to a particular subset of objects if board_ids: query_params['idBoards'] = board_ids if org_ids: query_params['idOrganizations'] = org_ids if card_ids: query_params['idCards'] = card_ids query_params['cards_limit'] = cards_limit # Request result fields required to instantiate class objects query_params['board_fields'] = ['name,url,desc,closed'] query_params['member_fields'] = ['fullName,initials,username'] query_params['organization_fields'] = ['name,url,desc'] json_obj = self.fetch_json('/search', query_params=query_params) if not json_obj: return [] results = [] board_cache = {} for board_json in json_obj.get('boards', []): # Cache board objects if board_json['id'] not in board_cache: board_cache[board_json['id']] = Board.from_json( self, json_obj=board_json) results.append(board_cache[board_json['id']]) for card_json in json_obj.get('cards', []): # Cache board objects if card_json['idBoard'] not in board_cache: board_cache[card_json['idBoard']] = Board( self, card_json['idBoard']) # Fetch the board attributes as the Board object created # from the card initially result lacks a URL and name. # This Board will be stored in Card.parent board_cache[card_json['idBoard']].fetch() results.append(Card.from_json(board_cache[card_json['idBoard']], card_json)) for member_json in json_obj.get('members', []): results.append(Member.from_json(self, member_json)) for org_json in json_obj.get('organizations', []): org = Organization.from_json(self, org_json) results.append(org) return results
def search(self, query, partial_match=False, models=[], board_ids=[], org_ids=[], card_ids=[]): """ Search trello given a query string. :param str query: A query string up to 16K characters :param bool partial_match: True means that trello will look for content that starts with any of the words in your query. :param list models: Comma-separated list of types of objects to search. This can be 'actions', 'boards', 'cards', 'members', or 'organizations'. The default is 'all' models. :param list board_ids: Comma-separated list of boards to limit search :param org_ids: Comma-separated list of organizations to limit search :param card_ids: Comma-separated list of cards to limit search :return: All objects matching the search criterial. These can be Cards, Boards, Organizations, and Members. The attributes of the objects in the results are minimal; the user must call the fetch method on the resulting objects to get a full set of attributes populated. :rtype list: """ query_params = {'query': query} if partial_match: query_params['partial'] = 'true' # Limit search to one or more object types if models: query_params['modelTypes'] = models # Limit search to a particular subset of objects if board_ids: query_params['idBoards'] = board_ids if org_ids: query_params['idOrganizations'] = org_ids if card_ids: query_params['idCards'] = card_ids # Request result fields required to instantiate class objects query_params['board_fields'] = ['name,url,desc,closed'] query_params['member_fields'] = ['fullName,initials,username'] query_params['organization_fields'] = ['name,url,desc'] json_obj = self.fetch_json('/search', query_params=query_params) if not json_obj: return [] results = [] board_cache = {} for board_json in json_obj.get('boards', []): # Cache board objects if board_json['id'] not in board_cache: board_cache[board_json['id']] = Board.from_json( self, json_obj=board_json) results.append(board_cache[board_json['id']]) for card_json in json_obj.get('cards', []): # Cache board objects if card_json['idBoard'] not in board_cache: board_cache[card_json['idBoard']] = Board( self, card_json['idBoard']) # Fetch the board attributes as the Board object created # from the card initially result lacks a URL and name. # This Board will be stored in Card.parent board_cache[card_json['idBoard']].fetch() results.append( Card.from_json(board_cache[card_json['idBoard']], card_json)) for member_json in json_obj.get('members', []): results.append(Member.from_json(self, member_json)) for org_json in json_obj.get('organizations', []): org = Organization.from_json(self, org_json) results.append(org) return results
from trello.base import TrelloBase from trello.trelloclient import TrelloClient from trello.board import Board from trello.trellolist import List from trello.label import Label from . import enums as e trello_client = TrelloClient( api_key='8c38f7619e8c0dbd0af427a6f145538f', api_secret='01d89258fa41a284468d9983eaec2a1f', token='46a437ceea33ce9c130d9ad4aea6bda1b7262225be966a858753d26f50bf4a8a', token_secret= '1af4d3041e6caa4165cfe1bace2115749db8d060d7e9dd67218a9e9667d0f8c3') board = Board(client=trello_client, board_id=e.Board.INSTAGRAM.value) list_complaints = List(board=board, list_id=e.List.COMPLAINTS.value) list_on_progress = List(board=board, list_id=e.List.ON_PROGRESS.value) list_done = List(board=board, list_id=e.List.DONE.value) labels = { 'transaksi': Label(trello_client, e.Label.TRANSAKSI.value, 'Transaksi'), 'produk': Label(trello_client, e.Label.PRODUCT.value, 'Product'), 'pengiriman': Label(trello_client, e.Label.PENGIRIMAN.value, 'Pengiriman'), 'servis': Label(trello_client, e.Label.SERVICE.value, 'Service'), 'pertanyaan': Label(trello_client, e.Label.PERTANYAAN.value, 'Pertanyaan'), 'misuh': Label(trello_client, e.Label.MISUH.value, 'Misuh'), 'lainnya': Label(trello_client, e.Label.LAINNYA.value, 'Lainnya') }