Source code for tcutility.cite

import json
import os

from tcutility import environment, spell_check
from tcutility.cache import cache

__all__ = ["cite"]


# @cache.cache_file('dois')
@environment.requires_optional_package("requests")
@cache
def _get_doi_data(doi: str) -> dict:
    """
    Get information about an article using the crossref.org API.

    Args:
            doi: the DOI to get information about.
    """
    import requests

    print(f"http://api.crossref.org/works/{doi}")
    data = requests.get(f"http://api.crossref.org/works/{doi}").text
    if data == "Resource not found.":
        raise ValueError(f"Could not find DOI {doi}.")
    data = json.loads(data)
    return data


# @cache.cache_file('journal_abbrvs')
@environment.requires_optional_package("requests")
@cache
def _get_journal_abbreviation(journal: str) -> str:
    """
    Get the journal name abbreviation using the abbreviso API.

    Args:
            journal: the name of the journal to get the abbreviation of.
    """
    import requests

    return requests.get(f"https://abbreviso.toolforge.org/a/{journal}").text


@cache
def _get_publisher_city(publisher: str) -> str:
    """
    Get the city of a publisher.
    """
    with open(os.path.join(os.path.split(__file__)[0], "data", "cite", "_publisher_cities.json")) as cities:
        cities = json.loads(cities.read())
    return cities.get(publisher)


[docs] def cite(doi: str, style: str = "wiley", mode="html") -> str: """ Format an article in a certain style. Args: doi: the article DOI to generate a citation for. style: the style formatting to use. Can be ``['wiley', 'acs', 'rsc']``. mode: the formatting mode. Can be ``['html', 'latex', 'plain']``. """ # check if the style was correctly given spell_check.check(style, ["wiley", "acs", "rsc"]) spell_check.check(mode, ["html", "latex", "plain"]) # get the information about the DOI data = _get_doi_data(doi) citation = "" if data["message"]["type"] == "journal-article": citation = _format_article(data, style) if data["message"]["type"] == "book-chapter" or (data["message"]["type"] == "other" and "ISBN" in data["message"]): citation = _format_book_chapter(data, style) if mode == "plain": citation = citation.replace("<i>", "") citation = citation.replace("</i>", "") citation = citation.replace("<b>", "") citation = citation.replace("</b>", "") if mode == "latex": citation = citation.replace("<i>", r"\textit{") citation = citation.replace("</i>", "}") citation = citation.replace("<b>", r"\textbf{") citation = citation.replace("</b>", "}") return citation
def get_pages(data): try: pages = data["message"].get("page").replace("-", "–") return pages except AttributeError: pages = "???" url = data["message"]["URL"] if "ceur." in url: return "e" + url.split("ceur.")[-1] def is_accepted(data): for assertion in data["message"].get("assertion", []): if assertion["name"] == "accepted": return True return False def _format_article(data: dict, style: str) -> str: # grab usefull data journal = data["message"]["container-title"][0] journal_abbreviation = _get_journal_abbreviation(journal) year = data["message"]["issued"]["date-parts"][0][0] volume = data["message"].get("volume", "") pages = get_pages(data) title = data["message"]["title"][0] doi = data["message"]["DOI"] # accepted = is_accepted(data) # not using this one yet citation = "" # Get the initials from the author given names # also store the family names initials = [] last_names = [] for author in data["message"]["author"]: # we get the capital letters from the first names # these will become the initials for this author firsts = [char + "." for char in author["given"].title() if char.isupper()] firsts = " ".join(firsts) initials.append(firsts) last_names.append(author["family"].title()) # format the citation correctly if style == "wiley": names = [f"{first} {last}" for first, last in zip(initials, last_names)] citation = f"{', '.join(names)}, <i>{journal_abbreviation}</i> <b>{year}</b>, <i>{volume}</i>" if pages: citation += f", {pages}" citation += "." elif style == "acs": names = [f"{last}, {first}" for first, last in zip(initials, last_names)] citation = f"{'; '.join(names)} {title} <i>{journal_abbreviation}</i> <b>{year}</b>, <i>{volume}</i>" if pages: citation += f", {pages}" citation += f". DOI: {doi}" elif style == "rsc": names = [f"{first} {last}" for first, last in zip(initials, last_names)] citation = f"{', '.join(names)}, <i>{journal_abbreviation}</i> {year}, <b>{volume}</b>" if pages: citation += f", {pages}" citation += "." return citation def _format_book_chapter(data: dict, style: str) -> str: # grab usefull data publisher = data["message"]["publisher"] year = data["message"]["published-print"]["date-parts"][0][0] pages = data["message"].get("page") book_title = data["message"]["container-title"][0] chapter_title = data["message"]["title"][0] city = _get_publisher_city(publisher) citation = "" original_book_data = None for isbn in data["message"]["isbn-type"]: if isbn["type"] == "electronic": original_book_data = _get_doi_data(f"{data['message']['prefix']}/{isbn['value']}") break # Get the initials from the author given names # also store the family names n_authors = len(data["message"]["author"]) initials = [] last_names = [] for author in data["message"]["author"]: # we get the capital letters from the first names # these will become the initials for this author firsts = [char + "." for char in author["given"].title() if char.isupper()] firsts = " ".join(firsts) initials.append(firsts) last_names.append(author["family"].title()) if original_book_data and "editor" in original_book_data["message"]: n_editors = len(original_book_data["message"]["editor"]) editor_initials = [] editor_last_names = [] for author in original_book_data["message"]["editor"]: # we get the capital letters from the first names # these will become the initials for this author firsts = [char + "." for char in author["given"].title() if char.isupper()] firsts = " ".join(firsts) editor_initials.append(firsts) editor_last_names.append(author["family"].title()) else: n_editors = 0 editor_initials = [] editor_last_names = [] # format the citation correctly if style == "wiley": names = [f"{last}, {first}" for first, last in zip(initials, last_names)] editors = [f"{first} {last}" for first, last in zip(editor_initials, editor_last_names)] if n_authors == 1: names = names[0] if 1 < n_authors < 4: names = ", ".join(names[:-1]) + " and " + names[-1] if n_authors >= 4: names = ", ".join(names[:3]) + " et al." if n_editors == 1: editors = editors[0] if 1 < n_editors < 4: editors = ", ".join(editors[:-1]) + " and " + editors[-1] if n_editors >= 4: editors = ", ".join(editors[:3]) + " et al." citation = f"{names} ({year}). {chapter_title}. In: <i>{book_title}</i> (ed. {editors}), {pages}. {city}: {publisher}" elif style == "acs": raise NotImplementedError("No support for ACS style yet") elif style == "rsc": raise NotImplementedError("No support for RSC style yet") return citation