Merge pull request #106 from pubs/feat/use-bwriter
Uses bibtexparser bwriter instead of internal encoder and adds `--ignore-fields` option to export.
This commit is contained in:
commit
79003c2fb8
@ -1,14 +1,32 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
|
||||||
from .. import repo
|
from .. import repo
|
||||||
from ..uis import get_ui
|
from ..uis import get_ui
|
||||||
from .. import endecoder
|
from .. import endecoder
|
||||||
from ..utils import resolve_citekey_list
|
from ..utils import resolve_citekey_list
|
||||||
from ..completion import CiteKeyCompletion
|
from ..endecoder import BIBFIELD_ORDER
|
||||||
|
from ..completion import CiteKeyCompletion, CommaSeparatedListCompletion
|
||||||
|
|
||||||
|
|
||||||
|
class CommaSeparatedList(argparse.Action):
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
setattr(namespace, self.dest, [s for s in values.split(',') if s])
|
||||||
|
|
||||||
|
|
||||||
|
class FieldCommaSeparatedListCompletion(CommaSeparatedListCompletion):
|
||||||
|
|
||||||
|
values = BIBFIELD_ORDER
|
||||||
|
|
||||||
|
|
||||||
def parser(subparsers, conf):
|
def parser(subparsers, conf):
|
||||||
parser = subparsers.add_parser('export', help='export bibliography')
|
parser = subparsers.add_parser('export', help='export bibliography')
|
||||||
|
parser.add_argument(
|
||||||
|
'--ignore-fields', default=[], action=CommaSeparatedList,
|
||||||
|
help='exclude field(s) from output (comma separated if multiple)'
|
||||||
|
).completer = FieldCommaSeparatedListCompletion(conf)
|
||||||
# parser.add_argument('-f', '--bib-format', default='bibtex',
|
# parser.add_argument('-f', '--bib-format', default='bibtex',
|
||||||
# help='export format')
|
# help='export format')
|
||||||
parser.add_argument('citekeys', nargs='*', help='one or several citekeys'
|
parser.add_argument('citekeys', nargs='*', help='one or several citekeys'
|
||||||
@ -36,7 +54,7 @@ def command(conf, args):
|
|||||||
bib[p.citekey] = p.bibdata
|
bib[p.citekey] = p.bibdata
|
||||||
|
|
||||||
exporter = endecoder.EnDecoder()
|
exporter = endecoder.EnDecoder()
|
||||||
bibdata_raw = exporter.encode_bibdata(bib)
|
bibdata_raw = exporter.encode_bibdata(bib, args.ignore_fields)
|
||||||
ui.message(bibdata_raw)
|
ui.message(bibdata_raw)
|
||||||
|
|
||||||
rp.close()
|
rp.close()
|
||||||
|
@ -57,3 +57,15 @@ class TagModifierCompletion(BaseCompleter):
|
|||||||
partial_expr = prefix[:start]
|
partial_expr = prefix[:start]
|
||||||
t_prefix = prefix[start:]
|
t_prefix = prefix[start:]
|
||||||
return [partial_expr + t for t in tags if t.startswith(t_prefix)]
|
return [partial_expr + t for t in tags if t.startswith(t_prefix)]
|
||||||
|
|
||||||
|
|
||||||
|
class CommaSeparatedListCompletion(BaseCompleter):
|
||||||
|
|
||||||
|
values = []
|
||||||
|
|
||||||
|
def _complete(self, prefix, **kwargs):
|
||||||
|
split = prefix.split(',')
|
||||||
|
item_prefix = split[-1]
|
||||||
|
partial = split[:-1]
|
||||||
|
return [','.join(partial + [x]) for x in self.values
|
||||||
|
if x.startswith(item_prefix)]
|
||||||
|
@ -27,6 +27,11 @@ else:
|
|||||||
BP_ENTRYTYPE_KEY = 'type'
|
BP_ENTRYTYPE_KEY = 'type'
|
||||||
|
|
||||||
|
|
||||||
|
BIBFIELD_ORDER = ['author', 'title', 'journal', 'institution', 'publisher',
|
||||||
|
'year', 'month', 'number', 'volume', 'pages', 'link', 'doi',
|
||||||
|
'note', 'abstract']
|
||||||
|
|
||||||
|
|
||||||
def sanitize_citekey(record):
|
def sanitize_citekey(record):
|
||||||
record[BP_ID_KEY] = record[BP_ID_KEY].strip('\n')
|
record[BP_ID_KEY] = record[BP_ID_KEY].strip('\n')
|
||||||
return record
|
return record
|
||||||
@ -53,10 +58,6 @@ def customizations(record):
|
|||||||
|
|
||||||
return record
|
return record
|
||||||
|
|
||||||
bibfield_order = ['author', 'title', 'journal', 'institution', 'publisher',
|
|
||||||
'year', 'month', 'number', 'volume', 'pages', 'link', 'doi', 'note',
|
|
||||||
'abstract']
|
|
||||||
|
|
||||||
|
|
||||||
class EnDecoder(object):
|
class EnDecoder(object):
|
||||||
""" Encode and decode content.
|
""" Encode and decode content.
|
||||||
@ -69,6 +70,9 @@ class EnDecoder(object):
|
|||||||
* encode_bibdata will try to recognize exceptions
|
* encode_bibdata will try to recognize exceptions
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
bwriter = bp.bwriter.BibTexWriter()
|
||||||
|
bwriter.display_order = BIBFIELD_ORDER
|
||||||
|
|
||||||
def encode_metadata(self, metadata):
|
def encode_metadata(self, metadata):
|
||||||
return yaml.safe_dump(metadata, allow_unicode=True,
|
return yaml.safe_dump(metadata, allow_unicode=True,
|
||||||
encoding=None, indent=4)
|
encoding=None, indent=4)
|
||||||
@ -76,41 +80,35 @@ class EnDecoder(object):
|
|||||||
def decode_metadata(self, metadata_raw):
|
def decode_metadata(self, metadata_raw):
|
||||||
return yaml.safe_load(metadata_raw)
|
return yaml.safe_load(metadata_raw)
|
||||||
|
|
||||||
def encode_bibdata(self, bibdata):
|
def encode_bibdata(self, bibdata, ignore_fields=[]):
|
||||||
"""Encode bibdata """
|
"""Encode bibdata """
|
||||||
return '\n'.join(self._encode_bibentry(citekey, entry)
|
bpdata = bp.bibdatabase.BibDatabase()
|
||||||
for citekey, entry in bibdata.items())
|
bpdata.entries = [self._entry_to_bp_entry(k, copy.copy(bibdata[k]),
|
||||||
|
ignore_fields=ignore_fields)
|
||||||
|
for k in bibdata]
|
||||||
|
return self.bwriter.write(bpdata)
|
||||||
|
|
||||||
@staticmethod
|
def _entry_to_bp_entry(self, key, entry, ignore_fields=[]):
|
||||||
def _encode_field(key, value):
|
"""Convert back entries to the format expected by bibtexparser."""
|
||||||
if key == 'link':
|
entry[BP_ID_KEY] = key
|
||||||
return ', '.join(link['url'] for link in value)
|
# Convert internal 'type' to bibtexparser entrytype key
|
||||||
elif key == 'author':
|
entry[BP_ENTRYTYPE_KEY] = entry.pop(TYPE_KEY)
|
||||||
return ' and '.join(author for author in value)
|
for f in ignore_fields:
|
||||||
elif key == 'editor':
|
entry.pop(f, None)
|
||||||
return ' and '.join(editor['name'] for editor in value)
|
if 'link' in entry:
|
||||||
elif key == 'journal':
|
entry['link'] = ', '.join(link['url'] for link in entry['link'])
|
||||||
return value['name']
|
if 'author' in entry:
|
||||||
elif key == 'keyword':
|
entry['author'] = ' and '.join(
|
||||||
return ', '.join(keyword for keyword in value)
|
author for author in entry['author'])
|
||||||
else:
|
if 'editor' in entry:
|
||||||
return value
|
entry['editor'] = ' and '.join(
|
||||||
|
editor['name'] for editor in entry['editor'])
|
||||||
@staticmethod
|
if 'journal' in entry:
|
||||||
def _encode_bibentry(citekey, bibentry):
|
entry['journal'] = entry['journal']['name']
|
||||||
bibraw = '@{}{{{},\n'.format(bibentry[TYPE_KEY], citekey)
|
if 'keyword' in entry:
|
||||||
bibentry = copy.copy(bibentry)
|
entry['keyword'] = ', '.join(
|
||||||
for key in bibfield_order:
|
keyword for keyword in entry['keyword'])
|
||||||
if key in bibentry:
|
return entry
|
||||||
value = bibentry.pop(key)
|
|
||||||
bibraw += ' {} = {{{}}},\n'.format(
|
|
||||||
key, EnDecoder._encode_field(key, value))
|
|
||||||
for key, value in bibentry.items():
|
|
||||||
if key != TYPE_KEY:
|
|
||||||
bibraw += ' {} = {{{}}},\n'.format(
|
|
||||||
key, EnDecoder._encode_field(key, value))
|
|
||||||
bibraw += '}\n'
|
|
||||||
return bibraw
|
|
||||||
|
|
||||||
def decode_bibdata(self, bibdata):
|
def decode_bibdata(self, bibdata):
|
||||||
""""""
|
""""""
|
||||||
|
@ -91,13 +91,41 @@ class TestEnDecode(unittest.TestCase):
|
|||||||
self.assertIn(u'keyword', entry)
|
self.assertIn(u'keyword', entry)
|
||||||
self.assertEqual(set(keywords), set(entry[u'keyword']))
|
self.assertEqual(set(keywords), set(entry[u'keyword']))
|
||||||
|
|
||||||
|
|
||||||
def test_endecode_metadata(self):
|
def test_endecode_metadata(self):
|
||||||
decoder = endecoder.EnDecoder()
|
decoder = endecoder.EnDecoder()
|
||||||
entry = decoder.decode_metadata(metadata_raw0)
|
entry = decoder.decode_metadata(metadata_raw0)
|
||||||
metadata_output0 = decoder.encode_metadata(entry)
|
metadata_output0 = decoder.encode_metadata(entry)
|
||||||
self.assertEqual(set(metadata_raw0.split('\n')), set(metadata_output0.split('\n')))
|
self.assertEqual(set(metadata_raw0.split('\n')), set(metadata_output0.split('\n')))
|
||||||
|
|
||||||
|
def test_endecode_bibtex_field_order(self):
|
||||||
|
decoder = endecoder.EnDecoder()
|
||||||
|
entry = decoder.decode_bibdata(bibtex_raw0)
|
||||||
|
lines = decoder.encode_bibdata(entry).splitlines()
|
||||||
|
self.assertEqual(lines[1].split('=')[0].strip(), u'author')
|
||||||
|
self.assertEqual(lines[2].split('=')[0].strip(), u'title')
|
||||||
|
self.assertEqual(lines[3].split('=')[0].strip(), u'institution')
|
||||||
|
self.assertEqual(lines[4].split('=')[0].strip(), u'publisher')
|
||||||
|
self.assertEqual(lines[5].split('=')[0].strip(), u'year')
|
||||||
|
self.assertEqual(lines[6].split('=')[0].strip(), u'month')
|
||||||
|
self.assertEqual(lines[7].split('=')[0].strip(), u'number')
|
||||||
|
self.assertEqual(lines[8].split('=')[0].strip(), u'link')
|
||||||
|
self.assertEqual(lines[9].split('=')[0].strip(), u'note')
|
||||||
|
self.assertEqual(lines[10].split('=')[0].strip(), u'abstract')
|
||||||
|
|
||||||
|
def test_endecode_bibtex_ignores_fields(self):
|
||||||
|
decoder = endecoder.EnDecoder()
|
||||||
|
entry = decoder.decode_bibdata(bibtex_raw0)
|
||||||
|
|
||||||
|
bibraw1 = decoder.encode_bibdata(
|
||||||
|
entry, ignore_fields=['title', 'note', 'abstract', 'journal'])
|
||||||
|
entry1 = list(decoder.decode_bibdata(bibraw1).values())[0]
|
||||||
|
|
||||||
|
self.assertNotIn('title', entry1)
|
||||||
|
self.assertNotIn('note', entry1)
|
||||||
|
self.assertNotIn('abtract', entry1)
|
||||||
|
self.assertIn('author', entry1)
|
||||||
|
self.assertIn('institution', entry1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -669,6 +669,16 @@ class TestUsecase(DataCommandTestCase):
|
|||||||
self.assertEqual(endecoder.EnDecoder().decode_bibdata(outs[2]),
|
self.assertEqual(endecoder.EnDecoder().decode_bibdata(outs[2]),
|
||||||
fixtures.page_bibentry)
|
fixtures.page_bibentry)
|
||||||
|
|
||||||
|
def test_export_ignore_field(self):
|
||||||
|
cmds = ['pubs init',
|
||||||
|
('pubs add', [str_fixtures.bibtex_external0]),
|
||||||
|
'pubs export --ignore-fields author,title Page99',
|
||||||
|
]
|
||||||
|
outs = self.execute_cmds(cmds)
|
||||||
|
expected = endecoder.EnDecoder().encode_bibdata(
|
||||||
|
fixtures.page_bibentry, ignore_fields=['author', 'title'])
|
||||||
|
self.assertEqual(outs[2], expected + os.linesep)
|
||||||
|
|
||||||
def test_import(self):
|
def test_import(self):
|
||||||
cmds = ['pubs init',
|
cmds = ['pubs init',
|
||||||
'pubs import data/',
|
'pubs import data/',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user