{ "cells": [ { "cell_type": "markdown", "id": "5c63af16-35dc-4095-9cd7-19edc54bb532", "metadata": {}, "source": [ "# List of publications" ] }, { "cell_type": "code", "execution_count": 3, "id": "4cf95d24-6d63-4460-b490-cd50743a9f6b", "metadata": { "is_executing": true, "nbsphinx": "hidden" }, "outputs": [], "source": [ "import re\n", "import requests\n", "from IPython.display import HTML\n", "\n", "# === CONFIG ===\n", "ORCID_ID = \"0000-0002-0705-7662\" # your ORCID iD\n", "API_BASE = \"https://pub.orcid.org/v3.0\"\n", "# ==============\n", "\n", "session = requests.Session()\n", "session.headers.update({\"Accept\": \"application/json\"})\n", "\n", "\n", "def get_all_put_codes(orcid: str):\n", " url = f\"{API_BASE}/{orcid}/works\"\n", " r = session.get(url, timeout=30)\n", " r.raise_for_status()\n", " works = r.json()\n", "\n", " put_codes = []\n", " for group in works.get(\"group\", []):\n", " for ws in group.get(\"work-summary\", []):\n", " put_codes.append(ws[\"put-code\"])\n", " return put_codes\n", "\n", "\n", "def get_work_detail(orcid: str, put_code: int):\n", " url = f\"{API_BASE}/{orcid}/work/{put_code}\"\n", " r = session.get(url, timeout=30)\n", " r.raise_for_status()\n", " return r.json()\n", "\n", "\n", "def _norm(s):\n", " return re.sub(r\"\\s+\", \" \", (s or \"\").strip())\n", "\n", "\n", "def extract_external_ids(work):\n", " ext = (work.get(\"external-ids\") or {}).get(\"external-id\") or []\n", " out = {}\n", " for e in ext:\n", " t = (e.get(\"external-id-type\") or \"\").lower()\n", " v = e.get(\"external-id-value\") or \"\"\n", " u = ((e.get(\"external-id-url\") or {}).get(\"value\")) or \"\"\n", " if t and v:\n", " out[t] = u or v\n", " return out\n", "\n", "\n", "def extract_contributors(work):\n", " contribs = (work.get(\"contributors\") or {}).get(\"contributor\") or []\n", " people = []\n", " for c in contribs:\n", " name = ((c.get(\"credit-name\") or {}).get(\"value\")) or \"\"\n", " attrs = c.get(\"contributor-attributes\") or {}\n", " seq = attrs.get(\"contributor-sequence\") or \"\"\n", " people.append({\"name\": _norm(name), \"seq\": seq})\n", "\n", " first = [p for p in people if p[\"seq\"] == \"FIRST\"]\n", " additional = [p for p in people if p[\"seq\"] != \"FIRST\"]\n", " ordered = first + additional\n", "\n", " return [p[\"name\"] for p in ordered if p[\"name\"]]\n", "\n", "\n", "def name_to_apa(full_name: str) -> str:\n", " s = _norm(full_name)\n", " if not s:\n", " return \"\"\n", "\n", " # Case 1: already in \"Last, First/Middle\" form\n", " if \",\" in s:\n", " last, rest = s.split(\",\", 1)\n", " last = _norm(last).strip(\",\")\n", " rest = _norm(rest)\n", "\n", " # If rest already looks like initials (e.g. \"K.\" or \"T. E.\"), keep them\n", " tokens = [t for t in rest.replace(\"-\", \" \").split() if t]\n", " initials = []\n", " for t in tokens:\n", " # pick first letter in the token\n", " ch = re.sub(r\"[^A-Za-zÀ-ÖØ-öø-ÿ]\", \"\", t)[:1]\n", " if ch:\n", " initials.append(ch.upper() + \".\")\n", " return f\"{last}, {' '.join(initials)}\".strip()\n", "\n", " # Case 2: \"First Middle Last\"\n", " parts = [p for p in s.split() if p]\n", " if len(parts) == 1:\n", " return parts[0]\n", "\n", " last = parts[-1]\n", " initials = []\n", " for p in parts[:-1]:\n", " ch = re.sub(r\"[^A-Za-zÀ-ÖØ-öø-ÿ]\", \"\", p)[:1]\n", " if ch:\n", " initials.append(ch.upper() + \".\")\n", " return f\"{last}, {' '.join(initials)}\".strip()\n", "\n", "\n", "def format_authors_apa(authors):\n", " if not authors:\n", " return \"\"\n", "\n", " apa = [name_to_apa(a) for a in authors if a]\n", " apa = [a for a in apa if a]\n", "\n", " if len(apa) == 0:\n", " return \"\"\n", " if len(apa) <= 2:\n", " return \" & \".join(apa)\n", " if len(apa) <= 20:\n", " return \", \".join(apa[:-1]) + \", & \" + apa[-1]\n", "\n", " first_19 = apa[:19]\n", " last = apa[-1]\n", " return \", \".join(first_19) + \", …, \" + last\n", "\n", "\n", "def extract_info(work):\n", " title_obj = work.get(\"title\") or {}\n", " title = _norm((title_obj.get(\"title\") or {}).get(\"value\"))\n", " subtitle = _norm((title_obj.get(\"subtitle\") or {}).get(\"value\"))\n", " if subtitle:\n", " title = f\"{title}: {subtitle}\" if title else subtitle\n", "\n", " journal = _norm((work.get(\"journal-title\") or {}).get(\"value\"))\n", "\n", " pub_date = work.get(\"publication-date\") or {}\n", " year = ((pub_date.get(\"year\") or {}).get(\"value\")) or None\n", "\n", " ext = extract_external_ids(work)\n", " doi = ext.get(\"doi\")\n", " doi_url = None\n", " if doi:\n", " doi_url = doi if str(doi).startswith(\"http\") else f\"https://doi.org/{doi}\"\n", "\n", " fallback_url = ext.get(\"url\") or ext.get(\"uri\")\n", " if fallback_url and not str(fallback_url).startswith(\"http\"):\n", " fallback_url = None\n", "\n", " authors = extract_contributors(work)\n", "\n", " return {\n", " \"authors\": authors,\n", " \"year\": year,\n", " \"title\": title,\n", " \"journal\": journal,\n", " \"doi_url\": doi_url,\n", " \"fallback_url\": fallback_url,\n", " }\n", "\n", "\n", "def apa_reference(rec):\n", " authors_str = format_authors_apa(rec[\"authors\"])\n", " year = rec[\"year\"] or \"n.d.\"\n", " title = rec[\"title\"] or \"Untitled work\"\n", " journal = rec[\"journal\"]\n", "\n", " title_part = f\"{title}.\"\n", " journal_part = f\"{journal}. \" if journal else \"\"\n", " link = rec[\"doi_url\"] or rec[\"fallback_url\"] or \"\"\n", "\n", " if authors_str:\n", " ref = f\"{authors_str} ({year}). {title_part} {journal_part}{link}\".strip()\n", " else:\n", " ref = f\"{title_part} ({year}). {journal_part}{link}\".strip()\n", "\n", " return re.sub(r\"\\s{2,}\", \" \", ref)\n", "\n", "\n", "def render_apa_list(records):\n", " items = []\n", " for rec in records:\n", " ref = apa_reference(rec)\n", "\n", " link = rec[\"doi_url\"] or rec[\"fallback_url\"]\n", " if link:\n", " ref = ref.replace(link, f'{link}')\n", "\n", " items.append(f\"