Beispiel #1
0
def test_add_overwrite_label():
    """Test add command while specifying a label manually.

    Regression test against #4.
    """
    # use temporary config
    tmp_config = "[DATABASE]\nfile=/tmp/cobib_test_database.yaml\n"
    with open('/tmp/cobib_test_config.ini', 'w') as file:
        file.write(tmp_config)
    CONFIG.set_config(Path('/tmp/cobib_test_config.ini'))
    # ensure database file exists and is empty
    open('/tmp/cobib_test_database.yaml', 'w').close()
    # freshly read in database to overwrite anything that was read in during setup()
    read_database(fresh=True)
    # add some data
    commands.AddCommand().execute(['-b', './test/example_literature.bib'])
    # add potentially duplicate entry
    commands.AddCommand().execute([
        '-b', './test/example_duplicate_entry.bib', '--label',
        'duplicate_resolver'
    ])
    # compare with reference file
    with open('./test/example_literature.yaml', 'r') as expected:
        true_lines = expected.readlines()
    with open('./test/example_duplicate_entry.yaml', 'r') as extra:
        true_lines += extra.readlines()
    with open('/tmp/cobib_test_database.yaml', 'r') as file:
        for line, truth in zip_longest(file, true_lines):
            assert line == truth
    # clean up file system
    os.remove('/tmp/cobib_test_database.yaml')
    os.remove('/tmp/cobib_test_config.ini')
Beispiel #2
0
 def tui(tui):
     """See base class."""
     LOGGER.debug('Add command triggered from TUI.')
     # handle input via prompt
     tui.execute_command('add')
     # update database list
     LOGGER.debug('Updating list after Add command.')
     read_database(fresh=True)
     tui.update_list()
Beispiel #3
0
def test_tui_quit_prompt(setting, keys):
    """Test the prompt_before_quit setting of the TUI."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # set prompt_before_quit setting
    CONFIG.config['TUI']['prompt_before_quit'] = setting
    read_database()
    test_tui(None, keys, assert_quit, {'prompt': setting})
Beispiel #4
0
def test_tui_scrolling(keys, assertion, assertion_kwargs):
    """Test TUI scrolling behavior."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # overwrite database file
    CONFIG.config['DATABASE']['file'] = './test/scrolling_database.yaml'
    read_database()
    test_tui(None, keys, assertion, assertion_kwargs)
Beispiel #5
0
def setup():
    """Setup."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # NOTE: normally you would never trigger an Add command before reading the database but in this
    # controlled testing scenario we can be certain that this is fine
    AddCommand().execute(['-b', './test/dummy_scrolling_entry.bib'])
    read_database()
    yield setup
    DeleteCommand().execute(['dummy_entry_for_scroll_testing'])
Beispiel #6
0
def test_tui_open_menu():
    """Test the open prompt menu for multiple associated files."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # NOTE: normally you would never trigger an Add command before reading the database but in this
    # controlled testing scenario we can be certain that this is fine
    AddCommand().execute(['-b', './test/dummy_multi_file_entry.bib'])
    read_database()
    try:
        test_tui(None, 'o', assert_open, {})
    finally:
        DeleteCommand().execute(['dummy_multi_file_entry'])
Beispiel #7
0
 def tui(tui):
     """See base class."""
     LOGGER.debug('Edit command triggered from TUI.')
     # get current label
     label, _ = tui.get_current_label()
     # populate buffer with entry data
     EditCommand().execute([label])
     # update bibliography data
     read_database()
     # redraw total screen after closing external editor
     LOGGER.debug('Manually redrawing TUI to clear out any editor artefacts.')
     tui.resize_handler(None, None)
     # update database list
     tui.update_list()
Beispiel #8
0
def list_tags(args=None):
    """List all tags.

    Args:
        args (dict, optional): dictionary of keyword arguments.

    Returns:
        A list of all available tags in the database.
    """
    if not args:
        args = {}
    CONFIG.set_config(args.get('config', None))
    read_database()
    tags = list(CONFIG.config['BIB_DATA'].keys())
    return tags
