diff --git a/papers/commands/import_cmd.py b/papers/commands/import_cmd.py index fb4b039..2a21f0b 100644 --- a/papers/commands/import_cmd.py +++ b/papers/commands/import_cmd.py @@ -1,7 +1,3 @@ -import os -import sys -import shutil - from .. import repo from ..paper import Paper, NoDocumentFile from .. import files @@ -27,30 +23,23 @@ def command(config, ui, bibpath, copy): if copy is None: copy = config.get('papers', 'import-copy') rp = repo.Repository.from_directory() - # Get directory for document - doc_path = files.clean_path(rp.get_document_directory(config)) - if not (os.path.exists(doc_path) and os.path.isdir(doc_path)): - print "Document directory %s, does not exist." % doc_path - sys.exit(1) # Extract papers from bib papers = Paper.many_from_path(bibpath, fatal=False) for p in papers: doc_file = None try: file_path = p.get_document_file_from_bibdata(remove=True) - if os.path.exists(file_path): + if files.check_file(file_path): doc_file = file_path else: - print "File does not exist for %s." % p.citekey + print("File does not exist for %s (%s)." + % (p.citekey, file_path)) except NoDocumentFile: print "No file for %s." % p.citekey rp.add_paper(p) if doc_file: if copy: - ext = os.path.splitext(doc_file)[1] - new_doc_file = os.path.join(doc_path, p.citekey + ext) - shutil.copy(doc_file, new_doc_file) + rp.import_document(p.citekey, doc_file) else: - new_doc_file = doc_file - p.set_document(new_doc_file) - rp.add_or_update(p) + p.set_external_document(doc_file) + rp.add_or_update(p) diff --git a/papers/commands/open_cmd.py b/papers/commands/open_cmd.py index 490c75e..8f11e00 100644 --- a/papers/commands/open_cmd.py +++ b/papers/commands/open_cmd.py @@ -17,14 +17,10 @@ def command(config, ui, citekey): rp = repo.Repository.from_directory() paper = rp.paper_from_ref(citekey, fatal=True) try: - if paper.check_file(): - filepath = paper.get_file_path() - subprocess.Popen([config.get('papers', 'open-cmd'), - filepath]) - print('{} opened.'.format(colored(filepath, 'filepath'))) - else: - raise NoDocumentFile + filepath = paper.get_document_path() + subprocess.Popen([config.get('papers', 'open-cmd'), filepath]) + print("%s opened." % colored(filepath, 'filepath')) except NoDocumentFile: - print('{}: No document associated to this entry {}{}{}'.format( - colored('error', 'error'), colored('citekey', 'citekey'))) + print("%s: No document associated to this entry %s." + % (colored('error', 'error'), colored(citekey, 'citekey'))) exit(-1) diff --git a/papers/files.py b/papers/files.py index 779539a..302de4e 100644 --- a/papers/files.py +++ b/papers/files.py @@ -72,17 +72,15 @@ def name_from_path(fullpdfpath, verbose=False): return name, ext -def check_file(filepath): - if not os.path.exists(filepath): - print(colored('error', 'error') + - ': {} does not exists'.format( - colored(filepath, 'filepath'))) - exit(-1) - if not os.path.isfile(filepath): - print(colored('error', 'error') - + ': {} is not a file'.format( - colored(filepath, 'filepath'))) - exit(-1) +def check_file(path, fail=False): + if fail: + if not os.path.exists(path): + raise(IOError, "File does not exist: %s." % path) + if not os.path.isfile(path): + raise(IOError, "%s is not a file." % path) + return True + else: + return os.path.exists(path) and os.path.isfile(path) # yaml I/O @@ -99,7 +97,7 @@ def write_yamlfile(filepath, datamap): def read_yamlfile(filepath): - check_file(filepath) + check_file(filepath, fail=True) try: with open(filepath, 'r') as f: return yaml.load(f) @@ -131,8 +129,7 @@ def load_meta(filepath): # specific to bibliography data def load_externalbibfile(fullbibpath): - check_file(fullbibpath) - + check_file(fullbibpath, fail=True) filename, ext = os.path.splitext(os.path.split(fullbibpath)[1]) if ext[1:] in FORMATS.keys(): with open(fullbibpath) as f: diff --git a/papers/paper.py b/papers/paper.py index c8a14aa..d6fa1c2 100644 --- a/papers/paper.py +++ b/papers/paper.py @@ -1,6 +1,9 @@ import os + import unicodedata import re +from cStringIO import StringIO +import glob from pybtex.database import Entry, BibliographyData @@ -16,9 +19,7 @@ CITEKEY_EXCLUDE_RE = re.compile('[%s]' % re.escape(CONTROL_CHARS + CITEKEY_FORBIDDEN_CHARS)) BASE_META = { - 'filename': None, - 'extension': None, - 'path': None, + 'external-document': None, 'notes': [] } @@ -30,6 +31,31 @@ def str2citekey(s): return key +def get_bibentry_from_file(bibfile): + """Extract first entry (supposed to be the only one) from given file. + """ + bib_data = files.load_externalbibfile(bibfile) + first_key = bib_data.entries.keys()[0] + first_entry = bib_data.entries[first_key] + return first_key, first_entry + + +def get_bibentry_from_string(content): + """Extract first entry (supposed to be the only one) from given file. + """ + bib_data = files.parse_bibdata(StringIO(content)) + first_key = bib_data.entries.keys()[0] + first_entry = bib_data.entries[first_key] + return first_key, first_entry + + +def get_safe_metadata(metapath): + if metapath is None: + return None + else: + return files.read_yamlfile(metapath) + + class NoDocumentFile(Exception): pass @@ -69,20 +95,22 @@ class Paper(object): # TODO add mechanism to verify keys (15/12/2012) - 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'] + def get_external_document_path(self): + if self.metadata['external-document'] is not None: + return self.metadata['external-document'] else: raise NoDocumentFile - def check_file(self): - path = self.get_file_path() - return os.path.exists(path) and os.path.isfile(path) + def get_document_path(self): + return self.get_external_document_path() + + def set_external_document(self, docpath): + fullpdfpath = os.path.abspath(docpath) + files.check_file(fullpdfpath, fail=True) + self.metadata['external-document'] = fullpdfpath + + def check_document_path(self): + return files.check_file(self.get_external_document_path()) def generate_citekey(self): """Generate a citekey from bib_data. @@ -105,14 +133,6 @@ class Paper(object): citekey = u'{}{}'.format(u''.join(first_author.last()), year) return str2citekey(citekey) - def set_document(self, docpath): - fullpdfpath = os.path.abspath(docpath) - files.check_file(fullpdfpath) - name, ext = files.name_from_path(docpath) - self.metadata['filename'] = name - self.metadata['extension'] = ext - self.metadata['path'] = fullpdfpath - def save_to_disc(self, bib_filepath, meta_filepath): """Creates a BibliographyData object containing a single entry and saves it to disc. @@ -150,23 +170,11 @@ class Paper(object): @classmethod def load(cls, bibpath, metapath=None): - key, entry = cls.get_bibentry(bibpath) - if metapath is None: - metadata = None - else: - metadata = files.read_yamlfile(metapath) + key, entry = get_bibentry_from_file(bibpath) + metadata = get_safe_metadata(metapath) p = Paper(bibentry=entry, metadata=metadata, citekey=key) return p - @classmethod - def get_bibentry(cls, bibfile): - """Extract first entry (supposed to be the only one) from given file. - """ - bib_data = files.load_externalbibfile(bibfile) - first_key = bib_data.entries.keys()[0] - first_entry = bib_data.entries[first_key] - return first_key, first_entry - @classmethod def create_meta(cls): return BASE_META.copy() @@ -194,3 +202,32 @@ class Paper(object): except ValueError, e: print "Warning, skipping paper (%s)." % e return papers + + +class PaperInRepo(Paper): + + def __init__(self, repo, *args, **kwargs): + Paper.__init__(self, *args, **kwargs) + self.repo = repo + + def get_document_path_in_repo(self): + doc_dir = files.clean_path(self.repo.get_document_directory()) + found = glob.glob(doc_dir + "/%s.*" % self.citekey) + if found: + return found[0] + else: + raise NoDocumentFile + + def get_document_path(self): + try: + return self.get_document_path_in_repo() + except NoDocumentFile: + return self.get_external_document_path() + + @classmethod + def load(cls, repo, bibpath, metapath=None): + key, entry = get_bibentry_from_file(bibpath) + metadata = get_safe_metadata(metapath) + p = PaperInRepo(repo, bibentry=entry, metadata=metadata, + citekey=key) + return p diff --git a/papers/pretty.py b/papers/pretty.py index 36f07c7..99d15e0 100644 --- a/papers/pretty.py +++ b/papers/pretty.py @@ -14,12 +14,14 @@ def person_repr(p): def short_authors(bibentry): - authors = [person_repr(p) for p in bibentry.persons['author']] - if len(authors) < 3: - return ', '.join(authors) - else: - return authors[0] + (' et al.' if len(authors) > 1 else '') - + try: + authors = [person_repr(p) for p in bibentry.persons['author']] + if len(authors) < 3: + return ', '.join(authors) + else: + return authors[0] + (' et al.' if len(authors) > 1 else '') + except KeyError: # When no author is defined + return '' def bib_oneliner(bibentry): authors = short_authors(bibentry) diff --git a/papers/repo.py b/papers/repo.py index a7cf9cb..375a833 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -1,8 +1,10 @@ -from .color import colored import os +import shutil import files -from paper import Paper +from paper import Paper, PaperInRepo +from color import colored +import configs ALPHABET = 'abcdefghijklmopqrstuvwxyz' @@ -14,16 +16,20 @@ DOC_DIR = 'doc' class Repository(object): - def __init__(self): + def __init__(self, config=None): self.papersdir = None self.citekeys = [] + if config is None: + config = configs.CONFIG + self.config = config # loading existing papers def paper_from_citekey(self, citekey): """Load a paper by its citekey from disk, if necessary.""" - return Paper.load(self.path_to_paper_file(citekey, 'bib'), - metapath=self.path_to_paper_file(citekey, 'meta')) + return PaperInRepo.load( + self, self.path_to_paper_file(citekey, 'bib'), + metapath=self.path_to_paper_file(citekey, 'meta')) def citekey_from_ref(self, ref, fatal=True): """Tries to get citekey from given ref. @@ -48,9 +54,11 @@ class Repository(object): # creating new papers + # Deprecated + # TODO merge def add_paper_from_paths(self, docpath, bibpath): p = Paper.load(bibpath) - p.set_document(docpath) + p.set_external_document(docpath) self.add_paper(p) def add_paper(self, p): @@ -73,6 +81,15 @@ class Repository(object): else: self.save_paper(paper) + def remove(self, citekey): + self.citetekeys.remove(citekey) + paper = self.paper_from_citekey(citekey) + for f in ('bib', 'meta'): + shutil.rmtree(self.path_to_paper_file(citekey, f)) + # TODO change + if paper.metadata['in-repo']: + shutil.rmtree(self.path_to_paper_file(citekey, f)) + def save_paper(self, paper): if not paper.citekey in self.citekeys: raise(ValueError('Paper not in repository, first add it.')) @@ -117,9 +134,9 @@ class Repository(object): else: raise(ValueError("%s is not a valid paper file." % file_)) - def get_document_directory(self, config): - if config.has_option('papers', 'document-directory'): - return config.get('papers', 'document-directory') + def get_document_directory(self): + if self.config.has_option('papers', 'document-directory'): + return self.config.get('papers', 'document-directory') else: return os.path.join(self.papersdir, DOC_DIR) @@ -127,6 +144,18 @@ class Repository(object): for key in self.citekeys: yield self.paper_from_citekey(key) + def import_document(self, citekey, doc_file): + if citekey not in self.citekeys: + raise(ValueError, "Unknown citekey: %s." % citekey) + else: + doc_path = self.get_document_directory() + if not (os.path.exists(doc_path) and os.path.isdir(doc_path)): + raise(ValueError, + "Document directory %s, does not exist." % doc_path) + ext = os.path.splitext(doc_file)[1] + new_doc_file = os.path.join(doc_path, citekey + ext) + shutil.copy(doc_file, new_doc_file) + @classmethod def from_directory(cls, papersdir=None): repo = cls()