def bib(style_path, ds, return_cites_and_keys = False, formatter = "chocolate", apa_tweaks = True, # The below options are ignored unless apa_tweaks is on. always_include_issue = False, include_isbn = False, url_after_doi = False, publisher_website = True, abbreviate_given_names = True): if isinstance(formatter, str): try: formatter = formatter_from_name[formatter] except KeyError: raise ValueError('Unknown formatter "{}"'.format(formatter)) style = get_style(style_path, apa_tweaks, include_isbn, url_after_doi, abbreviate_given_names) ds = deepcopy(ds) if apa_tweaks: # Distinguish entries that would have identical authors and years # by adding suffixes to the years. # Group works by author and year. # # (Actually, we use only an initial subset of authors, # the same number that would be included in an inline citation # after the first inline citation. This is 2 for 2 authors # and 1 otherwise.) ay = defaultdict(list) for d in ds: names = d.get('author') or d.get('editor') if len(names) != 2: names = [names[0]] k = repr(names) + '/' + str(d['issued']['date-parts'][0][0]) if not any(d is v for v in ay[k]): ay[k].append(d) # If any group has more than one element, add suffixes. for v in ay.values(): if len(v) > 1: for i, d in enumerate(sorted(v, key = title_sort_key)): d['year_suffix'] = ascii_lowercase[i] for d in ds: if 'id' not in d: d['id'] = str(random()) for k in list(d.keys()): if d[k] is None: del d[k] if apa_tweaks: # By default, don't include the issue number for # journal articles. if not always_include_issue and d['type'] == 'article-journal': delf(d, 'issue') # Use the weird "Retrieved from Dewey, Cheatem, & # Howe website: http://example.com" format prescribed # for reports. if publisher_website and d['type'] == 'report' and 'publisher' in d and 'URL' in d: d['URL'] = '{} website: {}'.format( d.pop('publisher'), d['URL']) # Add structure words for presentations and include # the event place. if d['type'] == 'speech' and d['genre'] == 'paper': d['event'] = 'meeting of the {}, {}'.format( d.pop('publisher'), d['event-place']) if d['type'] == 'speech' and d['genre'] == 'video': d['medium'] = 'Video file' del d['genre'] # Format encyclopedia entries like book chapters. if d['type'] == 'entry-encyclopedia': d['type'] = 'chapter' # When abbreviating given names, remove hyphens # preceding lowercase letters. Otherwise, weird # stuff happens. if abbreviate_given_names and 'author' in d: for a in d['author']: if 'given' in a: a['given'] = sub( '-(.)', lambda mo: ("" if mo.group(1).islower() else "-") + mo.group(1), a['given']) bibliography = CitationStylesBibliography( style, CiteProcJSON(ds), formatter) cites = [ Citation([CitationItem(d['id'])]) for d in ds ] for c in cites: bibliography.register(c) def sort_key_f(item): ref = item.reference names = [(name['family'].lower(), name['given'][0].lower() if 'given' in name else '') for name in ref.get('author') or ref.get('editor')] return (names, ref['issued']['year'], title_sort_key(ref), ref['page']['first'] if 'page' in ref else '') if len(ds) > 1: # Sort the bibliography # bibliography.sort() # Doesn't appear to handle leading "the"s correctly. bibliography.items = sorted(bibliography.items, key = sort_key_f) bibliography.keys = [item.key for item in bibliography.items] bibl = bibliography.bibliography() for i, s in enumerate(bibl): s = ''.join(s) # Fix spacing and punctuation issues. s = s.replace(' ', ' ') s = sub(r'([.!?…])\.', r'\1', s) if apa_tweaks: if formatter is citeproc.formatter.html or formatter is chocolate: # Italicize the stuff between a journal name and a volume # number. s = sub(r'</i>, <i>(\d)', r', \1', s) # Remove redundant periods that are separated # from the first end-of-sentence mark by an </i> # tag. s = sub(r'([.!?…]</i>)\.', r'\1', s) # If there are two authors and the first is a mononym, # remove the comma after it. s = sub('^([^.,]+), &', r'\1 &', s) bibl[i] = s if return_cites_and_keys: fcites = [bibliography.cite(c, lambda x: None) for c in cites] return (fcites, bibliography.keys, bibl) else: return bibl
def bib(style_path, ds, return_cites_and_keys = False, formatter = "chocolate", dumb_quotes = True, # Turning this off won't educate any straight quotes in # the data, but leaving it on will stupefy all the # smart quotes in the output. apa_tweaks = True, # The below options are ignored unless apa_tweaks is on. always_include_issue = False, include_isbn = False, url_after_doi = False, publisher_website = True, abbreviate_given_names = True): if isinstance(formatter, str): try: formatter = formatter_from_name[formatter] except KeyError: raise ValueError('Unknown formatter "{}"'.format(formatter)) style = get_style(style_path, apa_tweaks, include_isbn, url_after_doi, abbreviate_given_names) ds = deepcopy(ds) if apa_tweaks: # Distinguish entries that would have identical authors and years # by adding suffixes to the years. # Group works by author and year. ay = defaultdict(list) for d in ds: k = repr(d.get('author') or d.get('editor')) + '/' + str(d['issued']['date-parts'][0][0]) if not any(d is v for v in ay[k]): ay[k].append(d) # If any group has more than one element, add suffixes. for v in ay.values(): if len(v) > 1: for i, d in enumerate(sorted(v, key = title_sort_key)): d['year_suffix'] = ascii_lowercase[i] for d in ds: if 'id' not in d: d['id'] = str(random()) for k in list(d.keys()): if d[k] is None: del d[k] if apa_tweaks: # By default, don't include the issue number for # journal articles. if not always_include_issue and d['type'] == 'article-journal': delf(d, 'issue') # Use the weird "Retrieved from Dewey, Cheatem, & # Howe website: http://example.com" format prescribed # for reports. if publisher_website and d['type'] == 'report' and 'publisher' in d and 'URL' in d: d['URL'] = '{} website: {}'.format( d.pop('publisher'), d['URL']) # Add structure words for presentations and include # the event place. if d['type'] == 'speech' and d['genre'] == 'paper': d['event'] = 'meeting of the {}, {}'.format( d.pop('publisher'), d['event-place']) if d['type'] == 'speech' and d['genre'] == 'video': d['medium'] = 'Video file' del d['genre'] # When abbreviating given names, remove hyphens # preceding lowercase letters. Otherwise, weird # stuff happens. if abbreviate_given_names and 'author' in d: for a in d['author']: if 'given' in a: a['given'] = sub( '-(.)', lambda mo: ("" if mo.group(1).islower() else "-") + mo.group(1), a['given']) # Abbreviate a long list of authors with an ellipsis # and the final author. if 'author' in d and len(d['author']) > 7: d['author'] = ( d['author'][0:6] + [{'given': '', 'family': '⣥<ellipsis>⣥'}] + d['author'][-1:]) bibliography = CitationStylesBibliography( style, {ref.key: ref for ref in parse_references(ds)}, formatter) cites = [ Citation([CitationItem(d['id'])]) for d in ds ] for c in cites: bibliography.register(c) def sort_key_f(item): ref = item.reference names = [(name['family'].lower(), name['given'][0].lower() if 'given' in name else '') for name in ref.get('author') or ref.get('editor') if name.family != '⣥<ellipsis>⣥'] return (names, ref['issued']['year'], title_sort_key(ref), ref['page']['first'] if 'page' in ref else '') if len(ds) > 1: # Sort the bibliography # bibliography.sort() # Doesn't appear to handle leading "the"s correctly. bibliography.items = sorted(bibliography.items, key = sort_key_f) bibliography.keys = [item.key for item in bibliography.items] bibl = bibliography.bibliography() for i, s in enumerate(bibl): s = ''.join(s) # Fix spacing and punctuation issues. s = s.replace(' ', ' ') s = sub(r'([.!?…])\.', r'\1', s) if dumb_quotes: s = s.replace('‘', "'").replace('’', "'").replace('“', '"').replace('”', '"') if apa_tweaks: if formatter is citeproc.formatter.html or formatter is chocolate: # Italicize the stuff between a journal name and a volume # number. s = sub(r'</i>, <i>(\d)', r', \1', s) # Make "p." into "pp." when more than one page is cited. s = sub(r'(\W)p\. (\S+[,–])', r'\1pp. \2', s) # Replace the ellipsis placeholder. s = s.replace('⣥<ellipsis>⣥, ., &', '…') bibl[i] = s if return_cites_and_keys: fcites = [bibliography.cite(c, lambda x: None) for c in cites] return (fcites, bibliography.keys, bibl) else: return bibl