From a354e79523cf9940e080a0752f0b8b5cb2289d58 Mon Sep 17 00:00:00 2001 From: Fabien Benureau Date: Tue, 2 Jul 2013 10:59:55 +0100 Subject: [PATCH] first test of new repo passing --- papers/repo.py | 266 +++++++++++++++++++-------------------------- tests/test_repo.py | 26 ++--- 2 files changed, 124 insertions(+), 168 deletions(-) diff --git a/papers/repo.py b/papers/repo.py index 6486890..ce3f015 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -1,13 +1,14 @@ import os import shutil import glob +import itertools from . import files from .paper import PaperInRepo, NoDocumentFile from . import configs -ALPHABET = 'abcdefghijklmopqrstuvwxyz' + BASE_FILE = 'papers.yaml' BIB_DIR = 'bibdata' META_DIR = 'meta' @@ -24,24 +25,65 @@ class InvalidReference(Exception): class Repository(object): - def __init__(self, config=None): - self.papersdir = None - self.citekeys = [] - if config is None: - config = configs.CONFIG + def __init__(self, config, load = True): + """Initialize the repository. + + :param load: if load is True, load the repository from disk, + from path config.papers_dir. + """ self.config = config + self.citekeys = [] - def has_paper(self, citekey): + # @classmethod + # def from_directory(cls, config, papersdir=None): + # repo = cls(config) + # if papersdir is None: + # papersdir = config.papers_dir + # repo.papersdir = files.clean_path(papersdir) + # repo.load() + # return repo + + def __contains__(self, citekey): + """Allows to use 'if citekey in repo' pattern""" return citekey in self.citekeys - def get_paper(self, citekey): - """Load a paper by its citekey from disk, if necessary.""" - return PaperInRepo.load( - self, self.path_to_paper_file(citekey, 'bib'), - metapath=self.path_to_paper_file(citekey, 'meta')) + def __len__(self): + return len(self.citekeys) + + + # load, save repo + + def init_dirs(self): + """Create, if necessary, the repository directories. + + Can be called more than once. + """ + self.bib_dir = files.clean_path(self.config.papers_dir, BIB_DIR) + self.meta_dir = files.clean_path(self.config.papers_dir, META_DIR) + self.doc_dir = files.clean_path(self.config.doc_dir) + self.cfg_path = files.clean_path(self.config.papers_dir, 'papers.yaml') + + for d in [self.bib_dir, self.meta_dir, self.doc_dir]: + if not os.path.exists(d): + os.makedirs(d) + + def load(self): + """Load the repository, creating dirs if necessary""" + self.init_dirs() + repo_config = files.read_yamlfile(self.cfg_path) + self.citekeys = repo_config['citekeys'] + + def save(self): + """Save the repo, creating dirs if necessary""" + self.init_dirs() + repo_cfg = {'citekeys': self.citekeys} + files.write_yamlfile(self.cfg_path, repo_cfg) + - def citekey_from_ref(self, ref): - """Tries to get citekey from given ref. + # reference + + def ref2citekey(self, ref): + """Tries to get citekey from given reference. Ref can be a citekey or a number. """ if ref in self.citekeys: @@ -52,139 +94,80 @@ class Repository(object): except (IndexError, ValueError): raise(InvalidReference) - # creating new papers - def add_paper(self, p): + # papers + + def all_papers(self): + for key in self.citekeys: + yield self.get_paper(key) + + def get_paper(self, citekey): + """Load a paper by its citekey from disk, if necessary.""" + return PaperInRepo.load(self, self._bibfile(citekey), + self._metafile(citekey)) + + + # add, remove papers + + def add_paper(self, p, overwrite = False): if p.citekey is None: # TODO also test if citekey is valid raise(ValueError("Invalid citekey: %s." % p.citekey)) - elif self.has_paper(p.citekey): - raise(ValueError("Citekey already exists in repository: %s" - % p.citekey)) + if not overwrite and p.citekey in self: + raise(ValueError('citekey {} already in use'.format(p.citekey))) self.citekeys.append(p.citekey) - # 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 - def add_or_update(self, paper): - if not self.has_paper(paper.citekey): - self.add_paper(paper) - else: - self.save_paper(paper) + def rename_paper(self, paper, new_citekey, overwrite=False): + """Modify the citekey of a paper, and propagate changes to disk""" + if paper.citekey not in self: + raise(ValueError, 'paper {} not in repository'.format(p.citekey)) + if not overwrite and new_citekey not in self: + raise(ValueError, 'citekey {} already in use'.format(new_citekey)) + paper.remove_paper(paper.citekey, remove_doc = False) + paper.citekey = new_citekey + self.add_paper(paper, overwrite = overwrite) - 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 except if the overwrite argument - 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(CiteKeyAlreadyExists, - "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 _bibfile(self, citekey): + return os.path.join(self.bib_dir, citekey + '.bibyaml') + + def _metafile(self, citekey): + return os.path.join(self.meta_dir, citekey + '.meta') + + def remove_paper(self, citekey, remove_doc = True): paper = self.get_paper(citekey) self.citekeys.remove(citekey) - self.save() - for f in ('bib', 'meta'): - os.remove(self.path_to_paper_file(citekey, f)) + os.remove(self._metafile(citekey)) + os.remove(self._bibfile(citekey)) + # Eventually remove associated document - try: - path = paper.get_document_path_in_repo() - os.remove(path) - except NoDocumentFile: - pass + if remove_doc: + try: + path = paper.get_document_path_in_repo() + os.remove(path) + except NoDocumentFile: + pass + + self.save() def save_paper(self, paper): - if not self.has_paper(paper.citekey): + if not paper.citekey in self: 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')) + paper.save(self._bibfile(paper.citekey), + self._metafile(paper.citekey)) - def get_free_citekey(self, paper, citekey=None): - """Create a unique citekey for the given paper. - """ + def generate_citekey(self, paper, citekey=None): + """Create a unique citekey for the given paper.""" if citekey is None: citekey = paper.generate_citekey() - num = [] - while citekey + _to_suffix(num) in self.citekeys: - _str_incr(num) - return citekey + _to_suffix(num) + for n in itertools.count(): + if not citekey + _base27(n) in self.citekeys: + return citekey + _base27(n) - 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, papersdir): - self.papersdir = papersdir - os.makedirs(os.path.join(self.papersdir, BIB_DIR)) - os.makedirs(os.path.join(self.papersdir, META_DIR)) - doc_dir = self.get_document_directory() - if not os.path.exists(doc_dir): - os.makedirs(doc_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_)) - - def get_document_directory(self): - if self.config.has_option(configs.MAIN_SECTION, 'document-directory'): - doc_dir = self.config.get(configs.MAIN_SECTION, - 'document-directory') - else: - doc_dir = os.path.join(self.papersdir, DOC_DIR) - return files.clean_path(doc_dir) - - def find_document(self, citekey): - doc_dir = 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.get_paper(key) def import_document(self, citekey, doc_file): if citekey not in self.citekeys: @@ -204,35 +187,12 @@ class Repository(object): tags = tags.union(p.tags) return tags - @classmethod - def from_directory(cls, config, papersdir=None): - repo = cls(config=config) - if papersdir is None: - papersdir = config.get(configs.MAIN_SECTION, 'papers-directory') - repo.papersdir = files.clean_path(papersdir) - repo.load() - return repo - -def _char_incr(c): - return chr(ord(c) + 1) -def _str_incr(l): - """Increment a number in a list string representation. - - Numbers are represented in base 26 with letters as digits. - """ - pos = 0 - while pos < len(l): - if l[pos] == 'z': - l[pos] = 'a' - pos += 1 - else: - l[pos] = _char_incr(l[pos]) - return - l.append('a') - +def _base27(n): + return _base27((n-1) // 26) + chr(97+((n-1)% 26)) if n else '' -def _to_suffix(l): - return ''.join(l[::-1]) +def _base(num, b): + q, r = divmod(num - 1, len(b)) + return _base(q, b) + b[r] if num else '' diff --git a/tests/test_repo.py b/tests/test_repo.py index 730c44c..24daebc 100644 --- a/tests/test_repo.py +++ b/tests/test_repo.py @@ -3,31 +3,27 @@ import tempfile import shutil import os +import testenv import fixtures -from papers.repo import (Repository, _str_incr, _to_suffix, BIB_DIR, META_DIR, +from papers.repo import (Repository, _base27, BIB_DIR, META_DIR, CiteKeyAlreadyExists) from papers.paper import PaperInRepo - +from papers import configs class TestCitekeyGeneration(unittest.TestCase): def test_string_increment(self): - l = [] - self.assertEqual(_to_suffix(l), '') - _str_incr(l) - self.assertEqual(_to_suffix(l), 'a') - _str_incr(l) - self.assertEqual(_to_suffix(l), 'b') - l = ['z'] - _str_incr(l) - self.assertEqual(_to_suffix(l), 'aa') + self.assertEqual(_base27(0), '') + for i in range(26): + self.assertEqual(_base27(i+1), chr(97+i)) + self.assertEqual(_base27(26+i+1), 'a' + chr(97+i)) def test_generated_key_is_unique(self): - repo = Repository() + repo = Repository(configs.Config()) repo.citekeys = ['Turing1950', 'Doe2003'] - c = repo.get_free_citekey(fixtures.turing1950) + c = repo.generate_citekey(fixtures.turing1950) repo.citekeys.append('Turing1950a') - c = repo.get_free_citekey(fixtures.turing1950) + c = repo.generate_citekey(fixtures.turing1950) self.assertEqual(c, 'Turing1950b') @@ -35,7 +31,7 @@ class TestRepo(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp() - self.repo = Repository() + self.repo = Repository(configs.Config()) self.repo.init(self.tmpdir) self.repo.add_paper(fixtures.turing1950)