From 655fb25bfa69dc3d4e3725942134f5d322b99d3f Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Fri, 2 Nov 2012 19:48:13 +0100 Subject: [PATCH] Adds command to add multiple reference at once. Also fixes a few bugs. --- papers/commands/__init__.py | 3 ++- papers/commands/add_cmd.py | 11 +-------- papers/commands/add_library_cmd.py | 15 ++++++++++++ papers/commands/list_cmd.py | 2 +- papers/paper.py | 38 +++++++++++++++++++++++++----- papers/papers | 4 ++-- papers/pretty.py | 26 +++++++++++--------- papers/repo.py | 37 ++++++++++++++++++++++++----- 8 files changed, 99 insertions(+), 37 deletions(-) create mode 100644 papers/commands/add_library_cmd.py diff --git a/papers/commands/__init__.py b/papers/commands/__init__.py index cf33784..5b9cdd1 100644 --- a/papers/commands/__init__.py +++ b/papers/commands/__init__.py @@ -1,5 +1,6 @@ import add_cmd +import add_library_cmd import init_cmd import list_cmd import open_cmd -import websearch_cmd \ No newline at end of file +import websearch_cmd diff --git a/papers/commands/add_cmd.py b/papers/commands/add_cmd.py index 0a0e55c..83ee256 100644 --- a/papers/commands/add_cmd.py +++ b/papers/commands/add_cmd.py @@ -1,12 +1,3 @@ -import os -try: - import ConfigParser as configparser -except ImportError: - import configparser - -from .. import color -from .. import files -from .. import pretty from .. import repo @@ -22,4 +13,4 @@ def command(config, pdffile, bibfile): :param bibtex bibtex file (in .bib, .bibml or .yaml format. """ rp = repo.Repository() - rp.add_paper(pdffile, bibfile) \ No newline at end of file + rp.add_paper_from_paths(pdffile, bibfile) diff --git a/papers/commands/add_library_cmd.py b/papers/commands/add_library_cmd.py new file mode 100644 index 0000000..ca744b7 --- /dev/null +++ b/papers/commands/add_library_cmd.py @@ -0,0 +1,15 @@ +from .. import repo + + +def parser(subparsers, config): + parser = subparsers.add_parser('add_library', + help='add a set of papers to the repository') + parser.add_argument('bibfile', help='bibtex, bibtexml or bibyaml file') + return parser + +def command(config, bibfile): + """ + :param bibtex bibtex file (in .bib, .bibml or .yaml format. + """ + rp = repo.Repository() + rp.add_papers(bibfile) diff --git a/papers/commands/list_cmd.py b/papers/commands/list_cmd.py index 9e49bd5..d84e7e9 100644 --- a/papers/commands/list_cmd.py +++ b/papers/commands/list_cmd.py @@ -16,7 +16,7 @@ def command(config): for n in sorted(rp.numbers.keys()): paper = rp.paper_from_number(n, fatal = True) bibdesc = pretty.bib_oneliner(paper.bib_data) - articles.append('{:3d} {}{}{}{} {}'.format(int(paper.number), color.purple, paper.citekey, color.end, (8-len(paper.citekey))*' ', bibdesc)) + articles.append(u'{:3d} {}{}{}{} {}'.format(int(paper.number), color.purple, paper.citekey, color.end, (8-len(paper.citekey))*' ', bibdesc)) with tempfile.NamedTemporaryFile(suffix=".tmp", delete=True) as tmpf: tmpf.write('\n'.join(articles)) diff --git a/papers/paper.py b/papers/paper.py index 2ad9073..8217bea 100644 --- a/papers/paper.py +++ b/papers/paper.py @@ -4,6 +4,11 @@ import files import color import pretty + +class NoDocumentFile(Exception): + pass + + class Paper(object): """Paper class. The object is responsible for the integrity of its own data, and for loading and writing it to disc. @@ -20,7 +25,7 @@ class Paper(object): @classmethod def from_bibpdffiles(cls, pdfpath, bibpath): bib_data = cls.import_bibdata(bibpath) - name, meta = cls.create_meta(pdfpath, bib_data) + name, meta = cls.create_meta(bib_data, pdfpath=pdfpath) p = Paper(name, bib_data = bib_data, metadata = meta) return p @@ -32,6 +37,20 @@ class Paper(object): self.metadata = metadata self.citekey = citekey self.number = number + + def has_file(self): + """Whether there exist a document file for this entry. + """ + return self.metadata['path'] is not None + + def get_file_path(self): + if self.has_file(): + return self.metadata['path'] + else: + raise NoDocumentFile + + def check_file(self): + return files.check_file(self.get_file_path()) def save_to_disc(self): files.save_bibdata(self.bib_data, self.name) @@ -50,12 +69,19 @@ class Paper(object): return bib_data @classmethod - def create_meta(cls, pdfpath, bib_data): + def create_meta(cls, bib_data, pdfpath=None): - fullpdfpath = os.path.abspath(pdfpath) - files.check_file(fullpdfpath) - - name, ext = files.name_from_path(pdfpath) + if pdfpath is None: + name = bib_data.entries.keys()[0] + # TODO this introduces a bug and a security issue since the name + # is used to generate a file name that is written. It should be + # escaped here. (22/10/2012) + fullpdfpath, ext = None, None + else: + fullpdfpath = os.path.abspath(pdfpath) + files.check_file(fullpdfpath) + + name, ext = files.name_from_path(pdfpath) meta = {} diff --git a/papers/papers b/papers/papers index 49dde87..5825363 100755 --- a/papers/papers +++ b/papers/papers @@ -3,12 +3,12 @@ import argparse import collections -import papers from papers import commands cmds = collections.OrderedDict([ ('init', commands.init_cmd), ('add' , commands.add_cmd), + ('add_library', commands.add_library_cmd), ('list', commands.list_cmd), ('open', commands.open_cmd), ('websearch', commands.websearch_cmd) @@ -27,4 +27,4 @@ args.config = config cmd = args.command del args.command -cmds[cmd].command(**vars(args)) \ No newline at end of file +cmds[cmd].command(**vars(args)) diff --git a/papers/pretty.py b/papers/pretty.py index bbad9aa..5e97581 100644 --- a/papers/pretty.py +++ b/papers/pretty.py @@ -3,26 +3,30 @@ import color def person_repr(p): - return ' '.join(s for s in [' '.join(p.first(abbr = True)), - ' '.join(p.middle(abbr = True)), - ' '.join(p.prelast(abbr = False)), - ' '.join(p.last(abbr = False)), - ' '.join(p.lineage(abbr = True))] if s) + return u' '.join(s for s in [u' '.join(p.first(abbr = True)), + u' '.join(p.middle(abbr = True)), + u' '.join(p.prelast(abbr = False)), + u' '.join(p.last(abbr = False)), + u' '.join(p.lineage(abbr = True))] if s) def bib_oneliner(bib_data): article = bib_data.entries[list(bib_data.entries.keys())[0]] authors = ', '.join(person_repr(p) for p in article.persons['author']) title = article.fields['title'] - year = article.fields['year'] - journal = article.fields['journal'] - return '{}{}{} \"{}{}{}\" {}{}{} {}({}{}{}){}'.format( + year = article.fields.get('year', '') + journal = '' + field = 'journal' + if article.type == 'inproceedings': + field = 'booktitle' + journal = article.fields.get(field, '') + return u'{}{}{} \"{}{}{}\" {}{}{} {}({}{}{}){}'.format( color.green, authors, color.grey, color.bcyan, title, color.grey, color.yellow, journal, color.end, color.grey, color.end, year, color.grey, color.end) def bib_desc(bib_data): article = bib_data.entries[list(bib_data.entries.keys())[0]] - s = '\n'.join('author: {}'.format(person_repr(p)) for p in article.persons['author']) - s += '\n' - s += '\n'.join('{}: {}'.format(k, v) for k, v in article.fields.items()) + s = u'\n'.join(u'author: {}'.format(person_repr(p)) for p in article.persons['author']) + s += u'\n' + s += u'\n'.join(u'{}: {}'.format(k, v) for k, v in article.fields.items()) return s diff --git a/papers/repo.py b/papers/repo.py index eae2dea..9d575ba 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -1,5 +1,6 @@ import files import color + from paper import Paper alphabet = 'abcdefghijklmopqrstuvwxyz' @@ -57,10 +58,12 @@ class Repository(object): # creating new papers - def add_paper(self, pdfpath, bibpath): + def add_paper_from_paths(self, pdfpath, bibpath): p = Paper.from_bibpdffiles(pdfpath, bibpath) + self.add_paper(p) + def add_paper(self, p): # updating papersconfig p.citekey = self.create_citekey(p.bib_data) p.number = self.create_number() @@ -74,18 +77,40 @@ class Repository(object): # writing all to disk files.save_papers(self.papers_config) p.save_to_disc() - + print "Added: %s" % p.citekey return p + def add_papers(self, bibpath): + bib_data = Paper.import_bibdata(bibpath) + for k in bib_data.entries: + sub_bib = type(bib_data)(preamble=bib_data._preamble) + sub_bib.add_entry(k, bib_data.entries[k]) + name, meta = Paper.create_meta(sub_bib, pdfpath=None) + p = Paper(name, bib_data = sub_bib, metadata = meta) + self.add_paper(p) + def create_citekey(self, bib_data, allowed = tuple()): - """Create a cite key unique to a given bib_data""" + """Create a cite key unique to a given bib_data. + + Raises: + KeyError if no author is defined. + """ article = bib_data.entries[list(bib_data.entries.keys())[0]] - first_author = article.persons['author'][0] - year = article.fields['year'] + author_key = 'author' + if not 'author' in article.persons: + author_key = 'editor' + try: + first_author = article.persons[author_key][0] + except KeyError: + raise(ValueError, + 'No author or editor defined: cannot generate a citekey.') + try: + year = article.fields['year'] + except KeyError: + year = '' prefix = '{}{}'.format(first_author.last()[0][:6], year) letter = 0 - citekey = None citekey = prefix while citekey in self.citekeys and citekey not in allowed: