aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcheck2
-rw-r--r--lesana/collection.py226
-rw-r--r--lesana/command.py267
-rwxr-xr-xscripts/lesana24
-rwxr-xr-xscripts/tellico2lesana56
-rw-r--r--setup.py16
-rw-r--r--tests/test_collection.py156
7 files changed, 372 insertions, 375 deletions
diff --git a/check b/check
index df52aa5..9e5c819 100755
--- a/check
+++ b/check
@@ -1,4 +1,4 @@
#!/bin/sh
nosetests3 --with-coverage --cover-erase --cover-package=lesana
-flake8 .
+flake8 --select=E,F,W,C90,E123 .
diff --git a/lesana/collection.py b/lesana/collection.py
index ca66fcf..b265b4d 100644
--- a/lesana/collection.py
+++ b/lesana/collection.py
@@ -11,6 +11,7 @@ from pkg_resources import resource_string
try:
import git
+
git_available = True
except ImportError:
git_available = False
@@ -52,21 +53,21 @@ class Entry(object):
data += "# {name} ({type}): {help}\n".format(**field)
t = field['type']
if field.get('default', None):
- data += ("{name}: '{default}'\n".format(**field))
+ data += "{name}: '{default}'\n".format(**field)
elif t == 'string':
- data += ("{name}: ''\n".format(**field))
+ data += "{name}: ''\n".format(**field)
elif t == 'text':
- data += ("{name}: |\n .\n".format(**field))
+ data += "{name}: |\n .\n".format(**field)
elif t == 'integer':
- data += ("{name}: 0\n".format(**field))
+ data += "{name}: 0\n".format(**field)
elif t == 'float':
- data += ("{name}: 0.0\n".format(**field))
+ data += "{name}: 0.0\n".format(**field)
elif t == 'decimal':
- data += ("{name}: 0.00\n".format(**field))
+ data += "{name}: 0.00\n".format(**field)
elif t == 'list':
- data += ("{name}: []\n".format(**field))
+ data += "{name}: []\n".format(**field)
else:
- data += ("{name}: \n".format(**field))
+ data += "{name}: \n".format(**field)
return ruamel.yaml.load(data, Loader=ruamel.yaml.RoundTripLoader)
@property
@@ -75,7 +76,7 @@ class Entry(object):
@property
def idterm(self):
- return "Q"+self.eid
+ return "Q" + self.eid
@property
def short_id(self):
@@ -95,43 +96,54 @@ class Entry(object):
int(value)
except ValueError:
valid = False
- errors.append({
- 'field': field['name'],
- 'error': 'Invalid value for integer field: {}'.format(
- value
+ errors.append(
+ {
+ 'field': field['name'],
+ 'error':
+ 'Invalid value for integer field: {}'.format(
+ value
),
- })
+ }
+ )
elif t == 'float':
try:
float(value)
except ValueError:
valid = False
- errors.append({
- 'field': field['name'],
- 'error': 'Invalid value for float field: {}'.format(
- value
+ errors.append(
+ {
+ 'field': field['name'],
+ 'error':
+ 'Invalid value for float field: {}'.format(
+ value
),
- })
+ }
+ )
elif t == 'decimal':
try:
decimal.Decimal(value)
except decimal.InvalidOperation:
valid = False
- errors.append({
- 'field': field['name'],
- 'error': 'Invalid value for decimal field: {}'.format(
- value
+ errors.append(
+ {
+ 'field': field['name'],
+ 'error':
+ 'Invalid value for decimal field: {}'.format(
+ value
),
- })
+ }
+ )
elif t == 'list':
if not hasattr(value, '__iter__'):
valid = False
- errors.append({
- 'field': field['name'],
- 'error': 'Invalid value for list field: {}'.format(
- value
+ errors.append(
+ {
+ 'field': field['name'],
+ 'error': 'Invalid value for list field: {}'.format(
+ value
),
- })
+ }
+ )
return valid, errors
def render(self, template, searchpath='.'):
@@ -139,18 +151,19 @@ class Entry(object):
try:
return jtemplate.render(entry=self)
except jinja2.exceptions.TemplateSyntaxError as e:
- raise TemplatingError('Template Syntax Error: '+str(e))
+ raise TemplatingError('Template Syntax Error: ' + str(e))
class Collection(object):
"""
"""
+
PARSER_FLAGS = (
- xapian.QueryParser.FLAG_BOOLEAN |
- xapian.QueryParser.FLAG_PHRASE |
- xapian.QueryParser.FLAG_LOVEHATE |
- xapian.QueryParser.FLAG_WILDCARD
- )
+ xapian.QueryParser.FLAG_BOOLEAN
+ | xapian.QueryParser.FLAG_PHRASE # noqa: W503
+ | xapian.QueryParser.FLAG_LOVEHATE # noqa: W503
+ | xapian.QueryParser.FLAG_WILDCARD # noqa: W503
+ )
def __init__(self, directory=None, itemdir='items'):
self.basedir = directory or os.getcwd()
@@ -158,9 +171,8 @@ class Collection(object):
try:
with open(os.path.join(self.basedir, 'settings.yaml')) as fp:
self.settings = ruamel.yaml.load(
- fp,
- ruamel.yaml.RoundTripLoader
- )
+ fp, ruamel.yaml.RoundTripLoader
+ )
except FileNotFoundError:
self.settings = ruamel.yaml.safe_load("{}")
os.makedirs(os.path.join(self.basedir, '.lesana'), exist_ok=True)
@@ -170,8 +182,8 @@ class Collection(object):
except xapian.InvalidArgumentError:
logging.warning(
"Invalid language %s, in settings.yaml: using english.",
- self.settings['lang']
- )
+ self.settings['lang'],
+ )
self.stemmer = xapian.Stem('english')
else:
self.stemmer = xapian.Stem('english')
@@ -192,7 +204,7 @@ class Collection(object):
if not valid:
logging.warning(
"Not indexing {fname}: invalid data".format(fname=fname)
- )
+ )
return False, errors
doc = xapian.Document()
@@ -208,14 +220,13 @@ class Collection(object):
values = []
for v in values:
try:
- self.indexer.index_text(
- str(v),
- 1,
- field['prefix'])
+ self.indexer.index_text(str(v), 1, field['prefix'])
except ValueError as e:
- logging.info("Not indexing empty? value {}: {}".format(
- entry.data.get(field['name']),
- str(e)))
+ logging.info(
+ "Not indexing empty? value {}: {}".format(
+ entry.data.get(field['name']), str(e)
+ )
+ )
# unprefixed fields, for full text search
for field in self.indexed_fields:
if field.get('free_search', False):
@@ -244,13 +255,15 @@ class Collection(object):
fields = []
for field in self.settings['fields']:
if field.get('index', '') in ['free', 'field']:
- prefix = field.get('prefix', 'X'+field['name'].upper())
- fields.append({
- 'prefix': prefix,
- 'name': field['name'],
- 'free_search': field['index'] == 'free',
- 'multi': field['type'] in ['list']
- })
+ prefix = field.get('prefix', 'X' + field['name'].upper())
+ fields.append(
+ {
+ 'prefix': prefix,
+ 'name': field['name'],
+ 'free_search': field['index'] == 'free',
+ 'multi': field['type'] in ['list'],
+ }
+ )
return fields
def update_cache(self, fnames=None):
@@ -265,8 +278,8 @@ class Collection(object):
"""
cache = xapian.WritableDatabase(
os.path.join(self.basedir, '.lesana/xapian'),
- xapian.DB_CREATE_OR_OPEN
- )
+ xapian.DB_CREATE_OR_OPEN,
+ )
self.indexer = xapian.TermGenerator()
self.indexer.set_stemmer(self.stemmer)
if not fnames:
@@ -275,55 +288,51 @@ class Collection(object):
except FileNotFoundError:
logging.warning(
"No such file or directory: {}, not updating cache".format(
- self.itemdir)
+ self.itemdir
)
+ )
return 0
updated = 0
for fname in fnames:
try:
valid, errors = self._index_file(fname, cache)
except IOError as e:
- logging.warning("Could not load file {}: {}".format(
- fname,
- str(e))
- )
+ logging.warning(
+ "Could not load file {}: {}".format(fname, str(e))
+ )
else:
if valid:
updated += 1
else:
logging.warning(
"File {fname} could not be indexed: {errors}".format(
- fname=fname,
- errors=errors)
+ fname=fname, errors=errors
)
+ )
return updated
def save_entries(self, entries=[]):
for e in entries:
- complete_name = os.path.join(
- self.itemdir,
- e.fname
- )
+ complete_name = os.path.join(self.itemdir, e.fname)
with open(complete_name, 'w') as fp:
fp.write(e.yaml_data)
def git_add_files(self, files=[]):
if not git_available:
logging.warning(
- "python3-git not available, could not initalise " +
- "the git repository.")
+ "python3-git not available, could not initalise "
+ + "the git repository." # noqa: W503
+ )
return False
if not self.settings.get('git', False):
- logging.info(
- "This collection is configured not to use git"
- )
+ logging.info("This collection is configured not to use git")
return False
try:
repo = git.Repo(self.basedir, search_parent_directories=True)
except git.exc.InvalidGitRepositoryError:
logging.warning(
- "Could not find a git repository in {}".format(
- self.basedir))
+ "Could not find a git repository in {}".format(self.basedir)
+ )
return False
repo.index.add(files)
return True
@@ -332,13 +341,13 @@ class Collection(object):
try:
cache = xapian.Database(
os.path.join(self.basedir, '.lesana/xapian'),
- )
+ )
except xapian.DatabaseOpeningError:
logging.info("No database found, indexing entries.")
self.update_cache()
cache = xapian.Database(
os.path.join(self.basedir, '.lesana/xapian'),
- )
+ )
return cache
def start_search(self, querystring):
@@ -353,9 +362,7 @@ class Collection(object):
for field in self.indexed_fields:
queryparser.add_prefix(field['name'], field['prefix'])
- query = queryparser.parse_query(
- querystring,
- self.PARSER_FLAGS)
+ query = queryparser.parse_query(querystring, self.PARSER_FLAGS)
self._enquire = xapian.Enquire(cache)
self._enquire.set_query(query)
@@ -395,19 +402,14 @@ class Collection(object):
data = ruamel.yaml.safe_load(doc.get_data())
else:
data = ruamel.yaml.load(
- doc.get_data(),
- ruamel.yaml.RoundTripLoader
- )
- entry = self.entry_class(
- self,
- data=data,
- fname=fname,
+ doc.get_data(), ruamel.yaml.RoundTripLoader
)
+ entry = self.entry_class(self, data=data, fname=fname,)
return entry
def entry_from_eid(self, eid):
cache = self._get_cache()
- postlist = cache.postlist('Q'+eid)
+ postlist = cache.postlist('Q' + eid)
for pitem in postlist:
return self._doc_to_entry(cache.get_document(pitem.docid))
return None
@@ -421,14 +423,14 @@ class Collection(object):
os.path.splitext(f)[0]
for f in os.listdir(self.itemdir)
if f.startswith(seid)
- ]
+ ]
return [self.entry_from_eid(u) for u in potential_eids if u]
def remove_entries(self, eids):
cache = xapian.WritableDatabase(
os.path.join(self.basedir, '.lesana/xapian'),
- xapian.DB_CREATE_OR_OPEN
- )
+ xapian.DB_CREATE_OR_OPEN,
+ )
for eid in eids:
for entry in self.entries_from_short_eid(eid):
if entry is not None:
@@ -447,7 +449,9 @@ class Collection(object):
except git.exc.InvalidGitRepositoryError:
logging.warning(
"Could not find a git repository in {}".format(
- self.basedir))
+ self.basedir
+ )
+ )
return False
repo.index.remove([f_path])
os.remove(f_path)
@@ -459,19 +463,18 @@ class Collection(object):
e.data[field] = value
changed.append(e)
self.save_entries(changed)
- self.git_add_files([
- os.path.join(self.itemdir, e.fname) for e in changed
- ])
+ self.git_add_files(
+ [os.path.join(self.itemdir, e.fname) for e in changed]
+ )
self.update_cache([e.fname for e in changed])
def get_template(self, template_fname, searchpath='.'):
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(
- searchpath=searchpath,
- followlinks=True,
- ),
+ searchpath=searchpath, followlinks=True,
+ ),
# TODO: add autoescaping settings
- )
+ )
try:
template = env.get_template(template_fname)
except jinja2.exceptions.TemplateNotFound as e:
@@ -480,12 +483,8 @@ class Collection(object):
@classmethod
def init(
- cls,
- directory=None,
- git_enabled=True,
- edit_file=None,
- settings={}
- ):
+ cls, directory=None, git_enabled=True, edit_file=None, settings={}
+ ):
"""
Initialize a lesana repository
@@ -504,8 +503,9 @@ class Collection(object):
repo = git.Repo.init(c_dir, bare=False)
else:
logging.warning(
- "python3-git not available, could not initalise " +
- "the git repository.")
+ "python3-git not available, could not initalise "
+ + "the git repository." # noqa: W503
+ )
repo = None
# Add .lesana directory to .gitignore and add it to the
# staging
@@ -528,18 +528,16 @@ class Collection(object):
# then open settings.yaml for editing
filepath = os.path.join(c_dir, 'settings.yaml')
if not os.path.exists(filepath):
- skel = resource_string(
- 'lesana', 'data/settings.yaml'
- ).decode('utf-8')
+ skel = resource_string('lesana', 'data/settings.yaml').decode(
+ 'utf-8'
+ )
skel_dict = ruamel.yaml.load(skel, ruamel.yaml.RoundTripLoader)
skel_dict['git'] = git_enabled
skel_dict.update(settings)
with open(filepath, 'w') as fp:
ruamel.yaml.dump(
- skel_dict,
- stream=fp,
- Dumper=ruamel.yaml.RoundTripDumper
- )
+ skel_dict, stream=fp, Dumper=ruamel.yaml.RoundTripDumper
+ )
if edit_file:
edit_file(filepath)
if git_enabled and repo:
diff --git a/lesana/command.py b/lesana/command.py
index 3c72e68..407544a 100644
--- a/lesana/command.py
+++ b/lesana/command.py
@@ -17,10 +17,9 @@ def edit_file_in_external_editor(filepath):
if editor in str(e):
logging.info(
'Could not open file {} with $EDITOR (currently {})'.format(
- filepath,
- editor
- )
+ filepath, editor
)
+ )
else:
logging.warning("Could not open file {}".format(filepath))
return False
@@ -35,8 +34,8 @@ def edit_file_in_external_editor(filepath):
logging.debug(
"Could not open file {} with editor: sensible-editor".format(
filepath
- )
)
+ )
else:
logging.warning("Could not open file {}".format(filepath))
return False
@@ -50,7 +49,7 @@ def edit_file_in_external_editor(filepath):
if 'vi' in e.strerror:
logging.warning(
"Could not open file {} with any known editor".format(filepath)
- )
+ )
return False
else:
logging.warning("Could not open file {}".format(filepath))
@@ -59,7 +58,7 @@ def edit_file_in_external_editor(filepath):
return True
-class Command():
+class Command:
help = ''
def __init__(self, collection_class=Collection, entry_class=Entry):
@@ -73,24 +72,25 @@ class Command():
class New(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['--no-git'], dict(
- help="Don't add the new entry to git",
- action="store_false",
- dest='git'
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['--no-git'],
+ dict(
+ help="Don't add the new entry to git",
+ action="store_false",
+ dest='git',
+ ),
+ ),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
new_entry = self.entry_class(collection)
collection.save_entries([new_entry])
- filepath = os.path.join(
- collection.itemdir,
- new_entry.fname
- )
+ filepath = os.path.join(collection.itemdir, new_entry.fname)
if edit_file_in_external_editor(filepath):
collection.update_cache([filepath])
if self.args.git:
@@ -101,18 +101,20 @@ class New(Command):
class Edit(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['--no-git'], dict(
- help="Don't add the new entry to git",
- action="store_false",
- dest='git'
- )),
- (['eid'], dict(
- help='eid of an entry to edit',
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['--no-git'],
+ dict(
+ help="Don't add the new entry to git",
+ action="store_false",
+ dest='git',
+ ),
+ ),
+ (['eid'], dict(help='eid of an entry to edit',)),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
@@ -122,12 +124,9 @@ class Edit(Command):
if not entries:
return "Could not find an entry with eid starting with: {}".format(
self.args.eid
- )
- entry = entries[0]
- filepath = os.path.join(
- collection.itemdir,
- entry.fname
)
+ entry = entries[0]
+ filepath = os.path.join(collection.itemdir, entry.fname)
if edit_file_in_external_editor(filepath):
collection.update_cache([filepath])
if self.args.git:
@@ -138,16 +137,16 @@ class Edit(Command):
class Show(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['--template', '-t'], dict(
- help='Use the specified template to display results.',
- )),
- (['eid'], dict(
- help='eid of an entry to edit',
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['--template', '-t'],
+ dict(help='Use the specified template to display results.',),
+ ),
+ (['eid'], dict(help='eid of an entry to edit',)),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
@@ -157,7 +156,7 @@ class Show(Command):
if not entries:
return "Could not find an entry with eid starting with: {}".format(
self.args.eid
- )
+ )
entry = entries[0]
if self.args.template:
try:
@@ -171,15 +170,19 @@ class Show(Command):
class Index(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['files'], dict(
- help='List of files to index (default: everything)',
- default=None,
- nargs='*',
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['files'],
+ dict(
+ help='List of files to index (default: everything)',
+ default=None,
+ nargs='*',
+ ),
+ ),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
@@ -193,40 +196,38 @@ class Index(Command):
class Search(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['--template', '-t'], dict(
- help='Template to use when displaying results',
- )),
- (['--offset'], dict(
- type=int,
- )),
- (['--pagesize'], dict(
- type=int,
- )),
- (['--all'], dict(
- action='store_true',
- help='Return all available results'
- )),
- (['query'], dict(
- help='Xapian query to search in the collection',
- nargs='+'
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['--template', '-t'],
+ dict(help='Template to use when displaying results',),
+ ),
+ (['--offset'], dict(type=int,)),
+ (['--pagesize'], dict(type=int,)),
+ (
+ ['--all'],
+ dict(action='store_true', help='Return all available results'),
+ ),
+ (
+ ['query'],
+ dict(help='Xapian query to search in the collection', nargs='+'),
+ ),
+ ]
def main(self):
# TODO: implement "searching" for everything
if self.args.offset:
logging.warning(
- "offset exposes an internal knob and MAY BE" +
- " REMOVED from a future release of lesana"
- )
+ "offset exposes an internal knob and MAY BE REMOVED "
+ + "from a future release of lesana" # noqa: W503
+ )
if self.args.pagesize:
logging.warning(
- "pagesize exposes an internal knob and MAY BE" +
- " REMOVED from a future release of lesana"
- )
+ "pagesize exposes an internal knob and MAY BE REMOVED "
+ + "from a future release of lesana" # noqa: W503
+ )
offset = self.args.offset or 0
pagesize = self.args.pagesize or 12
collection = self.collection_class(self.args.collection)
@@ -237,9 +238,7 @@ class Search(Command):
if self.args.all:
results = collection.get_all_search_results()
else:
- results = collection.get_search_results(
- offset,
- pagesize)
+ results = collection.get_search_results(offset, pagesize)
if self.args.template:
try:
template = collection.get_template(self.args.template)
@@ -249,26 +248,22 @@ class Search(Command):
sys.exit(1)
else:
for entry in results:
- print("{entry}".format(
- entry=entry,
- ))
+ print("{entry}".format(entry=entry,))
class Export(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)'
- )),
- (['--query', '-q'], dict(
- help='Xapian query to search in the collection',
- )),
- (['destination'], dict(
- help='The collection to export entries to'
- )),
- (['template'], dict(
- help='Template to convert entries',
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)'),
+ ),
+ (
+ ['--query', '-q'],
+ dict(help='Xapian query to search in the collection',),
+ ),
+ (['destination'], dict(help='The collection to export entries to')),
+ (['template'], dict(help='Template to convert entries',)),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
@@ -298,35 +293,36 @@ class Export(Command):
class Init(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The directory to work on (default .)',
- default='.'
- )),
- (['--no-git'], dict(
- help='Skip setting up git in this directory',
- action="store_false",
- dest='git'
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The directory to work on (default .)', default='.'),
+ ),
+ (
+ ['--no-git'],
+ dict(
+ help='Skip setting up git in this directory',
+ action="store_false",
+ dest='git',
+ ),
+ ),
+ ]
def main(self):
self.collection_class.init(
self.args.collection,
git_enabled=self.args.git,
- edit_file=edit_file_in_external_editor
- )
+ edit_file=edit_file_in_external_editor,
+ )
class Remove(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)',
- )),
- (['entries'], dict(
- help='List of entries to remove',
- nargs='+',
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)',),
+ ),
+ (['entries'], dict(help='List of entries to remove', nargs='+',)),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
@@ -335,25 +331,22 @@ class Remove(Command):
class Update(Command):
arguments = [
- (['--collection', '-c'], dict(
- help='The collection to work on (default .)',
- )),
- (['--field', '-f'], dict(
- help='The field to change',
- )),
- (['--value', '-t'], dict(
- help='The value to set',
- )),
- (['query'], dict(
- help='Xapian query to search in the collection',
- nargs='+'
- )),
- ]
+ (
+ ['--collection', '-c'],
+ dict(help='The collection to work on (default .)',),
+ ),
+ (['--field', '-f'], dict(help='The field to change',)),
+ (['--value', '-t'], dict(help='The value to set',)),
+ (
+ ['query'],
+ dict(help='Xapian query to search in the collection', nargs='+'),
+ ),
+ ]
def main(self):
collection = self.collection_class(self.args.collection)
collection.update_field(
' '.join(self.args.query),
field=self.args.field,
- value=self.args.value
+ value=self.args.value,
)
diff --git a/scripts/lesana b/scripts/lesana
index 4b0179b..c8e6b7f 100755
--- a/scripts/lesana
+++ b/scripts/lesana
@@ -8,22 +8,22 @@ import argparse
import lesana.command
-class Lesana():
+class Lesana:
"""
Manage collections
"""
commands = (
- ('new', lesana.command.New()),
- ('edit', lesana.command.Edit()),
- ('show', lesana.command.Show()),
- ('index', lesana.command.Index()),
- ('search', lesana.command.Search()),
- ('update', lesana.command.Update()),
- ('export', lesana.command.Export()),
- ('init', lesana.command.Init()),
- ('rm', lesana.command.Remove()),
- )
+ ("new", lesana.command.New()),
+ ("edit", lesana.command.Edit()),
+ ("show", lesana.command.Show()),
+ ("index", lesana.command.Index()),
+ ("search", lesana.command.Search()),
+ ("update", lesana.command.Update()),
+ ("export", lesana.command.Export()),
+ ("init", lesana.command.Init()),
+ ("rm", lesana.command.Remove()),
+ )
def _main(self, args):
self.parser.print_help()
@@ -41,5 +41,5 @@ class Lesana():
self.args.func(self.args)
-if __name__ == '__main__':
+if __name__ == "__main__":
Lesana().main()
diff --git a/scripts/tellico2lesana b/scripts/tellico2lesana
index a24415e..28a1f15 100755
--- a/scripts/tellico2lesana
+++ b/scripts/tellico2lesana
@@ -12,34 +12,32 @@ NS = {'tellico': 'http://periapsis.org/tellico/'}
# https://docs.kde.org/trunk5/en/extragear-office/tellico/field-type-values.html
F_TYPE_MAP = {
- '0': 'string', # not in the specs, but seen in the wild
+ '0': 'string', # not in the specs, but seen in the wild
'1': 'string',
'2': 'text',
'3': 'string',
'4': 'bool',
'6': 'integer',
'7': 'url',
- '8': 'list', # single column table
+ '8': 'list', # single column table
'10': 'file',
'12': 'timestamp', # date
- '14': 'integer', # rating
+ '14': 'integer', # rating
+}
- }
-
-class T2L():
+class T2L:
"""
Manage collections
"""
+
arguments = [
- (['-c', '--collection'], dict(
- help='Name of the new lesana collection',
- default=None,
- )),
- (['file'], dict(
- help='Tellico file to convert to lesana.',
- )),
- ]
+ (
+ ['-c', '--collection'],
+ dict(help='Name of the new lesana collection', default=None,),
+ ),
+ (['file'], dict(help='Tellico file to convert to lesana.',)),
+ ]
def _load_args(self):
self.parser = argparse.ArgumentParser()
@@ -57,11 +55,7 @@ class T2L():
elif 'day' in child.tag:
day = child.text
try:
- data = datetime.date(
- int(year),
- int(month),
- int(day)
- )
+ data = datetime.date(int(year), int(month), int(day))
except ValueError:
data = None
elif xfield.iter().__next__():
@@ -88,7 +82,7 @@ class T2L():
if xf.attrib['type'] == '12':
self.date_fields.append(
'{' + NS['tellico'] + '}' + xf.attrib['name']
- )
+ )
f_type = F_TYPE_MAP.get(xf.attrib['type'])
# TODO: support fields with the multiple values flag
# (they should probably become lists)
@@ -107,33 +101,25 @@ class T2L():
'name': xf.attrib['name'] + plural,
'type': f_type,
'help': xf.attrib['title'],
- }
+ }
if l_type:
field['list'] = l_type
fields.append(field)
# Create a collection with the settings we have loaded
directory = self.args.collection or self.args.file.replace(
- '.tc',
- '.lesana'
- )
+ '.tc', '.lesana'
+ )
self.collection = lesana.collection.Collection.init(
directory=directory,
git_enabled=False,
- settings={
- 'name': title,
- 'fields': fields,
- }
- )
+ settings={'name': title, 'fields': fields,},
+ )
# import data
for xe in xml_collection.findall('tellico:entry', NS):
- data = {
- 'eid': xe.attrib['id']
- }
+ data = {'eid': xe.attrib['id']}
for xfield in xe.getchildren():
- field_name = xfield.tag.replace(
- '{'+NS['tellico']+'}',
- '')
+ field_name = xfield.tag.replace('{' + NS['tellico'] + '}', '')
data[field_name] = self.read_field_data(xfield)
new_entry = lesana.collection.Entry(self.collection, data=data)
self.collection.save_entries([new_entry])
diff --git a/setup.py b/setup.py
index 72ac7ea..ffc79b9 100644
--- a/setup.py
+++ b/setup.py
@@ -12,19 +12,16 @@ setup(
version='0.5.1',
packages=find_packages(),
scripts=['scripts/lesana'],
-
package_data={
'': ['*.yaml']
- },
+ },
test_suite='tests',
-
install_requires=[
# 'xapian >= 1.4',
'ruamel.yaml',
'jinja2',
- ],
+ ],
python_requires='>=3',
-
# Metadata
author="Elena ``of Valhalla'' Grandi",
author_email='valhalla@trueelena.org',
@@ -39,7 +36,7 @@ setup(
'Environment :: Console',
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
- 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
+ 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', # noqa: E501
'Operating System :: POSIX',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.5',
@@ -48,9 +45,10 @@ setup(
'Programming Language :: Python :: 3.8',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Utilities',
- ],
+ ],
project_urls={
'Source': 'https://git.trueelena.org/cgit.cgi/software/lesana/',
- 'Documentation': 'https://git.trueelena.org/cgit.cgi/software/lesana/tree/docs',
- }
+ 'Documentation':
+ 'https://git.trueelena.org/cgit.cgi/software/lesana/tree/docs',
+ },
)
diff --git a/tests/test_collection.py b/tests/test_collection.py
index 04ec410..832f421 100644
--- a/tests/test_collection.py
+++ b/tests/test_collection.py
@@ -26,13 +26,13 @@ class testEntries(unittest.TestCase):
with open(os.path.join(self.basepath, fname)) as fp:
data = ruamel.yaml.safe_load(fp)
entry = lesana.Entry(self.collection, data=data, fname=fname)
- self.assertEqual(entry.idterm, 'Q'+data['eid'])
+ self.assertEqual(entry.idterm, 'Q' + data['eid'])
fname = '11189ee47ddf4796b718a483b379f976.yaml'
eid = '11189ee47ddf4796b718a483b379f976'
with open(os.path.join(self.basepath, fname)) as fp:
data = ruamel.yaml.safe_load(fp)
entry = lesana.Entry(self.collection, data=data, fname=fname)
- self.assertEqual(entry.idterm, 'Q'+eid)
+ self.assertEqual(entry.idterm, 'Q' + eid)
self.assertEqual(entry.short_id, eid[:8])
def test_write_new(self):
@@ -51,16 +51,12 @@ class testEntries(unittest.TestCase):
def test_entry_representation(self):
eid = '11189ee47ddf4796b718a483b379f976'
entry = self.collection.entry_from_eid(eid)
- self.assertEqual(
- str(entry),
- eid
- )
+ self.assertEqual(str(entry), eid)
label = '{{ eid }}: {{ name }}'
self.collection.settings['entry_label'] = label
self.assertEqual(
- str(entry),
- '{eid}: {name}'.format(eid=eid, name='Another item')
- )
+ str(entry), '{eid}: {name}'.format(eid=eid, name='Another item')
+ )
def test_entry_creation_eid_but_no_filename(self):
fname = '11189ee47ddf4796b718a483b379f976.yaml'
@@ -149,9 +145,8 @@ class testSimpleCollection(unittest.TestCase):
def test_loaded(self):
self.assertIsNotNone(self.collection.settings)
self.assertEqual(
- self.collection.settings['name'],
- "Simple lesana collection"
- )
+ self.collection.settings['name'], "Simple lesana collection"
+ )
self.assertEqual(len(self.collection.settings['fields']), 7)
self.assertEqual(len(self.collection.indexed_fields), 3)
@@ -204,28 +199,24 @@ class testSimpleCollection(unittest.TestCase):
def test_entry_from_eid(self):
entry = self.collection.entry_from_eid(
'11189ee47ddf4796b718a483b379f976'
- )
+ )
self.assertEqual(entry.eid, '11189ee47ddf4796b718a483b379f976')
self.collection.safe = True
entry = self.collection.entry_from_eid(
'11189ee47ddf4796b718a483b379f976'
- )
+ )
self.assertEqual(entry.eid, '11189ee47ddf4796b718a483b379f976')
def test_entry_from_short_eid(self):
- entries = self.collection.entries_from_short_eid(
- '11189ee4'
- )
+ entries = self.collection.entries_from_short_eid('11189ee4')
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].eid, '11189ee47ddf4796b718a483b379f976')
entries = self.collection.entries_from_short_eid(
'11189ee47ddf4796b718a483b379f976'
- )
+ )
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0].eid, '11189ee47ddf4796b718a483b379f976')
- entries = self.collection.entries_from_short_eid(
- '12345678'
- )
+ entries = self.collection.entries_from_short_eid('12345678')
self.assertEqual(len(entries), 0)
def test_index_missing_file(self):
@@ -241,24 +232,26 @@ class testSimpleCollection(unittest.TestCase):
def test_render_collection(self):
template = self.collection.get_template(
'tests/data/simple/templates/collection_template.txt'
- )
+ )
res = template.render(entries=self.collection.get_all_documents())
self.assertIn('11189ee4: Another item', res)
def test_update(self):
self.collection.update_field('Item', field="position", value="new_pos")
- with open(os.path.join(
- self.collection.basedir,
- 'items',
- '11189ee47ddf4796b718a483b379f976.yaml'
- )) as fp:
+ with open(
+ os.path.join(
+ self.collection.basedir,
+ 'items',
+ '11189ee47ddf4796b718a483b379f976.yaml',
+ )
+ ) as fp:
self.assertIn("new_pos", fp.read())
pass
self.assertEqual(
self.collection.entry_from_eid(
"11189ee47ddf4796b718a483b379f976"
).data['position'],
- "new_pos"
+ "new_pos",
)
self.assertIsNone(
@@ -281,8 +274,8 @@ class testComplexCollection(unittest.TestCase):
self.assertIsNotNone(self.collection.settings)
self.assertEqual(
self.collection.settings['name'],
- "Fully featured lesana collection"
- )
+ "Fully featured lesana collection",
+ )
self.assertEqual(len(self.collection.settings['fields']), 9)
self.assertIsNotNone(self.collection.stemmer)
self.assertEqual(len(self.collection.indexed_fields), 6)
@@ -302,7 +295,7 @@ class testComplexCollection(unittest.TestCase):
def test_boolean_field(self):
entry = self.collection.entry_from_eid(
'73097121f1874a6ea2f927db7dc4f11e'
- )
+ )
self.assertIsInstance(entry.data['exists'], bool)
def test_empty_data(self):
@@ -348,8 +341,8 @@ class testCollectionWithErrors(unittest.TestCase):
self.assertIsNotNone(self.collection.settings)
self.assertEqual(
self.collection.settings['name'],
- "Lesana collection with certain errors"
- )
+ "Lesana collection with certain errors",
+ )
self.assertEqual(len(self.collection.settings['fields']), 7)
self.assertIsNotNone(self.collection.stemmer)
self.assertEqual(len(self.collection.indexed_fields), 3)
@@ -371,15 +364,23 @@ class testCollectionCreation(unittest.TestCase):
self.assertIsInstance(collection, lesana.Collection)
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.git')))
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.lesana')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, '.gitignore')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, '.gitignore'))
+ )
# and then run it twice on the same directory, nothing should break
collection = lesana.Collection.init(self.tmpdir)
self.assertIsInstance(collection, lesana.Collection)
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.git')))
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.lesana')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, '.gitignore')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, '.gitignore'))
+ )
created = lesana.Collection(self.tmpdir)
self.assertTrue(created.settings['git'])
@@ -388,36 +389,49 @@ class testCollectionCreation(unittest.TestCase):
pass
def test_init_edit_file(self):
- collection = lesana.Collection.init(self.tmpdir, edit_file=self._do_nothing)
+ collection = lesana.Collection.init(
+ self.tmpdir, edit_file=self._do_nothing
+ )
self.assertIsInstance(collection, lesana.Collection)
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.git')))
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.lesana')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, '.gitignore')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, '.gitignore'))
+ )
def test_init_no_git(self):
collection = lesana.Collection.init(self.tmpdir, git_enabled=False)
self.assertIsInstance(collection, lesana.Collection)
self.assertFalse(os.path.isdir(os.path.join(self.tmpdir, '.git')))
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.lesana')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
- self.assertFalse(os.path.isfile(os.path.join(self.tmpdir, '.gitignore')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
+ self.assertFalse(
+ os.path.isfile(os.path.join(self.tmpdir, '.gitignore'))
+ )
# and then run it twice on the same directory, nothing should break
collection = lesana.Collection.init(self.tmpdir, git_enabled=False)
self.assertIsInstance(collection, lesana.Collection)
self.assertFalse(os.path.isdir(os.path.join(self.tmpdir, '.git')))
self.assertTrue(os.path.isdir(os.path.join(self.tmpdir, '.lesana')))
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
- self.assertFalse(os.path.isfile(os.path.join(self.tmpdir, '.gitignore')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
+ self.assertFalse(
+ os.path.isfile(os.path.join(self.tmpdir, '.gitignore'))
+ )
created = lesana.Collection(self.tmpdir)
self.assertFalse(created.settings['git'])
def test_deletion(self):
shutil.copy('tests/data/simple/settings.yaml', self.tmpdir)
shutil.copytree(
- 'tests/data/simple/items',
- os.path.join(self.tmpdir, 'items'),
- )
+ 'tests/data/simple/items', os.path.join(self.tmpdir, 'items'),
+ )
collection = lesana.Collection.init(self.tmpdir)
# We start with one item indexed with the term "another"
collection.start_search('another')
@@ -426,11 +440,15 @@ class testCollectionCreation(unittest.TestCase):
# Then delete it
collection.remove_entries(['11189ee47ddf4796b718a483b379f976'])
# An now we should have none
- self.assertFalse(os.path.exists(os.path.join(
- self.tmpdir,
- 'items',
- '11189ee47ddf4796b718a483b379f976.yaml'
- )))
+ self.assertFalse(
+ os.path.exists(
+ os.path.join(
+ self.tmpdir,
+ 'items',
+ '11189ee47ddf4796b718a483b379f976.yaml',
+ )
+ )
+ )
collection.start_search('another')
mset = collection._enquire.get_mset(0, 10)
self.assertEqual(mset.get_matches_estimated(), 0)
@@ -438,9 +456,8 @@ class testCollectionCreation(unittest.TestCase):
def test_partial_eid_deletion(self):
shutil.copy('tests/data/simple/settings.yaml', self.tmpdir)
shutil.copytree(
- 'tests/data/simple/items',
- os.path.join(self.tmpdir, 'items'),
- )
+ 'tests/data/simple/items', os.path.join(self.tmpdir, 'items'),
+ )
collection = lesana.Collection.init(self.tmpdir)
# We start with one item indexed with the term "another"
collection.start_search('another')
@@ -449,11 +466,15 @@ class testCollectionCreation(unittest.TestCase):
# Then delete it, using the short id
collection.remove_entries(['11189ee4'])
# An now we should have none
- self.assertFalse(os.path.exists(os.path.join(
- self.tmpdir,
- 'items',
- '11189ee47ddf4796b718a483b379f976.yaml'
- )))
+ self.assertFalse(
+ os.path.exists(
+ os.path.join(
+ self.tmpdir,
+ 'items',
+ '11189ee47ddf4796b718a483b379f976.yaml',
+ )
+ )
+ )
collection.start_search('another')
mset = collection._enquire.get_mset(0, 10)
self.assertEqual(mset.get_matches_estimated(), 0)
@@ -469,9 +490,8 @@ class testCollectionCreation(unittest.TestCase):
def test_git_adding(self):
shutil.copy('tests/data/simple/settings.yaml', self.tmpdir)
shutil.copytree(
- 'tests/data/simple/items',
- os.path.join(self.tmpdir, 'items'),
- )
+ 'tests/data/simple/items', os.path.join(self.tmpdir, 'items'),
+ )
collection = lesana.Collection.init(self.tmpdir)
fname = '11189ee47ddf4796b718a483b379f976.yaml'
repo = git.Repo(self.tmpdir)
@@ -498,11 +518,13 @@ class testCollectionCreation(unittest.TestCase):
'fields': [
{'name': 'title', 'type': 'string'},
{'name': 'author', 'type': 'string'},
- ],
- },
- )
+ ],
+ },
+ )
self.assertIsInstance(collection, lesana.Collection)
- self.assertTrue(os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml')))
+ self.assertTrue(
+ os.path.isfile(os.path.join(self.tmpdir, 'settings.yaml'))
+ )
self.assertEqual(collection.settings['name'], 'A different name')
self.assertEqual(len(collection.settings['fields']), 2)