"""Loading challenge definitions, solution sources, and submission files.""" from __future__ import annotations import json import subprocess from dataclasses import dataclass from pathlib import Path @dataclass class Source: """One place solutions come from: a registered repository clone, or a local directory.""" label: str | None # short name used to namespace submission ids; None for a single local directory dir: Path # directory holding /.json repo: str | None = None # "/" url: str | None = None # web URL of the repository branch: str | None = None root: Path | None = None # clone root, to express files as repository-relative paths description: str | None = None def blob_url(self, path: Path) -> str | None: if self.url is None or self.root is None: return None try: rel = Path(path).resolve().relative_to(self.root.resolve()) except ValueError: return None return f"{self.url}/blob/{self.branch or 'main'}/{rel.as_posix()}" def load_challenges(challenges_dir: Path) -> dict[str, dict]: out = {} for p in sorted(Path(challenges_dir).glob("*/challenge.json")): with open(p) as f: ch = json.load(f) ch["_dir"] = p.parent out[ch["challenge_id"]] = ch return out def read_registry(registry_path: Path) -> list[dict]: with open(registry_path) as f: reg = json.load(f) repos = reg["repos"] for r in repos: r.setdefault("branch", "main") r.setdefault("path", "solutions") r["label"] = r.get("label") or r["repo"].split("/")[-1] labels = [r["label"] for r in repos] if len(set(labels)) != len(labels): raise ValueError("registry labels are not unique; give colliding entries an explicit 'label'") return repos def clone_dir(clones: Path, entry: dict) -> Path: return Path(clones) / entry["label"] def fetch_registry(registry_path: Path, clones: Path, vault_url: str) -> list[Source]: """Clone or update every registered repository (shallow, one branch). Anonymous, so public repositories only.""" sources = [] clones.mkdir(parents=True, exist_ok=True) for entry in read_registry(registry_path): dest = clone_dir(clones, entry) remote = f"{vault_url.rstrip('/')}/{entry['repo']}" if (dest / ".git").exists(): subprocess.run(["git", "-C", str(dest), "fetch", "--depth", "1", "origin", entry["branch"]], check=True) subprocess.run(["git", "-C", str(dest), "checkout", "-q", "--force", "FETCH_HEAD"], check=True) else: subprocess.run(["git", "clone", "-q", "--depth", "1", "--branch", entry["branch"], remote, str(dest)], check=True) # bring in LFS objects when git-lfs is available; otherwise pointer files stay and are tolerated downstream if subprocess.run(["git", "lfs", "version"], capture_output=True).returncode == 0: lfs = subprocess.run(["git", "-C", str(dest), "lfs", "pull"], capture_output=True, text=True) if lfs.returncode != 0: print(f"warning: git lfs pull failed for {entry['repo']}: {lfs.stderr.strip()[:200]}") sha = subprocess.run(["git", "-C", str(dest), "rev-parse", "HEAD"], check=True, capture_output=True, text=True).stdout.strip() print(f"{entry['repo']}@{entry['branch']} -> {dest} ({sha[:10]})") sources.append(source_from_entry(entry, clones, vault_url)) return sources def source_from_entry(entry: dict, clones: Path, vault_url: str) -> Source: root = clone_dir(clones, entry) return Source( label=entry["label"], dir=root / entry["path"], repo=entry["repo"], url=f"{vault_url.rstrip('/')}/{entry['repo']}", branch=entry["branch"], root=root, description=entry.get("description"), ) def registry_sources(registry_path: Path, clones: Path, vault_url: str) -> list[Source]: return [source_from_entry(e, clones, vault_url) for e in read_registry(registry_path)] def local_source(solutions_dir: Path) -> Source: return Source(label=None, dir=Path(solutions_dir)) def submission_key(sub: dict) -> str: label = sub.get("_source").label if sub.get("_source") else None return f"{label}/{sub['submission_id']}" if label else sub["submission_id"] def load_submissions(sources: list[Source]) -> list[dict]: """Every submission JSON under //*.json, with _source, _path, _dir_challenge, _key set.""" out = [] for src in sources: if not src.dir.exists(): print(f"warning: solutions directory {src.dir} does not exist") continue for p in sorted(src.dir.glob("*/*.json")): with open(p) as f: sub = json.load(f) sub["_source"] = src sub["_path"] = p sub["_dir_challenge"] = p.parent.name sub["_key"] = submission_key(sub) out.append(sub) return out def public_fields(sub: dict) -> dict: return {k: v for k, v in sub.items() if not k.startswith("_")}