From a926c4c6548f728e74b785e89866e462aba9b4dc Mon Sep 17 00:00:00 2001 From: 73 Date: Sun, 13 Dec 2015 21:34:48 +0100 Subject: [PATCH 1/4] adds a *doc add|remove|export|open* command; depricates commands *open* and *attach*; fixed typos in readme.md and uis; --- pubs/commands/__init__.py | 3 +- pubs/commands/attach_cmd.py | 50 ----------- pubs/commands/doc_cmd.py | 160 ++++++++++++++++++++++++++++++++++++ pubs/commands/open_cmd.py | 47 ----------- pubs/pubs_cmd.py | 3 +- pubs/repo.py | 38 ++++++--- pubs/uis.py | 8 +- pubs/utils.py | 21 ++++- readme.md | 2 +- tests/test_usecase.py | 57 ++++++++----- 10 files changed, 251 insertions(+), 138 deletions(-) delete mode 100644 pubs/commands/attach_cmd.py create mode 100644 pubs/commands/doc_cmd.py delete mode 100644 pubs/commands/open_cmd.py diff --git a/pubs/commands/__init__.py b/pubs/commands/__init__.py index a9e0abe..150dae5 100644 --- a/pubs/commands/__init__.py +++ b/pubs/commands/__init__.py @@ -7,8 +7,7 @@ from . import rename_cmd from . import remove_cmd from . import list_cmd # doc -from . import attach_cmd -from . import open_cmd +from . import doc_cmd from . import tag_cmd from . import note_cmd # bulk diff --git a/pubs/commands/attach_cmd.py b/pubs/commands/attach_cmd.py deleted file mode 100644 index 99bd10b..0000000 --- a/pubs/commands/attach_cmd.py +++ /dev/null @@ -1,50 +0,0 @@ -from .. import repo -from .. import color -from ..uis import get_ui -from .. import content - -def parser(subparsers): - parser = subparsers.add_parser('attach', - help='attach a document to an existing paper') - # parser.add_argument('-c', '--copy', action='store_true', default=True, - # help="copy document files into library directory (default)") - parser.add_argument('-L', '--link', action='store_false', dest='copy', default=True, - help="don't copy document files, just create a link.") - parser.add_argument('-M', '--move', action='store_true', dest='move', default=False, - help="move document instead of of copying (ignored if --link).") - parser.add_argument('citekey', - help='citekey of the paper') - parser.add_argument('document', - help='document file') - return parser - - -def command(conf, args): - """ - :param bibfile: bibtex file (in .bib, .bibml or .yaml format. - :param docfile: path (no url yet) to a pdf or ps file - """ - - ui = get_ui() - - rp = repo.Repository(conf) - paper = rp.pull_paper(args.citekey) - - try: - document = args.document - rp.push_doc(paper.citekey, document, copy=args.copy) - if args.copy: - if args.move: - content.remove_file(document) - # else: - # if ui.input_yn('{} has been copied into pubs; should the original be removed?'.format(color.dye_out(document, 'filepath'))): - # content.remove_file(document) - - ui.message('{} attached to {}'.format(color.dye_out(document, 'filepath'), color.dye_out(paper.citekey, 'citekey'))) - - except ValueError as v: - ui.error(v.message) - ui.exit(1) - except IOError as v: - ui.error(v.message) - ui.exit(1) diff --git a/pubs/commands/doc_cmd.py b/pubs/commands/doc_cmd.py new file mode 100644 index 0000000..f221d8c --- /dev/null +++ b/pubs/commands/doc_cmd.py @@ -0,0 +1,160 @@ +import os +import subprocess + +from .. import repo +from .. import color +from ..uis import get_ui +from .. import content +from ..utils import resolve_citekey, resolve_citekey_list + +# doc --+- add $file $key [[-L|--link] | [-M|--move]] [-f|--force] +# +- remove $key [$key [...]] [-f|--force] +# +- export $key [$path] +# +- open $key [-w|--with $cmd] +# supplements attach, open + +def parser(subparsers): + doc_parser = subparsers.add_parser('doc', help='manage the document relating to a publication') + doc_subparsers = doc_parser.add_subparsers(title='document actions', help='actions to interact with the documents', + dest='action') + + add_parser = doc_subparsers.add_parser('add', help='add a document to a publication') + add_parser.add_argument('-f', '--force', action='store_true', dest='force', default=False, + help='force overwriting an already assigned document') + add_parser.add_argument('document', nargs=1, help='document file to assign') + add_parser.add_argument('citekey', nargs=1, help='citekey of the publication') + add_exclusives = add_parser.add_mutually_exclusive_group() + add_exclusives.add_argument('-L', '--link', action='store_false', dest='link', default=False, + help='do not copy document files, just create a link') + add_exclusives.add_argument('-M', '--move', action='store_true', dest='move', default=False, + help='move document instead of of copying (ignored if --link)') + + remove_parser = doc_subparsers.add_parser('remove', help='remove assigned documents from publications') + remove_parser.add_argument('citekeys', nargs='+', help='citekeys of the publications') + remove_parser.add_argument('-f', '--force', action='store_true', dest='force', default=False, + help='force removing assigned documents') + + # favor key+ path over: key + export_parser = doc_subparsers.add_parser('export', help='export assigned documents to given path') + export_parser.add_argument('citekeys', nargs='+', help='citekeys of the documents to export') + export_parser.add_argument('path', nargs=1, help='directory to export the files to') + + open_parser = doc_subparsers.add_parser('open', help='open an assigned document') + open_parser.add_argument('citekey', nargs=1, help='citekey of the document to open') + open_parser.add_argument('-w', '--with', dest='cmd', help='command to open the file with') + + return doc_parser + +def command(conf, args): + + ui = get_ui() + rp = repo.Repository(conf) + + + # print(args) + # ui.exit() + + if args.action == 'add': + citekey = resolve_citekey(rp, args.citekey[0], ui=ui, exit_on_fail=True) + paper = rp.pull_paper(citekey) + + if paper.docpath is not None and not args.force: + msg = ("The publication {} has already the document {} assigned." + os.linesep + + "Overwrite? ").format(color.dye_out(paper.citekey, 'citekey'), color.dye_out(paper.docpath, 'filepath')) + if not ui.input_yn(question=msg, default='n'): + ui.exit(0) + else: + try: + rp.remove_doc(paper.citekey) + except (ValueError, IOError) as v: + ui.error(v.message) + ui.exit(1) + + try: + document = args.document[0] + if args.link: + rp.push_doc(paper.citekey, document, copy=False) + else: + rp.push_doc(paper.citekey, document, copy=True) + if not args.link and args.move: + content.remove_file(document) + + ui.message('{} added to {}'.format(color.dye_out(document, 'filepath'), + color.dye_out(paper.citekey, 'citekey'))) + except (ValueError, IOError) as v: + ui.error(v.message) + ui.exit(1) + + elif args.action == 'remove': + + for key in resolve_citekey_list(rp, args.citekeys, ui=ui, exit_on_fail=True): + paper = rp.pull_paper(key) + + # if there is no document (and the user cares) -> inform + continue + if paper.docpath is None and not args.force: + ui.message('Publication {} has no assigned document. Not removed.'.format( + color.dye_out(paper.citekey, 'citekey'))) + continue + + if not args.force: + msg = 'Do you really want to remove {} from {} ?'.format(color.dye_out(paper.docpath, 'filepath'), + color.dye_out(paper.citekey, 'citekey')) + if not ui.input_yn(question=msg, default='n'): + continue + + try: + rp.remove_doc(paper.citekey) + except (ValueError, IOError) as v: + ui.error(v.message) + ui.exit(1) + + elif args.action == 'export': + + if os.path.isdir(args.path[0]): + path = args.path[0] + if not path.endswith('/'): + path += '/' + else: + ui.error('{} is not a directory.'.format( + color.dye_err(args.path[0], 'filepath'))) + ui.exit(1) + + for key in resolve_citekey_list(rp, args.citekeys, ui=ui, exit_on_fail=True): + try: + paper = rp.pull_paper(key) + doc = paper.docpath + + if doc is None: + ui.message('Publication {} has no document assigned.'.format( + color.dye_out(paper.citekey, 'citekey'))) + else: + real_doc_path = rp.pull_docpath(key) + dest_path = path + os.path.basename(real_doc_path) + content.copy_content(real_doc_path, dest_path) + except (ValueError, IOError) as e: + ui.error(e.message) + + elif args.action == 'open': + with_command = args.cmd + citekey = resolve_citekey(rp, args.citekey[0], ui=ui, exit_on_fail=True) + paper = rp.pull_paper(citekey) + + if paper.docpath is None: + ui.error('No document associated with the entry {}.'.format( + color.dye_err(citekey, 'citekey'))) + ui.exit() + + if with_command is None: + with_command = conf['main']['open_cmd'] + if with_command is None: # default in conf have not been changed + pass # TODO platform specific + + try: + docpath = content.system_path(rp.databroker.real_docpath(paper.docpath)) + cmd = with_command.split() + cmd.append(docpath) + subprocess.Popen(cmd) + ui.message('{} opened.'.format(color.dye_out(docpath, 'filepath'))) + except OSError: + ui.error("Command does not exist: %s." % with_command) + ui.exit(127) diff --git a/pubs/commands/open_cmd.py b/pubs/commands/open_cmd.py deleted file mode 100644 index bee1378..0000000 --- a/pubs/commands/open_cmd.py +++ /dev/null @@ -1,47 +0,0 @@ -import subprocess - -from .. import repo - -from ..uis import get_ui -from .. import color -from ..content import system_path -from ..utils import resolve_citekey - -def parser(subparsers): - parser = subparsers.add_parser('open', - help='open the paper in a pdf viewer') - parser.add_argument('-w', '--with', dest='with_command', default=None, - help='command to use to open the document file') - parser.add_argument('citekey', - help='citekey of the paper') - return parser - - -def command(conf, args): - - ui = get_ui() - with_command = args.with_command - - rp = repo.Repository(conf) - citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True) - paper = rp.pull_paper(citekey) - - if with_command is None: - with_command = conf['main']['open_cmd'] - if with_command is None: # default in conf have not been changed - pass # TODO platform specific - - if paper.docpath is None: - ui.error('No document associated with the entry {}.'.format( - color.dye_err(citekey, 'citekey'))) - ui.exit() - - try: - docpath = system_path(rp.databroker.real_docpath(paper.docpath)) - cmd = with_command.split() - cmd.append(docpath) - subprocess.Popen(cmd) - ui.message('{} opened.'.format(color.dye_out(docpath, 'filepath'))) - except OSError: - ui.error("Command does not exist: %s." % with_command) - ui.exit(127) diff --git a/pubs/pubs_cmd.py b/pubs/pubs_cmd.py index b791234..3c450b1 100644 --- a/pubs/pubs_cmd.py +++ b/pubs/pubs_cmd.py @@ -18,8 +18,7 @@ CORE_CMDS = collections.OrderedDict([ ('remove', commands.remove_cmd), ('list', commands.list_cmd), - ('attach', commands.attach_cmd), - ('open', commands.open_cmd), + ('doc', commands.doc_cmd), ('tag', commands.tag_cmd), ('note', commands.note_cmd), diff --git a/pubs/repo.py b/pubs/repo.py index d08caaa..4bf4a3d 100644 --- a/pubs/repo.py +++ b/pubs/repo.py @@ -84,22 +84,40 @@ class Repository(object): def remove_paper(self, citekey, remove_doc=True, event=True): """ Remove a paper. Is silent if nothing needs to be done.""" - if event: events.RemoveEvent(citekey).send() if remove_doc: - try: - metadata = self.databroker.pull_metadata(citekey) - docpath = metadata.get('docfile') - self.databroker.remove_doc(docpath, silent=True) - self.databroker.remove_note(citekey, silent=True) - except IOError: - pass # FXME: if IOError is about being unable to - # remove the file, we need to issue an error.I - + self.remove_doc(citekey, detach_only=True) + try: + self.databroker.remove_note(citekey, silent=True) + except IOError: + pass # FIXME: if IOError is about being unable to + # remove the file, we need to issue an error.I self.citekeys.remove(citekey) self.databroker.remove(citekey) + def remove_doc(self, citekey, detach_only=False): + """ Remove a doc. Is silent if nothing needs to be done.""" + try: + metadata = self.databroker.pull_metadata(citekey) + docpath = metadata.get('docfile') + self.databroker.remove_doc(docpath, silent=True) + if not detach_only: + p = self.pull_paper(citekey) + p.docpath = None + self.push_paper(p, overwrite=True, event=False) + except IOError: + pass # FIXME: if IOError is about being unable to + # remove the file, we need to issue an error.I + + def pull_docpath(self, citekey): + try: + p = self.pull_paper(citekey) + return self.databroker.real_docpath(p.docpath) + except IOError: + pass # FIXME: if IOError is about being unable to + # remove the file, we need to issue an error.I + def rename_paper(self, paper, new_citekey=None, old_citekey=None): if old_citekey is None: old_citekey = paper.citekey diff --git a/pubs/uis.py b/pubs/uis.py index a08126f..2db2cbd 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -87,8 +87,8 @@ class InputUI(PrintUI): return ustr(data) #.decode('utf-8') def input_choice_ng(self, options, option_chars=None, default=None, question=''): - """Ask the user to chose between a set of options. The iser is asked - to input a char corresponding to the option he choses. + """Ask the user to chose between a set of options. The user is asked + to input a char corresponding to the option he chooses. :param options: list of strings list of options @@ -126,8 +126,8 @@ class InputUI(PrintUI): def input_choice(self, options, option_chars, default=None, question=''): - """Ask the user to chose between a set of options. The iser is asked - to input a char corresponding to the option he choses. + """Ask the user to chose between a set of options. The user is asked + to input a char corresponding to the option he chooses. :param options: list of strings list of options diff --git a/pubs/utils.py b/pubs/utils.py index ed06087..3e08b6f 100644 --- a/pubs/utils.py +++ b/pubs/utils.py @@ -9,18 +9,18 @@ def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): citekeys = repo.citekeys_from_prefix(citekey) if len(citekeys) == 0: if ui is not None: - ui.error("no citekey named or beginning with '{}'".format(color.dye_out(citekey, 'citekey'))) + ui.error("No citekey named or beginning with '{}'".format(color.dye_out(citekey, 'citekey'))) if exit_on_fail: ui.exit() elif len(citekeys) == 1: if citekeys[0] != citekey: if ui is not None: - ui.warning("provided citekey '{}' has been autocompleted into '{}'".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) + ui.warning("Provided citekey '{}' has been autocompleted into '{}'".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) citekey = citekeys[0] elif citekey not in citekeys: if ui is not None: citekeys = sorted(citekeys) - ui.error("be more specific; provided citekey '{}' matches multiples citekeys:".format( + ui.error("Be more specific; Provided citekey '{}' matches multiples citekeys:".format( citekey)) for c in citekeys: p = repo.pull_paper(c) @@ -28,3 +28,18 @@ def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): if exit_on_fail: ui.exit() return citekey + + +def resolve_citekey_list(repo, citekeys, ui=None, exit_on_fail=True): + shutdown = False + keys = [] + for key in citekeys: + try: + keys.append(resolve_citekey(repo, key, ui, exit_on_fail)) + except SystemExit: + shutdown = exit_on_fail + + if shutdown and ui is not None: + ui.exit() + else: + return keys diff --git a/readme.md b/readme.md index 9409f46..851fa42 100644 --- a/readme.md +++ b/readme.md @@ -46,7 +46,7 @@ If you use latex, you can automatize references, by creating a bash script with: bibtex manuscript latex manuscript.tex -This ensure that your reference file is always up-to-date; you can cite a paper in your manuscript a soon as you add it in pubs. This means that if you have, for instance, a doi on a webpage, you only need to do: +This ensures that your reference file is always up-to-date; you can cite a paper in your manuscript a soon as you add it in pubs. This means that if you have, for instance, a doi on a webpage, you only need to do: pubs add -D 10.1007/s00422-012-0514-6 diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 4f55c04..297a686 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -352,7 +352,7 @@ class TestUsecase(DataCommandTestCase): 'pubs add data/martius.bib', 'pubs add data/10.1371%2Fjournal.pone.0038236.bib', 'pubs list', - 'pubs attach Page99 data/pagerank.pdf' + 'pubs doc add data/pagerank.pdf Page99' ] self.execute_cmds(cmds) @@ -363,7 +363,7 @@ class TestUsecase(DataCommandTestCase): 'pubs add data/martius.bib', 'pubs add data/10.1371%2Fjournal.pone.0038236.bib', 'pubs list', - 'pubs attach Page99 data/pagerank.pdf', + 'pubs doc add data/pagerank.pdf Page99', ('pubs remove Page99', ['y']), 'pubs remove -f turing1950computing', ] @@ -461,19 +461,6 @@ class TestUsecase(DataCommandTestCase): outs = self.execute_cmds(cmds) self.assertEqual(1 + 1, len(outs[-1].split('\n'))) - def test_open(self): - cmds = ['pubs init', - 'pubs add data/pagerank.bib', - 'pubs open Page99' - ] - - with self.assertRaises(SystemExit): - self.execute_cmds(cmds) - - with self.assertRaises(SystemExit): - cmds[-1] == 'pubs open Page8' - self.execute_cmds(cmds) - def test_update(self): cmds = ['pubs init', 'pubs add data/pagerank.bib', @@ -491,10 +478,21 @@ class TestUsecase(DataCommandTestCase): outs = self.execute_cmds(cmds) self.assertEqual(1, len(outs[2].splitlines())) - def test_attach(self): + def test_doc_open(self): cmds = ['pubs init', 'pubs add data/pagerank.bib', - 'pubs attach Page99 data/pagerank.pdf' + 'pubs doc open Page99' + ] + with self.assertRaises(SystemExit): + self.execute_cmds(cmds) + with self.assertRaises(SystemExit): + cmds[-1] == 'pubs doc open Page8' + self.execute_cmds(cmds) + + def test_doc_add(self): + cmds = ['pubs init', + 'pubs add data/pagerank.bib', + 'pubs doc add data/pagerank.pdf Page99' ] self.execute_cmds(cmds) self.assertTrue(self.fs['os'].path.exists( @@ -504,15 +502,36 @@ class TestUsecase(DataCommandTestCase): # Also test that do not remove original self.assertTrue(self.fs['os'].path.exists('/data/pagerank.pdf')) - def test_attach_with_move(self): + def test_doc_add_with_move(self): cmds = ['pubs init -p paper_second/', 'pubs add data/pagerank.bib', - 'pubs attach --move Page99 data/pagerank.pdf' + 'pubs doc add --move data/pagerank.pdf Page99' ] self.execute_cmds(cmds) self.assertTrue(self.fs['os'].path.isfile(self.default_conf_path)) self.assertFalse(self.fs['os'].path.exists('/data/pagerank.pdf')) + def test_doc_remove(self): + cmds = ['pubs init', + 'pubs add data/pagerank.bib', + 'pubs doc add data/pagerank.pdf Page99', + ('pubs doc remove Page99', ['y']), + ] + self.execute_cmds(cmds) + docdir = self.fs['os'].path.expanduser('~/.pubs/doc/') + self.assertNotIn('turing-mind-1950.pdf', self.fs['os'].listdir(docdir)) + + def test_doc_export(self): + cmds = ['pubs init', + 'pubs add data/pagerank.bib', + 'pubs rename Page99 page100', + 'pubs doc add data/pagerank.pdf page100', + 'pubs doc export page100 /' + ] + self.execute_cmds(cmds) + self.assertIn('page100.pdf', self.fs['os'].listdir('/')) + + def test_alternate_config(self): alt_conf = self.fs['os'].path.expanduser('~/.alt_conf') cmds = ['pubs -c ' + alt_conf + ' init', From 69b2879fa2d99af8c767407bfb283f0ddf55664b Mon Sep 17 00:00:00 2001 From: 73 Date: Mon, 14 Dec 2015 13:24:13 +0100 Subject: [PATCH 2/4] added ui.info() and made it default for citekey completion --- pubs/color.py | 1 + pubs/uis.py | 4 ++++ pubs/utils.py | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pubs/color.py b/pubs/color.py index ef22452..f00d1f9 100644 --- a/pubs/color.py +++ b/pubs/color.py @@ -3,6 +3,7 @@ Small code to handle colored text """ import sys import re +import os def _color_supported(stream): """Returns True is the stream supports colors""" diff --git a/pubs/uis.py b/pubs/uis.py index 2db2cbd..07f45c9 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -58,6 +58,10 @@ class PrintUI(object): kwargs['file'] = self._stdout print(*messages, **kwargs) + def info(self, message, **kwargs): + kwargs['file'] = self._stderr + print('{}: {}'.format(color.dye_err('info', 'ok'), message), **kwargs) + def warning(self, message, **kwargs): kwargs['file'] = self._stderr print('{}: {}'.format(color.dye_err('warning', 'warning'), message), **kwargs) diff --git a/pubs/utils.py b/pubs/utils.py index 3e08b6f..76c582c 100644 --- a/pubs/utils.py +++ b/pubs/utils.py @@ -15,7 +15,7 @@ def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): elif len(citekeys) == 1: if citekeys[0] != citekey: if ui is not None: - ui.warning("Provided citekey '{}' has been autocompleted into '{}'".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) + ui.info("Provided citekey '{}' has been autocompleted into '{}'".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) citekey = citekeys[0] elif citekey not in citekeys: if ui is not None: From 5cc3f892a0c770976f48cba3ff10bd028f09d057 Mon Sep 17 00:00:00 2001 From: 73 Date: Mon, 14 Dec 2015 16:31:30 +0100 Subject: [PATCH 3/4] All commands that consume a citekey as an argument will now complete a prefix. --- pubs/commands/conf_cmd.py | 4 ++-- pubs/commands/doc_cmd.py | 4 ++-- pubs/commands/export_cmd.py | 30 +++++++++++++++--------------- pubs/commands/import_cmd.py | 8 ++++---- pubs/commands/init_cmd.py | 2 +- pubs/commands/note_cmd.py | 14 +++++++------- pubs/commands/tag_cmd.py | 11 +++++++++-- pubs/uis.py | 2 +- pubs/utils.py | 3 ++- 9 files changed, 43 insertions(+), 35 deletions(-) diff --git a/pubs/commands/conf_cmd.py b/pubs/commands/conf_cmd.py index d09d61c..2fe1574 100644 --- a/pubs/commands/conf_cmd.py +++ b/pubs/commands/conf_cmd.py @@ -22,8 +22,8 @@ def command(conf, args): config.check_conf(new_conf) ui.message('The configuration file was updated.') break - except AssertionError: # TODO better error message - ui.error('Error reading the modified configuration file.') + except AssertionError as e: # TODO better error message + ui.error('Error reading the modified configuration file [' + e.message + '].') options = ['edit_again', 'abort'] choice = options[ui.input_choice( options, ['e', 'a'], diff --git a/pubs/commands/doc_cmd.py b/pubs/commands/doc_cmd.py index f221d8c..c031a55 100644 --- a/pubs/commands/doc_cmd.py +++ b/pubs/commands/doc_cmd.py @@ -115,7 +115,7 @@ def command(conf, args): if not path.endswith('/'): path += '/' else: - ui.error('{} is not a directory.'.format( + ui.error('{} is not a directory. Quit.'.format( color.dye_err(args.path[0], 'filepath'))) ui.exit(1) @@ -140,7 +140,7 @@ def command(conf, args): paper = rp.pull_paper(citekey) if paper.docpath is None: - ui.error('No document associated with the entry {}.'.format( + ui.error('No document associated with the entry {}. Quit.'.format( color.dye_err(citekey, 'citekey'))) ui.exit() diff --git a/pubs/commands/export_cmd.py b/pubs/commands/export_cmd.py index a55a791..be559b7 100644 --- a/pubs/commands/export_cmd.py +++ b/pubs/commands/export_cmd.py @@ -3,7 +3,7 @@ from __future__ import print_function from .. import repo from ..uis import get_ui from .. import endecoder - +from ..utils import resolve_citekey_list def parser(subparsers): parser = subparsers.add_parser('export', help='export bibliography') @@ -19,20 +19,20 @@ def command(conf, args): # :param bib_format (only 'bibtex' now) ui = get_ui() - rp = repo.Repository(conf) try: - papers = [rp.pull_paper(c) for c in args.citekeys] - except repo.InvalidReference as v: - ui.error(v) - ui.exit(1) - - if len(papers) == 0: - papers = rp.all_papers() - bib = {} - for p in papers: - bib[p.citekey] = p.bibdata - exporter = endecoder.EnDecoder() - bibdata_raw = exporter.encode_bibdata(bib) - ui.message(bibdata_raw) + papers = [] + if len(args.citekeys) < 1: + papers = rp.all_papers() + else: + for key in resolve_citekey_list(repo=rp, citekeys=args.citekeys, ui=ui, exit_on_fail=True): + papers.append(rp.pull_paper(key)) + bib = {} + for p in papers: + bib[p.citekey] = p.bibdata + exporter = endecoder.EnDecoder() + bibdata_raw = exporter.encode_bibdata(bib) + ui.message(bibdata_raw) + except Exception as e: + ui.error(e.message) \ No newline at end of file diff --git a/pubs/commands/import_cmd.py b/pubs/commands/import_cmd.py index ceda2bd..e9ffa94 100644 --- a/pubs/commands/import_cmd.py +++ b/pubs/commands/import_cmd.py @@ -77,17 +77,17 @@ def command(conf, args): try: p = papers[k] if isinstance(p, Exception): - ui.error('could not load entry for citekey {}.'.format(k)) + ui.error('Could not load entry for citekey {}.'.format(k)) else: rp.push_paper(p) - ui.message('{} imported'.format(color.dye_out(p.citekey, 'citekey'))) + ui.info('{} imported.'.format(color.dye_out(p.citekey, 'citekey'))) docfile = bibstruct.extract_docfile(p.bibdata) if docfile is None: - ui.warning("no file for {}.".format(p.citekey)) + ui.warning("No file for {}.".format(p.citekey)) else: rp.push_doc(p.citekey, docfile, copy=copy) #FIXME should move the file if configured to do so. except KeyError: - ui.error('no entry found for citekey {}.'.format(k)) + ui.error('No entry found for citekey {}.'.format(k)) except IOError as e: ui.error(e.message) diff --git a/pubs/commands/init_cmd.py b/pubs/commands/init_cmd.py index 048e713..e20dd3e 100644 --- a/pubs/commands/init_cmd.py +++ b/pubs/commands/init_cmd.py @@ -33,7 +33,7 @@ def command(conf, args): pubsdir = system_path(pubsdir) if check_directory(pubsdir, fail=False) and len(os.listdir(pubsdir)) > 0: - ui.error('directory {} is not empty.'.format( + ui.error('Directory {} is not empty.'.format( color.dye_err(pubsdir, 'filepath'))) ui.exit() diff --git a/pubs/commands/note_cmd.py b/pubs/commands/note_cmd.py index 77be6d2..f68ef35 100644 --- a/pubs/commands/note_cmd.py +++ b/pubs/commands/note_cmd.py @@ -1,6 +1,7 @@ from .. import repo from .. import content from ..uis import get_ui +from ..utils import resolve_citekey def parser(subparsers): @@ -16,11 +17,10 @@ def command(conf, args): """ ui = get_ui() - rp = repo.Repository(conf) - if not rp.databroker.exists(args.citekey): - ui.error("citekey {} not found".format(args.citekey)) - ui.exit(1) - - notepath = rp.databroker.real_notepath(args.citekey) - content.edit_file(conf['main']['edit_cmd'], notepath, temporary=False) + citekey = resolve_citekey(rp, args.citekey, ui=ui, exit_on_fail=True) + try: + notepath = rp.databroker.real_notepath(citekey) + content.edit_file(conf['main']['edit_cmd'], notepath, temporary=False) + except Exception as e: + ui.error(e.message) \ No newline at end of file diff --git a/pubs/commands/tag_cmd.py b/pubs/commands/tag_cmd.py index 2cd8339..1f05cb3 100644 --- a/pubs/commands/tag_cmd.py +++ b/pubs/commands/tag_cmd.py @@ -23,6 +23,7 @@ from ..repo import Repository from ..uis import get_ui from .. import pretty from .. import color +from ..utils import resolve_citekey def parser(subparsers): @@ -81,7 +82,12 @@ def command(conf, args): if citekeyOrTag is None: ui.message(color.dye_out(' '.join(sorted(rp.get_tags())), 'tag')) else: - if rp.databroker.exists(citekeyOrTag): + not_citekey = False + try: + citekeyOrTag = resolve_citekey(repo=rp, citekey=citekeyOrTag, ui=ui, exit_on_fail=True) + except SystemExit: + not_citekey = True + if not not_citekey: p = rp.pull_paper(citekeyOrTag) if tags is None: ui.message(color.dye_out(' '.join(sorted(p.tags)), 'tag')) @@ -93,9 +99,10 @@ def command(conf, args): p.remove_tag(tag) rp.push_paper(p, overwrite=True) elif tags is not None: - ui.error(ui.error('no entry found for citekey {}.'.format(citekeyOrTag))) + ui.error(ui.error('No entry found for citekey {}.'.format(citekeyOrTag))) ui.exit() else: + ui.info('Assuming {} to be a tag.'.format(color.dye_out(citekeyOrTag))) # case where we want to find papers with specific tags included, excluded = _tag_groups(_parse_tag_seq(citekeyOrTag)) papers_list = [] diff --git a/pubs/uis.py b/pubs/uis.py index 07f45c9..bc503ac 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -112,7 +112,7 @@ class InputUI(PrintUI): option_str = '/'.join(["{}{}".format(color.dye_out(c, 'bold'), s[1:]) for c, s in zip(displayed_chars, options)]) - self.message('{} {}: '.format(question, option_str), end='') + self.message('{}: {} {}: '.format(color.dye_err('prompt', 'warning'), question, option_str), end='') while True: answer = self.input() if answer is None or answer == '': diff --git a/pubs/utils.py b/pubs/utils.py index 76c582c..381f1b5 100644 --- a/pubs/utils.py +++ b/pubs/utils.py @@ -5,6 +5,7 @@ from . import pretty def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): """Check that a citekey exists, or autocompletes it if not ambiguous.""" + """ :returns found citekey """ # FIXME. Make me optionally non ui interactive/exiting citekeys = repo.citekeys_from_prefix(citekey) if len(citekeys) == 0: @@ -15,7 +16,7 @@ def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): elif len(citekeys) == 1: if citekeys[0] != citekey: if ui is not None: - ui.info("Provided citekey '{}' has been autocompleted into '{}'".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) + ui.info("Provided citekey '{}' has been autocompleted into [{}].".format(color.dye_out(citekey, 'citekey'), color.dye_out(citekeys[0], 'citekey'))) citekey = citekeys[0] elif citekey not in citekeys: if ui is not None: From 23890bf579fd125bdd80c682bd8adfe3512f5c8e Mon Sep 17 00:00:00 2001 From: 73 Date: Wed, 16 Dec 2015 13:41:20 +0100 Subject: [PATCH 4/4] missed two somehow --- pubs/commands/remove_cmd.py | 26 +++++++++++++++++--------- pubs/commands/rename_cmd.py | 13 ++++++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/pubs/commands/remove_cmd.py b/pubs/commands/remove_cmd.py index ec39f48..d18f185 100644 --- a/pubs/commands/remove_cmd.py +++ b/pubs/commands/remove_cmd.py @@ -1,13 +1,13 @@ from .. import repo from .. import color from ..uis import get_ui - +from ..utils import resolve_citekey_list def parser(subparsers): - parser = subparsers.add_parser('remove', help='removes a paper') + parser = subparsers.add_parser('remove', help='removes a publication') parser.add_argument('-f', '--force', action='store_true', default=None, help="does not prompt for confirmation.") - parser.add_argument('citekeys', nargs='*', + parser.add_argument('citekeys', nargs='+', help="one or several citekeys") return parser @@ -18,15 +18,23 @@ def command(conf, args): force = args.force rp = repo.Repository(conf) + keys = resolve_citekey_list(repo=rp, citekeys=args.citekeys, ui=ui, exit_on_fail=True) + if force is None: - are_you_sure = (("Are you sure you want to delete paper(s) [{}]" + are_you_sure = (("Are you sure you want to delete the publication(s) [{}]" " (this will also delete associated documents)?") .format(', '.join([color.dye_out(c, 'citekey') for c in args.citekeys]))) sure = ui.input_yn(question=are_you_sure, default='n') if force or sure: - for c in args.citekeys: - rp.remove_paper(c) - ui.message('The paper(s) [{}] were removed'.format(', '.join([color.dye_out(c, 'citekey') for c in args.citekeys]))) - # FIXME: print should check that removal proceeded well. + for c in keys: + try: + rp.remove_paper(c) + except Exception as e: + ui.error(e.message) + ui.message('The publication(s) [{}] were removed'.format( + ', '.join([color.dye_out(c, 'citekey') for c in keys]))) + # FIXME: print should check that removal proceeded well. else: - ui.message('The paper(s) [{}] were *not* removed'.format(', '.join([color.dye_out(c, 'citekey') for c in args.citekeys]))) + ui.message('The publication(s) [{}] were {} removed'.format( + ', '.join([color.dye_out(c, 'citekey') for c in keys]), + color.dye_out('not','bold'))) diff --git a/pubs/commands/rename_cmd.py b/pubs/commands/rename_cmd.py index bc2df2d..d832980 100644 --- a/pubs/commands/rename_cmd.py +++ b/pubs/commands/rename_cmd.py @@ -1,8 +1,6 @@ from ..uis import get_ui -from .. import bibstruct -from .. import content from .. import repo -from .. import paper +from ..utils import resolve_citekey def parser(subparsers): parser = subparsers.add_parser('rename', help='rename the citekey of a repository') @@ -22,5 +20,10 @@ def command(conf, args): ui = get_ui() rp = repo.Repository(conf) - paper = rp.pull_paper(args.citekey) - rp.rename_paper(paper, args.new_citekey) + # TODO: here should be a test whether the new citekey is valid + try: + key = resolve_citekey(repo=rp, citekey=args.citekey, ui=ui, exit_on_fail=True) + paper = rp.pull_paper(key) + rp.rename_paper(paper, args.new_citekey) + except Exception as e: + ui.error(e.message) \ No newline at end of file