From 1ad64d7859cdc5822db16c4f2cdbc6124f0ecaa8 Mon Sep 17 00:00:00 2001 From: Fabien Benureau Date: Mon, 11 Nov 2013 04:07:42 +0100 Subject: [PATCH 1/3] notes cmd --- papers/commands/__init__.py | 1 + papers/commands/note_cmd.py | 27 +++++++++++++++++++++++++++ papers/databroker.py | 18 ++++++++++++++---- papers/datacache.py | 15 ++++++++++++--- papers/filebroker.py | 30 ++++++++++++++++-------------- papers/papers_cmd.py | 1 + papers/repo.py | 2 +- tests/test_databroker.py | 4 ++-- tests/test_filebroker.py | 6 +++--- tests/test_usecase.py | 4 ++-- 10 files changed, 79 insertions(+), 29 deletions(-) create mode 100644 papers/commands/note_cmd.py 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..073c46d --- /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('notesdir://{}.txt'.format(args.citekey)) + content.edit_file(config().edit_cmd, notepath, temporary=False) diff --git a/papers/databroker.py b/papers/databroker.py index 5679d53..e5c5680 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 @@ -52,8 +53,8 @@ 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) @@ -62,4 +63,13 @@ class DataBroker(object): 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 + return self.docbroker.real_docpath(docpath) + + + # notesbroker + + def in_notesdir(self, docpath): + return self.notebroker.in_docsdir(docpath) + + def real_notepath(self, docpath): + return self.notebroker.real_docpath(docpath) \ No newline at end of file diff --git a/papers/datacache.py b/papers/datacache.py index 9d7a1f6..ef6646d 100644 --- a/papers/datacache.py +++ b/papers/datacache.py @@ -60,10 +60,10 @@ 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 copy_doc(self, citekey, source_path, overwrite=False): return self.databroker.copy_doc(citekey, source_path, overwrite=overwrite) @@ -74,6 +74,15 @@ class DataCache(object): def real_docpath(self, docpath): return self.databroker.real_docpath(docpath) + # notesbroker + + def in_notesdir(self, docpath): + return self.databroker.in_notesdir(docpath) + + def real_notepath(self, docpath): + return self.databroker.real_notepath(docpath) + + # class ChangeTracker(object): # def __init__(self, cache, directory): diff --git a/papers/filebroker.py b/papers/filebroker.py index aa43e74..1b257ce 100644 --- a/papers/filebroker.py +++ b/papers/filebroker.py @@ -107,38 +107,37 @@ 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. """ - 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 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 +146,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)) @@ -165,7 +164,10 @@ class DocBroker(object): Essentially transform pubsdir://doc/{citekey}.{ext} to /path/to/pubsdir/doc/{citekey}.{ext}. Return absoluted paths of regular ones otherwise. """ - if self.is_pubsdir_doc(docpath): + if self.in_docsdir(docpath): parsed = urlparse.urlparse(docpath) - docpath = os.path.join(self.docdir, parsed.path[1:]) + 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)) 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..7469d66 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -109,7 +109,7 @@ class Repository(object): paper.bibdata = new_bibdata # move doc file if necessary - if self.databroker.is_pubsdir_doc(paper.docpath): + if self.databroker.in_docsdir(paper.docpath): new_docpath = self.databroker.copy_doc(new_citekey, paper.docpath) self.databroker.remove_doc(paper.docpath) paper.docpath = new_docpath 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): From 29897cc0ad95b6caa81cbcf6c56532be9dcbc079 Mon Sep 17 00:00:00 2001 From: Fabien Benureau Date: Mon, 11 Nov 2013 15:24:09 +0100 Subject: [PATCH 2/3] rename also rename notes --- papers/databroker.py | 10 ++++++++-- papers/datacache.py | 6 ++++++ papers/filebroker.py | 3 +++ papers/repo.py | 8 ++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/papers/databroker.py b/papers/databroker.py index e5c5680..7ffa6ee 100644 --- a/papers/databroker.py +++ b/papers/databroker.py @@ -35,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) @@ -65,11 +65,17 @@ class DataBroker(object): def real_docpath(self, docpath): return self.docbroker.real_docpath(docpath) - # notesbroker def in_notesdir(self, docpath): return self.notebroker.in_docsdir(docpath) + def copy_note(self, citekey, source_path, overwrite=False): + return self.notebroker.copy_doc(citekey, source_path, overwrite=overwrite) + + def remove_note(self, docpath, silent=True): + return self.notebroker.remove_doc(docpath, silent=silent) + + def real_notepath(self, docpath): return self.notebroker.real_docpath(docpath) \ No newline at end of file diff --git a/papers/datacache.py b/papers/datacache.py index ef6646d..636d8e5 100644 --- a/papers/datacache.py +++ b/papers/datacache.py @@ -79,6 +79,12 @@ class DataCache(object): def in_notesdir(self, docpath): return self.databroker.in_notesdir(docpath) + def copy_note(self, citekey, source_path, overwrite=False): + return self.databroker.copy_note(citekey, source_path, overwrite=overwrite) + + def remove_note(self, docpath, silent=True): + return self.databroker.remove_note(docpath, silent=silent) + def real_notepath(self, docpath): return self.databroker.real_notepath(docpath) diff --git a/papers/filebroker.py b/papers/filebroker.py index 1b257ce..c721950 100644 --- a/papers/filebroker.py +++ b/papers/filebroker.py @@ -126,6 +126,9 @@ class DocBroker(object): return False return parsed.scheme == self.scheme + # def doc_exists(self, citekey, ext='.txt'): + # return check_file(os.path.join(self.docdir, citekey + ext), fail=False) + def copy_doc(self, citekey, source_path, overwrite=False): """ Copy a document to the pubsdir/doc, and return the location diff --git a/papers/repo.py b/papers/repo.py index 7469d66..21b7a2c 100644 --- a/papers/repo.py +++ b/papers/repo.py @@ -114,6 +114,14 @@ class Repository(object): self.databroker.remove_doc(paper.docpath) paper.docpath = new_docpath + try: + old_notepath = 'notesdir://{}.txt'.format(old_citekey) + new_notepath = self.databroker.copy_note(new_citekey, old_notepath) + self.databroker.remove_notei(old_notepath) + except IOError: + import traceback + traceback.print_exc() + # push_paper to new_citekey paper.citekey = new_citekey self.push_paper(paper, event=False) From 148917c70c8257e9a6fa31a0cd56d06d03afbb2b Mon Sep 17 00:00:00 2001 From: Fabien Benureau Date: Tue, 12 Nov 2013 13:34:39 +0100 Subject: [PATCH 3/3] move_doc in docbroker + consequences + remove_cmd remove notes too --- papers/commands/note_cmd.py | 2 +- papers/databroker.py | 29 ++++++++++++++----------- papers/datacache.py | 24 ++++++++++----------- papers/filebroker.py | 43 +++++++++++++++++++++++++------------ papers/repo.py | 14 ++++++------ 5 files changed, 64 insertions(+), 48 deletions(-) diff --git a/papers/commands/note_cmd.py b/papers/commands/note_cmd.py index 073c46d..ad2a3eb 100644 --- a/papers/commands/note_cmd.py +++ b/papers/commands/note_cmd.py @@ -23,5 +23,5 @@ def command(args): ui.error("citekey {} not found".format(args.citekey)) ui.exit(1) - notepath = rp.databroker.real_notepath('notesdir://{}.txt'.format(args.citekey)) + 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 7ffa6ee..7614a43 100644 --- a/papers/databroker.py +++ b/papers/databroker.py @@ -56,26 +56,29 @@ class DataBroker(object): 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) + def rename_doc(self, docpath, new_citekey): + return self.docbroker.rename_doc(docpath, new_citekey) # notesbroker - def in_notesdir(self, docpath): - return self.notebroker.in_docsdir(docpath) - - def copy_note(self, citekey, source_path, overwrite=False): - return self.notebroker.copy_doc(citekey, source_path, overwrite=overwrite) + def real_notepath(self, citekey): + notepath = 'notesdir://{}.txt'.format(citekey) + return self.notebroker.real_docpath(notepath) - def remove_note(self, docpath, silent=True): - return self.notebroker.remove_doc(docpath, silent=silent) + 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) - def real_notepath(self, docpath): - return self.notebroker.real_docpath(docpath) \ No newline at end of file diff --git a/papers/datacache.py b/papers/datacache.py index 636d8e5..8dbec1a 100644 --- a/papers/datacache.py +++ b/papers/datacache.py @@ -65,28 +65,28 @@ class DataCache(object): 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 in_notesdir(self, docpath): - return self.databroker.in_notesdir(docpath) - - def copy_note(self, citekey, source_path, overwrite=False): - return self.databroker.copy_note(citekey, source_path, overwrite=overwrite) + def real_notepath(self, citekey): + return self.databroker.real_notepath(citekey) - def remove_note(self, docpath, silent=True): - return self.databroker.remove_note(docpath, silent=silent) + def remove_note(self, citekey, silent=True): + return self.databroker.remove_note(citekey, silent=True) - def real_notepath(self, docpath): - return self.databroker.real_notepath(docpath) + 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 c721950..a723ab9 100644 --- a/papers/filebroker.py +++ b/papers/filebroker.py @@ -110,7 +110,7 @@ class DocBroker(object): * 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, scheme='docsdir', subdir='doc'): @@ -129,8 +129,21 @@ class DocBroker(object): # def doc_exists(self, citekey, ext='.txt'): # return check_file(os.path.join(self.docdir, citekey + ext), fail=False) - def copy_doc(self, citekey, source_path, overwrite=False): - """ Copy a document to the pubsdir/doc, and return the location + 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 docsdir://{citekey}.{ext}. @@ -162,15 +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.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)) + 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/repo.py b/papers/repo.py index 21b7a2c..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,6 +104,7 @@ 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] @@ -110,17 +112,13 @@ class Repository(object): # move doc file if necessary if self.databroker.in_docsdir(paper.docpath): - new_docpath = self.databroker.copy_doc(new_citekey, paper.docpath) - self.databroker.remove_doc(paper.docpath) - paper.docpath = new_docpath + paper.docpath = self.databroker.rename_doc(paper.docpath, new_citekey) + # move note file if necessary try: - old_notepath = 'notesdir://{}.txt'.format(old_citekey) - new_notepath = self.databroker.copy_note(new_citekey, old_notepath) - self.databroker.remove_notei(old_notepath) + self.databroker.rename_note(old_citekey, new_citekey) except IOError: - import traceback - traceback.print_exc() + pass # push_paper to new_citekey paper.citekey = new_citekey