diff --git a/papers/commands/__init__.py b/papers/commands/__init__.py index f832ba3..bc524c7 100644 --- a/papers/commands/__init__.py +++ b/papers/commands/__init__.py @@ -8,6 +8,7 @@ import list_cmd import attach_cmd import open_cmd import tag_cmd +import note_cmd # bulk import export_cmd import import_cmd diff --git a/papers/commands/note_cmd.py b/papers/commands/note_cmd.py new file mode 100644 index 0000000..ad2a3eb --- /dev/null +++ b/papers/commands/note_cmd.py @@ -0,0 +1,27 @@ +from .. import repo +from .. import content +from ..configs import config +from ..uis import get_ui + +def parser(subparsers): + parser = subparsers.add_parser('note', + help='edit the note attached to a paper') + parser.add_argument('citekey', + help='citekey of the paper') + return parser + + +def command(args): + """ + """ + + ui = get_ui() + + + rp = repo.Repository(config()) + if not rp.databroker.exists(args.citekey): + ui.error("citekey {} not found".format(args.citekey)) + ui.exit(1) + + notepath = rp.databroker.real_notepath(args.citekey) + content.edit_file(config().edit_cmd, notepath, temporary=False) diff --git a/papers/databroker.py b/papers/databroker.py index 5679d53..7614a43 100644 --- a/papers/databroker.py +++ b/papers/databroker.py @@ -12,7 +12,8 @@ class DataBroker(object): def __init__(self, directory, create=False): self.filebroker = filebroker.FileBroker(directory, create=create) self.endecoder = endecoder.EnDecoder() - self.docbroker = filebroker.DocBroker(directory) + self.docbroker = filebroker.DocBroker(directory, scheme='docsdir', subdir='doc') + self.notebroker = filebroker.DocBroker(directory, scheme='notesdir', subdir='notes') # filebroker+endecoder @@ -34,7 +35,7 @@ class DataBroker(object): def push(self, citekey, metadata, bibdata): self.filebroker.push(citekey, metadata, bibdata) - + def remove(self, citekey): self.filebroker.remove(citekey) @@ -52,14 +53,32 @@ class DataBroker(object): # docbroker - def is_pubsdir_doc(self, docpath): - return self.docbroker.is_pubsdir_doc(docpath) + def in_docsdir(self, docpath): + return self.docbroker.in_docsdir(docpath) - def copy_doc(self, citekey, source_path, overwrite=False): - return self.docbroker.copy_doc(citekey, source_path, overwrite=overwrite) + def real_docpath(self, docpath): + return self.docbroker.real_docpath(docpath) + + def add_doc(self, citekey, source_path, overwrite=False): + return self.docbroker.add_doc(citekey, source_path, overwrite=overwrite) def remove_doc(self, docpath, silent=True): return self.docbroker.remove_doc(docpath, silent=silent) - def real_docpath(self, docpath): - return self.docbroker.real_docpath(docpath) \ No newline at end of file + def rename_doc(self, docpath, new_citekey): + return self.docbroker.rename_doc(docpath, new_citekey) + + # notesbroker + + def real_notepath(self, citekey): + notepath = 'notesdir://{}.txt'.format(citekey) + return self.notebroker.real_docpath(notepath) + + def remove_note(self, citekey, silent=True): + notepath = 'notesdir://{}.txt'.format(citekey) + return self.notebroker.remove_doc(notepath, silent=silent) + + def rename_note(self, old_citekey, new_citekey): + notepath = 'notesdir://{}.txt'.format(old_citekey) + return self.notebroker.rename_doc(notepath, new_citekey) + diff --git a/papers/datacache.py b/papers/datacache.py index 9d7a1f6..8dbec1a 100644 --- a/papers/datacache.py +++ b/papers/datacache.py @@ -60,19 +60,34 @@ class DataCache(object): """Will return None if bibdata_raw can't be decoded""" return self.databroker.verify(bibdata_raw) - # docbroker + # docbroker - def is_pubsdir_doc(self, docpath): - return self.databroker.is_pubsdir_doc(docpath) + def in_docsdir(self, docpath): + return self.databroker.in_docsdir(docpath) + + def real_docpath(self, docpath): + return self.databroker.real_docpath(docpath) def copy_doc(self, citekey, source_path, overwrite=False): - return self.databroker.copy_doc(citekey, source_path, overwrite=overwrite) + return self.databroker.add_doc(citekey, source_path, overwrite=overwrite) def remove_doc(self, docpath, silent=True): return self.databroker.remove_doc(docpath, silent=silent) - def real_docpath(self, docpath): - return self.databroker.real_docpath(docpath) + def rename_doc(self, docpath, new_citekey): + return self.databroker.rename_doc(docpath, new_citekey) + + # notesbroker + + def real_notepath(self, citekey): + return self.databroker.real_notepath(citekey) + + def remove_note(self, citekey, silent=True): + return self.databroker.remove_note(citekey, silent=True) + + def rename_note(self, old_citekey, new_citekey): + return self.databroker.rename_note(old_citekey, new_citekey) + # class ChangeTracker(object): diff --git a/papers/filebroker.py b/papers/filebroker.py index aa43e74..a723ab9 100644 --- a/papers/filebroker.py +++ b/papers/filebroker.py @@ -107,38 +107,53 @@ class DocBroker(object): * only one document can be attached to a paper (might change in the future) * this document can be anything, the content is never processed. - * these document have an adress of the type "pubsdir://doc/citekey.pdf" + * these document have an adress of the type "docsdir://citekey.pdf" + * docsdir:// correspond to /path/to/pubsdir/doc (configurable) * document outside of the repository will not be removed. - * deliberately, there is no move_doc method. + * move_doc only applies from inside to inside the docsdir """ - def __init__(self, directory): - self.docdir = os.path.join(directory, 'doc') + def __init__(self, directory, scheme='docsdir', subdir='doc'): + self.scheme = scheme + self.docdir = os.path.join(directory, subdir) if not check_directory(self.docdir, fail = False): os.mkdir(self.docdir) - def is_pubsdir_doc(self, docpath): + def in_docsdir(self, docpath): try: parsed = urlparse.urlparse(docpath) except Exception: return False - if parsed.scheme == 'pubsdir': - assert parsed.netloc == 'doc' - assert parsed.path[0] == '/' - return parsed.scheme == 'pubsdir' + return parsed.scheme == self.scheme - def copy_doc(self, citekey, source_path, overwrite=False): - """ Copy a document to the pubsdir/doc, and return the location + # def doc_exists(self, citekey, ext='.txt'): + # return check_file(os.path.join(self.docdir, citekey + ext), fail=False) + + def real_docpath(self, docpath): + """Return the full path + Essentially transform pubsdir://doc/{citekey}.{ext} to /path/to/pubsdir/doc/{citekey}.{ext}. + Return absoluted paths of regular ones otherwise. + """ + if self.in_docsdir(docpath): + parsed = urlparse.urlparse(docpath) + if parsed.path == '': + docpath = os.path.join(self.docdir, parsed.netloc) + else: + docpath = os.path.join(self.docdir, parsed.netloc, parsed.path[1:]) + return os.path.normpath(os.path.abspath(docpath)) + + def add_doc(self, citekey, source_path, overwrite=False): + """ Add a document to the docsdir, and return its location. The document will be named {citekey}.{ext}. - The location will be pubsdir://doc/{citekey}.{ext}. + The location will be docsdir://{citekey}.{ext}. :param overwrite: will overwrite existing file. :return: the above location """ full_source_path = self.real_docpath(source_path) check_file(full_source_path) - target_path = 'pubsdir://' + os.path.join('doc', citekey + os.path.splitext(source_path)[-1]) + target_path = '{}://{}'.format(self.scheme, citekey + os.path.splitext(source_path)[-1]) full_target_path = self.real_docpath(target_path) if not overwrite and check_file(full_target_path, fail=False): raise IOError('{} file exists.'.format(full_target_path)) @@ -147,11 +162,11 @@ class DocBroker(object): return target_path def remove_doc(self, docpath, silent=True): - """ Will remove only file hosted in pubsdir://doc/ + """ Will remove only file hosted in docsdir:// :raise ValueError: for other paths, unless :param silent: is True """ - if not self.is_pubsdir_doc(docpath): + if not self.in_docsdir(docpath): if not silent: raise ValueError(('the file to be removed {} is set as external. ' 'you should remove it manually.').format(docpath)) @@ -160,12 +175,17 @@ class DocBroker(object): if check_file(filepath): os.remove(filepath) - def real_docpath(self, docpath): - """Return the full path - Essentially transform pubsdir://doc/{citekey}.{ext} to /path/to/pubsdir/doc/{citekey}.{ext}. - Return absoluted paths of regular ones otherwise. + def rename_doc(self, docpath, new_citekey): + """ Move a document inside the docsdir + + :raise IOError: if docpath doesn't point to a file + if new_citekey doc exists already. + :raise ValueError: if docpath is not in docsdir(). + + if an exception is raised, the files on disk haven't changed. """ - if self.is_pubsdir_doc(docpath): - parsed = urlparse.urlparse(docpath) - docpath = os.path.join(self.docdir, parsed.path[1:]) - return os.path.normpath(os.path.abspath(docpath)) + if not self.in_docsdir(docpath): + raise ValueError('cannot rename an external file ({}).'.format(docpath)) + + new_notepath = self.add_doc(new_citekey, docpath) + self.remove_doc(docpath) \ No newline at end of file diff --git a/papers/papers_cmd.py b/papers/papers_cmd.py index a308f9d..0433fc5 100644 --- a/papers/papers_cmd.py +++ b/papers/papers_cmd.py @@ -22,6 +22,7 @@ CORE_CMDS = collections.OrderedDict([ ('attach', commands.attach_cmd), ('open', commands.open_cmd), ('tag', commands.tag_cmd), + ('note', commands.note_cmd), ('export', commands.export_cmd), ('import', commands.import_cmd), diff --git a/papers/repo.py b/papers/repo.py index e717433..df474ba 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -87,6 +87,7 @@ class Repository(object): metadata = self.databroker.pull_metadata(citekey) docpath = metadata.get('docfile') self.databroker.remove_doc(docpath, silent=True) + self.databroker.remove_note(citekey, silent=True) except IOError: pass # FXME: if IOError is about being unable to # remove the file, we need to issue an error.I @@ -103,16 +104,21 @@ class Repository(object): # check if new_citekey does not exists if self.databroker.exists(new_citekey, both=False): raise IOError("can't rename paper to {}, conflicting files exists".format(new_citekey)) + # modify bibdata (__delitem__ not implementd by pybtex) new_bibdata = BibliographyData() new_bibdata.entries[new_citekey] = paper.bibdata.entries[old_citekey] paper.bibdata = new_bibdata # move doc file if necessary - if self.databroker.is_pubsdir_doc(paper.docpath): - new_docpath = self.databroker.copy_doc(new_citekey, paper.docpath) - self.databroker.remove_doc(paper.docpath) - paper.docpath = new_docpath + if self.databroker.in_docsdir(paper.docpath): + paper.docpath = self.databroker.rename_doc(paper.docpath, new_citekey) + + # move note file if necessary + try: + self.databroker.rename_note(old_citekey, new_citekey) + except IOError: + pass # push_paper to new_citekey paper.citekey = new_citekey diff --git a/tests/test_databroker.py b/tests/test_databroker.py index 255585b..7e4971a 100644 --- a/tests/test_databroker.py +++ b/tests/test_databroker.py @@ -67,8 +67,8 @@ class TestDataBroker(TestFakeFs): with self.assertRaises(IOError): db.pull_metadata('citekey') - db.copy_doc('Larry99', 'pubsdir://doc/Page99.pdf') + db.copy_doc('Larry99', 'docsdir://Page99.pdf') self.assertTrue(content.check_file('repo/doc/Page99.pdf', fail=False)) self.assertTrue(content.check_file('repo/doc/Larry99.pdf', fail=False)) - db.remove_doc('pubsdir://doc/Page99.pdf') + db.remove_doc('docsdir://Page99.pdf') diff --git a/tests/test_filebroker.py b/tests/test_filebroker.py index 2691dad..32833a8 100644 --- a/tests/test_filebroker.py +++ b/tests/test_filebroker.py @@ -101,9 +101,9 @@ class TestDocBroker(TestFakeFs): docpath = docb.copy_doc('Page99', 'data/pagerank.pdf') self.assertTrue(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf'))) - self.assertTrue(docb.is_pubsdir_doc(docpath)) - self.assertEqual(docpath, 'pubsdir://doc/Page99.pdf') - docb.remove_doc('pubsdir://doc/Page99.pdf') + self.assertTrue(docb.in_docsdir(docpath)) + self.assertEqual(docpath, 'docsdir://Page99.pdf') + docb.remove_doc('docsdir://Page99.pdf') self.assertFalse(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf'), fail=False)) with self.assertRaises(IOError): diff --git a/tests/test_usecase.py b/tests/test_usecase.py index 5688304..b6c7c77 100644 --- a/tests/test_usecase.py +++ b/tests/test_usecase.py @@ -100,13 +100,13 @@ class TestInit(CommandTestCase): pubsdir = os.path.expanduser('~/papers_test2') papers_cmd.execute('papers init -p {}'.format(pubsdir).split()) self.assertEqual(set(self.fs['os'].listdir(pubsdir)), - {'bib', 'doc', 'meta'}) + {'bib', 'doc', 'meta', 'notes'}) def test_init2(self): pubsdir = os.path.expanduser('~/.papers') papers_cmd.execute('papers init'.split()) self.assertEqual(set(self.fs['os'].listdir(pubsdir)), - {'bib', 'doc', 'meta'}) + {'bib', 'doc', 'meta', 'notes'}) class TestAdd(DataCommandTestCase):