Merge branch 'feat/docsdir' into feat/cache

pubsdir://doc/ replaced by docsdir:// so that docsdir can be relocated.
added support for note, completely internal.
main
Fabien Benureau 11 years ago
commit 0fc3df98fe

@ -8,6 +8,7 @@ import list_cmd
import attach_cmd import attach_cmd
import open_cmd import open_cmd
import tag_cmd import tag_cmd
import note_cmd
# bulk # bulk
import export_cmd import export_cmd
import import_cmd import import_cmd

@ -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)

@ -12,7 +12,8 @@ class DataBroker(object):
def __init__(self, directory, create=False): def __init__(self, directory, create=False):
self.filebroker = filebroker.FileBroker(directory, create=create) self.filebroker = filebroker.FileBroker(directory, create=create)
self.endecoder = endecoder.EnDecoder() 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 # filebroker+endecoder
@ -34,7 +35,7 @@ class DataBroker(object):
def push(self, citekey, metadata, bibdata): def push(self, citekey, metadata, bibdata):
self.filebroker.push(citekey, metadata, bibdata) self.filebroker.push(citekey, metadata, bibdata)
def remove(self, citekey): def remove(self, citekey):
self.filebroker.remove(citekey) self.filebroker.remove(citekey)
@ -52,14 +53,32 @@ class DataBroker(object):
# docbroker # docbroker
def is_pubsdir_doc(self, docpath): def in_docsdir(self, docpath):
return self.docbroker.is_pubsdir_doc(docpath) return self.docbroker.in_docsdir(docpath)
def copy_doc(self, citekey, source_path, overwrite=False): def real_docpath(self, docpath):
return self.docbroker.copy_doc(citekey, source_path, overwrite=overwrite) 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): def remove_doc(self, docpath, silent=True):
return self.docbroker.remove_doc(docpath, silent=silent) return self.docbroker.remove_doc(docpath, silent=silent)
def real_docpath(self, docpath): def rename_doc(self, docpath, new_citekey):
return self.docbroker.real_docpath(docpath) 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)

@ -60,19 +60,34 @@ class DataCache(object):
"""Will return None if bibdata_raw can't be decoded""" """Will return None if bibdata_raw can't be decoded"""
return self.databroker.verify(bibdata_raw) return self.databroker.verify(bibdata_raw)
# docbroker # docbroker
def is_pubsdir_doc(self, docpath): def in_docsdir(self, docpath):
return self.databroker.is_pubsdir_doc(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): 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): def remove_doc(self, docpath, silent=True):
return self.databroker.remove_doc(docpath, silent=silent) return self.databroker.remove_doc(docpath, silent=silent)
def real_docpath(self, docpath): def rename_doc(self, docpath, new_citekey):
return self.databroker.real_docpath(docpath) 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): # class ChangeTracker(object):

