diff options
| -rwxr-xr-x | check | 2 | ||||
| -rw-r--r-- | lesana/collection.py | 226 | ||||
| -rw-r--r-- | lesana/command.py | 267 | ||||
| -rwxr-xr-x | scripts/lesana | 24 | ||||
| -rwxr-xr-x | scripts/tellico2lesana | 56 | ||||
| -rw-r--r-- | setup.py | 16 | ||||
| -rw-r--r-- | tests/test_collection.py | 156 | 
7 files changed, 372 insertions, 375 deletions
| @@ -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]) @@ -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) | 
