Every campus matches NA, tailored: Restart page, Companion-Mac programs, NA's auto-fix hours, nightly backups, restart from NA
- Auto-setup: programs on the Companion Mac itself (ProPresenter at Woonsocket) get a local process check and local start/restart fixes; every campus gets a Restart page (keys arrive as computers join); NA's auto-fix hours (Tue, Wed, Sun); a program found after its Companion connection joins that key instead of making a second one. settings.campus rebuild remakes a campus from what is found now, keeping its name, PIN and pages; upgrade-companion brings Companion to 5.0.7 from NA's copy (/campus/companion.zip), as Woonsocket was; setup stops with that advice on an older one. - NA's CAMPUSES page has a RESTART key per campus; the CAMPUS page shows that campus's Restart page and a hold there restarts it at that campus. - Backups: backup/backup-campuses.sh, nightly 04:30 on NA (installed everywhere, acts on the hub), into Drive > Waters Creative > Apps > Status Panel Backups/<campus>, 30 kept, alert email via the checklist's alert.js. Campuses report the account to back up with. sites/ws/config.json in the repo. - Woonsocket rebuilt: 9 keys (PROP with ProPresenter on its Mac), Restart page 20 (SUPERRACK, VISTA, ABLETON, PROP), auto-fix on NA's hours; all OK. Both campuses backed up to Drive by the job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+54
-3
@@ -142,6 +142,34 @@ class TestBuild(unittest.TestCase):
|
||||
chk = {"type": "midi_peer", "host": "tracks", "session": "WS Tracks", "peer": "Lighting"}
|
||||
self.assertEqual(sp.check_midi_peer(chk, snap, {"now": time.time()})[0][1].level, sp.OK) # no state: older macOS, configured
|
||||
|
||||
def test_a_program_on_the_companion_mac_gets_a_local_check_and_fixes(self):
|
||||
"""Woonsocket: ProPresenter runs on the Mac that runs Companion."""
|
||||
inv = small()
|
||||
inv["computers"] = {}
|
||||
exe = "/Applications/ProPresenter.app/Contents/MacOS/ProPresenter"
|
||||
inv["ingest"].update(procs=["ProPresenter"], paths={"ProPresenter": "/Applications/ProPresenter.app"}, exes={"ProPresenter": exe})
|
||||
inv["connections"] = [dict(c, host="10.4.1.1") if c["label"] == "ProP" else c for c in inv["connections"]]
|
||||
cfg, _ = ac.build(BASE, inv, {"id": "ws", "name": "Woonsocket"}, "1234")
|
||||
self.assertEqual(schema.validate(cfg)[0], [])
|
||||
prop = next(t for t in cfg["tiles"] if t["label"] == "PROP")
|
||||
self.assertEqual([(c["type"], c.get("match") or c.get("conn")) for c in prop["checks"]], [("conn", "ProP"), ("process", exe)])
|
||||
fix = cfg["fixes"][prop["checks"][1]["fix"]]
|
||||
self.assertEqual((fix["kind"], fix["run"]), ("local", "pgrep -xq ProPresenter || open /Applications/ProPresenter.app"))
|
||||
self.assertIn(prop["checks"][1]["fix"], cfg["auto_fix"]["fixes"])
|
||||
self.assertEqual([k["label"] for k in cfg["restart_page"]["keys"]], ["PROP"])
|
||||
self.assertEqual(cfg["fixes"][cfg["restart_page"]["keys"][0]["fix"]]["kind"], "local")
|
||||
|
||||
def test_every_campus_gets_a_restart_page_and_north_attleboros_auto_fix_hours(self):
|
||||
inv = small()
|
||||
inv["computers"] = {}
|
||||
cfg, _ = ac.build(BASE, inv, {"id": "sc", "name": "Southcoast"}, "1234")
|
||||
self.assertEqual(cfg["restart_page"]["keys"], []) # computers join later: their keys come then
|
||||
na = json.load(open(os.path.join(ROOT, "config.json")))
|
||||
self.assertEqual(cfg["auto_fix"]["windows"], na["auto_fix"]["windows"])
|
||||
self.assertEqual(cfg["service_windows"], na["service_windows"])
|
||||
self.assertEqual(cfg["nav_keys"], na["nav_keys"])
|
||||
make_page.build_restart(cfg)
|
||||
|
||||
def test_an_m32_is_called_m32(self):
|
||||
inv = small()
|
||||
inv["connections"] = [c for c in inv["connections"] if c["label"] != "x32"] + [
|
||||
@@ -188,6 +216,21 @@ class TestExtend(unittest.TestCase):
|
||||
again, added2, _ = ac.extend(new, inv, set(self.seen) | set(now_seen))
|
||||
self.assertEqual(added2, [])
|
||||
|
||||
def test_a_program_found_later_joins_its_connections_key(self):
|
||||
"""Woonsocket got a second PROP key when ProPresenter on the Companion Mac was found after the connection."""
|
||||
inv = small()
|
||||
inv["computers"] = {}
|
||||
cfg, seen = ac.build(BASE, inv, {"id": "ws", "name": "Woonsocket"}, "1234")
|
||||
exe = "/Applications/ProPresenter.app/Contents/MacOS/ProPresenter"
|
||||
inv["ingest"].update(procs=["ProPresenter"], paths={"ProPresenter": "/Applications/ProPresenter.app"}, exes={"ProPresenter": exe})
|
||||
inv["connections"] = [dict(c, host="127.0.0.1") if c["label"] == "ProP" else c for c in inv["connections"]]
|
||||
new, added, _ = ac.extend(cfg, inv, seen)
|
||||
self.assertEqual([t["label"] for t in new["tiles"]].count("PROP"), 1)
|
||||
prop = next(t for t in new["tiles"] if t["label"] == "PROP")
|
||||
self.assertEqual([c["type"] for c in prop["checks"]], ["conn", "process"])
|
||||
self.assertIn("PROP checks on the PROP key", added)
|
||||
self.assertEqual(schema.validate(new)[0], [])
|
||||
|
||||
def test_a_key_someone_took_off_is_not_added_back(self):
|
||||
cfg = json.loads(json.dumps(self.cfg))
|
||||
cfg["tiles"] = [t for t in cfg["tiles"] if t["label"] != "X32"]
|
||||
@@ -338,12 +381,17 @@ class TestCampusesView(unittest.TestCase):
|
||||
export = cv.build_overview(na, roster, 98, 99)
|
||||
keys = [(r, c, ctl) for r, row in export["page"]["controls"].items() for c, ctl in row.items() if "'campus " in (ctl.get("options") or {}).get("notes", "")]
|
||||
self.assertEqual([(r, c) for r, c, _ in keys], [("0", "1"), ("0", "2"), ("0", "3")])
|
||||
rs = export["page"]["controls"]["1"]["3"] # WS's RESTART key, under its ALL key
|
||||
acts = rs["steps"]["0"]["action_sets"]["up"]
|
||||
self.assertEqual([a["options"].get("value", {}).get("value") for a in acts[:2]] + [acts[2]["options"]["page"]["value"]], ["WS", "RESTART", 99])
|
||||
self.assertEqual(cv.campus_command([{"id": "rs_prop", "pos": [0, 1]}], "tap 0 1 1000;hold 0 1 1000", 1001, restart=True),
|
||||
["force rs_prop 1000"]) # on its Restart page a hold forces, a tap does nothing
|
||||
self.assertIn("'campus na'", keys[0][2]["options"]["notes"])
|
||||
self.assertIn("page 19", keys[0][2]["options"]["notes"])
|
||||
ws_key = next(k for _, _, k in keys if "'campus ws'" in k["options"]["notes"])
|
||||
acts = ws_key["steps"]["0"]["action_sets"]["up"]
|
||||
self.assertEqual([a["definitionId"] for a in acts], ["custom_variable_set_value", "set_page"])
|
||||
self.assertEqual((acts[0]["options"]["value"]["value"], acts[1]["options"]["page"]["value"]), ("WS", 99))
|
||||
self.assertEqual([a["definitionId"] for a in acts], ["custom_variable_set_value", "custom_variable_set_value", "set_page"])
|
||||
self.assertEqual((acts[0]["options"]["value"]["value"], acts[1]["options"]["value"]["value"], acts[2]["options"]["page"]["value"]), ("WS", "", 99))
|
||||
|
||||
def test_a_campus_tells_the_hub_and_learns_nothing_back(self):
|
||||
from settings import campuses as cv
|
||||
@@ -365,7 +413,10 @@ class TestCampusesView(unittest.TestCase):
|
||||
m = cv.Mirror(FakeSite())
|
||||
data = m.sync_registry()
|
||||
m.poll()
|
||||
self.assertEqual(m.refresh_pages()["list"], {"ws": {"id": "ws", "name": "Woonsocket", "url": "http://10.5.1.1:8020"}})
|
||||
listed = m.refresh_pages()["list"]
|
||||
self.assertEqual({k: {f: v for f, v in e.items() if f != "user"} for k, e in listed.items()},
|
||||
{"ws": {"id": "ws", "name": "Woonsocket", "url": "http://10.5.1.1:8020"}})
|
||||
self.assertTrue(listed["ws"]["user"]) # the account the hub backs it up with
|
||||
finally:
|
||||
cv.http_json, cv.my_url = real, real_url
|
||||
self.assertEqual(list(data["list"]), ["ws"]) # the others are forgotten
|
||||
|
||||
Reference in New Issue
Block a user