Exemplo n.º 1
0
    def add_labels(self, label_choices):
        '''Give the user a way to toggle labels on this card by their
        name rather than by a numeric selection interface. Using
        prompt_toolkit, we have automatic completion which makes
        things substantially faster without having to do a visual
        lookup against numeric IDs

        Options:
            label_choices: str->trello.Label, the names and objects of labels on this board
        '''
        print('Enter a tag name to toggle it, <TAB> completes. Ctrl+D to exit')
        while True:
            label_completer = FuzzyWordCompleter(label_choices.keys())
            userinput = prompt('gtd.py > tag > ',
                               completer=label_completer).strip()
            if userinput not in label_choices.keys():
                if prompt_for_confirmation(
                        f'Unrecognized tag name {userinput}, would you like to create it?',
                        False):
                    label = self.connection.main_board().add_label(
                        userinput, 'green')
                    self.add_label(label.id)
                    click.echo(
                        f'Added tag {label.name} to board {self.connection.main_board().name} and to the card {self}'
                    )
                    label_choices[userinput] = label
            else:
                label_obj = label_choices[userinput]
                try:
                    self.add_label(label_obj.id)
                    click.secho(f'Added tag {userinput}', fg='green')
                except trello.exceptions.ResourceUnavailable:
                    # This label already exists on the card so remove it
                    self.remove_label(label_obj.id)
                    click.secho(f'Removed tag {userinput}', fg='red')
Exemplo n.º 2
0
def delete_cards(config, force, noninteractive, tags, no_tags, match, listname, attachments, has_due):
    '''Delete a set of cards specified
    '''
    _, board = BoardTool.start(config)
    display = Display(config.color)
    if config.banner and not json:
        display.banner()
    cards = BoardTool.filter_cards(
        board,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due
    )
    method = 'delete' if force else 'archive'
    if noninteractive:
        if force:
            [c.delete() for c in cards]
        else:
            [c.set_closed(True) for c in cards]
    else:
        for card in cards:
            display.show_card(card)
            if prompt_for_confirmation('Delete this card?'):
                if force:
                    card.delete()
                else:
                    card.set_closed(True)
                click.secho('Card {}d!'.format(method), fg='red')
Exemplo n.º 3
0
def delete_cards(config, force, noninteractive, tags, no_tags, match, listname, attachments, has_due):
    '''Delete a set of cards specified
    '''
    _, board, _ = BoardTool.start(config)
    display = Display(config.color)
    if config.banner and not json:
        display.banner()
    cards = BoardTool.filter_cards(
        board,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due
    )
    method = 'delete' if force else 'archive'
    if noninteractive:
        if force:
            [c.delete() for c in cards]
        else:
            [c.set_closed(True) for c in cards]
    else:
        for card in cards:
            display.show_card(card)
            if prompt_for_confirmation('Delete this card?'):
                if force:
                    card.delete()
                else:
                    card.set_closed(True)
                click.secho('Card {}d!'.format(method), fg='red')
Exemplo n.º 4
0
def delete_cards(ctx, force, noninteractive, tags, no_tags, match, listname,
                 attachments, has_due, status):
    '''Delete a set of cards specified
    '''
    ctx.display.banner()
    cards = CardView.create(
        ctx,
        status=status,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due,
    )
    method = 'delete' if force else 'archive'
    if noninteractive:
        if force:
            [c.delete() for c in cards]
        else:
            [c.set_closed(True) for c in cards]
    else:
        for card in cards:
            ctx.display.show_card(card)
            if prompt_for_confirmation('Delete this card?'):
                if force:
                    card.delete()
                else:
                    card.set_closed(True)
                click.secho(f'Card {method}d!', fg='red')
Exemplo n.º 5
0
def batch_attach(ctx):
    '''Extract HTTP links from card titles'''
    cards = CardView.create(ctx, status='visible', title_regex=VALID_URL_REGEX)
    ctx.display.banner()
    for card in cards:
        ctx.display.show_card(card)
        if prompt_for_confirmation('Attach title?', True):
            card.title_to_link()
Exemplo n.º 6
0
def delete_tag(ctx, name, noninteractive):
    '''Delete a tag by name'''
    tags = [l for l in ctx.board.get_labels() if l.name == name]
    if not tags:
        click.secho(f'No such tag {name}', fg='red')
    for t in tags:
        if noninteractive or prompt_for_confirmation(
                f'Delete tag "{t.name}"?'):
            ctx.board.delete_label(t.id)
            click.secho(f'Deleted {t.name}!', fg='green')
Exemplo n.º 7
0
def delete_list(ctx, name, noninteractive):
    '''Delete a list by name'''
    lists = [l for l in ctx.board.get_lists('open') if l.name == name]
    if not lists:
        click.secho(f'No such list {name}', fg='red')
    for l in lists:
        if noninteractive or prompt_for_confirmation(
                f'Close list "{l.name}"?'):
            l.close()
            click.secho(f'Closed {l.name}!', fg='green')
