Spaces:
Runtime error
Runtime error
Download utils/citations.py from NurseCitizenDeveloper/nursing-ebp-tool: direct link, hf CLI and curl.
- Browser
- Download file 12.1 kB
-
https://huggingface.co/spaces/NurseCitizenDeveloper/nursing-ebp-tool/resolve/main/utils/citations.py
- Command line
-
hf download hf://spaces/NurseCitizenDeveloper/nursing-ebp-tool/utils/citations.py
-
curl -L -o citations.py https://huggingface.co/spaces/NurseCitizenDeveloper/nursing-ebp-tool/resolve/main/utils/citations.py
12.1 kB
| """ | |
| Citation formatters: Vancouver, APA 7th edition, and AMA style. | |
| Vancouver is the standard for UK nursing and medical publications. | |
| APA 7th is used by some UK university nursing programmes. | |
| """ | |
| # --------------------------------------------------------------------------- | |
| # 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: | |
| # Detect PubMed/Europe PMC "Surname JK" format | |
| if _is_initials_token(parts[-1]): | |
| last = " ".join(parts[:-1]) # handles "van der Berg JK" | |
| initials = _initials_from_str(parts[-1]) | |
| return f"{last}, {initials}" | |
| last = parts[-1] | |
| initials = _initials(" ".join(parts[:-1])) | |
| return f"{last}, {initials}" | |
| return name | |
| # --------------------------------------------------------------------------- | |
| # Vancouver Style (UK nursing / medical standard) | |
| # --------------------------------------------------------------------------- | |
| def format_vancouver(article: dict) -> str: | |
| """ | |
| Vancouver citation format — standard for UK nursing and medical journals | |
| (Nursing Standard, BJN, JAN, BMJ, Lancet, NEJM etc.) | |
| Format: Surname Initial(s), Surname Initial(s). Title. Journal Abbrev. | |
| Year;Volume(Issue):Pages. doi:xxxxx | |
| Up to 6 authors; if more, list first 6 then 'et al.' | |
| """ | |
| authors_str = _vancouver_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", "")) | |
| url = article.get("url", "") | |
| # Vancouver: no period after title if journal follows immediately | |
| citation = f"{authors_str}. {title}." | |
| if journal: | |
| citation += f" {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}" | |
| elif url: | |
| citation += f" Available from: {url}" | |
| return citation | |
| def _vancouver_authors(authors: list[str]) -> str: | |
| if not authors: | |
| return "Unknown Author" | |
| formatted = [_vancouver_single(a) for a in authors[:6]] | |
| if len(authors) > 6: | |
| formatted.append("et al") | |
| return ", ".join(formatted) | |
| def _vancouver_single(name: str) -> str: | |
| """Convert name to Vancouver format: Surname AB""" | |
| name = name.strip() | |
| if name.lower() in ("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: | |
| # Detect PubMed/Europe PMC "Surname JK" format | |
| if _is_initials_token(parts[-1]): | |
| last = " ".join(parts[:-1]) | |
| initials = parts[-1].upper() # already run-together: "JK" stays "JK" | |
| return f"{last} {initials}" | |
| last = parts[-1] | |
| initials = "".join(p[0].upper() for p in parts[:-1] if p) | |
| return f"{last} {initials}" | |
| return name | |
| # --------------------------------------------------------------------------- | |
| # Harvard Style (Cite Them Right — common UK university nursing standard) | |
| # --------------------------------------------------------------------------- | |
| def format_harvard(article: dict) -> str: | |
| """ | |
| Harvard citation format (Cite Them Right 12th ed.) — used by many UK | |
| universities for nursing essays where Vancouver is not mandated. | |
| Format: Surname, Initial(s). and Surname, Initial(s). (Year) 'Title of | |
| article', *Journal Name*, Volume(Issue), pp. Pages. | |
| Up to 3 authors listed; 4+ → first author et al. | |
| """ | |
| authors_str = _harvard_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})" | |
| citation += "," | |
| if pages: | |
| citation += f" pp. {pages}." | |
| else: | |
| citation = citation.rstrip(",") + "." | |
| else: | |
| citation = citation.rstrip(",") + "." | |
| if doi: | |
| citation += f" doi:{doi}" | |
| elif url: | |
| citation += f" Available at: {url} (Accessed: {_access_year(year)})" | |
| return citation | |
| def _harvard_authors(authors: list[str]) -> str: | |
| if not authors: | |
| return "Anon." | |
| if len(authors) > 3: | |
| return _harvard_single(authors[0]) + " et al." | |
| formatted = [_harvard_single(a) for a in authors] | |
| if len(formatted) == 1: | |
| return formatted[0] | |
| return ", ".join(formatted[:-1]) + " and " + formatted[-1] | |
| def _harvard_single(name: str) -> str: | |
| """Convert to Harvard format: Surname, A.B.""" | |
| name = name.strip() | |
| if name.lower() in ("et al.", "et al"): | |
| return "et al." | |
| if "," in name: | |
| last, *rest = name.split(",", 1) | |
| initials = _initials(" ".join(rest)) | |
| return f"{last.strip()}, {initials}" | |
| parts = name.split() | |
| if len(parts) >= 2: | |
| # Detect PubMed/Europe PMC "Surname JK" format (e.g. "Smith JK") | |
| # Without this check, "Smith JK" → last="JK", initials="S." → "JK, S." (wrong) | |
| if _is_initials_token(parts[-1]): | |
| last = " ".join(parts[:-1]) # handles multi-word surnames too | |
| initials = _initials_from_str(parts[-1]) # "JK" → "J. K." | |
| return f"{last}, {initials}" | |
| last = parts[-1] | |
| initials = _initials(" ".join(parts[:-1])) | |
| return f"{last}, {initials}" | |
| return name | |
| def _access_year(year: str) -> str: | |
| """Return a sensible access date placeholder.""" | |
| return "date accessed" | |
| # --------------------------------------------------------------------------- | |
| # 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: | |
| # Detect PubMed/Europe PMC "Surname JK" format | |
| if _is_initials_token(parts[-1]): | |
| last = " ".join(parts[:-1]) | |
| initials = parts[-1].upper() # AMA uses run-together: "Smith JK" | |
| return f"{last} {initials}" | |
| 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 _initials_from_str(token: str) -> str: | |
| """ | |
| Expand a run-together initials token from PubMed/Europe PMC into dotted | |
| initials: "JK" → "J. K." | "AB" → "A. B." | "J" → "J." | |
| """ | |
| return " ".join(c.upper() + "." for c in token if c.isalpha()) | |
| def _is_initials_token(token: str) -> bool: | |
| """ | |
| Return True when a name token looks like run-together initials rather than | |
| a proper word — i.e. it is 1–4 uppercase letters with no vowels OR is | |
| entirely uppercase and short. | |
| Examples that return True : "JK", "AB", "J", "JKL", "ABCD" | |
| Examples that return False: "Smith", "John", "van", "De", "O'Brien" | |
| """ | |
| if not token or not token.isalpha(): | |
| return False | |
| if len(token) > 4: | |
| return False | |
| # Single letter is always an initial | |
| if len(token) == 1: | |
| return token.isupper() | |
| # Multi-char: must be all-uppercase (PubMed stores "Smith JK", not "Smith jk") | |
| return token.isupper() | |
| 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 | |