diff --git a/book/_config.yml b/book/_config.yml index a81c825..769fcce 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -16,6 +16,7 @@ sphinx: - sphinx.ext.imgconverter - teachbooks_favourites - teachbooks_sphinx_grasple + - sphinx_apa_references config: html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js @@ -38,16 +39,11 @@ sphinx: html_show_copyright: false html_last_updated_fmt: '%B %-d, %Y' html_static_path: ["images"] - bibtex_default_style: myapastyle - bibtex_reference_style: author_year_round mathjax3_config: tex: macros: "newcolor": ['\definecolor{newcolor}{RGB}{111, 29, 119}{\color{newcolor}#1}',1] named_colors_custom_colors: {'onlylight':[165,21,160],'lightanddark':[45,180,117,204,158,110],'hyphen-color':[45,180,117,165,21,160],'attributiongrey':[150,150,150]} - local_extensions: - apastyle: _ext/ - bracket_citation_style: _ext/ teachbooks: attribution_location: margin diff --git a/book/_ext/apastyle.py b/book/_ext/apastyle.py deleted file mode 100644 index 16828fe..0000000 --- a/book/_ext/apastyle.py +++ /dev/null @@ -1,17 +0,0 @@ - -# from pybtex.style.formatting.unsrt import Style -from pybtexapastyle.formatting.apa import APAStyle -from pybtexapastyle.labels.apa import LabelStyle as APALabelStyle -from pybtex.plugin import register_plugin -# from pybtex.style.template import names, sentence - -class MyAPALabelStyle(APALabelStyle): - def format_label(self, entry): - return APALabelStyle.format_label(self, entry) - -class MyAPAStyle(APAStyle): - default_label_style = 'myapa' - -def setup(app): - register_plugin('pybtex.style.labels', 'myapa', MyAPALabelStyle) - register_plugin('pybtex.style.formatting', 'myapastyle', MyAPAStyle) \ No newline at end of file diff --git a/book/_ext/bracket_citation_style.py b/book/_ext/bracket_citation_style.py deleted file mode 100644 index cab10f3..0000000 --- a/book/_ext/bracket_citation_style.py +++ /dev/null @@ -1,27 +0,0 @@ -from dataclasses import dataclass, field -import sphinxcontrib.bibtex.plugin - -from sphinxcontrib.bibtex.style.referencing import BracketStyle -from sphinxcontrib.bibtex.style.referencing.author_year import AuthorYearReferenceStyle - - -def bracket_style() -> BracketStyle: - return BracketStyle( - left='(', - right=')', - ) - - -@dataclass -class MyReferenceStyle(AuthorYearReferenceStyle): - bracket_parenthetical: BracketStyle = field(default_factory=bracket_style) - bracket_textual: BracketStyle = field(default_factory=bracket_style) - bracket_author: BracketStyle = field(default_factory=bracket_style) - bracket_label: BracketStyle = field(default_factory=bracket_style) - bracket_year: BracketStyle = field(default_factory=bracket_style) - - -def setup(app): - sphinxcontrib.bibtex.plugin.register_plugin( - 'sphinxcontrib.bibtex.style.referencing', - 'author_year_round', MyReferenceStyle) \ No newline at end of file diff --git a/book/_ext/pybtexapastyle/LICENSE b/book/_ext/pybtexapastyle/LICENSE deleted file mode 100644 index 579d367..0000000 --- a/book/_ext/pybtexapastyle/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Naeka - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/book/_ext/pybtexapastyle/formatting/__init__.py b/book/_ext/pybtexapastyle/formatting/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/book/_ext/pybtexapastyle/formatting/apa.py b/book/_ext/pybtexapastyle/formatting/apa.py deleted file mode 100644 index dfa5a58..0000000 --- a/book/_ext/pybtexapastyle/formatting/apa.py +++ /dev/null @@ -1,486 +0,0 @@ -# -*- coding:Utf-8 -*- -from __future__ import unicode_literals - -import re -import six - -from pybtex.plugin import find_plugin -from pybtex.style.formatting import BaseStyle, toplevel -from pybtex.style.template import ( - field, first_of, href, join, optional, optional_field, sentence, tag, - together, words, node, FieldIsMissing -) - -from pybtex.richtext import Text, Symbol - - -firstlast = find_plugin('pybtex.style.names', 'lastfirst')() - -if six.PY2: - def format_pages(text): - dash_re = re.compile(r'-+') - pages = Text(Symbol('ndash')).join( - re.split(dash_re, six.text_type(text))) - if re.search('[-‒–—―]', six.text_type(text)): - return Text("pp.", Symbol('nbsp'), pages) - return Text("p.", Symbol('nbsp'), pages) -else: - def format_pages(text): - dash_re = re.compile(r'-+') - pages = Text(Symbol('ndash')).join(text.split(dash_re)) - if re.search('[-‒–—―]', str(text)): - return Text("pp.", Symbol('nbsp'), pages) - return Text("p.", Symbol('nbsp'), pages) - - -pages = field('pages', apply_func=format_pages) -date = words[field('year'), optional[", ", field('month')]] - - -@node -def apa_names(children, context, role, **kwargs): - """ - Returns formatted names as an APA compliant reference list citation. - """ - assert not children - - try: - persons = context['entry'].persons[role] - except KeyError: - raise FieldIsMissing(role, context['entry']) - - style = context['style'] - - if len(persons) > 7: - persons = persons[:6] + persons[-1:] - formatted_names = [style.format_name( - person, style.abbreviate_names) for person in persons] - return join(sep=', ', last_sep=', … ')[ - formatted_names].format_data(context) - else: - formatted_names = [style.format_name( - person, style.abbreviate_names) for person in persons] - return join(sep=', ', sep2=', & ', last_sep=', & ')[ - formatted_names].format_data(context) - - -@node -def editor_names(children, context, with_suffix=True, **kwargs): - """ - Returns formatted editor names for inbook. - """ - assert not children - - try: - editors = context['entry'].persons['editor'] - except KeyError: - raise FieldIsMissing('editor', context['entry']) - - formatted_names = [ - firstlast.format(editor, True) for editor in editors] - - if with_suffix: - return words[ - join(sep=', ', sep2=', & ', last_sep=', & ')[formatted_names], - "(Eds.)" if len(editors) > 1 else "(Ed.)" - ].format_data(context) - - return join(sep=', ', sep2=', & ', last_sep=', & ')[ - formatted_names - ].format_data(context) - - -class APAStyle(BaseStyle): - name = 'apa' - default_name_style = 'lastfirst' - default_sorting_style = 'author_year_title' - default_label_style = 'apa' - - def __init__(self, *args, **kwargs): - super(APAStyle, self).__init__(*args, **kwargs) - self.abbreviate_names = True - - def format_names(self, role, as_sentence=True): - formatted_names = apa_names(role) - if as_sentence: - return sentence(capfirst=False)[formatted_names] - else: - return formatted_names - - def format_author_or_editor_and_date(self, e): - if 'author' in e.persons and 'editor' in e.persons: - return sentence(sep=' ')[ - self.format_names('author'), join["(", date, ")."], - self.format_editor(e, as_sentence=False)] - elif 'author' in e.persons: - return sentence(sep=' ')[ - self.format_names('author'), join["(", date, ")"]] - else: - return sentence(sep=' ')[ - self.format_editor(e, as_sentence=False), - join["(", date, ")"]] - - def format_editor(self, e, as_sentence=True): - editors = self.format_names('editor', as_sentence=False) - if 'editor' not in e.persons: - # when parsing the template, a FieldIsMissing exception - # will be thrown anyway; no need to do anything now, - # just return the template that will throw the exception - return editors - if len(e.persons['editor']) > 1: - word = '(Eds.)' - else: - word = '(Ed.)' - result = join(sep=' ')[editors, word] - if as_sentence: - return sentence[result] - else: - return result - - def format_volume(self, e, for_article=False): - prefix = "Vol." - if for_article: - return join[ - tag('em')[field('volume')], - optional['(', field('number'), ')'], - ] - else: - return optional[together[prefix, field('volume')]] - - def format_title(self, e, which_field, as_sentence=True): - formatted_title = field( - which_field, apply_func=lambda text: text.capitalize() - ) - if as_sentence: - return sentence[formatted_title] - else: - return formatted_title - - def format_btitle(self, e, which_field, as_sentence=True): - formatted_title = tag('em')[field(which_field)] - if as_sentence: - return sentence[formatted_title] - else: - return formatted_title - - def format_web_refs(self, e): - # Based on urlbst output.web.refs - return sentence(add_period=False)[ - optional[self.format_url(e)], - optional[self.format_eprint(e)], - optional[self.format_pubmed(e)], - optional[self.format_doi(e)], - ] - - def format_url(self, e): - # Based on urlbst format.url - return words[ - 'URL:', - href[ - field('url', raw=True), - field('url', raw=True) - ] - ] - - def format_pubmed(self, e): - # Based on urlbst format.pubmed - return href[ - join[ - 'https://www.ncbi.nlm.nih.gov/pubmed/', - field('pubmed', raw=True) - ], - join[ - 'PMID:', - field('pubmed', raw=True) - ] - ] - - def format_doi(self, e): - # Based on urlbst format.doi - return href[ - join[ - 'https://doi.org/', - field('doi', raw=True) - ], - join[ - 'doi:', - field('doi', raw=True) - ] - ] - - def format_eprint(self, e): - # Based on urlbst format.eprint - return href[ - join[ - 'https://arxiv.org/abs/', - field('eprint', raw=True) - ], - join[ - 'arXiv:', - field('eprint', raw=True) - ] - ] - - def get_article_template(self, e): - # Required fields: author, title, journal, year - # Optional fields: volume, number, pages, month, note, key - volume_and_pages = first_of[ - # Volume and pages, with optional issue number - optional[ - join[ - self.format_volume(e, for_article=True), - optional[', ', field('pages')] - ], - ], - # Pages only - pages, - ] - template = toplevel[ - self.format_names('author'), - sentence[ - join["(", date, ")"] - ], - self.format_title(e, 'title'), - sentence[ - tag('em')[field('journal')], - optional[volume_and_pages], - ], - sentence[optional_field('note')], - self.format_web_refs(e), - ] - return template - - def get_book_template(self, e): - # Required fields: author/editor, title, publisher, year - # Optional fields: volume, series, address, edition, month, note, key, - # isbn - return toplevel[ - self.format_author_or_editor_and_date(e), - sentence(sep=' ')[ - self.format_btitle(e, 'title'), - optional[ - sentence[ - optional[field('edition'), ' ed.'], - self.format_volume(e), - ] - ] - ], - sentence(sep=': ')[ - optional_field('address'), - field('publisher'), - ], - sentence[optional_field('note')], - ] - - def get_booklet_template(self, e): - # Required fields: title - # Optional fields: author, howpublished, address, month, year, note, - # key - return toplevel[ - sentence(sep=' ')[ - first_of[ - optional[self.format_names( - 'author', as_sentence=False)], - "None to claim their bones" - ], - join["(", first_of[optional[date], "n.d."], ")"] - ], - self.format_title(e, 'title'), - sentence[optional_field('address')], - sentence[optional_field('note')], - ] - - def get_inbook_template(self, e): - # Required fields: author/editor, title, chapter/pages, publisher, year - # Optional fields: volume, series, address, edition, month, note, key - return toplevel[ - sentence(sep=' ')[ - self.format_names('author'), - join["(", date, ")"] - ], - self.format_title(e, 'title'), - sentence(sep=' ')[ - optional[ - "In ", - editor_names(), - "," - ], - self.format_btitle(e, 'booktitle', as_sentence=False), - optional[ - join[ - "(", - sentence(add_period=False)[ - optional[field('edition'), ' ed.'], - self.format_volume(e), - pages, - ], - ")" - ] - ] - ], - sentence(sep=': ')[ - optional_field('address'), - field('publisher'), - ], - sentence[optional_field('note')], - ] - - def get_incollection_template(self, e): - # Required fields: author, title, booktitle, year - # Optional fields: editor, pages, organization, publisher, address, - # month, note, key - return toplevel[ - self.format_author_or_editor_and_date(e), - self.format_title(e, 'title'), - sentence(sep=' ')[ - self.format_btitle(e, 'booktitle', as_sentence=False), - optional["(", pages, ")"] - ], - sentence(sep=': ')[ - optional_field('address'), - optional_field('publisher'), - ], - sentence[optional_field('note')], - ] - - def get_inproceedings_template(self, e): - # Required fields: author, title, booktitle, year - # Optional fields: editor, pages, organization, publisher, address, - # month, note, key - return toplevel[ - self.format_author_or_editor_and_date(e), - self.format_title(e, 'title'), - sentence(sep=' ')[ - self.format_btitle(e, 'booktitle', as_sentence=False), - optional["(", pages, ")"] - ], - sentence(sep=': ')[ - optional_field('address'), - optional_field('publisher'), - ], - sentence[optional_field('note')], - self.format_web_refs(e) - ] - - def get_manual_template(self, e): - # Required fields: title - # Optional fields: author, organization, address, edition, month, year, - # note, key - return toplevel[ - sentence(sep=' ')[ - first_of[ - optional[self.format_names('author', as_sentence=False)], - optional_field('organization'), - "None to claim their bones" - ], - join["(", first_of[optional[date], "n.d."], ")"] - ], - self.format_btitle(e, 'title'), - sentence[optional_field('address')], - sentence[optional_field('note')], - self.format_web_refs(e) - ] - - def get_mastersthesis_template(self, e): - # Required fields: author, title, school, year - # Optional fields: address, month, note, key - return toplevel[ - sentence(sep=' ')[ - self.format_names('author'), - join["(", date, ")"] - ], - sentence(sep=' ')[ - self.format_btitle(e, 'title', as_sentence=False), - "(Master's thesis)" - ], - sentence[ - field('school'), - optional_field('address'), - ], - sentence[optional_field('note')], - ] - - def get_misc_template(self, e): - # Required fields: aucun - # Optional fields: author, title, howpublished, month, year, note, key, - # type - template = toplevel[ - sentence(sep=' ')[ - first_of[ - optional[self.format_names('author', as_sentence=False)], - optional_field('publisher'), - "None to claim their bones" - ], - join["(", first_of[optional[date], "n.d."], ")"] - ], - optional[self.format_title(e, 'title')], - sentence[optional_field('note')], - self.format_web_refs(e) - ] - return template - - def get_phdthesis_template(self, e): - # Required fields: author, title, school, year - # Optional fields: address, month, note, key - return toplevel[ - sentence(sep=' ')[ - self.format_names('author'), - join["(", date, ")"] - ], - sentence(sep=' ')[ - self.format_btitle(e, 'title', as_sentence=False), - "(Doctoral dissertation)" - ], - sentence[ - field('school'), - optional_field('address'), - ], - sentence[optional_field('note')], - ] - - def get_proceedings_template(self, e): - # Required fields: title, year - # Optional fields: editor, publisher, organization, address, month, - # note, key - return toplevel[ - sentence(sep=' ')[ - first_of[ - optional[self.format_editor(e, as_sentence=False)], - optional_field('organization'), - "None to claim their bones" - ], - join["(", first_of[optional[date], "n.d."], ")"] - ], - self.format_btitle(e, 'title'), - sentence(sep=': ')[ - optional_field('address'), - optional_field('publisher'), - ], - sentence[optional_field('note')], - ] - - def get_techreport_template(self, e): - # Required fields: author, title, institution, year - # Optional fields: type, number, address, month, note, key - return toplevel[ - sentence(sep=' ')[ - self.format_names('author', as_sentence=False), - join["(", date, ")"] - ], - self.format_btitle(e, 'title'), - sentence[field('institution')], - sentence[optional_field('note')], - ] - - def get_unpublished_template(self, e): - # Required fields: author, title, note - # Optional fields: month, year, key - template = toplevel[ - sentence(sep=' ')[ - self.format_names('author', as_sentence=False), - join["(", first_of[optional[date], "n.d."], ")"] - ], - self.format_btitle(e, 'title'), - sentence[field('note')], - ] - return template diff --git a/book/_ext/pybtexapastyle/formatting/pybtex/style/formatting/__init__.py b/book/_ext/pybtexapastyle/formatting/pybtex/style/formatting/__init__.py deleted file mode 100644 index 5ac42b8..0000000 --- a/book/_ext/pybtexapastyle/formatting/pybtex/style/formatting/__init__.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import unicode_literals - -from pybtex.style import FormattedEntry, FormattedBibliography -from pybtex.style.template import node, join -from pybtex.richtext import Symbol -from pybtex.plugin import Plugin, find_plugin - - -@node -def toplevel(children, data): - return join(sep=Symbol('newblock')) [children].format_data(data) - - -class BaseStyle(Plugin): - """ - The base class for pythonic formatting styles. - """ - - default_name_style = None - default_label_style = None - default_sorting_style = None - - def __init__(self, label_style=None, name_style=None, sorting_style=None, abbreviate_names=False, min_crossrefs=2, **kwargs): - self.name_style = find_plugin('pybtex.style.names', name_style or self.default_name_style)() - self.label_style = find_plugin('pybtex.style.labels', label_style or self.default_label_style)() - self.sorting_style = find_plugin('pybtex.style.sorting', sorting_style or self.default_sorting_style)() - self.format_name = self.name_style.format - self.format_labels = self.label_style.format_labels - self.sort = self.sorting_style.sort - self.abbreviate_names = abbreviate_names - self.min_crossrefs = min_crossrefs - - def format_entries(self, entries, bib_data=None): - sorted_entries = self.sort(entries) - labels = self.format_labels(sorted_entries) - for label, entry in zip(labels, sorted_entries): - yield self.format_entry(label, entry, bib_data=bib_data) - - def format_entry(self, label, entry, bib_data=None): - context = { - 'entry': entry, - 'style': self, - 'bib_data': bib_data, - } - try: - get_template = getattr(self, 'get_{}_template'.format(entry.type)) - except AttributeError: - format_method = getattr(self, "format_" + entry.type) - text = format_method(context) - else: - text = get_template(entry).format_data(context) - return FormattedEntry(entry.key, text, label) - - def format_bibliography(self, bib_data, citations=None): - """ - Format bibliography entries with the given keys and return a - ``FormattedBibliography`` object. - - :param bib_data: A :py:class:`pybtex.database.BibliographyData` object. - :param citations: A list of citation keys. - """ - - if citations is None: - citations = list(bib_data.entries.keys()) - citations = bib_data.add_extra_citations(citations, self.min_crossrefs) - entries = [bib_data.entries[key] for key in citations] - formatted_entries = self.format_entries(entries) - formatted_bibliography = FormattedBibliography(formatted_entries, style=self, preamble=bib_data.preamble) - return formatted_bibliography diff --git a/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/__init__.py b/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/__init__.py deleted file mode 100644 index 3430087..0000000 --- a/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import unicode_literals - - -from pybtex.plugin import Plugin - - -class BaseSortingStyle(Plugin): - def sorting_key(self, entry): - raise NotImplementedError - - def sort(self, entries): - return sorted(entries, key=self.sorting_key) \ No newline at end of file diff --git a/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/author_year_title.py b/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/author_year_title.py deleted file mode 100644 index c315c0c..0000000 --- a/book/_ext/pybtexapastyle/formatting/pybtex/style/sorting/author_year_title.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import unicode_literals - -from pybtex.style.sorting import BaseSortingStyle - -class SortingStyle(BaseSortingStyle): - - def sorting_key(self, entry): - if entry.type in ('book', 'inbook'): - author_key = self.author_editor_key(entry) - elif 'author' in entry.persons: - author_key = self.persons_key(entry.persons['author']) - else: - author_key = '' - return (author_key, entry.fields.get('year', ''), entry.fields.get('title', '')) - - def persons_key(self, persons): - return ' '.join(self.person_key(person) for person in persons) - - def person_key(self, person): - if person.prelast_names[0] != '{' and person.last_names[0] != '{' and person.first_names[0] != '{': - return ' '.join(( - ' '.join(person.prelast_names + person.last_names), - ' '.join(person.first_names + person.middle_names), - ' '.join(person.lineage_names), - )).lower() - else: - return ' '.join(( - ' '.join(person.first_names + person.middle_names), - ' '.join(person.prelast_names + person.last_names), - ' '.join(person.lineage_names), - )).lower() - - def author_editor_key(self, entry): - if entry.persons.get('author'): - return self.persons_key(entry.persons['author']) - elif entry.persons.get('editor'): - return self.persons_key(entry.persons['editor']) - else: - return '' diff --git a/book/_ext/pybtexapastyle/labels/__init__.py b/book/_ext/pybtexapastyle/labels/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/book/_ext/pybtexapastyle/labels/apa.py b/book/_ext/pybtexapastyle/labels/apa.py deleted file mode 100644 index 3632dc6..0000000 --- a/book/_ext/pybtexapastyle/labels/apa.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding:Utf-8 -*- -from __future__ import unicode_literals - -from collections import Counter -import re -import unicodedata - -from pybtex.style.labels import BaseLabelStyle - - -_nonalnum_pattern = re.compile('[^A-Za-z0-9 \-]+', re.UNICODE) - - -def _strip_accents(s): - return "".join( - (c for c in unicodedata.normalize('NFD', s) - if not unicodedata.combining(c))) - - -def _strip_nonalnum(parts): - """Strip all non-alphanumerical characters from a list of strings. - - >>> print(_strip_nonalnum([u"ÅA. B. Testing 12+}[.@~_", u" 3%"])) - AABTesting123 - """ - s = "".join(parts) - return _nonalnum_pattern.sub("", _strip_accents(s)) - - -class LabelStyle(BaseLabelStyle): - def format_labels(self, sorted_entries): - labels = [self.format_label(entry) for entry in sorted_entries] - count = Counter(labels) - counted = Counter() - for label in labels: - if count[label] == 1: - yield label - else: - yield label # + chr(ord('a') + counted[label]) - # counted.update([label]) - - def format_label(self, entry): - label = "Anonymous" - if 'author' in entry.persons: - label = self.format_author_or_editor_names(entry.persons['author']) - elif 'editor' in entry.persons: - label = self.format_author_or_editor_names(entry.persons['editor']) - elif 'organization' in entry.fields: - label = entry.fields['organization'] - if label.startswith("The "): - label = label[4:] - return '' - # if 'year' in entry.fields: - # return "{}, {}".format(label, entry.fields['year']) - # else: - # return "{}, n.d.".format(label) - - def format_author_or_editor_names(self, persons): - if len(persons)==1: - return _strip_nonalnum(persons[0].last_names) - elif len(persons)==2: - return "{} & {}".format( - _strip_nonalnum(persons[0].last_names), - _strip_nonalnum(persons[1].last_names)) - else: - return "{} et al.".format( - _strip_nonalnum(persons[0].last_names)) diff --git a/book/_ext/pybtexapastyle/names/__init__.py b/book/_ext/pybtexapastyle/names/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/book/_ext/pybtexapastyle/names/firstlast.py b/book/_ext/pybtexapastyle/names/firstlast.py deleted file mode 100644 index 8f23263..0000000 --- a/book/_ext/pybtexapastyle/names/firstlast.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding:Utf-8 -*- -from __future__ import unicode_literals - -from pybtex.style.names import BaseNameStyle, name_part -from pybtex.style.template import join - - -class NameStyle(BaseNameStyle): - - def format(self, person, abbr=False): - r""" - Format names similarly to {vv~}{ll}{, jj}{, f.} in BibTeX. - - >>> from pybtex.database import Person - >>> name = Person(string=r"Charles Louis Xavier Joseph de la Vall{\'e}e Poussin") - >>> firstlast = NameStyle().format - - >>> print(firstlast(name).format().render_as('latex')) - Charles Louis Xavier~Joseph de~la Vall{é}e~Poussin - >>> print(firstlast(name).format().render_as('html')) - Charles Louis Xavier Joseph de la Vallée Poussin - - >>> print(firstlast(name, abbr=True).format().render_as('latex')) - C.~L. X.~J. de~la Vall{é}e~Poussin - >>> print(firstlast(name, abbr=True).format().render_as('html')) - C. L. X. J. de la Vallée Poussin - - >>> name = Person(first='First', last='Last', middle='Middle') - >>> print(firstlast(name).format().render_as('latex')) - First~Middle Last - >>> print(firstlast(name, abbr=True).format().render_as('latex')) - F.~M. Last - - """ - return join[ - name_part(abbr=abbr)[person.rich_first_names + person.rich_middle_names], - name_part(before=' ')[person.rich_prelast_names], - name_part(before=' ')[person.rich_last_names], - ] diff --git a/book/_ext/pybtexapastyle/setup.py b/book/_ext/pybtexapastyle/setup.py deleted file mode 100644 index a62d122..0000000 --- a/book/_ext/pybtexapastyle/setup.py +++ /dev/null @@ -1,27 +0,0 @@ -import setuptools - - -setuptools.setup( - name='pybtex-apa-style', - version='1.3', - author='Naeka', - author_email='contact@naeka.fr', - description='Pybtex APA-like style', - url='https://github.com/naeka/pybtex-apa-style', - py_modules=['formatting.apa', 'labels.apa', 'names.firstlast'], - entry_points={ - 'pybtex.style.formatting': 'apa = formatting.apa:APAStyle', - 'pybtex.style.labels': 'apa = labels.apa:LabelStyle', - 'pybtex.style.names': 'firstlast = names.firstlast:NameStyle', - }, - classifiers=( - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Topic :: Text Processing :: Markup', - 'Topic :: Utilities', - ), -) diff --git a/book/_static/apastyle.css b/book/_static/apastyle.css deleted file mode 100644 index 01d759c..0000000 --- a/book/_static/apastyle.css +++ /dev/null @@ -1,3 +0,0 @@ -span.label:not(:has(:not(.fn-bracket))) { - display: none; -} diff --git a/book/features/apa.md b/book/features/apa.md index 897e160..6d2c670 100644 --- a/book/features/apa.md +++ b/book/features/apa.md @@ -4,25 +4,13 @@ This page is useful for user type 4 and 5. ``` -{bdg-primary}`(To be converted into) Sphinx Extension` -{bdg-link-secondary}`Supported by TeachBooks package <./teachbooks_intro.html>` - +{bdg-primary}`Sphinx Extension` ```` (apa)= # APA References -```{note} -This feature is stable but because it relies on a local extension that is part of your book files and _not_ managed as part of a computing environment (e.g., `pip` or `conda`), unknown issues may arise. We would like to convert this feature into an independent Sphinx extension and/or Pybtext plugin. Visit the README in the [project page on GitHub](https://github.com/orgs/TeachBooks/projects/17?pane=info) to learn more. - -The instructions provided here will work under conventional usage with TeachBooks and Jupyter Book v1 (e.g., `jupyter book build book`). - -A temporary fix has also been implemented in the TeachBooks package for use with the Deploy Book Workflow (release mode) (see [release notes from v0.1.0](https://github.com/TeachBooks/TeachBooks/releases/tag/v0.1.0)). - -_Update, October 21, 2025:_ a new fix was necessitated due to dependency issues with docutils making it no longer necessary to pin this package. See [PR 207 of the Manual for a description](https://github.com/TeachBooks/manual/pull/207). -``` - ## Introduction Do you include references in your book, but you're tired of the default options available in a TeachBook / Jupyter Book v1? For example, the square-bracket style of citation and reference, where the first three letters of the name year are combined in a way that seems designed to minimize transparency? @@ -30,77 +18,95 @@ Do you include references in your book, but you're tired of the default options There is a solution! This extension allows you to have APA formatting in your book. ## Installation -To use APA-references, a set of Python files need to be manually loaded into your book. -**Step 1: files to root directory of your book** +To use APA-references, install the [Sphinx-APA-References extension](https://github.com/TeachBooks/Sphinx-APA-References) by following these two steps: -Download this zip-file [`apa_config.zip`](apa_config.zip), which contains the necessary files, located in _two_ subdirectories: `_ext` and `_static`. Unzip and place the contents in the root of your book directory so that it looks like the schematic here: +**Step 1: Install the Package** +Install the module `sphinx-apa-references` package using `pip`: ``` -book -├── _ext/ -│ ├── pybtexapastyle/ -│ ├── apastyle.py -│ └── bracket_citation_style.py -├── _static/ -| ├── ... -│ └── apastyle.css -├── _config.yml -├── _toc.yml -├── requirements.txt -├── +pip install sphinx-apa-references +``` + +**Step 2: Add to `requirements.txt`** + +Make sure that the package is included in your project's `requirements.txt` to track the dependency: +``` +sphinx-apa-references ``` -Note that you may or may not already have a `_static` subdirectory, to which the file `apastyle.css` should be added. - -**Step 2: Enable in `_config.yml`** - -In your `_config.yml` file, add a `bibtex_default_style`, `bybtex_reference_style` and the local extensions `apastyle` and `bracket_citation_style.py`. This enables the (local) extension. +**Step 3: Enable in `_config.yml` +In your `_config.yml` file, add the extension to the list of Sphinx extra extensions (**important**: underscore, not dash this time): ``` -sphinx: - config: - ... - bibtex_default_style: myapastyle - bibtex_reference_style: author_year_round - local_extensions: - apastyle: _ext/ - bracket_citation_style: _ext/ - ... +sphinx: + extra_extensions: + . + . + . + - sphinx_apa_references + . + . + . ``` ## Usage -All references are now made in APA-style. See for example this reference: {cite:t}`jason_moore` which shows up on the [references page](../references.md) too. The form of the citation looks like this: +All references are now made in APA-style. Here are five examples how to create a citation in APA style: -``` -{cite:t}`jason_moore` -``` - -Here are three examples for making citations: - -| Syntax | Result | -| :---: | :---: | -|`{cite:t}` | {cite:t}`jason_moore` | -|`{cite:p}`| {cite:p}`jason_moore` | -|`{cite}`| {cite}`jason_moore` | +| Syntax | Result with 1 author | Result with many authors | Explanation | +| :---: | :---: | :---: | :---: | +|`{cite:t}` | {cite:t}`jason_moore` | {cite:t}`dummyreference` | Textual citation | +|`{cite:p}`| {cite:p}`jason_moore` | {cite:p}`dummyreference` | Parenthetical citation | +|`{cite}`| {cite}`jason_moore` | {cite}`dummyreference` | Same as `{cite:p}` | +|`{cite:ts}` | {cite:ts}`jason_moore` | {cite:ts}`dummyreference` | Same as `{cite:t}`, but without abbreviation of authors | +|`{cite:ps}`| {cite:ps}`jason_moore` | {cite:ps}`dummyreference` |Same as `{cite:p}`, but without abbreviation of authors | For more options on the in-line citation style, see https://jupyterbook.org/v1/content/citations.html#change-the-in-line-citation-style. -### Known Issues +The references section at the end of your book will also be formatted in APA style: -Two issues are known: -- During the build, warning will be raised with `... WARNING: duplicate label for keys ...`. In most cases, these warnings can be ignored. As noted above, the book will not build references properly -- In case you have both an `.ipynb` and `.md` version of a file, the `.md` version will always be used whenever this extension is used. This removes the possibility to show code outputs. +::::{admonition} Example of APA References +:class: example + +```{iframe} ../references.html +:class: no-blend +``` +:::: + +## Duplicate label warning + +You might encounter a warning during the build process that has this structure: + +``` +:: WARNING: duplicate label ", " for keys "" and "" +``` + +This is might have different cause, but often it is because two different references have the same author(s) and the same year. A common solution is to add letters to the year in the `.bib` file, like this: + +```bibtex +@misc{launch_buttons, + author = {{TeachBooks}}, + title = {Custom launch buttons}, + url = {https://github.com/TeachBooks/Sphinx-launch-buttons}, + year = {2024a}, + note = {Retrieved December, 2024. BSD-3-Clause.}, +} + +@misc{nested, + author = {{TeachBooks}}, + title = {Git workflow: Share content between book}, + url = {https://github.com/TeachBooks/Nested-Books}, + year = {2024b}, + note = {Retrieved December, 2024. CC BY 4.0.}, +} + +``` + +This does negate the automatic ordering of references by the order `authors > year > title`, so our advice is to only use this solution when the bibliography contains duplicate author-year combinations ánd the bibliography is final. That way the you can use the sorted order with warnings to determine the correct letters to add to the years. ## Implementation The extension is based on [`pybtex`](https://pybtex.org/), which is a BibTeX-compatible bibliography processor in Python that is extendible with plugins. -Although some customization is possible with the standard Jupyter Book v1 features, [as described here](https://jupyterbook.org/v1/content/citations.html#change-the-in-line-citation-style), this extension implements the complete APA style, as well as enforcing round brackets (like this). - -The need to include a CSS file is an easy solution to the issue where empty brackets `[]` are left on the references page (to try it, simply delete `apastyle.css`). - -## Contribute - -This tool needs to be developed further to make it into a proper sphinx-extension (and/or an independent `pybtext` plugin). The process is described in the README of this [project page on GitHub](https://github.com/orgs/TeachBooks/projects/17?pane=info). If you've ideas or questions, please reach out to us at info@teachbooks.io! +Although some customization is possible with the standard Jupyter Book v1 features, [as described here](https://jupyterbook.org/v1/content/citations.html#change-the-in-line-citation-style), this extension implements a complete APA style, as well as enforcing round brackets (like this). diff --git a/book/features/apa_config.zip b/book/features/apa_config.zip deleted file mode 100644 index eedf090..0000000 Binary files a/book/features/apa_config.zip and /dev/null differ diff --git a/book/references.bib b/book/references.bib index 292c741..0358ae3 100644 --- a/book/references.bib +++ b/book/references.bib @@ -197,4 +197,13 @@ url = {https://github.com/executablebooks/sphinx-exercise}, year = {2024}, note = {Retrieved September, 2025. MIT License.}, -} \ No newline at end of file +} + +@inproceedings{dummyreference, + author = {Author, Some and Writer, Another and Novelist, Also and Cowriter, Maybe}, + title = {Efficient Estimation of Word Representations in Vector Space}, + booktitle = {Proceedings of Workshop at ICLR}, + year = {2013}, + url = {https://arxiv.org/abs/1301.3781}, +} + diff --git a/requirements.txt b/requirements.txt index 32f8e84..9378a18 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,6 @@ sphinx-inline-tabs git+https://github.com/TeachBooks/TeachBooks-Favourites -teachbooks-sphinx-grasple \ No newline at end of file +teachbooks-sphinx-grasple + +sphinx-apa-references \ No newline at end of file