Files
stack/docs/scripts/extract_citations.py
kert ec44e937f5 add Docusaurus docs generation: API reference, CLI, REST API, citations, DAG viz
Wire cli/docs.py build/serve/generate commands to run extraction scripts
and Docusaurus. Add extract_cli.py (27 CLI docs from typer help),
extract_openapi.py (8 REST API docs from FastAPI schema),
extract_citations.py (14 pipeline pages with bib references),
extract_dag.py (pipeline + table lineage Mermaid diagrams).
Closes #26, #27, #28, #29, #30.
2026-03-12 17:38:19 -04:00

127 lines
3.7 KiB
Python

"""Extract Zotero-sourced regulatory citations for pipeline docs.
For each pipeline, finds bib items tagged with the pipeline's module
namespace and generates a citations page with linked references.
Usage::
uv run python docs/scripts/extract_citations.py
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.path.insert(0, str(Path("src").resolve()))
OUT_DIR = Path("docs/docs/pipelines")
def main() -> None:
from conf import path as _conf_path
bib_db = _conf_path("db.bib")
if not bib_db.exists():
print("bib.sqlite not found, skipping citations")
return
from aco.pipe import registry
from bib.store import Store
store = Store(str(bib_db))
if OUT_DIR.exists():
import shutil
shutil.rmtree(OUT_DIR)
OUT_DIR.mkdir(parents=True, exist_ok=True)
cat_json = OUT_DIR / "_category_.json"
cat_json.write_text('{"label": "Pipelines", "position": 12}\n')
count = 0
# Overview page
overview = [
"---\ntitle: Pipelines\nsidebar_position: 1\n---\n",
"# Pipelines\n",
"| Pipeline | Steps | Description |",
"|----------|-------|-------------|",
]
for name, pipeline in sorted(registry.items()):
overview.append(f"| [{name}](./{name}) | {len(pipeline)} | |")
overview.append("")
(OUT_DIR / "index.md").write_text("\n".join(overview))
count += 1
# Per-pipeline pages with citations
all_items = store.list_items()
for pos, (name, pipeline) in enumerate(sorted(registry.items()), start=2):
lines = [
f"---\ntitle: {name}\nsidebar_position: {pos}\n---\n",
f"# {name}\n",
f"**Steps:** {len(pipeline)}\n",
]
# Step list
lines.append("## Steps\n")
for i, expr in enumerate(pipeline.exprs, 1):
doc = expr.fn.__doc__ or ""
doc_line = doc.strip().split("\n")[0] if doc else ""
lines.append(f"{i}. **{expr.name}**{f'{doc_line}' if doc_line else ''}")
lines.append("")
# Find citations: items tagged with module:{pipeline_name} or
# related tags
module_tag = f"module:{name}"
cited_items = [
item for item in all_items if module_tag in item.tags
]
# Also check for aco-related tags
if not cited_items:
aco_tag = "module:aco"
cited_items = [
item for item in all_items if aco_tag in item.tags
][:5] # Limit to top 5 general references
if cited_items:
lines.append("## Regulatory References\n")
for item in cited_items:
title = item.title
url = item.url
date = item.date_published or ""
institution = item.institution or ""
if url:
lines.append(f"- [{title}]({url})")
else:
lines.append(f"- {title}")
meta_parts = []
if institution:
meta_parts.append(institution)
if date:
meta_parts.append(date)
if meta_parts:
lines.append(f" *{' | '.join(meta_parts)}*")
abstract = item.abstract or ""
if abstract:
short = abstract[:200] + "..." if len(abstract) > 200 else abstract
lines.append(f" {short}")
lines.append("")
out_path = OUT_DIR / f"{name}.md"
out_path.write_text("\n".join(lines))
count += 1
print(f" {name} -> {out_path}")
print(f"\nDone: {count} pipeline docs written to {OUT_DIR}")
if __name__ == "__main__":
main()