import pkg_resources
import functools
import os
import urllib2
pkg_resources.require('Beaker')
from beaker.cache import CacheManager
from beaker.util import parse_cache_config_options
import logging
log = logging.getLogger( __name__ )
[docs]class CitationsManager( object ):
def __init__( self, app ):
self.app = app
self.doi_cache = DoiCache( app.config )
[docs] def parse_citation( self, citation_elem, tool_directory ):
return parse_citation( citation_elem, tool_directory, self )
def _get_tool( self, tool_id ):
tool = self.app.toolbox.get_tool( tool_id )
return tool
[docs]class DoiCache( object ):
def __init__( self, config ):
cache_opts = {
'cache.type': getattr( config, 'citation_cache_type', 'file'),
'cache.data_dir': getattr( config, 'citation_cache_data_dir', None),
'cache.lock_dir': getattr( config, 'citation_cache_lock_dir', None),
}
self._cache = CacheManager(**parse_cache_config_options(cache_opts)).get_cache('doi')
def _raw_get_bibtex( self, doi ):
dx_url = "http://dx.doi.org/" + doi
headers = {'Accept': 'text/bibliography; style=bibtex, application/x-bibtex'}
req = urllib2.Request(dx_url, data="", headers=headers)
response = urllib2.urlopen(req)
bibtex = response.read()
return bibtex
[docs] def get_bibtex( self, doi ):
createfunc = functools.partial(self._raw_get_bibtex, doi)
return self._cache.get(key=doi, createfunc=createfunc)
[docs]def parse_citation( elem, directory, citation_manager ):
""" Parse an abstract citation entry from the specified XML element.
The directory parameter should be used to find external files for this
citation.
"""
citation_type = elem.attrib.get( 'type', None )
citation_class = CITATION_CLASSES.get( citation_type, None )
if not citation_class:
log.warn("Unknown or unspecified citation type: %s" % citation_type)
return None
return citation_class( elem, directory, citation_manager )
[docs]class CitationCollection( object ):
def __init__( self ):
self.citations = []
def __iter__( self ):
return self.citations.__iter__()
def __len__( self ):
return len( self.citations )
[docs] def add( self, new_citation ):
for citation in self.citations:
if citation.equals( new_citation ):
# TODO: We have two equivalent citations, pick the more
# informative/complete/correct.
return False
self.citations.append( new_citation )
return True
[docs]class BaseCitation( object ):
[docs] def to_dict( self, citation_format ):
if citation_format == "bibtex":
return dict(
format="bibtex",
content=self.to_bibtex(),
)
else:
raise Exception("Unknown citation format %s" % citation_format)
[docs] def equals( self, other_citation ):
if self.has_doi() and other_citation.has_doi():
return self.doi() == other_citation.doi()
else:
# TODO: Do a better job figuring out if this is the same citation.
return self.to_bibtex() == other_citation.to_bibtex()
[docs] def has_doi( self ):
return False
[docs]class BibtexCitation( BaseCitation ):
def __init__( self, elem, directory, citation_manager ):
bibtex_file = elem.attrib.get("file", None)
if bibtex_file:
raw_bibtex = open(os.path.join(directory, bibtex_file), "r").read()
else:
raw_bibtex = elem.text.strip()
self._set_raw_bibtex( raw_bibtex )
def _set_raw_bibtex( self, raw_bibtex ):
self.raw_bibtex = raw_bibtex
[docs] def to_bibtex( self ):
return self.raw_bibtex
[docs]class DoiCitation( BaseCitation ):
BIBTEX_UNSET = object()
def __init__( self, elem, directory, citation_manager ):
self.__doi = elem.text.strip()
self.doi_cache = citation_manager.doi_cache
self.raw_bibtex = DoiCitation.BIBTEX_UNSET
[docs] def has_doi( self ):
return True
[docs] def doi( self ):
return self.__doi
[docs] def to_bibtex( self ):
if self.raw_bibtex is DoiCitation.BIBTEX_UNSET:
try:
self.raw_bibtex = self.doi_cache.get_bibtex(self.__doi)
except Exception:
log.exception("Failed to fetch bibtex for DOI %s" % self.__doi)
if self.raw_bibtex is DoiCitation.BIBTEX_UNSET:
return """@MISC{%s,
DOI = {%s},
note = {Failed to fetch BibTeX for DOI.}
}""" % (self.__doi, self.__doi)
else:
return self.raw_bibtex
CITATION_CLASSES = dict(
bibtex=BibtexCitation,
doi=DoiCitation,
)