KevinIsInCoding commited on
Commit
5c88f56
·
unverified ·
2 Parent(s): 5fb461d83263a0

Merge pull request #2 from KevinIsInCoding/fix/pmc-elink-id-mapping

Browse files
Files changed (2) hide show
  1. ingestion/pmc.py +37 -24
  2. scripts/ingest_papers.py +1 -2
ingestion/pmc.py CHANGED
@@ -23,44 +23,57 @@ def _configure_entrez() -> None:
23
 
24
 
25
  def _sleep() -> None:
 
26
  time.sleep(0.1 if os.getenv("NCBI_API_KEY") else 0.4)
27
 
28
 
29
  def get_pmcids(pmids: list[str]) -> dict[str, str]:
30
  """
31
  Map PubMed IDs to PMC IDs for papers with Open Access full text.
 
 
32
  Returns {pmid: pmcid}.
33
  """
34
  _configure_entrez()
35
  if not pmids:
36
  return {}
37
 
 
38
  result: dict[str, str] = {}
39
- for i in range(0, len(pmids), 200):
40
- batch = pmids[i : i + 200]
41
- for attempt in range(3):
42
- try:
43
- handle = Entrez.elink(dbfrom="pubmed", db="pmc", id=",".join(batch))
44
- link_sets = Entrez.read(handle)
45
- handle.close()
46
- break
47
- except Exception as exc:
48
- if attempt == 2:
49
- _logger.warning(f"elink failed: {exc}")
50
- link_sets = []
51
- break
52
- time.sleep(2 ** attempt)
53
-
54
- for link_set in link_sets:
55
- source_ids = link_set.get("IdList", [])
56
- source_id = str(source_ids[0]) if source_ids else None
57
- for db_link in link_set.get("LinkSetDb", []):
58
- if db_link.get("DbTo") == "pmc":
59
- links = db_link.get("Link", [])
60
- if links and source_id:
61
- result[source_id] = str(links[0]["Id"])
62
  break
63
- _sleep()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  _logger.info("PMC ID lookup", extra={"data": {"pmids": len(pmids), "found": len(result)}})
66
  return result
 
23
 
24
 
25
  def _sleep() -> None:
26
+ # NCBI rate limit: 10 req/s with API key, 3 req/s without
27
  time.sleep(0.1 if os.getenv("NCBI_API_KEY") else 0.4)
28
 
29
 
30
  def get_pmcids(pmids: list[str]) -> dict[str, str]:
31
  """
32
  Map PubMed IDs to PMC IDs for papers with Open Access full text.
33
+ Sends one PMID at a time — batch elink merges all results into one
34
+ LinkSet with no per-ID mapping, making it unusable for this purpose.
35
  Returns {pmid: pmcid}.
36
  """
37
  _configure_entrez()
38
  if not pmids:
39
  return {}
40
 
41
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TaskProgressColumn, TimeRemainingColumn, TextColumn
42
  result: dict[str, str] = {}
43
+
44
+ with Progress(
45
+ SpinnerColumn(),
46
+ TextColumn("[progress.description]{task.description}"),
47
+ BarColumn(),
48
+ TaskProgressColumn(),
49
+ TimeRemainingColumn(),
50
+ ) as progress:
51
+ task = progress.add_task("Looking up PMC IDs...", total=len(pmids))
52
+
53
+ for pmid in pmids:
54
+ for attempt in range(3):
55
+ try:
56
+ handle = Entrez.elink(dbfrom="pubmed", db="pmc", id=pmid)
57
+ link_sets = Entrez.read(handle)
58
+ handle.close()
 
 
 
 
 
 
 
59
  break
60
+ except Exception as exc:
61
+ if attempt == 2:
62
+ _logger.debug(f"elink failed for PMID {pmid}: {exc}")
63
+ link_sets = []
64
+ break
65
+ time.sleep(2 ** attempt)
66
+
67
+ for ls in link_sets:
68
+ for db_link in ls.get("LinkSetDb", []):
69
+ if db_link.get("DbTo") == "pmc":
70
+ links = db_link.get("Link", [])
71
+ if links:
72
+ result[pmid] = str(links[0]["Id"])
73
+ break
74
+
75
+ _sleep()
76
+ progress.advance(task)
77
 
78
  _logger.info("PMC ID lookup", extra={"data": {"pmids": len(pmids), "found": len(result)}})
79
  return result
scripts/ingest_papers.py CHANGED
@@ -69,9 +69,8 @@ def main() -> None:
69
 
70
  # Step 3: Enrich with PMC full text
71
  if not args.skip_fulltext:
72
- console.print("[cyan]Looking up PMC IDs for Open Access full text...[/cyan]")
73
  all_pmids = [p.pmid for p in papers]
74
- pmcid_map = pmc.get_pmcids(all_pmids)
75
  console.print(f"[green]{len(pmcid_map)} papers have PMC full text available[/green]")
76
 
77
  pmid_to_paper = {p.pmid: p for p in papers}
 
69
 
70
  # Step 3: Enrich with PMC full text
71
  if not args.skip_fulltext:
 
72
  all_pmids = [p.pmid for p in papers]
73
+ pmcid_map = pmc.get_pmcids(all_pmids) # shows its own progress bar
74
  console.print(f"[green]{len(pmcid_map)} papers have PMC full text available[/green]")
75
 
76
  pmid_to_paper = {p.pmid: p for p in papers}