Some checks failed
CI / skinny-install (aco) (push) Successful in 1m12s
CI / skinny-install (api) (push) Successful in 30s
CI / skinny-install (bcda) (push) Successful in 36s
CI / skinny-install (bib) (push) Successful in 35s
CI / skinny-install (bls) (push) Successful in 27s
CI / skinny-install (ccw) (push) Successful in 32s
CI / skinny-install (cli) (push) Successful in 41s
CI / skinny-install (cms) (push) Successful in 37s
CI / skinny-install (conf) (push) Successful in 38s
CI / skinny-install (opps) (push) Successful in 33s
CI / skinny-install (perf) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 38s
CI / skinny-install (rex) (push) Successful in 34s
Deploy / build-scan-report (push) Failing after 46s
Infra CI / notebooks (push) Failing after 25s
Infra CI / zotero (push) Successful in 12s
Infra CI / docs (push) Failing after 16s
CI / lint-test (push) Failing after 11m2s
Infra CI / mc (push) Successful in 21s
Infra CI / api (push) Successful in 29s
Package Supply Chain / pkg-supply-chain (push) Failing after 41s
Mail: Maddy on DO (corwins.media+Resend, fhirworx.io+Postmark), touchless/stateless/idempotent. Gitea SMTP via env_file. CMS inbox at cmsupdates@mail.fhirworx.io with IMAP→bib poller. Bib: regulations.gov v4 client, Federal Register discovery, 164K comment backfill (running), IMAP email ingest, Zotero sync routing. PRISMA: altcha PoW solver, CrossRef DOI resolution, 83/129 PDFs. Zotero: schema parity, ops module, CLI, fail-fast guard. CI: docs.Dockerfile COPY glob fix (tracks #341). Infra: Gitea+marimo fhirworx themes, IOM/OIG modules.
97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
"""Check PDF retrieval coverage for skin substitute evidence base.
|
|
|
|
Reports how many Zotero items have PDF attachments vs DOI-bearing items
|
|
that could potentially be retrieved. Run after using Zotero's
|
|
"Find Available PDF" with the scipdf plugin.
|
|
|
|
Usage:
|
|
uv run python dev/scripts/check_pdf_coverage.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
from conf import path as conf_path
|
|
|
|
ZOTERO_DB = str(conf_path("db.zotero"))
|
|
|
|
|
|
def main() -> None:
|
|
print("Checking PDF coverage for skin-subs items in Zotero ...\n")
|
|
|
|
con = sqlite3.connect(ZOTERO_DB, timeout=10)
|
|
con.row_factory = sqlite3.Row
|
|
|
|
# Total skin-subs items
|
|
total = con.execute("""
|
|
SELECT count(DISTINCT it.itemID) FROM itemTags it
|
|
JOIN tags t ON it.tagID = t.tagID
|
|
WHERE t.name = 'module:skin-subs'
|
|
""").fetchone()[0]
|
|
|
|
# Items with DOI (retrievable)
|
|
with_doi = con.execute("""
|
|
SELECT count(DISTINCT i.itemID) FROM items i
|
|
JOIN itemTags it ON i.itemID = it.itemID
|
|
JOIN tags t ON it.tagID = t.tagID
|
|
JOIN itemData id ON i.itemID = id.itemID
|
|
WHERE t.name = 'module:skin-subs' AND id.fieldID = {FIELD_MAP['DOI']}
|
|
""").fetchone()[0]
|
|
|
|
# Items with PDF attachments
|
|
with_pdf = con.execute("""
|
|
SELECT count(DISTINCT parent.itemID) FROM items parent
|
|
JOIN itemTags it ON parent.itemID = it.itemID
|
|
JOIN tags t ON it.tagID = t.tagID
|
|
JOIN itemAttachments ia ON ia.parentItemID = parent.itemID
|
|
JOIN items att ON ia.itemID = att.itemID
|
|
WHERE t.name = 'module:skin-subs'
|
|
AND ia.contentType = 'application/pdf'
|
|
""").fetchone()[0]
|
|
|
|
print(f"Total skin-subs items: {total:>6}")
|
|
print(f"Items with DOI: {with_doi:>6} ({with_doi * 100 // total}%)")
|
|
print(
|
|
f"Items with PDF attached: {with_pdf:>6} ({with_pdf * 100 // max(total, 1)}%)"
|
|
)
|
|
print(f"DOIs without PDF: {with_doi - with_pdf:>6}")
|
|
print(f"Coverage (of DOI items): {with_pdf * 100 // max(with_doi, 1)}%")
|
|
|
|
# Breakdown by item type
|
|
print("\nBy item type:")
|
|
for r in con.execute("""
|
|
SELECT it2.typeName,
|
|
count(DISTINCT i.itemID) as total,
|
|
count(DISTINCT CASE WHEN ia.itemID IS NOT NULL THEN i.itemID END) as with_pdf
|
|
FROM items i
|
|
JOIN itemTags it ON i.itemID = it.itemID
|
|
JOIN tags t ON it.tagID = t.tagID
|
|
JOIN itemTypes it2 ON i.itemTypeID = it2.itemTypeID
|
|
LEFT JOIN itemAttachments ia ON ia.parentItemID = i.itemID
|
|
AND ia.contentType = 'application/pdf'
|
|
WHERE t.name = 'module:skin-subs'
|
|
GROUP BY it2.typeName
|
|
ORDER BY total DESC
|
|
""").fetchall():
|
|
pct = r["with_pdf"] * 100 // max(r["total"], 1)
|
|
print(
|
|
f" {r['typeName']:20s} total={r['total']:>5} pdf={r['with_pdf']:>5} ({pct}%)"
|
|
)
|
|
|
|
# Storage size
|
|
storage = Path(conf_path("storage.zotero"))
|
|
if storage.exists():
|
|
total_bytes = sum(f.stat().st_size for f in storage.rglob("*") if f.is_file())
|
|
print(f"\nZotero storage size: {total_bytes / 1024 / 1024:.1f} MB")
|
|
|
|
con.close()
|
|
print("\nTo retrieve PDFs: open Zotero, select 'Skin Substitutes' collection,")
|
|
print("select all items (Ctrl+A), right-click → Find Available PDF.")
|
|
print("scipdf plugin (v8.0.4) will try Unpaywall then 7 SciHub mirrors.")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|