""" 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