From 83845d2360abdd7f447de7197961ce6ff0f4b991 Mon Sep 17 00:00:00 2001 From: Olivier Mangin Date: Fri, 1 Mar 2013 16:33:56 +0100 Subject: [PATCH] Adds update funciton for paper and required API and test updates. - Adds update function that handle changes in citekey. - Adds copy functions to paper classes. - Adds tests. - FIX a few bugs. --- papers/paper.py | 47 +++++++++++++++++++++++++------- papers/repo.py | 66 ++++++++++++++++++++++++++++++++++++++------- tests/test_paper.py | 23 +++++++++++++--- tests/test_repo.py | 56 +++++++++++++++++++++++++++++++++++--- 4 files changed, 167 insertions(+), 25 deletions(-) diff --git a/papers/paper.py b/papers/paper.py index d6fa1c2..5b4ff22 100644 --- a/papers/paper.py +++ b/papers/paper.py @@ -3,9 +3,8 @@ import os import unicodedata import re from cStringIO import StringIO -import glob -from pybtex.database import Entry, BibliographyData +from pybtex.database import Entry, BibliographyData, FieldDict, Person import files @@ -49,6 +48,21 @@ def get_bibentry_from_string(content): return first_key, first_entry +def copy_person(p): + return Person(first=p.get_part_as_text('first'), + middle=p.get_part_as_text('middle'), + prelast=p.get_part_as_text('prelast'), + last=p.get_part_as_text('last'), + lineage=p.get_part_as_text('lineage')) + + +def copy_bibentry(entry): + fd = FieldDict(entry.fields.parent, entry.fields) + persons = dict([(k, [copy_person(p) for p in v]) + for k, v in entry.persons.items()]) + return Entry(entry.type, fields=fd, persons=persons) + + def get_safe_metadata(metapath): if metapath is None: return None @@ -81,7 +95,7 @@ class Paper(object): self.citekey = citekey def __eq__(self, other): - return (type(other) is Paper + return (isinstance(self, Paper) and type(other) is type(self) and self.bibentry == other.bibentry and self.metadata == other.metadata and self.citekey == other.citekey) @@ -168,6 +182,11 @@ class Paper(object): except (KeyError, IndexError): raise(NoDocumentFile('No file found in bib data.')) + def copy(self): + return Paper(bibentry=copy_bibentry(self.bibentry), + metadata=self.metadata.copy(), + citekey=self.citekey) + @classmethod def load(cls, bibpath, metapath=None): key, entry = get_bibentry_from_file(bibpath) @@ -211,12 +230,7 @@ class PaperInRepo(Paper): 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 + return self.repo.find_document(self.citekey) def get_document_path(self): try: @@ -224,6 +238,14 @@ class PaperInRepo(Paper): except NoDocumentFile: return self.get_external_document_path() + def copy(self): + return PaperInRepo.from_paper(self.as_paper().copy(), self.repo) + + def as_paper(self): + return Paper(bibentry=self.bibentry, + metadata=self.metadata, + citekey=self.citekey) + @classmethod def load(cls, repo, bibpath, metapath=None): key, entry = get_bibentry_from_file(bibpath) @@ -231,3 +253,10 @@ class PaperInRepo(Paper): p = PaperInRepo(repo, bibentry=entry, metadata=metadata, citekey=key) return p + + @classmethod + def from_paper(cls, paper, repo): + return PaperInRepo(repo, + bibentry=paper.bibentry, + metadata=paper.metadata, + citekey=paper.citekey) diff --git a/papers/repo.py b/papers/repo.py index 375a833..985f796 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -1,8 +1,9 @@ import os import shutil +import glob import files -from paper import Paper, PaperInRepo +from paper import Paper, PaperInRepo, NoDocumentFile from color import colored import configs @@ -23,7 +24,8 @@ class Repository(object): config = configs.CONFIG self.config = config - # loading existing papers + def has_paper(self, citekey): + return citekey in self.citekeys def paper_from_citekey(self, citekey): """Load a paper by its citekey from disk, if necessary.""" @@ -64,7 +66,7 @@ class Repository(object): def add_paper(self, p): if p.citekey is None: # TODO also test if citekey is valid raise(ValueError("Invalid citekey: %s." % p.citekey)) - elif p.citekey in self.citekeys: + elif self.has_paper(p.citekey): raise(ValueError("Citekey already exists in repository: %s" % p.citekey)) self.citekeys.append(p.citekey) @@ -76,22 +78,57 @@ class Repository(object): print "Added: %s" % p.citekey def add_or_update(self, paper): - if not paper.citekey in self.citekeys: + if not self.has_paper(paper.citekey): self.add_paper(paper) else: self.save_paper(paper) + def update(self, paper, old_citekey=None, overwrite=False): + """Updates a paper, eventually changing its citekey. + The paper should be in repository. If the citekey changes, + the new citekey should be free excpet if the overwrite argumnent + is set to True. + """ + if old_citekey is None: + old_citekey = paper.citekey + if old_citekey not in self.citekeys: + raise(ValueError, 'Paper not in repository. Add first') + else: + if paper.citekey == old_citekey: + self.save_paper(paper) + else: + if self.has_paper(paper.citekey): + if not overwrite: + raise(ValueError, + "There is already a paper with citekey: %s." + % paper.citekey) + else: + self.save_paper(paper) + else: + self.add_paper(paper) + # Eventually move document file + paper = PaperInRepo.from_paper(paper, self) + try: + path = self.find_document(old_citekey) + self.import_document(paper.citekey, path) + except NoDocumentFile: + pass + self.remove(old_citekey) + def remove(self, citekey): - self.citetekeys.remove(citekey) + self.citekeys.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)) + os.remove(self.path_to_paper_file(citekey, f)) + # Eventually remove associated document + try: + path = paper.get_document_path_in_repo() + os.remove(path) + except NoDocumentFile: + pass def save_paper(self, paper): - if not paper.citekey in self.citekeys: + if not self.has_paper(paper.citekey): raise(ValueError('Paper not in repository, first add it.')) paper.save_to_disc(self.path_to_paper_file(paper.citekey, 'bib'), self.path_to_paper_file(paper.citekey, 'meta')) @@ -124,6 +161,7 @@ class Repository(object): self.papersdir = papersdir os.makedirs(os.path.join(self.papersdir, BIB_DIR)) os.makedirs(os.path.join(self.papersdir, META_DIR)) + os.makedirs(self.get_document_directory()) self.save() def path_to_paper_file(self, citekey, file_): @@ -140,6 +178,14 @@ class Repository(object): else: return os.path.join(self.papersdir, DOC_DIR) + def find_document(self, citekey): + doc_dir = files.clean_path(self.get_document_directory()) + found = glob.glob(doc_dir + "/%s.*" % citekey) + if found: + return found[0] + else: + raise NoDocumentFile + def all_papers(self): for key in self.citekeys: yield self.paper_from_citekey(key) diff --git a/tests/test_paper.py b/tests/test_paper.py index c04c7c8..416be15 100644 --- a/tests/test_paper.py +++ b/tests/test_paper.py @@ -22,10 +22,8 @@ entries: year: '1950' """ META = """ -filename: null -extension: null +external-document: null notes: [] -path: null """ @@ -100,3 +98,22 @@ class TestSaveLoad(unittest.TestCase): def tearDown(self): shutil.rmtree(self.tmpdir) + + +class TestCopy(unittest.TestCase): + + def setUp(self): + self.orig = Paper() + self.orig.bibentry.fields['title'] = u'Nice title.' + self.orig.bibentry.fields['year'] = u'2013' + self.orig.bibentry.persons['author'] = [Person(u'John Doe')] + self.orig.citekey = self.orig.generate_citekey() + + def test_copy_equal(self): + copy = self.orig.copy() + self.assertEqual(copy, self.orig) + + def test_copy_can_be_changed(self): + copy = self.orig.copy() + copy.bibentry.fields['year'] = 2014 + self.assertEqual(self.orig.bibentry.fields['year'], u'2013') diff --git a/tests/test_repo.py b/tests/test_repo.py index e4e73b1..7d799d8 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -5,6 +5,7 @@ import os import fixtures from papers.repo import Repository, _str_incr, _to_suffix, BIB_DIR, META_DIR +from papers.paper import PaperInRepo class TestCitekeyGeneration(unittest.TestCase): @@ -29,7 +30,7 @@ class TestCitekeyGeneration(unittest.TestCase): self.assertEqual(c, 'Turing1950b') -class TestAddPaper(unittest.TestCase): +class TestRepo(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() @@ -37,6 +38,12 @@ class TestAddPaper(unittest.TestCase): self.repo.init(self.tmpdir) self.repo.add_paper(fixtures.turing1950) + def tearDown(self): + shutil.rmtree(self.tmpdir) + + +class TestAddPaper(TestRepo): + def test_raises_value_error_on_existing_key(self): with self.assertRaises(ValueError): self.repo.add_paper(fixtures.turing1950) @@ -49,5 +56,48 @@ class TestAddPaper(unittest.TestCase): self.assertTrue(os.path.exists(os.path.join(self.tmpdir, META_DIR, 'Turing1950.meta'))) - def tearDown(self): - shutil.rmtree(self.tmpdir) + +class TestUpdatePaper(TestRepo): + + def test_raises_value_error_on_unknown_paper(self): + with self.assertRaises(ValueError): + self.repo.update(fixtures.doe2013) + with self.assertRaises(ValueError): + self.repo.update(fixtures.doe2013, old_citekey='zou') + + def test_raises_value_error_on_existing_destination(self): + self.repo.add_paper(fixtures.doe2013) + with self.assertRaises(ValueError): + self.repo.update(fixtures.turing1950, old_citekey='Doe2013') + + def test_updates_same_key(self): + new = self.repo.paper_from_citekey('Turing1950') + new.bibentry.fields['journal'] = u'Mind' + self.repo.update(new) + self.assertEqual(new, self.repo.paper_from_citekey('Turing1950')) + + def test_updates_same_key_with_old_arg(self): + new = self.repo.paper_from_citekey('Turing1950') + new.bibentry.fields['journal'] = u'Mind' + self.repo.update(new, old_citekey='Turing1950') + self.assertEqual(new, self.repo.paper_from_citekey('Turing1950')) + + def test_update_new_key_removes_old(self): + self.repo.update(fixtures.doe2013, old_citekey='Turing1950') + self.assertFalse(self.repo.has_paper('Turing1950')) + + def test_update_new_key_updates(self): + self.repo.update(fixtures.doe2013, old_citekey='Turing1950') + self.assertTrue(self.repo.has_paper('Doe2013')) + self.assertEqual(self.repo.paper_from_citekey('Doe2013'), + PaperInRepo.from_paper(fixtures.doe2013, self.repo)) + + def test_update_new_key_moves_doc(self): + self.repo.import_document('Turing1950', + os.path.join(os.path.dirname(__file__), + 'data/pagerank.pdf')) + self.repo.update(fixtures.doe2013, old_citekey='Turing1950') + self.assertFalse(os.path.exists(os.path.join( + self.repo.get_document_directory(), 'Turing1950.pdf'))) + self.assertTrue(os.path.exists(os.path.join( + self.repo.get_document_directory(), 'Doe2013.pdf')))