/ challenges-old / cymbal2-solutions
Sign in
challenges-old / cymbal2-solutions
cymbal2-solutions / web / build.py
75 lines · 3.2 KBCodeBlameHistory
38bdd34cymbal2-solutions: Hermite finite element solver (direct and modal) for the cymbal2 challenges, site builder, workflowJeremy Magland 1"""Assemble this repository's static site: a manifest of the submissions, the solution files, and the viewer page."""
2from __future__ import annotations
4import argparse
5import json
6import shutil
7from pathlib import Path
9ROOT = Path(__file__).resolve().parents[1]
10LFS_PREFIX = b"version https://git-lfs.github.com/spec/v1"
13def is_lfs_pointer(p: Path) -> bool:
14 try:
15 with open(p, "rb") as f:
16 return f.read(len(LFS_PREFIX)) == LFS_PREFIX
17 except OSError:
18 return False
21def main():
22 ap = argparse.ArgumentParser()
23 ap.add_argument("--out", type=Path, default=ROOT / "site")
24 args = ap.parse_args()
25 out: Path = args.out
26 if out.exists():
27 shutil.rmtree(out)
28 out.mkdir(parents=True)
29 manifest = {"challenges": {}}
30 for p in sorted((ROOT / "solutions").glob("*/*.json")):
31 sub = json.loads(p.read_text())
32 cid = p.parent.name
33 entry = manifest["challenges"].setdefault(cid, {"submissions": []})
34 rel_dir = p.parent.relative_to(ROOT)
35 for sol in sub["solutions"]:
36 sol["output"] = f"{rel_dir.as_posix()}/{sol['output']}"
37 if "fields" in sol and not sol["fields"]["path"].startswith("http"):
38 local = p.parent / sol["fields"]["path"]
39 sol["fields"]["path"] = f"{rel_dir.as_posix()}/{sol['fields']['path']}"
40 sol["fields"]["available"] = local.exists() and not is_lfs_pointer(local)
41 entry["submissions"].append(sub)
42 shutil.copytree(ROOT / "solutions", out / "solutions")
43 # experiments: wav files with a JSON note beside them, outside the challenges (strike schedules and the like)
44 manifest["experiments"] = []
45 if (ROOT / "experiments").exists():
46 shutil.copytree(ROOT / "experiments", out / "experiments")
47 for j in sorted((ROOT / "experiments").glob("*.json")):
48 meta = json.loads(j.read_text())
49 wav = j.with_suffix(".wav")
50 if wav.exists():
51 manifest["experiments"].append({"id": j.stem, "output": f"experiments/{wav.name}", "meta": meta})
52 from challenge_tools.build import player_version
54 html = (ROOT / "web" / "index.html").read_text().replace('src="player.js"', f'src="player.js?v={player_version()}"')
55 (out / "index.html").write_text(html)
56 shutil.copyfile(ROOT / "web" / "viewer.js", out / "viewer.js")
57 # the shared Web Audio player, from the installed challenge-tools package
58 from challenge_tools.build import player_js
60 (out / "player.js").write_text(player_js())
61 # suggested playback gains and the challenge parameters (strike point, radius), when the challenge repository is checked out beside us (as in the workflow)
62 gains = {}
63 params = {}
64 for cj in sorted((ROOT / "challenge-repo" / "challenges").glob("*/challenge.json")) if (ROOT / "challenge-repo").exists() else []:
65 ch = json.loads(cj.read_text())
66 gains[ch["challenge_id"]] = (ch.get("playback") or {}).get("gain_db")
67 params[ch["challenge_id"]] = {**ch.get("parameters", {}), "strikes": ch.get("strikes", [])}
68 manifest["playback_gain_db"] = gains
69 manifest["parameters"] = params
70 (out / "manifest.json").write_text(json.dumps(manifest, indent=1))
71 print(f"site written to {out}")
74if __name__ == "__main__":
75 main()
↑↓ move↵openescclose