Beispiel #9
0
def test_tui_config_color():
    """Test TUI color configuration."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # overwrite color configuration
    CONFIG.config['COLORS']['top_statusbar_bg'] = 'red'
    CONFIG.config['COLORS']['top_statusbar_fg'] = 'blue'
    CONFIG.config['COLORS']['bottom_statusbar_bg'] = 'green'
    CONFIG.config['COLORS']['bottom_statusbar_fg'] = 'magenta'
    CONFIG.config['COLORS']['cursor_line_bg'] = 'white'
    CONFIG.config['COLORS']['cursor_line_fg'] = 'black'
    read_database()
    test_tui(None, '', assert_config_color, {'colors': CONFIG.config['COLORS']})
Beispiel #10
0
def test_tui_config_keys(command, key):
    """Test TUI key binding configuration."""
    # ensure configuration is empty
    CONFIG.config = {}
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    # overwrite key binding configuration
    CONFIG.config['KEY_BINDINGS'][command] = key
    # NOTE: normally you would never trigger an Add command before reading the database but in this
    # controlled testing scenario we can be certain that this is fine
    AddCommand().execute(['-b', './test/dummy_scrolling_entry.bib'])
    read_database()
    try:
        test_tui(None, key, assert_show, {})
    finally:
        DeleteCommand().execute(['dummy_entry_for_scroll_testing'])
Beispiel #11
0
def list_filters(args=None):
    """List all filters.

    Args:
        args (dict, optional): dictionary of keyword arguments.

    Returns:
        A list of all field names available for filtering.
    """
    if not args:
        args = {}
    CONFIG.set_config(args.get('config', None))
    read_database()
    filters = set()
    for entry in CONFIG.config['BIB_DATA'].values():
        filters.update(entry.data.keys())
    return filters
Beispiel #12
0
 def tui(tui):
     """See base class."""
     LOGGER.debug('Delete command triggered from TUI.')
     if tui.selection:
         # use selection for command
         labels = list(tui.selection)
         tui.selection.clear()
     else:
         # get current label
         label, _ = tui.get_current_label()
         labels = [label]
     # delete selected entry
     DeleteCommand().execute(labels)
     # update database list
     LOGGER.debug('Updating list after Delete command.')
     read_database(fresh=True)
     tui.update_list()
     # if cursor line is below buffer height, move it one line back up
     if tui.current_line >= tui.buffer.height:
         tui.current_line -= 1
Beispiel #13
0
def test_add():
    """Test add command."""
    # use temporary config
    tmp_config = "[DATABASE]\nfile=/tmp/cobib_test_database.yaml\n"
    with open('/tmp/cobib_test_config.ini', 'w') as file:
        file.write(tmp_config)
    CONFIG.set_config(Path('/tmp/cobib_test_config.ini'))
    # ensure database file exists and is empty
    open('/tmp/cobib_test_database.yaml', 'w').close()
    # freshly read in database to overwrite anything that was read in during setup()
    read_database(fresh=True)
    # add some data
    commands.AddCommand().execute(['-b', './test/example_literature.bib'])
    # compare with reference file
    with open('/tmp/cobib_test_database.yaml', 'r') as file:
        with open('./test/example_literature.yaml', 'r') as expected:
            for line, truth in zip_longest(file, expected):
                assert line == truth
    # clean up file system
    os.remove('/tmp/cobib_test_database.yaml')
    os.remove('/tmp/cobib_test_config.ini')
Beispiel #14
0
    def execute(self, args, out=sys.stdout):
        """Add new entry.

        Adds new entries to the database.

        Args: See base class.
        """
        LOGGER.debug('Starting Add command.')
        parser = ArgumentParser(prog="add",
                                description="Add subcommand parser.")
        parser.add_argument("-l",
                            "--label",
                            type=str,
                            help="the label for the new database entry")
        parser.add_argument("-f",
                            "--file",
                            type=str,
                            nargs="+",
                            action="extend",
                            help="files associated with this entry")
        group_add = parser.add_mutually_exclusive_group()
        group_add.add_argument("-a",
                               "--arxiv",
                               type=str,
                               help="arXiv ID of the new references")
        group_add.add_argument("-b",
                               "--bibtex",
                               type=argparse.FileType('r'),
                               help="BibLaTeX bibliographic data")
        group_add.add_argument("-d",
                               "--doi",
                               type=str,
                               help="DOI of the new references")
        group_add.add_argument("-i",
                               "--isbn",
                               type=str,
                               help="ISBN of the new references")
        parser.add_argument(
            "tags",
            nargs=argparse.REMAINDER,
            help="A list of space-separated tags to associate with this entry."
            + "\nYou can use quotes to specify tags with spaces in them.")
        if not args:
            parser.print_usage(sys.stderr)
            sys.exit(1)

        try:
            largs = parser.parse_args(args)
        except argparse.ArgumentError as exc:
            print("{}: {}".format(exc.argument_name, exc.message),
                  file=sys.stderr)
            return

        new_entries = OrderedDict()

        edit_entries = False
        if largs.bibtex is not None:
            LOGGER.debug("Adding entries from BibLaTeX '%s'.", largs.bibtex)
            new_entries = Entry.from_bibtex(largs.bibtex)
        elif largs.arxiv is not None:
            LOGGER.debug("Adding entries from arXiv '%s'.", largs.arxiv)
            new_entries = Entry.from_arxiv(largs.arxiv)
        elif largs.doi is not None:
            LOGGER.debug("Adding entries from DOI '%s'.", largs.doi)
            new_entries = Entry.from_doi(largs.doi)
        elif largs.isbn is not None:
            LOGGER.debug("Adding entries from ISBN '%s'.", largs.isbn)
            new_entries = Entry.from_isbn(largs.isbn)
        elif largs.label is not None:
            LOGGER.warning(
                "No input to parse. Creating new entry '%s' manually.",
                largs.label)
            new_entries = {
                largs.label:
                Entry(
                    largs.label, {
                        'ID': largs.label,
                        'ENTRYTYPE':
                        CONFIG.config['FORMAT']['default_entry_type']
                    })
            }
            edit_entries = True
        else:
            LOGGER.error(
                "Neither an input to parse nor a label for manual creation specified!"
            )
            return

        if largs.label is not None:
            assert len(new_entries.values()) == 1
            for value in new_entries.values():
                # logging done by cobib/parser.py
                value.set_label = largs.label
            new_entries = OrderedDict(
                (largs.label, value) for value in new_entries.values())

        if largs.file is not None:
            assert len(new_entries.values()) == 1
            for value in new_entries.values():
                # logging done by cobib/parser.py
                value.set_file = largs.file

        if largs.tags != []:
            assert len(new_entries.values()) == 1
            for value in new_entries.values():
                # logging done by cobib/parser.py
                value.set_tags = largs.tags

        # Write the new entries to the database. This destructively overwrite the variable with a
        # list of labels of the entries which have actually been added to the database.
        new_entries = write_database(new_entries)

        if edit_entries:
            if largs.label not in new_entries:
                msg = f"You tried to add a new entry '{largs.label}' which already exists!\n" \
                    + f"Please use `cobib edit {largs.label}` instead!"
                LOGGER.warning(msg)
            else:
                read_database(fresh=True)
                EditCommand().execute([largs.label])

        for label in new_entries:
            msg = f"'{label}' was added to the database."
            print(msg)
            LOGGER.info(msg)
Beispiel #15
0
def main():
    """Main executable.

    CoBib's main function used to parse optional keyword arguments and subcommands.
    """
    if len(sys.argv) > 1 and any([a[0] == '_' for a in sys.argv]):
        # zsh helper function called
        zsh_main()
        sys.exit()

    # initialize logging
    log_to_stream()

    subcommands = [cmd.split(':')[0] for cmd in zsh_helper.list_commands()]
    parser = argparse.ArgumentParser(prog='CoBib',
                                     description="""
                                     Cobib input arguments.
                                     If no arguments are given, the TUI will start as a default.
                                     """)
    parser.add_argument("--version",
                        action="version",
                        version="%(prog)s v{}".format(__version__))
    parser.add_argument('--verbose', '-v', action='count', default=0)
    parser.add_argument("-l",
                        "--logfile",
                        type=argparse.FileType('w'),
                        help="Alternative log file")
    parser.add_argument("-c",
                        "--config",
                        type=argparse.FileType('r'),
                        help="Alternative config file")
    parser.add_argument('command',
                        help="subcommand to be called",
                        choices=subcommands,
                        nargs='?')
    parser.add_argument('args', nargs=argparse.REMAINDER)

    args = parser.parse_args()

    if args.logfile:
        LOGGER.info('Switching to FileHandler logger in %s', args.logfile.name)
        log_to_file('DEBUG' if args.verbose > 1 else 'INFO',
                    logfile=args.logfile.name)

    # set logging verbosity level
    if args.verbose == 1:
        logging.getLogger().setLevel(logging.INFO)
        LOGGER.info('Logging level set to INFO.')
    elif args.verbose > 1:
        logging.getLogger().setLevel(logging.DEBUG)
        LOGGER.info('Logging level set to DEBUG.')

    CONFIG.set_config(args.config)
    try:
        CONFIG.validate()
    except RuntimeError as exc:
        LOGGER.error(exc)
        sys.exit(1)

    if args.command == 'init':
        # the database file may not exist yet, thus we ensure to execute the command before trying
        # to read the database file
        subcmd = getattr(commands, 'InitCommand')()
        subcmd.execute(args.args)
        return

    read_database()
    if not args.command:
        if args.logfile is None:
            LOGGER.info('Switching to FileHandler logger in %s',
                        '/tmp/cobib.log')
            log_to_file('DEBUG' if args.verbose > 1 else 'INFO')
        else:
            LOGGER.info(
                'Already logging to %s. NOT switching to "/tmp/cobib.log"',
                args.logfile)
        tui()
    else:
        subcmd = getattr(commands, args.command.title() + 'Command')()
        subcmd.execute(args.args)
Beispiel #16
0
def setup():
    """Setup."""
    root = os.path.abspath(os.path.dirname(__file__))
    CONFIG.set_config(Path(root + '/../cobib/docs/debug.ini'))
    read_database()