You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.8 KiB

import os
import unicodedata
import re
from pybtex.database import Entry, BibliographyData
import files
import color
import pretty
DEFAULT_TYPE = 'article'
CONTROL_CHARS = ''.join(map(unichr, range(0, 32) + range(127, 160)))
CITEKEY_FORBIDDEN_CHARS = '@\'\\,#}{~%/' # '/' is OK for bibtex but forbidden
# here since we transform citekeys into filenames
CITEKEY_EXCLUDE_RE = re.compile('[%s]'
% re.escape(CONTROL_CHARS + CITEKEY_FORBIDDEN_CHARS))
def str2citekey(s):
key = unicodedata.normalize('NFKD', unicode(s)).encode('ascii', 'ignore')
key = CITEKEY_EXCLUDE_RE.sub('', key)
# Normalize chars and remove non-ascii
return key
class NoDocumentFile(Exception):
pass
class Paper(object):
"""Paper class. The object is responsible for the integrity of its own
data, and for loading and writing it to disc.
The object uses a pybtex.database.BibliographyData object to store
biblography data and an additional dictionary to store meta data.
"""
@classmethod
def load(cls, bibpath, metapath):
bib_data = files.load_externalbibfile(bibpath)
metadata = files.read_yamlfile(metapath)
# Extract first entry (supposed to be the only one)
first_key = bib_data.entries.keys()[0]
first_entry = bib_data.entries[first_key]
p = Paper(bibentry=first_entry, metadata=metadata, citekey=first_key)
return p
# @classmethod
# def from_bibpdffiles(cls, pdfpath, bibpath):
# bib_data = cls.import_bibdata(bibpath)
# name, meta = cls.create_meta(bib_data, pdfpath=pdfpath)
# p = Paper(name, bib_data = bib_data, metadata = meta)
#
# return p
def __init__(self, bibentry=None, metadata=None, citekey=None):
if not bibentry:
bibentry = Entry(DEFAULT_TYPE)
self.bibentry = bibentry
if not metadata:
metadata = Paper.create_meta()
self.metadata = metadata
self.citekey = citekey
def __eq__(self, other):
return (type(other) is Paper
and self.bibentry == other.bibentry
and self.metadata == other.metadata
and self.citekey == other.citekey)
def __repr__(self):
return 'Paper(%s, %s, %s)' % (
self.citekey, self.bibentry, self.metadata)
def __str__(self):
return self.__repr__()
# TODO add mechanism to verify keys (15/12/2012)
def has_file(self):
"""Whether there exist a document file for this entry.
"""
return self.metadata['path'] is not None
def get_file_path(self):
if self.has_file():
return self.metadata['path']
else:
raise NoDocumentFile
def check_file(self):
return files.check_file(self.get_file_path())
def generate_citekey(self):
"""Generate a citekey from bib_data.
Raises:
KeyError if no author nor editor is defined.
"""
author_key = 'author'
if not 'author' in self.bibentry.persons:
author_key = 'editor'
try:
first_author = self.bibentry.persons[author_key][0]
except KeyError:
raise(ValueError,
'No author or editor defined: cannot generate a citekey.')
try:
year = self.bibentry.fields['year']
except KeyError:
year = ''
citekey = u'{}{}'.format(u''.join(first_author.last()), year)
return str2citekey(citekey)
def save_to_disc(self, path):
"""Creates a BibliographyData object containing a single entry and
saves it to disc.
"""
if self.citekey is None:
raise(ValueError,
'No valid citekey initialized. Cannot save paper')
bibdata = BibliographyData(entries={self.citekey: self.bibentry})
files.save_bibdata(bibdata, self.citekey, path=path)
files.save_meta(self.metadata, self.citekey, path=path)
# TODO move to repo
@classmethod
def import_bibdata(cls, bibfile):
"""Import bibligraphic data from a .bibyaml, .bib or .bibtex file"""
fullbibpath = os.path.abspath(bibfile)
bib_data = files.load_externalbibfile(fullbibpath)
print('{}bibliographic data present in {}{}{}'.format(
color.grey, color.cyan, bibfile, color.end))
print(pretty.bib_desc(bib_data))
return bib_data
@classmethod
def create_meta(cls, pdfpath=None):
if pdfpath is None:
name, fullpdfpath, ext = None, None, None
else:
fullpdfpath = os.path.abspath(pdfpath)
files.check_file(fullpdfpath)
name, ext = files.name_from_path(pdfpath)
meta = {}
meta['filename'] = name # TODO remove ?
meta['extension'] = ext
meta['path'] = fullpdfpath
meta['notes'] = []
return meta