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