PageRenderTime 26ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/pelican_bibtex.py

https://bitbucket.org/joshua.adelman/pelican_bibtex
Python | 166 lines | 116 code | 5 blank | 45 comment | 3 complexity | bedb8ee58b2f38357b5983683670eba9 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Pelican BibTeX plugin
  4. =====================
  5. A Pelican plugin that makes available the contents of a BibTeX file to the template
  6. of a Page context.
  7. Author: Joshua L. Adelman
  8. '''
  9. from pelican import signals
  10. from collections import namedtuple
  11. import itertools
  12. try:
  13. from pybtex.database.input import bibtex
  14. from pybtex.database import PybtexError
  15. from pybtex.backends import html
  16. from pybtex.style.formatting import plain
  17. except ImportError:
  18. raise Exception("Unable to load pybtex module")
  19. class BibtexParser():
  20. '''Parses a BibTeX file and populates a list of namedtuples with fields and data.
  21. Each namedtuple corresponds to a publication in BibTeX file. The fields of the
  22. namedtuple can be set in the pelican config file, but by default all available tags
  23. in the BibTeX file are made available. Additionally the namedtuple will contain a
  24. formatted entry `pub.html` with the complete citation.
  25. Paramaters set in the Pelican config file:
  26. Parameters
  27. ----------
  28. BIBTEX_SOURCE : str, Required
  29. Path to .bib file to be parsed
  30. BIBTEX_TAGS : list
  31. A list of the tags that will be used to create the namedtuple. If not specified
  32. all tags present in the BIBTEX_SOURCE are used.
  33. BIBTEX_FOOTNOTES : dict of tuples
  34. A dict containing (symbol, footnote) tuples. If the symbol is found in list of
  35. authors, the key field of the publication object is populated with the tuple.
  36. This is convinient for specifying equal authorship, corresponding authorship, etc.
  37. BIBTEX_STYLE : module
  38. A module containing a class called `Style` that contains a pybtex template. This is
  39. used to generate the formatted html entry. By default the plugin uses the `plain` style
  40. (see pybtex/style/formatting/plain.py).
  41. Examples
  42. --------
  43. In pelicanconf.py:
  44. ...
  45. import pybtex_custom_style
  46. BIBTEX_SOURCE = 'publications.bib'
  47. BIBTEX_TAGS = ['Author', 'DOI', 'Abstract']
  48. BIBTEX_FOOTNOTES = {'shared': (u'‡', 'These authors contributed equally to this work.')}
  49. BIBTEX_STYLE = pybtex_custom_style
  50. ...
  51. Would return a list of namedtuples, with each entry `pub` containing the populated fields,
  52. ['author', 'doi', 'abstract', html', 'authors'], where `author` is the raw data from the .bib
  53. file and `authors` is a single formatted string generated from `author`.
  54. '''
  55. def __init__(self, generator):
  56. print('Initializing BibtexParser')
  57. self.parser = bibtex.Parser()
  58. self.bibtex_file = generator.settings['BIBTEX_SOURCE']
  59. self.retrieve_tags = generator.settings.get('BIBTEX_TAGS')
  60. self.footnote_map = generator.settings.get('BIBTEX_FOOTNOTES', {})
  61. self.style_format = generator.settings.get('BIBTEX_STYLE', plain)
  62. def get_all_tags(self, data):
  63. tags = []
  64. for key in data.entries:
  65. tags.extend(list(data.entries[key].fields))
  66. tags = set(tag.replace('-', '_') for tag in tags)
  67. return list(tags)
  68. def fetch(self):
  69. """
  70. returns a list of namedtuples containing publication data
  71. """
  72. try:
  73. data = self.parser.parse_file(self.bibtex_file)
  74. except PybtexError:
  75. raise('`pelican_bibtex` failed to open file {}'.format(self.bibtex_file))
  76. bibdata = []
  77. # Coerce data into a more reasonable data structure
  78. # Build named tuple from requested `retrieve_tags`, or by default
  79. # get all tags in bibtext file
  80. fields = [t.lower() for t in self.retrieve_tags or self.get_all_tags(data)]
  81. generated_fields = ['authors', 'html'] + list(self.footnote_map)
  82. pub = namedtuple('Publications',fields + generated_fields)
  83. style = self.style_format.Style()
  84. html_backend = html.Backend()
  85. formatted_entries = style.format_entries(data.entries.values())
  86. for formatted_entry in formatted_entries:
  87. pkey = formatted_entry.key
  88. p = data.entries[pkey]
  89. pfields = {f:p.fields.get(f) for f in fields if f not in generated_fields}
  90. # Reformat author list
  91. authors = p.persons['author']
  92. auth_list = []
  93. for author in authors:
  94. if len(author.middle()) > 0:
  95. middle = [author.middle()[0] + '.']
  96. else:
  97. middle = ''
  98. pieces = [author.first(), middle, author.prelast(), author.last(), author.lineage()]
  99. name = ' '.join(list(itertools.chain.from_iterable(pieces)))
  100. auth_list.append(name)
  101. pfields['authors'] = auth_list
  102. # Parse footnotes
  103. for foot_id, (symbol, note) in self.footnote_map.items():
  104. if any(symbol in name for name in auth_list):
  105. pfields[foot_id] = (symbol, note)
  106. else:
  107. pfields[foot_id] = None
  108. # Render the html
  109. text = formatted_entry.text.render(html_backend)
  110. pfields['html'] = text
  111. bibdata.append(pub(**pfields))
  112. return bibdata
  113. def bibtex_parser_initialization(generator):
  114. """
  115. Initialization of parser
  116. """
  117. generator.plugin_instance = BibtexParser(generator)
  118. def bibtex_fetch_publications(generator, metadata):
  119. if 'BIBTEX_SOURCE' in generator.settings.keys():
  120. generator.context['bibtex_entries'] = generator.plugin_instance.fetch()
  121. def register():
  122. """
  123. Plugin registration
  124. """
  125. signals.pages_generator_init.connect(bibtex_parser_initialization)
  126. signals.pages_generate_context.connect(bibtex_fetch_publications)