- Cleans code. - Clever mode for case by default (case sensitive only if capitals in query). - Adds basic tests. - Conjunction of blocks. - Options to override smart case.main
parent
acc56961e9
commit
e72f8fa7f2
@ -1,59 +1,107 @@
|
||||
from .. import pretty
|
||||
from .. import repo
|
||||
from .. import color
|
||||
from . import helpers
|
||||
from ..configs import config
|
||||
from ..uis import get_ui
|
||||
|
||||
|
||||
class InvalidQuery(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def parser(subparsers):
|
||||
parser = subparsers.add_parser('list', help="list papers")
|
||||
parser.add_argument('-k', '--citekeys-only', action='store_true',
|
||||
default=False, dest='citekeys',
|
||||
help='Only returns citekeys of matching papers.')
|
||||
parser.add_argument('-i', '--ignore-case', action='store_false',
|
||||
default=None, dest='case_sensitive')
|
||||
parser.add_argument('-I', '--force-case', action='store_true',
|
||||
dest='case_sensitive')
|
||||
parser.add_argument('query', nargs='*',
|
||||
help='Paper query (e.g. "year: 2000" or "tags: math")')
|
||||
return parser
|
||||
|
||||
|
||||
def command(args):
|
||||
|
||||
ui = get_ui()
|
||||
citekeys = args.citekeys
|
||||
query = args.query
|
||||
|
||||
rp = repo.Repository(config())
|
||||
papers = [(n, p) for n, p in enumerate(rp.all_papers())
|
||||
if test_paper(query, p)]
|
||||
ui.print_('\n'.join(helpers.paper_oneliner(p, n = n, citekey_only = citekeys) for n, p in papers))
|
||||
papers = filter(lambda p: filter_paper(p, args.query,
|
||||
case_sensitive=args.case_sensitive),
|
||||
rp.all_papers())
|
||||
ui.print_('\n'.join(
|
||||
helpers.paper_oneliner(p, n=n, citekey_only=args.citekeys)
|
||||
for n, p in enumerate(papers)))
|
||||
|
||||
|
||||
FIELD_ALIASES = {
|
||||
'a': 'author',
|
||||
'authors': 'author',
|
||||
't': 'title',
|
||||
'tags': 'tag',
|
||||
}
|
||||
|
||||
|
||||
def _get_field_value(query_block):
|
||||
split_block = query_block.split(':')
|
||||
if len(split_block) != 2:
|
||||
raise InvalidQuery("Invalid query (%s)" % query_block)
|
||||
field = split_block[0]
|
||||
if field in FIELD_ALIASES:
|
||||
field = FIELD_ALIASES[field]
|
||||
value = split_block[1]
|
||||
return (field, value)
|
||||
|
||||
|
||||
def _lower(string, lower=True):
|
||||
if lower:
|
||||
return string.lower()
|
||||
else:
|
||||
return string
|
||||
|
||||
|
||||
def _check_author_match(paper, query, case_sensitive=False):
|
||||
"""Only checks within last names."""
|
||||
if not 'author' in paper.bibentry.persons:
|
||||
return False
|
||||
return any([query in _lower(name, lower=(not case_sensitive))
|
||||
for p in paper.bibentry.persons['author']
|
||||
for name in p.last()])
|
||||
|
||||
|
||||
def _check_tag_match(paper, query, case_sensitive=False):
|
||||
return any([query in _lower(t, lower=(not case_sensitive))
|
||||
for t in paper.tags])
|
||||
|
||||
|
||||
def _check_field_match(paper, field, query, case_sensitive=False):
|
||||
return query in _lower(paper.bibentry.fields[field],
|
||||
lower=(not case_sensitive))
|
||||
|
||||
|
||||
def _check_query_block(paper, query_block, case_sensitive=None):
|
||||
field, value = _get_field_value(query_block)
|
||||
if case_sensitive is None:
|
||||
case_sensitive = not value.islower()
|
||||
elif not case_sensitive:
|
||||
value = value.lower()
|
||||
if field == 'tag':
|
||||
return _check_tag_match(paper, value, case_sensitive=case_sensitive)
|
||||
elif field == 'author':
|
||||
return _check_author_match(paper, value, case_sensitive=case_sensitive)
|
||||
elif field in paper.bibentry.fields:
|
||||
return _check_field_match(paper, field, value,
|
||||
case_sensitive=case_sensitive)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# TODO author is not implemented, should we do it by last name only or more
|
||||
# complex
|
||||
# TODO implement search by type of document
|
||||
def test_paper(query_string, p):
|
||||
for test in query_string:
|
||||
tmp = test.split(':')
|
||||
if len(tmp) != 2:
|
||||
raise ValueError('command not valid')
|
||||
|
||||
field = tmp[0]
|
||||
value = tmp[1]
|
||||
|
||||
if field in ['tags', 't']:
|
||||
if value not in p.tags:
|
||||
return False
|
||||
elif field in ['author', 'authors', 'a']: # that is the very ugly
|
||||
if not 'author' in p.bibentry.persons:
|
||||
return False
|
||||
a = False
|
||||
for p in p.bibentry.persons['author']:
|
||||
if value in p.last()[0]:
|
||||
a = True
|
||||
return a
|
||||
elif field in p.bibentry.fields:
|
||||
if value not in p.bibentry.fields[field]:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
def filter_paper(paper, query, case_sensitive=None):
|
||||
"""If case_sensitive is not given, only check case if query
|
||||
is not lowercase.
|
||||
|
||||
:args query: list of query blocks (strings)
|
||||
"""
|
||||
return all([_check_query_block(paper, query_block,
|
||||
case_sensitive=case_sensitive)
|
||||
for query_block in query])
|
||||
|
@ -0,0 +1,91 @@
|
||||
from unittest import TestCase
|
||||
|
||||
import testenv
|
||||
import fixtures
|
||||
from papers.commands.list_cmd import (_check_author_match,
|
||||
_check_field_match,
|
||||
_check_query_block,
|
||||
filter_paper,
|
||||
InvalidQuery)
|
||||
|
||||
|
||||
class TestAuthorFilter(TestCase):
|
||||
|
||||
def test_fails_if_no_author(self):
|
||||
no_doe = fixtures.doe2013.copy()
|
||||
no_doe.bibentry.persons = {}
|
||||
self.assertTrue(not _check_author_match(no_doe, 'whatever'))
|
||||
|
||||
def test_match_case(self):
|
||||
self.assertTrue(_check_author_match(fixtures.doe2013, 'doe'))
|
||||
self.assertTrue(_check_author_match(fixtures.doe2013, 'doe',
|
||||
case_sensitive=False))
|
||||
|
||||
def test_do_not_match_case(self):
|
||||
self.assertFalse(_check_author_match(fixtures.doe2013, 'dOe'))
|
||||
self.assertFalse(_check_author_match(fixtures.doe2013, 'doe',
|
||||
case_sensitive=True))
|
||||
|
||||
def test_match_not_first_author(self):
|
||||
self.assertTrue(_check_author_match(fixtures.page99, 'wani'))
|
||||
|
||||
def test_do_not_match_first_name(self):
|
||||
self.assertTrue(not _check_author_match(fixtures.page99, 'larry'))
|
||||
|
||||
|
||||
class TestCheckTag(TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestCheckField(TestCase):
|
||||
|
||||
def test_match_case(self):
|
||||
self.assertTrue(_check_field_match(fixtures.doe2013, 'title', 'nice'))
|
||||
self.assertTrue(_check_field_match(fixtures.doe2013, 'title', 'nice',
|
||||
case_sensitive=False))
|
||||
self.assertTrue(_check_field_match(fixtures.doe2013, 'year', '2013'))
|
||||
|
||||
def test_do_not_match_case(self):
|
||||
self.assertFalse(_check_field_match(fixtures.doe2013, 'title',
|
||||
'Title', case_sensitive=True))
|
||||
self.assertFalse(_check_field_match(fixtures.doe2013, 'title', 'nice',
|
||||
case_sensitive=True))
|
||||
|
||||
|
||||
class TestCheckQueryBlock(TestCase):
|
||||
|
||||
def test_raise_invalid_if_no_value(self):
|
||||
with self.assertRaises(InvalidQuery):
|
||||
_check_query_block(fixtures.doe2013, 'title')
|
||||
|
||||
def test_raise_invalid_if_too_much(self):
|
||||
with self.assertRaises(InvalidQuery):
|
||||
_check_query_block(fixtures.doe2013, 'whatever:value:too_much')
|
||||
|
||||
|
||||
class TestFilterPaper(TestCase):
|
||||
|
||||
def test_case(self):
|
||||
self.assertTrue(filter_paper(fixtures.doe2013, ['title:nice']))
|
||||
self.assertTrue(filter_paper(fixtures.doe2013, ['title:Nice']))
|
||||
self.assertFalse(filter_paper(fixtures.doe2013, ['title:nIce']))
|
||||
|
||||
def test_fields(self):
|
||||
self.assertTrue(filter_paper(fixtures.doe2013, ['year:2013']))
|
||||
self.assertFalse(filter_paper(fixtures.doe2013, ['year:2014']))
|
||||
self.assertTrue(filter_paper(fixtures.doe2013, ['author:doe']))
|
||||
self.assertTrue(filter_paper(fixtures.doe2013, ['author:Doe']))
|
||||
|
||||
def test_tags(self):
|
||||
self.assertTrue(filter_paper(fixtures.turing1950, ['tag:computer']))
|
||||
self.assertFalse(filter_paper(fixtures.turing1950, ['tag:Ai']))
|
||||
self.assertTrue(filter_paper(fixtures.turing1950, ['tag:AI']))
|
||||
self.assertTrue(filter_paper(fixtures.turing1950, ['tag:ai']))
|
||||
|
||||
def test_multiple(self):
|
||||
self.assertTrue(filter_paper(fixtures.doe2013,
|
||||
['author:doe', 'year:2013']))
|
||||
self.assertFalse(filter_paper(fixtures.doe2013,
|
||||
['author:doe', 'year:2014']))
|
||||
self.assertFalse(filter_paper(fixtures.doe2013,
|
||||
['author:doee', 'year:2014']))
|
Loading…
Reference in new issue