Exemplo n.º 8
0
def batch_attach(config):
    '''Extract HTTP links from card titles'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(board, title_regex=VALID_URL_REGEX)
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Attach title?', True):
            CardTool.title_to_link(card)
Exemplo n.º 9
0
def delete_list(config, name, noninteractive):
    '''Delete a list by name'''
    _, board = BoardTool.start(config)
    lists = [l for l in board.get_lists('open') if l.name == name]
    if not lists:
        click.secho('No such list {}'.format(name), fg='red')
    for l in lists:
        if noninteractive or prompt_for_confirmation('Close list "{}"?'.format(
                l.name)):
            l.close()
            click.secho('Closed {}!'.format(l.name), fg='green')
Exemplo n.º 10
0
def delete_tag(config, name, noninteractive):
    '''Delete a tag by name'''
    _, board = BoardTool.start(config)
    tags = [l for l in board.get_labels() if l.name == name]
    if not tags:
        click.secho('No such tag {}'.format(name), fg='red')
    for t in tags:
        if noninteractive or prompt_for_confirmation('Delete tag "{}"?'.format(
                t.name)):
            board.delete_label(t.id)
            click.secho('Deleted {}!'.format(t.name), fg='green')
Exemplo n.º 11
0
def batch_attach(config):
    '''Extract HTTP links from card titles'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(board, title_regex=VALID_URL_REGEX)
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Attach title?', True):
            CardTool.title_to_link(card)
Exemplo n.º 12
0
def delete_lists(config, name, noninteractive):
    '''Delete lists containing the substring <name>
    '''
    _, board, _ = BoardTool.start(config)
    lists = [l for l in board.get_lists('open') if name in l.name]
    if noninteractive:
        [l.set_closed() for l in lists]
    else:
        for l in lists:
            if prompt_for_confirmation('Close this list?'):
                l.set_closed()
                click.secho('Closed!', fg='green')
Exemplo n.º 13
0
def delete_lists(config, name, noninteractive):
    '''Delete lists containing the substring <name>
    '''
    _, board = BoardTool.start(config)
    lists = [l for l in board.get_lists('open') if name in l.name]
    if noninteractive:
        [l.set_closed() for l in lists]
    else:
        for l in lists:
            if prompt_for_confirmation('Close this list?'):
                l.set_closed()
                click.secho('Closed!', fg='green')
Exemplo n.º 14
0
def batch_move(config, tags, no_tags, match, listname, attachments, has_due):
    '''Change the list of each card selected'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(board,
                                   tags=tags,
                                   no_tags=no_tags,
                                   title_regex=match,
                                   list_regex=listname,
                                   has_attachments=attachments,
                                   has_due_date=has_due)
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Want to move this one?', True):
            CardTool.move_to_list(card)
Exemplo n.º 15
0
def batch_due(config, tags, no_tags, match, listname, attachments, has_due):
    '''Set due date for all cards selected'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(board,
                                   tags=tags,
                                   no_tags=no_tags,
                                   title_regex=match,
                                   list_regex=listname,
                                   has_attachments=attachments,
                                   has_due_date=has_due)
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Set due date?'):
            CardTool.set_due_date(card)
Exemplo n.º 16
0
def batch_due(ctx, tags, no_tags, match, listname, attachments, has_due,
              status):
    '''Set due date for all cards selected'''
    cards = CardView.create(
        ctx,
        status=status,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due,
    )
    ctx.display.banner()
    for card in cards:
        ctx.display.show_card(card)
        if prompt_for_confirmation('Set due date?'):
            card.set_due_date()
Exemplo n.º 17
0
def batch_move(ctx, tags, no_tags, match, listname, attachments, has_due,
               status):
    '''Change the list of each card selected'''
    cards = CardView.create(
        ctx,
        status=status,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due,
    )
    ctx.display.banner()
    for card in cards:
        ctx.display.show_card(card)
        if prompt_for_confirmation('Want to move this one?', True):
            card.move_to_list(ctx._list_choices)
Exemplo n.º 18
0
def batch_due(config, tags, no_tags, match, listname, attachments, has_due):
    '''Set due date for all cards selected'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(
        board,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due
    )
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Set due date?'):
            CardTool.set_due_date(card)
Exemplo n.º 19
0
def batch_move(config, tags, no_tags, match, listname, attachments, has_due):
    '''Change the list of each card selected'''
    connection, board = BoardTool.start(config)
    cards = BoardTool.filter_cards(
        board,
        tags=tags,
        no_tags=no_tags,
        title_regex=match,
        list_regex=listname,
        has_attachments=attachments,
        has_due_date=has_due
    )
    display = Display(config.color)
    if config.banner:
        display.banner()
    for card in cards:
        display.show_card(card)
        if prompt_for_confirmation('Want to move this one?', True):
            CardTool.move_to_list(card)
Exemplo n.º 20
0
    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'
                )