nursing-ebp-tool / utils /citations.py
Lincoln Gombedza
Initial commit: EBP Research Tool for Student Nurses
5095870
"""
Citation formatters: APA 7th edition and AMA style.
Standard in nursing education programs.
"""
# ---------------------------------------------------------------------------
# APA 7th Edition
# ---------------------------------------------------------------------------
def format_apa7(article: dict) -> str:
"""
APA 7th edition journal article format:
Author, F. M., & Author, F. M. (Year). Title of article.
*Journal Name*, *Volume*(Issue), Pages. https://doi.org/xxxxx
"""
authors_str = _apa_authors(article.get("authors", []))
year = article.get("year") or "n.d."
title = _clean_title(article.get("title", ""))
journal = article.get("journal", "")
volume = article.get("volume", "")
issue = article.get("issue", "")
pages = article.get("pages", "")
doi = _clean_doi(article.get("doi", ""))
url = article.get("url", "")
citation = f"{authors_str} ({year}). {title}."
if journal:
citation += f" *{journal}*"
if volume:
citation += f", *{volume}*"
if issue:
citation += f"({issue})"
if pages:
citation += f", {pages}"
citation += "."
if doi:
citation += f" https://doi.org/{doi}"
elif url:
citation += f" {url}"
return citation
def _apa_authors(authors: list[str]) -> str:
if not authors:
return "Unknown Author"
formatted = [_apa_single(a) for a in authors[:20]]
# APA 7: 21+ authors → first 19, ellipsis, last author
if len(authors) > 20:
last = _apa_single(authors[-1])
return ", ".join(formatted[:19]) + ", . . . " + last
if len(formatted) == 1:
return formatted[0]
if len(formatted) == 2:
return f"{formatted[0]}, & {formatted[1]}"
return ", ".join(formatted[:-1]) + f", & {formatted[-1]}"
def _apa_single(name: str) -> str:
name = name.strip()
if name.lower() in ("et al.", "et al"):
return "et al."
if "," in name:
# "Last, First M." → keep as-is, just ensure initials
last, *rest = name.split(",", 1)
initials = _initials(" ".join(rest))
return f"{last.strip()}, {initials}"
parts = name.split()
if len(parts) >= 2:
last = parts[-1]
initials = _initials(" ".join(parts[:-1]))
return f"{last}, {initials}"
return name
# ---------------------------------------------------------------------------
# AMA Style
# ---------------------------------------------------------------------------
def format_ama(article: dict) -> str:
"""
AMA citation format (used in some nursing / medical journals):
Last FM, Last FM. Title. Journal. Year;Vol(Issue):Pages. doi:xxxxx
"""
authors_str = _ama_authors(article.get("authors", []))
year = article.get("year", "")
title = _clean_title(article.get("title", ""))
journal = article.get("journal", "")
volume = article.get("volume", "")
issue = article.get("issue", "")
pages = article.get("pages", "")
doi = _clean_doi(article.get("doi", ""))
citation = f"{authors_str}. {title}. *{journal}*."
if year:
citation += f" {year}"
if volume:
citation += f";{volume}"
if issue:
citation += f"({issue})"
if pages:
citation += f":{pages}"
citation += "."
if doi:
citation += f" doi:{doi}"
return citation
def _ama_authors(authors: list[str]) -> str:
if not authors:
return "Unknown Author"
formatted = [_ama_single(a) for a in authors[:6]]
if len(authors) > 6:
formatted.append("et al")
return ", ".join(formatted)
def _ama_single(name: str) -> str:
name = name.strip()
if name.lower() in ("et al.", "et al", "et al."):
return "et al"
if "," in name:
last, *rest = name.split(",", 1)
initials = "".join(p[0].upper() for p in " ".join(rest).split() if p)
return f"{last.strip()} {initials}"
parts = name.split()
if len(parts) >= 2:
last = parts[-1]
initials = "".join(p[0].upper() for p in parts[:-1] if p)
return f"{last} {initials}"
return name
# ---------------------------------------------------------------------------
# Shared helpers
# ---------------------------------------------------------------------------
def _initials(first_middle: str) -> str:
"""Convert first/middle name string to initials: "John Paul" → "J. P." """
parts = first_middle.strip().split()
return " ".join(p[0].upper() + "." for p in parts if p)
def _clean_title(title: str) -> str:
return title.strip().rstrip(".")
def _clean_doi(doi: str) -> str:
doi = doi.strip()
for prefix in ("https://doi.org/", "http://doi.org/", "doi:", "doi: "):
if doi.lower().startswith(prefix):
doi = doi[len(prefix):]
return doi