#!/usr/bin/env python3 """Check catalog homepage + PDF URI targets after a site build.""" from __future__ import annotations import sys from html.parser import HTMLParser from pathlib import Path from urllib.parse import unquote, urljoin, urlparse ROOT = Path(__file__).resolve().parents[1] SITE = ROOT / "site" WEB = "https://zapier.georgelambert.org/" class Anchors(HTMLParser): def __init__(self) -> None: super().__init__() self.hrefs: list[str] = [] def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: if tag == "a": d = dict(attrs) if d.get("href"): self.hrefs.append(d["href"] or "") def local_ok(url: str) -> bool: if url.startswith(WEB): rel = url[len(WEB) :].split("#", 1)[0] dest = SITE / rel if dest.is_file(): return True if dest.is_dir() and any( (dest / n).exists() for n in ("index.html", "index.pdf", "README.html", "README.pdf") ): return True # yaml/svg/png on homepage return False if url.startswith("file:"): return False return True # http(s) off-site; homepage checker uses GET separately def main() -> int: idx = SITE / "index.html" if not idx.exists(): print("no site/index.html — run scripts/build-docs-site.py") return 1 p = Anchors() p.feed(idx.read_text(encoding="utf-8", errors="replace")) broken: list[str] = [] for h in sorted(set(p.hrefs)): if h.startswith("#") or h.startswith("mailto:"): continue if h.startswith("https://git.georgelambert.org"): continue if h.startswith("https://") and not h.startswith(WEB): continue if h.startswith(WEB): if not local_ok(h): broken.append(f"homepage {h}") continue rel = h.lstrip("/") dest = SITE / rel if not dest.exists(): broken.append(f"homepage missing {h}") from pypdf import PdfReader n_file = 0 for pdf in SITE.rglob("*.pdf"): try: reader = PdfReader(str(pdf)) except Exception as exc: broken.append(f"unreadable {pdf.relative_to(SITE)} {exc}") continue for page in reader.pages: annots = page.get("/Annots") if not annots: continue for annot in annots: obj = annot.get_object() action = obj.get("/A") if not action: continue uri = action.get("/URI") if not uri: continue raw = str(uri) if raw.startswith("file:"): n_file += 1 broken.append(f"file URI {pdf.relative_to(SITE)} -> {raw[:120]}") continue if raw.startswith(WEB): path = raw.split("#", 1)[0] if "/research/" in path: continue if not local_ok(path): broken.append(f"pdf missing {pdf.relative_to(SITE)} -> {raw}") print(f"file:// leftover {n_file}") print(f"broken {len(broken)}") for b in broken[:80]: print(" ", b) if len(broken) > 80: print(f" … {len(broken) - 80} more") return 1 if broken else 0 if __name__ == "__main__": sys.exit(main())