@ -107,38 +107,53 @@ class DocBroker(object):
* only one document can be attached to a paper (might change in the future) * only one document can be attached to a paper (might change in the future)
* this document can be anything, the content is never processed. * 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. * 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): def __init__(self, directory, scheme='docsdir', subdir='doc'):
self.docdir = os.path.join(directory, 'doc') self.scheme = scheme
self.docdir = os.path.join(directory, subdir)
if not check_directory(self.docdir, fail = False): if not check_directory(self.docdir, fail = False):
os.mkdir(self.docdir) os.mkdir(self.docdir)
def is_pubsdir_doc(self, docpath): def in_docsdir(self, docpath):
try: try:
parsed = urlparse.urlparse(docpath) parsed = urlparse.urlparse(docpath)
except Exception: except Exception:
return False return False
if parsed.scheme == 'pubsdir': return parsed.scheme == self.scheme
assert parsed.netloc == 'doc'
assert parsed.path[0] == '/'
return parsed.scheme == 'pubsdir'
def copy_doc(self, citekey, source_path, overwrite=False): # def doc_exists(self, citekey, ext='.txt'):
""" Copy a document to the pubsdir/doc, and return the location # 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 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. :param overwrite: will overwrite existing file.
:return: the above location :return: the above location
""" """
full_source_path = self.real_docpath(source_path) full_source_path = self.real_docpath(source_path)
check_file(full_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) full_target_path = self.real_docpath(target_path)
if not overwrite and check_file(full_target_path, fail=False): if not overwrite and check_file(full_target_path, fail=False):
raise IOError('{} file exists.'.format(full_target_path)) raise IOError('{} file exists.'.format(full_target_path))
@ -147,11 +162,11 @@ class DocBroker(object):
return target_path return target_path
def remove_doc(self, docpath, silent=True): 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 :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: if not silent:
raise ValueError(('the file to be removed {} is set as external. ' raise ValueError(('the file to be removed {} is set as external. '
'you should remove it manually.').format(docpath)) 'you should remove it manually.').format(docpath))
@ -160,12 +175,17 @@ class DocBroker(object):
if check_file(filepath): if check_file(filepath):
os.remove(filepath) os.remove(filepath)
def real_docpath(self, docpath): def rename_doc(self, docpath, new_citekey):
"""Return the full path """ Move a document inside the docsdir
Essentially transform pubsdir://doc/{citekey}.{ext} to /path/to/pubsdir/doc/{citekey}.{ext}.
Return absoluted paths of regular ones otherwise. :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): if not self.in_docsdir(docpath):
parsed = urlparse.urlparse(docpath) raise ValueError('cannot rename an external file ({}).'.format(docpath))
docpath = os.path.join(self.docdir, parsed.path[1:])
return os.path.normpath(os.path.abspath(docpath)) new_notepath = self.add_doc(new_citekey, docpath)
self.remove_doc(docpath)

@ -22,6 +22,7 @@ CORE_CMDS = collections.OrderedDict([
('attach', commands.attach_cmd), ('attach', commands.attach_cmd),
('open', commands.open_cmd), ('open', commands.open_cmd),
('tag', commands.tag_cmd), ('tag', commands.tag_cmd),
('note', commands.note_cmd),
('export', commands.export_cmd), ('export', commands.export_cmd),
('import', commands.import_cmd), ('import', commands.import_cmd),

@ -87,6 +87,7 @@ class Repository(object):
metadata = self.databroker.pull_metadata(citekey) metadata = self.databroker.pull_metadata(citekey)
docpath = metadata.get('docfile') docpath = metadata.get('docfile')
self.databroker.remove_doc(docpath, silent=True) self.databroker.remove_doc(docpath, silent=True)
self.databroker.remove_note(citekey, silent=True)
except IOError: except IOError:
pass # FXME: if IOError is about being unable to pass # FXME: if IOError is about being unable to
# remove the file, we need to issue an error.I # remove the file, we need to issue an error.I
@ -103,16 +104,21 @@ class Repository(object):
# check if new_citekey does not exists # check if new_citekey does not exists
if self.databroker.exists(new_citekey, both=False): if self.databroker.exists(new_citekey, both=False):
raise IOError("can't rename paper to {}, conflicting files exists".format(new_citekey)) raise IOError("can't rename paper to {}, conflicting files exists".format(new_citekey))
# modify bibdata (__delitem__ not implementd by pybtex) # modify bibdata (__delitem__ not implementd by pybtex)
new_bibdata = BibliographyData() new_bibdata = BibliographyData()
new_bibdata.entries[new_citekey] = paper.bibdata.entries[old_citekey] new_bibdata.entries[new_citekey] = paper.bibdata.entries[old_citekey]
paper.bibdata = new_bibdata paper.bibdata = new_bibdata
# move doc file if necessary # 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) paper.docpath = self.databroker.rename_doc(paper.docpath, new_citekey)
self.databroker.remove_doc(paper.docpath)
paper.docpath = new_docpath # move note file if necessary
try:
self.databroker.rename_note(old_citekey, new_citekey)
except IOError:
pass
# push_paper to new_citekey # push_paper to new_citekey
paper.citekey = new_citekey paper.citekey = new_citekey

@ -67,8 +67,8 @@ class TestDataBroker(TestFakeFs):
with self.assertRaises(IOError): with self.assertRaises(IOError):
db.pull_metadata('citekey') 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/Page99.pdf', fail=False))
self.assertTrue(content.check_file('repo/doc/Larry99.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')

@ -101,9 +101,9 @@ class TestDocBroker(TestFakeFs):
docpath = docb.copy_doc('Page99', 'data/pagerank.pdf') docpath = docb.copy_doc('Page99', 'data/pagerank.pdf')
self.assertTrue(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf'))) self.assertTrue(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf')))
self.assertTrue(docb.is_pubsdir_doc(docpath)) self.assertTrue(docb.in_docsdir(docpath))
self.assertEqual(docpath, 'pubsdir://doc/Page99.pdf') self.assertEqual(docpath, 'docsdir://Page99.pdf')
docb.remove_doc('pubsdir://doc/Page99.pdf') docb.remove_doc('docsdir://Page99.pdf')
self.assertFalse(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf'), fail=False)) self.assertFalse(content.check_file(os.path.join('tmpdir', 'doc/Page99.pdf'), fail=False))
with self.assertRaises(IOError): with self.assertRaises(IOError):

@ -100,13 +100,13 @@ class TestInit(CommandTestCase):
pubsdir = os.path.expanduser('~/papers_test2') pubsdir = os.path.expanduser('~/papers_test2')
papers_cmd.execute('papers init -p {}'.format(pubsdir).split()) papers_cmd.execute('papers init -p {}'.format(pubsdir).split())
self.assertEqual(set(self.fs['os'].listdir(pubsdir)), self.assertEqual(set(self.fs['os'].listdir(pubsdir)),
{'bib', 'doc', 'meta'}) {'bib', 'doc', 'meta', 'notes'})
def test_init2(self): def test_init2(self):
pubsdir = os.path.expanduser('~/.papers') pubsdir = os.path.expanduser('~/.papers')
papers_cmd.execute('papers init'.split()) papers_cmd.execute('papers init'.split())
self.assertEqual(set(self.fs['os'].listdir(pubsdir)), self.assertEqual(set(self.fs['os'].listdir(pubsdir)),
{'bib', 'doc', 'meta'}) {'bib', 'doc', 'meta', 'notes'})
class TestAdd(DataCommandTestCase): class TestAdd(DataCommandTestCase):

Loading…
Cancel
Save