Source code for bids2cite.authors

"""Deal with authors."""
from __future__ import annotations

import logging
from pathlib import Path
from typing import Any

import pandas as pd
import requests
from rich.prompt import Prompt

from bids2cite.utils import print_ordered_list
from bids2cite.utils import prompt_format

log = logging.getLogger("bids2datacite")


[docs]def affiliation_from_orcid(orcid_record: dict[str, Any]) -> str | None: """Get affiliation the most recent employment (top of the list).""" if employer := ( orcid_record.get("activities-summary", {}) .get("employments", {}) .get("employment-summary", []) ): return employer[0].get("organization", {}).get("name") else: return None
[docs]def first_name_from_orcid(orcid_record: dict[str, Any]) -> str: """Return first name from ORCID record.""" return ( orcid_record.get("person", {}).get("name", {}).get("given-names", {}).get("value") )
[docs]def last_name_from_orcid(orcid_record: dict[str, Any]) -> str: """Return last name from ORCID record.""" return ( orcid_record.get("person", {}).get("name", {}).get("family-name", {}).get("value") )
[docs]def get_author_info_from_orcid(orcid: str) -> dict[str, Any]: """Get author info from ORCID.""" orcid = orcid.strip() url = f"https://pub.orcid.org/v3.0/{orcid}/record" response = requests.get( url, headers={ "Accept": "application/json", }, ) author_info = {} if response.status_code == 200: record = response.json() first_name = first_name_from_orcid(record) last_name = last_name_from_orcid(record) affiliation = affiliation_from_orcid(record) author_info = { "firstname": first_name, "lastname": last_name, "affiliation": affiliation, "id": f"ORCID:{orcid}", } if not author_info: log.warning(f"Could not find author info for ORCID: {orcid}") return author_info
[docs]def parse_author(author: str) -> dict[str, str | None]: """Parse author string to get first name, last name, affiliation and ORCID.""" author = author.strip().replace(" ", " ") author_info: dict[str, str | None] = { "first_name": None, "last_name": None, "affiliation": None, "id": None, } if author in (""): return {"firstname": None, "lastname": None} if "orcid:" in author.lower(): if author_info := get_author_info_from_orcid(author.split(":")[1]): return author_info elif "orcid.org/" in author: if author_info := get_author_info_from_orcid(author.split("orcid.org/")[1]): return author_info elif author_info := get_author_info_from_orcid(author): return author_info if "," in author: first_name, last_name = author.split(",") elif " " in author: first_name = author.split(" ")[0] last_name = " ".join(author.split(" ")[1:]) else: first_name = author last_name = "" first_name = first_name.strip() last_name = last_name.strip() author_info["firstname"] = first_name author_info["lastname"] = last_name return author_info
[docs]def display_new_authors(authors_file: Path | None = None) -> int: """Display new authors from authors file.""" if authors_file is not None and authors_file.exists(): tmp = pd.read_csv(authors_file, sep="\t") authors_list = [ { "first_name": tmp["first_name"][ind], "last_name": tmp["last_name"][ind], "affiliation": tmp["affiliation"][ind], "ORCID": tmp["ORCID"][ind], } for ind in tmp.index ] print_ordered_list(msg="List of potential authors to add:", items=authors_list) return len(authors_list) else: return 0
[docs]def rm_empty_authors(authors: list[dict[str, str | None]]) -> list[dict[str, str | None]]: """Remove empty authors.""" return [x for x in authors if x["firstname"] is not None]
[docs]def update_authors( ds_desc: dict[str, Any], skip_prompt: bool = False, authors_file: Path | None = None ) -> list[dict[str, str | None]]: """Update authors.""" authors: list[dict[str, str | None]] = [] log.info("update authors") if "Authors" in ds_desc: desc_authors = [ x for x in ds_desc["Authors"] if x not in (None, "") or not x.isspace() ] authors.extend(parse_author(author) for author in desc_authors) if skip_prompt: return rm_empty_authors(authors) add_authors = "yes" while add_authors == "yes": print_ordered_list(msg="Current authors:", items=authors) add_authors = Prompt.ask( prompt_format("Do you want to add more authors?"), default="yes", choices=["yes", "no"], ) print() if add_authors != "yes": break author: Any = None if authors_file is not None: nb_authors = display_new_authors(authors_file) choices = [str(i) for i in range(1, nb_authors + 1)] choices.append("0") author_idx = Prompt.ask( prompt_format( "Select author to add. (0 --> add an author not listed above)" ), choices=choices, ) if author_idx == "0": author = manually_add_author() authors.append(parse_author(author)) else: author = choose_from_new_authors(authors_file, int(author_idx) - 1) authors.append(author) else: author = manually_add_author() authors.append(parse_author(author)) return rm_empty_authors(authors)
[docs]def choose_from_new_authors(authors_file: Path, author_idx: int) -> dict[str, str | None]: """Choose author from new authors file.""" tmp = pd.read_csv(authors_file, sep="\t") author = tmp.iloc[author_idx] author_info: dict[str, str | None] = { "firstname": author["first_name"], "lastname": author["last_name"], "affiliation": author["affiliation"], "id": f"ORCID:{author['ORCID']}", } return author_info
[docs]def manually_add_author() -> str: """Manually add author.""" author = Prompt.ask( prompt_format( """Please enter a new author (for example: 'firstname surname' or 'ORCID:0000-0002-9120-8098')""" ) ) return author
[docs]def authors_for_desc(authors: list[dict[str, str | None]]) -> list[str]: """Return authors formatted for dataset_description.json.""" tmp = [] for x in authors: this_author = f"{x['firstname']} {x['lastname']}" if x.get("id"): this_author += f", {x['id']}" tmp.append(this_author) return tmp
[docs]def authors_for_citation( authors: list[dict[str, str | None]] ) -> list[dict[str, str | None]]: """Return authors formatted for citation.cff.""" tmp = [] for x in authors: this_author = { "given-names": x.get("firstname", ""), "family-names": x.get("lastname", ""), } if x.get("id"): orcid = x.get("id") this_author["orcid"] = f"https://orcid.org/{orcid.replace('ORCID:', '')}" # type: ignore if x.get("affiliation"): this_author["affiliation"] = x.get("affiliation", "") tmp.append(this_author) return tmp