diff --git a/papers/commands/init_cmd.py b/papers/commands/init_cmd.py index b2a9178..3023d19 100644 --- a/papers/commands/init_cmd.py +++ b/papers/commands/init_cmd.py @@ -6,29 +6,24 @@ try: except ImportError: import configparser +from ..repo import Repository from .. import color -from .. import files + def parser(subparsers, config): parser = subparsers.add_parser('init', help="initialize the .papers directory") return parser + def command(config): """Create a .papers directory""" - # create dir papersdir = os.getcwd() + '/.papers' - if not os.path.exists(papersdir): print('{}initializing papers in {}{}{}'.format( color.grey, color.cyan, papersdir, color.end)) - - os.makedirs(os.path.join(papersdir, 'bibdata')) - os.makedirs(os.path.join(papersdir, 'meta')) - - papers = {} - papers['citekeys'] = [] - files.save_papers(papers) - + repo = Repository(papersdir=papersdir) + repo.init() # Creates directories + repo.save() # Saves empty repository description else: print('{}error {} : papers already present in {}{}{}'.format( color.red, color.grey, color.cyan, papersdir, color.end)) diff --git a/papers/files.py b/papers/files.py index da7eea5..9b5d529 100644 --- a/papers/files.py +++ b/papers/files.py @@ -98,34 +98,21 @@ def read_yamlfile(filepath): exit(-1) -def save_papers(datamap): - paperyaml = find_papersdir() + os.sep + 'papers.yaml' - write_yamlfile(paperyaml, datamap) - - -def load_papers(): - paperyaml = os.path.join(find_papersdir(), 'papers.yaml') - return read_yamlfile(paperyaml) +def load_bibdata(filename, filepath): + return load_externalbibfile(filepath) -def path_to_paper_file(name, file_, path_to_repo=None): - if path_to_repo is None: - path_to_repo = find_papersdir() - if file_ == 'bib': - return os.path.join(path_to_repo, 'bibdata', name + '.bibyaml') - elif file_ == 'meta': - return os.path.join(path_to_repo, 'meta', name + '.meta') - else: - raise(ValueError, "%s is not a valid paper file." % file_) +def save_bibdata(bib_data, filepath): + with open(filepath, 'w') as f: + parser = pybtex.database.output.bibyaml.Writer() + parser.write_stream(bib_data, f) -def save_meta(meta_data, filename, path=None): - filepath = path_to_paper_file(filename, 'meta', path_to_repo=path) +def save_meta(meta_data, filepath): write_yamlfile(filepath, meta_data) -def load_meta(filename, path=None): - filepath = path_to_paper_file(filename, 'meta', path_to_repo=path) +def load_meta(filepath): return read_yamlfile(filepath) @@ -152,18 +139,6 @@ def load_externalbibfile(fullbibpath): return bib_data -def load_bibdata(filename, path=None): - filepath = path_to_paper_file(filename, 'bib', path_to_repo=path) - return load_externalbibfile(filepath) - - -def save_bibdata(bib_data, filename, path=None): - filepath = path_to_paper_file(filename, 'bib', path_to_repo=path) - with open(filepath, 'w') as f: - parser = pybtex.database.output.bibyaml.Writer() - parser.write_stream(bib_data, f) - - # vim input def vim_input(initial=""): diff --git a/papers/paper.py b/papers/paper.py index f2c8a8d..9652fc9 100644 --- a/papers/paper.py +++ b/papers/paper.py @@ -37,16 +37,6 @@ class Paper(object): biblography data and an additional dictionary to store meta data. """ - @classmethod - def load(cls, bibpath, metapath): - bib_data = files.load_externalbibfile(bibpath) - metadata = files.read_yamlfile(metapath) - # Extract first entry (supposed to be the only one) - first_key = bib_data.entries.keys()[0] - first_entry = bib_data.entries[first_key] - p = Paper(bibentry=first_entry, metadata=metadata, citekey=first_key) - return p - # @classmethod # def from_bibpdffiles(cls, pdfpath, bibpath): # bib_data = cls.import_bibdata(bibpath) @@ -62,6 +52,9 @@ class Paper(object): if not metadata: metadata = Paper.create_meta() self.metadata = metadata + # TODO This is not the right way to test that (17/12/2012) + if unicode(citekey) != str2citekey(citekey): + raise(ValueError, "Wrong citekey: %s" % citekey) self.citekey = citekey def __eq__(self, other): @@ -114,7 +107,7 @@ class Paper(object): citekey = u'{}{}'.format(u''.join(first_author.last()), year) return str2citekey(citekey) - def save_to_disc(self, path): + def save_to_disc(self, bib_filepath, meta_filepath): """Creates a BibliographyData object containing a single entry and saves it to disc. """ @@ -122,9 +115,18 @@ class Paper(object): raise(ValueError, 'No valid citekey initialized. Cannot save paper') bibdata = BibliographyData(entries={self.citekey: self.bibentry}) - files.save_bibdata(bibdata, self.citekey, path=path) - files.save_meta(self.metadata, self.citekey, path=path) - # TODO move to repo + files.save_bibdata(bibdata, bib_filepath) + files.save_meta(self.metadata, meta_filepath) + + @classmethod + def load(cls, bibpath, metapath): + bib_data = files.load_externalbibfile(bibpath) + metadata = files.read_yamlfile(metapath) + # Extract first entry (supposed to be the only one) + first_key = bib_data.entries.keys()[0] + first_entry = bib_data.entries[first_key] + p = Paper(bibentry=first_entry, metadata=metadata, citekey=first_key) + return p @classmethod def import_bibdata(cls, bibfile): diff --git a/papers/repo.py b/papers/repo.py index 9750450..a315527 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -1,21 +1,24 @@ -import files import color +import os +import files from paper import Paper ALPHABET = 'abcdefghijklmopqrstuvwxyz' +BASE_FILE = 'papers.yaml' +BIB_DIR = 'bibdata' +META_DIR = 'meta' class Repository(object): - def __init__(self, paperdir=None): - if paperdir: - self.paperdir = paperdir + def __init__(self, papersdir=None): + if papersdir: + self.papersdir = papersdir else: - self.paperdir = files.find_papersdir() - self.papers_config = files.load_papers() - self.citekeys = self.papers_config['citekeys'] + self.papersdir = files.find_papersdir() + self.citekeys = [] # loading existing papers @@ -34,9 +37,10 @@ class Repository(object): def paper_from_citekey(self, citekey, fatal=True): """Load a paper by its citekey from disk, if necessary.""" - try: - return Paper.load(citekey) - except KeyError: + if citekey in self.citekeys: + return Paper.load(self.path_to_paper_file(citekey, 'bib'), + self.path_to_paper_file(citekey, 'meta')) + else: if fatal: print('{}error{}: no paper with citekey {}{}{}'.format( color.error, color.normal, color.citekey, citekey, @@ -64,28 +68,36 @@ class Repository(object): self.add_paper(p) def add_paper(self, p): - # updating papersconfig - bib_data_entry = p.bib_data.entries[list(p.bib_data.entries.keys())[0]] - p.citekey = self.get_valid_citekey(bib_data_entry) - - self.papers_config['citekeys'].append(p.citekey) + 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: + raise(ValueError, "Citekey already exists in repository: %s" + % p.citekey) self.citekeys.append(p.citekey) - - # writing all to disk - # TODO Update by giving filename (17/12/2012) - p.save_to_disc() - files.save_papers(self.papers_config) + # write paper files + self.save_paper(p) + # update repository files + self.save() + # TODO change to logging system (17/12/2012) print "Added: %s" % p.citekey - return p + + def add_or_update(self, paper): + if not paper.citekey in self.citekeys: + self.add_paper(paper) + else: + paper.save_paper(paper) + + def save_paper(self, paper): + if not paper.citekey in self.citekeys: + 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')) 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]) - meta = Paper.create_meta(pdfpath=None) - name = meta['filename'] - p = Paper(name, bib_data=sub_bib, metadata=meta) + p = Paper(bibentry=sub_bib, citekey=k) self.add_paper(p) def get_free_citekey(self, paper, citekey=None): @@ -93,14 +105,44 @@ class Repository(object): """ if citekey is None: citekey = paper.generate_citekey() - suffix = '' - while citekey + suffix in self.citekeys: - _str_incr(suffix) - return citekey + suffix + num = [] + while citekey + _to_suffix(num) in self.citekeys: + _str_incr(num) + return citekey + _to_suffix(num) + + def base_file_path(self): + return os.path.join(self.papersdir, 'papers.yaml') def size(self): return len(self.citekeys) + def save(self): + papers_config = {'citekeys': self.citekeys} + files.write_yamlfile(self.base_file_path(), papers_config) + + def load(self): + papers_config = files.read_yamlfile(self.base_file_path()) + self.citekeys = papers_config['citekeys'] + + def init(self): + os.makedirs(os.path.join(self.papersdir, BIB_DIR)) + os.makedirs(os.path.join(self.papersdir, META_DIR)) + self.save() + + def path_to_paper_file(self, citekey, file_): + if file_ == 'bib': + return os.path.join(self.papersdir, BIB_DIR, citekey + '.bibyaml') + elif file_ == 'meta': + return os.path.join(self.papersdir, META_DIR, citekey + '.meta') + else: + raise(ValueError, "%s is not a valid paper file." % file_) + + @classmethod + def from_directory(cls, papersdir=None): + repo = cls.__init__(papersdir=papersdir) + repo.load() + return repo + def _char_incr(c): return chr(ord(c) + 1) diff --git a/tests/fixtures.py b/tests/fixtures.py new file mode 100644 index 0000000..fb56d44 --- /dev/null +++ b/tests/fixtures.py @@ -0,0 +1,17 @@ +from pybtex.database import Person + +from papers.paper import Paper + + +turing1950 = Paper() +turing1950.bibentry.fields['title'] = u'Computing machinery and intelligence.' +turing1950.bibentry.fields['year'] = u'1950' +turing1950.bibentry.persons['author'] = [Person(u'Alan Turing')] +turing1950.citekey = turing1950.generate_citekey() + + +doe2013 = Paper() +doe2013.bibentry.fields['title'] = u'Nice title.' +doe2013.bibentry.fields['year'] = u'2013' +doe2013.bibentry.persons['author'] = [Person(u'John Doe')] +doe2013.citekey = doe2013.generate_citekey() diff --git a/tests/test_paper.py b/tests/test_paper.py index af32e86..49e619e 100644 --- a/tests/test_paper.py +++ b/tests/test_paper.py @@ -7,8 +7,8 @@ import shutil import yaml from pybtex.database import Person +import fixtures from papers.paper import Paper -from papers import files BIB = """ @@ -64,50 +64,39 @@ class TestSaveLoad(unittest.TestCase): self.metafile = os.path.join(self.tmpdir, 'meta.meta') with open(self.metafile, 'w') as f: f.write(META) - self.turing1950 = Paper() - self.turing1950.bibentry.fields['title'] = u'Computing machinery and intelligence.' - self.turing1950.bibentry.fields['year'] = u'1950' - self.turing1950.bibentry.persons['author'] = [Person(u'Alan Turing')] - self.turing1950.citekey = self.turing1950.generate_citekey() + self.dest_bibfile = os.path.join(self.tmpdir, 'written_bib.yaml') + self.dest_metafile = os.path.join(self.tmpdir, 'written_meta.yaml') def test_load_valid(self): p = Paper.load(self.bibfile, self.metafile) - self.assertEqual(self.turing1950, p) + self.assertEqual(fixtures.turing1950, p) def test_save_fails_with_no_citekey(self): p = Paper() with self.assertRaises(ValueError): - p.save_to_disc(self.tmpdir) + p.save_to_disc(self.dest_bibfile, self.dest_metafile) def test_save_creates_bib(self): - self.turing1950.save_to_disc(self.tmpdir) - bibfile = files.path_to_paper_file('Turing1950', 'bib', - path_to_repo=self.tmpdir) - self.assertTrue(os.path.exists(bibfile)) + fixtures.turing1950.save_to_disc(self.dest_bibfile, self.dest_metafile) + self.assertTrue(os.path.exists(self.dest_bibfile)) def test_save_creates_meta(self): - self.turing1950.save_to_disc(self.tmpdir) - metafile = files.path_to_paper_file('Turing1950', 'meta', - path_to_repo=self.tmpdir) - self.assertTrue(os.path.exists(metafile)) + fixtures.turing1950.save_to_disc(self.dest_bibfile, self.dest_metafile) + self.assertTrue(os.path.exists(self.dest_metafile)) def test_save_right_bib(self): - self.turing1950.save_to_disc(self.tmpdir) - bibfile = files.path_to_paper_file('Turing1950', 'bib', - path_to_repo=self.tmpdir) - with open(bibfile, 'r') as f: + fixtures.turing1950.save_to_disc(self.dest_bibfile, self.dest_metafile) + with open(self.dest_bibfile, 'r') as f: written = yaml.load(f) ok = yaml.load(BIB) self.assertEqual(written, ok) def test_save_right_meta(self): - self.turing1950.save_to_disc(self.tmpdir) - metafile = files.path_to_paper_file('Turing1950', 'meta', - path_to_repo=self.tmpdir) - with open(metafile, 'r') as f: + fixtures.turing1950.save_to_disc(self.dest_bibfile, self.dest_metafile) + with open(self.dest_metafile, 'r') as f: written = yaml.load(f) ok = yaml.load(META) self.assertEqual(written, ok) - def teardown(self): + def tearDown(self): shutil.rmtree(self.tmpdir) diff --git a/tests/test_repo.py b/tests/test_repo.py index 4a0eff4..0cf8efa 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -1,6 +1,10 @@ import unittest +import tempfile +import shutil +import os -from papers.repo import Repository, _str_incr, _to_suffix +import fixtures +from papers.repo import Repository, _str_incr, _to_suffix, BIB_DIR, META_DIR class TestCitekeyGeneration(unittest.TestCase): @@ -17,4 +21,36 @@ class TestCitekeyGeneration(unittest.TestCase): self.assertEqual(_to_suffix(l), 'aa') def test_generated_key_is_unique(self): - pass + repo = Repository() + repo.add_paper(fixtures.turing1950) + repo.add_paper(fixtures.doe2013) + c = repo.get_free_citekey(fixtures.turing1950) + self.assertEqual(c, 'Turing1950a') + fixtures.turing1950.citekey = 'Turing1950a' + repo.add_paper(fixtures.turing1950) + c = repo.get_free_citekey(fixtures.turing1950) + self.assertEqual(c, 'Turing1950b') + + +class TestAddPaper(unittest.TestCase): + + def setUp(self): + self.tmpdir = tempfile.mkdtemp() + self.repo = Repository(papersdir=self.tmpdir) + self.repo.init() + self.repo.add_paper(fixtures.turing1950) + + def test_raises_value_error_on_existing_key(self): + with self.assertRaises(ValueError): + self.repo.add_paper(fixtures.turing1950) + + def test_saves_bib(self): + self.assertTrue(os.path.exists(os.path.join(self.tmpdir, BIB_DIR, + 'Turing1950.bibyaml'))) + + def test_saves_meta(self): + self.assertTrue(os.path.exists(os.path.join(self.tmpdir, META_DIR, + 'Turing1950.meta'))) + + def tearDown(self): + shutil.rmtree(self.tmpdir)