"Location", never "campus", in everything people read; SC FOH joined with its MIDI links
- pages (NA 98 Locations / 99 Location, re-imported via PAGES_VERSION), keys, settings page, setup and install messages, alerts, docs, the Apollo Beach kit; python3 -m settings.location and /location/... addresses (the old names still answer for installed Macs) - Apply reads the before settings from the file: an in-place edit skipped the watchdog restart - SC: SUPERRACK (+SoundGrid) and X32 EDIT from SC FOH; FOH TRACKS and TRACKS LIGHTS MIDI links watched Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -160,6 +160,13 @@ class TestPipeline(unittest.TestCase):
|
||||
self.assertEqual(ap.history_config(self.data, entries[0]["id"])["debounce_seconds"], 3) # the version before
|
||||
self.assertEqual(oct(os.stat(os.path.join(self.data, "history", entries[0]["id"] + ".json")).st_mode)[-3:], "600")
|
||||
|
||||
def test_settings_edited_in_place_still_restart_the_watchdog(self):
|
||||
cfg = self.site.load() # the cached object, edited as a script would
|
||||
cfg["debounce_seconds"] = 5
|
||||
self.run_apply(cfg, "in place")
|
||||
names = {s["name"]: s["state"] for s in self.applier.status["steps"]}
|
||||
self.assertEqual(names["restart"], "ok")
|
||||
|
||||
def test_page_only_data_needs_no_restart(self):
|
||||
new = json.loads(json.dumps(self.cfg))
|
||||
new["gear"] = {"UHDJr": {"location": "lobby"}}
|
||||
|
||||
+16
-12
@@ -1,5 +1,5 @@
|
||||
"""Auto-setup for the other campuses: an inventory becomes keys that pass every rule, laid out like
|
||||
North Attleboro's; a running campus only gains what is new (never loses anything, never gets back
|
||||
"""Auto-setup for the other locations: an inventory becomes keys that pass every rule, laid out like
|
||||
North Attleboro's; a running location only gains what is new (never loses anything, never gets back
|
||||
what someone took off); a computer's join report is checked; the lines people paste are right."""
|
||||
import io
|
||||
import json
|
||||
@@ -356,7 +356,11 @@ class TestRoutes(unittest.TestCase):
|
||||
self.assertNotIn("outlets.json", names)
|
||||
status, body = get(self.port, "/campus/install?id=ws")
|
||||
self.assertIn("--site ws", body.decode())
|
||||
self.assertIn("SUPPORT='ssh-ed25519 AAAAC3NzaTESTKEY", body.decode()) # this campus can help the new one
|
||||
self.assertIn("SUPPORT='ssh-ed25519 AAAAC3NzaTESTKEY", body.decode()) # this location can help the new one
|
||||
status, body = get(self.port, "/location/install?id=ws") # the address people see now
|
||||
self.assertEqual(status, 200)
|
||||
self.assertIn("/location/program.tgz", body.decode())
|
||||
self.assertIn("settings.location setup", body.decode())
|
||||
|
||||
def test_look_now_needs_the_code(self):
|
||||
self.assertEqual(get(self.port, "/api/discover/now", {})[0], 401)
|
||||
@@ -367,7 +371,7 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
class TestCampusesView(unittest.TestCase):
|
||||
"""North Attleboro alone shows every campus: a copy of each status page (view only) and a CAMPUSES page."""
|
||||
"""North Attleboro alone shows every location: a copy of each status page (view only) and a LOCATIONS page."""
|
||||
|
||||
def test_the_campus_page_is_view_only_cells_with_a_way_back(self):
|
||||
from settings import campuses as cv
|
||||
@@ -375,10 +379,10 @@ class TestCampusesView(unittest.TestCase):
|
||||
na = json.load(open(os.path.join(ROOT, "config.json")))
|
||||
export = cv.build_view(na, 99, 98)
|
||||
ctrls = export["page"]["controls"]
|
||||
self.assertEqual(export["page"]["name"], "99\\nCampus")
|
||||
self.assertIn("CAMPUSES", json.dumps(ctrls["3"]["0"])) # where ALARMS sits on every status page
|
||||
self.assertIn("$(custom:sp_view)", json.dumps(ctrls["3"]["0"]["style"])) # it says which campus is shown
|
||||
cells = [ctl for row in ctrls.values() for ctl in row.values() if "campus view" in (ctl.get("options") or {}).get("notes", "")]
|
||||
self.assertEqual(export["page"]["name"], "99\\nLocation")
|
||||
self.assertIn("LOCATIONS", json.dumps(ctrls["3"]["0"])) # where ALARMS sits on every status page
|
||||
self.assertIn("$(custom:sp_view)", json.dumps(ctrls["3"]["0"]["style"])) # it says which location is shown
|
||||
cells = [ctl for row in ctrls.values() for ctl in row.values() if "location view" in (ctl.get("options") or {}).get("notes", "")]
|
||||
self.assertEqual(len(cells), 28)
|
||||
acts = cells[0]["steps"]["0"]["action_sets"]
|
||||
self.assertEqual(sorted(acts), ["2000", "down", "up"]) # tap = acknowledge, hold = fix, there
|
||||
@@ -404,16 +408,16 @@ class TestCampusesView(unittest.TestCase):
|
||||
na = json.load(open(os.path.join(ROOT, "config.json")))
|
||||
roster = {"na": {"name": "North Attleboro"}, "ws": {"name": "Woonsocket"}, "ab": {"name": "Apollo Beach"}}
|
||||
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", "")]
|
||||
keys = [(r, c, ctl) for r, row in export["page"]["controls"].items() for c, ctl in row.items() if "'location " 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("'location 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"])
|
||||
ws_key = next(k for _, _, k in keys if "'location 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", "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))
|
||||
@@ -462,7 +466,7 @@ class TestCampusHello(TestRoutes):
|
||||
|
||||
|
||||
class TestStandalone(unittest.TestCase):
|
||||
"""Apollo Beach runs its own panel (2026-10-21): it tells nobody, keeps no campus list, backs itself up."""
|
||||
"""Apollo Beach runs its own panel (2026-10-21): it tells nobody, keeps no location list, backs itself up."""
|
||||
|
||||
def test_a_standalone_campus_tells_nobody_and_is_no_hub(self):
|
||||
from settings import campuses as cv
|
||||
|
||||
@@ -19,7 +19,7 @@ sp.log.addHandler(logging.NullHandler())
|
||||
sp.log.propagate = False
|
||||
|
||||
CONFIG = sp.load_config(os.path.join(ROOT, "config.json"))
|
||||
# The tests pin their own READY hours (the campus sets its own on the settings page).
|
||||
# The tests pin their own READY hours (the location sets its own on the settings page).
|
||||
CONFIG["ready"]["due"] = [{"days": ["sat"], "start": "12:00", "end": "23:59"}, {"days": ["sun"], "start": "00:00", "end": "13:30"}]
|
||||
RC = CONFIG["ready"]
|
||||
WEDNESDAY = datetime(2026, 10, 14, 15, 0).timestamp() # the week before the plan: gaps are amber
|
||||
@@ -556,7 +556,7 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
class TestOtherCampuses(unittest.TestCase):
|
||||
"""The other campuses play the same songs: their plans get North Attleboro's timecode, in its order."""
|
||||
"""The other locations play the same songs: their plans get North Attleboro's timecode, in its order."""
|
||||
CAMPUS = {"name": "Apollo Beach", "short": "AB", "service_type": "1113969"}
|
||||
|
||||
def campus_plan(self, titles, notes=None):
|
||||
@@ -569,7 +569,7 @@ class TestOtherCampuses(unittest.TestCase):
|
||||
na = rd.song_ltc(RC, plan(), ableton())
|
||||
titles = [s["title"] for s in na]
|
||||
self.assertGreaterEqual(len(titles), 3)
|
||||
# the campus plays song 2 first, then 1, then 3: song 1 breaks the order and is skipped; a song NA does not play is skipped too
|
||||
# the location plays song 2 first, then 1, then 3: song 1 breaks the order and is skipped; a song NA does not play is skipped too
|
||||
cp = self.campus_plan([titles[1], titles[0], titles[2], "Something Else"])
|
||||
res = rd.campus_ltc(RC, plan(), na, self.CAMPUS, cp)
|
||||
self.assertEqual([s["title"] for s in res["songs"]], [titles[1], titles[2]])
|
||||
@@ -582,7 +582,7 @@ class TestOtherCampuses(unittest.TestCase):
|
||||
self.assertEqual(rd.eval_campus_ltc(RC, rd.campus_ltc(RC, plan(), na, self.CAMPUS, right), rd.Line()).faults, [])
|
||||
|
||||
def test_a_song_without_home_timecode_is_matched_but_not_faulted_or_written(self):
|
||||
"""The set is not on Tracks yet: the campus's songs are still in order, nothing is wrong at the campus, nothing is written."""
|
||||
"""The set is not on Tracks yet: the location's songs are still in order, nothing is wrong at the location, nothing is written."""
|
||||
p = plan()
|
||||
cp = self.campus_plan([it["title"] for it in rd.plan_songs(RC, p)])
|
||||
res = rd.campus_ltc(RC, p, [], self.CAMPUS, cp)
|
||||
@@ -607,4 +607,4 @@ class TestOtherCampuses(unittest.TestCase):
|
||||
self.assertTrue(rd.run_action(cfg, "ltc_to_pco", sleep=lambda s: None))
|
||||
finally:
|
||||
rd.http_json, rd.pco_get, rd.pco_login = real_http, real_get, real_login
|
||||
self.assertEqual(len([c for c in calls if "/1113969/" in c[1] and c[0] == "POST"]), len(res["songs"])) # one note per campus song
|
||||
self.assertEqual(len([c for c in calls if "/1113969/" in c[1] and c[0] == "POST"]), len(res["songs"])) # one note per location song
|
||||
|
||||
@@ -295,7 +295,7 @@ class TestSchemaMatchesCode(unittest.TestCase):
|
||||
def test_phrases(self):
|
||||
cfg = sp.load_config(os.path.join(ROOT, "config.json"))
|
||||
p = schema.phrases(cfg)
|
||||
self.assertEqual(p["auto_fix"], "may run by itself " + sp.windows_phrase(cfg["auto_fix"]["windows"])) # the campus's own hours
|
||||
self.assertEqual(p["auto_fix"], "may run by itself " + sp.windows_phrase(cfg["auto_fix"]["windows"])) # the location's own hours
|
||||
self.assertEqual(schema.phrases(dict(cfg, auto_fix={"windows": [{"days": ["sun"], "start": "06:30", "end": "13:30"}]}))["auto_fix"],
|
||||
"may run by itself on Sundays 06:30-13:30")
|
||||
self.assertEqual(p["live"], "changes wait while a HyperDeck is recording")
|
||||
|
||||
@@ -227,7 +227,7 @@ class TestServer(unittest.TestCase):
|
||||
self.assertIn("config.json", z.namelist())
|
||||
bundle_cfg = json.loads(z.read("config.json"))
|
||||
self.assertEqual(bundle_cfg["site"]["editing_code"], self.CODE) # the bundle is the real file
|
||||
# a new campus from that bundle: this campus's own site block stays, computers are flagged
|
||||
# a new location from that bundle: this location's own site block stays, computers are flagged
|
||||
other = json.loads(json.dumps(bundle_cfg)); other["site"]["name"] = "Woonsocket"; other["site"]["editing_code"] = "theirs"
|
||||
buf = io.BytesIO()
|
||||
with zipfile.ZipFile(buf, "w") as z2:
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
"""The generated set-up scripts and the campus template: every value comes from the record, the
|
||||
templates hold no site-shaped value, and the empty campus validates and builds its pages."""
|
||||
"""The generated set-up scripts and the location template: every value comes from the record, the
|
||||
templates hold no site-shaped value, and the empty location validates and builds its pages."""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -30,7 +30,7 @@ CONFIG["tiles"] = [dict(t, label="SERVICE") if t["id"] == "service" else t for t
|
||||
# A Sunday 08:00 (inside the service window) and a Wednesday 15:00 (outside it).
|
||||
SUNDAY = datetime(2026, 10, 11, 8, 0).timestamp()
|
||||
CONFIG["auto_fix"]["log_dir"] = tempfile.mkdtemp(prefix="sp-autolog-") # never the real Desktop
|
||||
# ...and their own auto-fix hours, so the campus can change its own on the settings page.
|
||||
# ...and their own auto-fix hours, so the location can change its own on the settings page.
|
||||
CONFIG["auto_fix"]["windows"] = [{"days": ["wed"], "start": "16:00", "end": "21:00"}, {"days": ["sun"], "start": "06:30", "end": "13:30"}]
|
||||
WEDNESDAY = datetime(2026, 10, 7, 15, 0).timestamp()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user