def __init__(self, config: Configuration): self.config = config self.connection = TrelloConnection(config) self.display = Display(config, self.connection) self.board = self.connection.main_board() # Cached state for card_repl self._list_choices = build_name_lookup( self.connection.main_board().get_lists('open')) self._label_choices = build_name_lookup( self.connection.main_board().get_labels(limit=200))
def start(config): connection = TrelloConnection(config) board = BoardTool.get_main_board(connection, config) current_user = BoardTool.get_current_user(connection, config) return connection, board, current_user
class CLIContext: '''CLIContext is a container for the commonly used objects in each gtd command. It is passed around as an argument injected by click after the cli() function runs. Any reference to "ctx" as a parameter in this file is an instance of CLIContext. config: todo.configuration.Configuration connection: todo.connection.TrelloConnection display: todo.display.Display ''' def __init__(self, config: Configuration): self.config = config self.connection = TrelloConnection(config) self.display = Display(config, self.connection) self.board = self.connection.main_board() # Cached state for card_repl self._list_choices = build_name_lookup( self.connection.main_board().get_lists('open')) self._label_choices = build_name_lookup( self.connection.main_board().get_labels(limit=200)) def card_repl(self, card: dict) -> bool: '''card_repl displays a command-prompt based UI for modifying a card, with tab-completion and suggestions. It is the logic behind "gtd review" and the "-e" flag in "gtd add" It makes assumptions about what a user might want to do with a card: - Are there attachments? Maybe you want to open them. - Does there appear to be a URL in the title? You might want to attach it. - Are there no tags? Maybe you want to add some. Returns: boolean: move forwards or backwards in the deck of cards ''' on = Colors.yellow if self.config.color else '' off = Colors.reset if self.config.color else '' self.display.show_card(card) if self.config.prompt_for_open_attachments and card['badges'][ 'attachments']: if prompt_for_confirmation(f'{on}Open attachments?{off}', False): with DevNullRedirect(): for url in [ a['url'] for a in card.fetch_attachments() if a['url'] ]: webbrowser.open(url) if re.search(VALID_URL_REGEX, card['name']): if prompt_for_confirmation( f'{on}Link in title detected, want to attach it & rename?{off}', True): card.title_to_link() if self.config.prompt_for_untagged_cards and not card['labels']: print(f'{on}No tags on this card yet, want to add some?{off}') card.add_labels(self._label_choices) commands = { 'archive': 'mark this card as closed', 'attach': 'add, delete, or open attachments', 'change-list': 'move this to a different list on the same board', 'comment': 'add a comment to this card', 'delete': 'permanently delete this card', 'duedate': 'add a due date or change the due date', 'description': 'change the description of this card (desc)', 'help': 'display this help output (h)', 'move': 'move to a different board and list (m)', 'next': 'move to the next card (n)', 'open': 'open all links on this card (o)', 'prev': 'go back to the previous card (p)', 'print': 're-display this card', 'rename': 'change title of this card', 'tag': 'add or remove tags on this card (t)', 'unarchive': 'mark this card as open', 'quit': 'exit program', } command_completer = FuzzyWordCompleter(commands.keys()) while True: user_input = prompt('gtd.py > ', completer=command_completer) if user_input in ['q', 'quit']: raise GTDException(0) elif user_input in ['n', 'next']: return True elif user_input in ['p', 'prev']: return False elif user_input == 'print': card.fetch() self.display.show_card(card) elif user_input in ['o', 'open']: with DevNullRedirect(): for url in [ a['url'] for a in card.fetch_attachments() if a['url'] is not None ]: webbrowser.open(url) elif user_input in ['desc', 'description']: card.change_description() elif user_input == 'delete': card.delete() print('Card deleted') return True elif user_input == 'attach': card.manipulate_attachments() elif user_input == 'archive': card.set_closed(True) print('Card archived') return True elif user_input == 'unarchive': card.set_closed(False) print('Card returned to board') elif user_input in ['t', 'tag']: card.add_labels(self._label_choices) elif user_input == 'rename': # TODO optional form 'rename New name of card' card.rename() elif user_input == 'duedate': card.set_due_date() elif user_input in ['h', 'help']: for cname, cdesc in commands.items(): print('{0:<16}| {1}{2}{3}'.format(cname, on, cdesc, off)) elif user_input == 'change-list': if card.move_to_list(self._list_choices): return True elif user_input in ['m', 'move']: self.move_between_boards(card) elif user_input == 'comment': # TODO Optional form 'comment Contents of a comment' new_comment = click.edit(text='<Comment here>', require_save=True) if new_comment: card.comment(new_comment) else: click.secho('Change the text & save to post the comment', fg='red') else: print( f'{on}{user_input}{off} is not a command, type "{on}help{off}" to view available commands' ) @return_on_eof def move_between_boards(self, card: Card) -> None: boards_by_name = self.connection.boards_by_name() board_name = prompt('gtd.py > move > board name? ', completer=FuzzyWordCompleter( boards_by_name.keys())) board_id = boards_by_name[board_name]['id'] lists_json = self.connection.trello.fetch_json( f'/boards/{board_id}/lists?cards=none&filter=open&fields=name') name_to_listid = {l['name']: l['id'] for l in lists_json} list_name = prompt( f'gtd.py > move > {board_name} > list name? ', completer=FuzzyWordCompleter(name_to_listid.keys()), ) card.change_board(board_id, list_id=name_to_listid[list_name]) click.secho(f'Changed list to {list_name} on {board_name}', fg='green')
def on_connection(): c = ConfigParser(parse_args=False).config return TrelloConnection(c)
def off_connection(): return TrelloConnection(None, False)
def start(config): connection = TrelloConnection(config) board = BoardTool.get_main_board(connection, config) return connection, board
def test_conn(config): if config.test_board is None: pytest.fail('Define test_board in your config to run gtd.py tests') return TrelloConnection(config)
def test_conn(config): if config.test_board is None: pytest.fail( 'Define test_board in your config to run gtd.py tests. This board will be deleted as part of the tests, so make sure the name does not conflict with an existing board name' ) return TrelloConnection(config)