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.
main
Olivier Mangin 12 years ago
parent 0798280f9a
commit 83845d2360

@ -3,9 +3,8 @@ import os
import unicodedata import unicodedata
import re import re
from cStringIO import StringIO from cStringIO import StringIO
import glob
from pybtex.database import Entry, BibliographyData from pybtex.database import Entry, BibliographyData, FieldDict, Person
import files import files
@ -49,6 +48,21 @@ def get_bibentry_from_string(content):
return first_key, first_entry 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): def get_safe_metadata(metapath):
if metapath is None: if metapath is None:
return None return None
@ -81,7 +95,7 @@ class Paper(object):
self.citekey = citekey self.citekey = citekey
def __eq__(self, other): 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.bibentry == other.bibentry
and self.metadata == other.metadata and self.metadata == other.metadata
and self.citekey == other.citekey) and self.citekey == other.citekey)
@ -168,6 +182,11 @@ class Paper(object):
except (KeyError, IndexError): except (KeyError, IndexError):
raise(NoDocumentFile('No file found in bib data.')) 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 @classmethod
def load(cls, bibpath, metapath=None): def load(cls, bibpath, metapath=None):
key, entry = get_bibentry_from_file(bibpath) key, entry = get_bibentry_from_file(bibpath)
@ -211,12 +230,7 @@ class PaperInRepo(Paper):
self.repo = repo self.repo = repo
def get_document_path_in_repo(self): def get_document_path_in_repo(self):
doc_dir = files.clean_path(self.repo.get_document_directory()) return self.repo.find_document(self.citekey)
found = glob.glob(doc_dir + "/%s.*" % self.citekey)
if found:
return found[0]
else:
raise NoDocumentFile
def get_document_path(self): def get_document_path(self):
try: try:
@ -224,6 +238,14 @@ class PaperInRepo(Paper):
except NoDocumentFile: except NoDocumentFile:
return self.get_external_document_path() 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 @classmethod
def load(cls, repo, bibpath, metapath=None): def load(cls, repo, bibpath, metapath=None):
key, entry = get_bibentry_from_file(bibpath) key, entry = get_bibentry_from_file(bibpath)
@ -231,3 +253,10 @@ class PaperInRepo(Paper):
p = PaperInRepo(repo, bibentry=entry, metadata=metadata, p = PaperInRepo(repo, bibentry=entry, metadata=metadata,
citekey=key) citekey=key)
return p return p
@classmethod
def from_paper(cls, paper, repo):
return PaperInRepo(repo,
bibentry=paper.bibentry,
metadata=paper.metadata,
citekey=paper.citekey)

@ -1,8 +1,9 @@
import os import os
import shutil import shutil
import glob
import files import files
from paper import Paper, PaperInRepo from paper import Paper, PaperInRepo, NoDocumentFile
from color import colored from color import colored
import configs import configs
@ -23,7 +24,8 @@ class Repository(object):
config = configs.CONFIG config = configs.CONFIG
self.config = config self.config = config
# loading existing papers def has_paper(self, citekey):
return citekey in self.citekeys
def paper_from_citekey(self, citekey): def paper_from_citekey(self, citekey):
"""Load a paper by its citekey from disk, if necessary.""" """Load a paper by its citekey from disk, if necessary."""
@ -64,7 +66,7 @@ class Repository(object):
def add_paper(self, p): def add_paper(self, p):
if p.citekey is None: # TODO also test if citekey is valid if p.citekey is None: # TODO also test if citekey is valid
raise(ValueError("Invalid citekey: %s." % p.citekey)) 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" raise(ValueError("Citekey already exists in repository: %s"
% p.citekey)) % p.citekey))
self.citekeys.append(p.citekey) self.citekeys.append(p.citekey)
@ -76,22 +78,57 @@ class Repository(object):
print "Added: %s" % p.citekey print "Added: %s" % p.citekey
def add_or_update(self, paper): def add_or_update(self, paper):
if not paper.citekey in self.citekeys: if not self.has_paper(paper.citekey):
self.add_paper(paper) self.add_paper(paper)
else: else:
self.save_paper(paper) 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): def remove(self, citekey):
self.citetekeys.remove(citekey) self.citekeys.remove(citekey)
paper = self.paper_from_citekey(citekey) paper = self.paper_from_citekey(citekey)
for f in ('bib', 'meta'): for f in ('bib', 'meta'):
shutil.rmtree(self.path_to_paper_file(citekey, f)) os.remove(self.path_to_paper_file(citekey, f))
# TODO change # Eventually remove associated document
if paper.metadata['in-repo']: try:
shutil.rmtree(self.path_to_paper_file(citekey, f)) path = paper.get_document_path_in_repo()
os.remove(path)
except NoDocumentFile:
pass
def save_paper(self, paper): 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.')) raise(ValueError('Paper not in repository, first add it.'))
paper.save_to_disc(self.path_to_paper_file(paper.citekey, 'bib'), paper.save_to_disc(self.path_to_paper_file(paper.citekey, 'bib'),
self.path_to_paper_file(paper.citekey, 'meta')) self.path_to_paper_file(paper.citekey, 'meta'))
@ -124,6 +161,7 @@ class Repository(object):
self.papersdir = papersdir self.papersdir = papersdir
os.makedirs(os.path.join(self.papersdir, BIB_DIR)) os.makedirs(os.path.join(self.papersdir, BIB_DIR))
os.makedirs(os.path.join(self.papersdir, META_DIR)) os.makedirs(os.path.join(self.papersdir, META_DIR))
os.makedirs(self.get_document_directory())
self.save() self.save()
def path_to_paper_file(self, citekey, file_): def path_to_paper_file(self, citekey, file_):
@ -140,6 +178,14 @@ class Repository(object):
else: else:
return os.path.join(self.papersdir, DOC_DIR) 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): def all_papers(self):
for key in self.citekeys: for key in self.citekeys:
yield self.paper_from_citekey(key) yield self.paper_from_citekey(key)

@ -22,10 +22,8 @@ entries:
year: '1950' year: '1950'
""" """
META = """ META = """
filename: null external-document: null
extension: null
notes: [] notes: []
path: null
""" """
@ -100,3 +98,22 @@ class TestSaveLoad(unittest.TestCase):
def tearDown(self): def tearDown(self):
shutil.rmtree(self.tmpdir) 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')

@ -5,6 +5,7 @@ import os
import fixtures import fixtures
from papers.repo import Repository, _str_incr, _to_suffix, BIB_DIR, META_DIR from papers.repo import Repository, _str_incr, _to_suffix, BIB_DIR, META_DIR
from papers.paper import PaperInRepo
class TestCitekeyGeneration(unittest.TestCase): class TestCitekeyGeneration(unittest.TestCase):
@ -29,7 +30,7 @@ class TestCitekeyGeneration(unittest.TestCase):
self.assertEqual(c, 'Turing1950b') self.assertEqual(c, 'Turing1950b')
class TestAddPaper(unittest.TestCase): class TestRepo(unittest.TestCase):
def setUp(self): def setUp(self):
self.tmpdir = tempfile.mkdtemp() self.tmpdir = tempfile.mkdtemp()
@ -37,6 +38,12 @@ class TestAddPaper(unittest.TestCase):
self.repo.init(self.tmpdir) self.repo.init(self.tmpdir)
self.repo.add_paper(fixtures.turing1950) 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): def test_raises_value_error_on_existing_key(self):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
self.repo.add_paper(fixtures.turing1950) 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, self.assertTrue(os.path.exists(os.path.join(self.tmpdir, META_DIR,
'Turing1950.meta'))) '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')))

Loading…
Cancel
Save