"""Assemble this repository's static site: a manifest of the submissions, the solution files, and the viewer page.""" from __future__ import annotations import argparse import json import shutil from pathlib import Path ROOT = Path(__file__).resolve().parents[1] LFS_PREFIX = b"version https://git-lfs.github.com/spec/v1" def is_lfs_pointer(p: Path) -> bool: try: with open(p, "rb") as f: return f.read(len(LFS_PREFIX)) == LFS_PREFIX except OSError: return False def main(): ap = argparse.ArgumentParser() ap.add_argument("--out", type=Path, default=ROOT / "site") args = ap.parse_args() out: Path = args.out if out.exists(): shutil.rmtree(out) out.mkdir(parents=True) manifest = {"challenges": {}} for p in sorted((ROOT / "solutions").glob("*/*.json")): sub = json.loads(p.read_text()) cid = p.parent.name entry = manifest["challenges"].setdefault(cid, {"submissions": []}) rel_dir = p.parent.relative_to(ROOT) for sol in sub["solutions"]: sol["output"] = f"{rel_dir.as_posix()}/{sol['output']}" if "fields" in sol and not sol["fields"]["path"].startswith("http"): local = p.parent / sol["fields"]["path"] sol["fields"]["path"] = f"{rel_dir.as_posix()}/{sol['fields']['path']}" sol["fields"]["available"] = local.exists() and not is_lfs_pointer(local) entry["submissions"].append(sub) shutil.copytree(ROOT / "solutions", out / "solutions") # experiments: wav files with a JSON note beside them, outside the challenges (strike schedules and the like) manifest["experiments"] = [] if (ROOT / "experiments").exists(): shutil.copytree(ROOT / "experiments", out / "experiments") for j in sorted((ROOT / "experiments").glob("*.json")): meta = json.loads(j.read_text()) wav = j.with_suffix(".wav") if wav.exists(): manifest["experiments"].append({"id": j.stem, "output": f"experiments/{wav.name}", "meta": meta}) from challenge_tools.build import player_version html = (ROOT / "web" / "index.html").read_text().replace('src="player.js"', f'src="player.js?v={player_version()}"') (out / "index.html").write_text(html) shutil.copyfile(ROOT / "web" / "viewer.js", out / "viewer.js") # the shared Web Audio player, from the installed challenge-tools package from challenge_tools.build import player_js (out / "player.js").write_text(player_js()) # suggested playback gains and the challenge parameters (strike point, radius), when the challenge repository is checked out beside us (as in the workflow) gains = {} params = {} for cj in sorted((ROOT / "challenge-repo" / "challenges").glob("*/challenge.json")) if (ROOT / "challenge-repo").exists() else []: ch = json.loads(cj.read_text()) gains[ch["challenge_id"]] = (ch.get("playback") or {}).get("gain_db") params[ch["challenge_id"]] = ch.get("parameters", {}) manifest["playback_gain_db"] = gains manifest["parameters"] = params (out / "manifest.json").write_text(json.dumps(manifest, indent=1)) print(f"site written to {out}") if __name__ == "__main__": main()