LTC timecode goes to the other campuses' plans too: Apollo Beach, Southcoast, Woonsocket
The TIMECODE key now reads each campus's plan for the same weekend, pairs its songs with North Attleboro's by title in North Attleboro's order (a song out of order, or one NA does not play, is left alone and said so), checks their LTC notes, and the hold writes the timecode into every campus's plan. ready.ltc_campuses lists the campuses (name, two letters, PCO service type). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+71
-9
@@ -201,9 +201,12 @@ def pick_plan(plans, now):
|
||||
return (upcoming[0][1], upcoming[0][0]) if upcoming else (None, None)
|
||||
|
||||
|
||||
def pco_plan(rc, now):
|
||||
"""This weekend's plan: {"id", "title", "dates", "time", "items": [...], "team": [...]}."""
|
||||
def pco_plan(rc, now, service_type=None, team=True):
|
||||
"""This weekend's plan: {"id", "title", "dates", "time", "items": [...], "team": [...]}.
|
||||
`service_type` reads another campus's plan for the same weekend (its team is not needed)."""
|
||||
auth, st = pco_login(rc)
|
||||
if service_type:
|
||||
st = str(service_type)
|
||||
try:
|
||||
future, _ = pco_get(auth, "/service_types/%s/plans?filter=future&order=sort_date&per_page=4" % st, pages=1)
|
||||
past, _ = pco_get(auth, "/service_types/%s/plans?filter=past&order=-sort_date&per_page=2" % st, pages=1)
|
||||
@@ -227,7 +230,7 @@ def pco_plan(rc, now):
|
||||
out_items.append({"id": it.get("id"), "note_ids": note_ids, "title": a.get("title") or "", "type": a.get("item_type") or "",
|
||||
"position": a.get("service_position") or "", "description": a.get("description") or "",
|
||||
"notes": ns})
|
||||
members, inc = pco_get(auth, "/service_types/%s/plans/%s/team_members?include=team&per_page=100" % (st, pid))
|
||||
members, inc = pco_get(auth, "/service_types/%s/plans/%s/team_members?include=team&per_page=100" % (st, pid)) if team else ([], [])
|
||||
teams = {i["id"]: (i.get("attributes") or {}).get("name") for i in inc if i.get("type") == "Team"}
|
||||
team = []
|
||||
for m in members:
|
||||
@@ -637,6 +640,46 @@ def song_ltc(rc, plan, look):
|
||||
return out
|
||||
|
||||
|
||||
def match_in_order(na_songs, campus_songs):
|
||||
"""Pair a campus's songs with the home campus's by title, keeping the home order: a campus song
|
||||
whose place breaks that order (or that the home campus does not play) is skipped, that song only.
|
||||
Returns ([(campus item, home song)], [skipped titles])."""
|
||||
pairs, skipped, last = [], [], -1
|
||||
for it in campus_songs:
|
||||
idx = next((i for i, s in enumerate(na_songs) if i > last and same(it["title"], s["title"])), None)
|
||||
if idx is None:
|
||||
skipped.append(it["title"])
|
||||
continue
|
||||
pairs.append((it, na_songs[idx]))
|
||||
last = idx
|
||||
return pairs, skipped
|
||||
|
||||
|
||||
def campus_ltc(rc, na_songs, campus, plan):
|
||||
"""One other campus's plan against the home timecode: its songs in home order, each with the
|
||||
timecode it should carry and what its LTC note says now."""
|
||||
note = rc.get("ltc_note", "LTC")
|
||||
pairs, skipped = match_in_order(na_songs, plan_songs(rc, plan))
|
||||
songs = [{"item": it, "title": it["title"], "tracks": s["tracks"], "pco": (it["notes"].get(note) or "").strip()} for it, s in pairs]
|
||||
return {"name": campus.get("name") or campus.get("short") or str(campus.get("service_type")), "short": campus.get("short") or (campus.get("name") or "?")[:2].upper(),
|
||||
"service_type": str(campus.get("service_type")), "plan_id": plan["id"], "dates": plan.get("dates", ""), "songs": songs, "skipped": skipped}
|
||||
|
||||
|
||||
def eval_campus_ltc(rc, result, line):
|
||||
"""The other campus's notes must say the home timecode for every matched song."""
|
||||
tag = result["short"]
|
||||
for s in result["songs"]:
|
||||
if not s["pco"]:
|
||||
line.fault(ERR, "%s NO LTC %s" % (tag, short(s["title"], 12)), "%s: %s has no %s note; the tracks start it at %s" % (
|
||||
result["name"], s["title"], rc.get("ltc_note", "LTC"), s["tracks"]))
|
||||
elif s["pco"] != s["tracks"]:
|
||||
line.fault(ERR, "%s LTC WRONG %s" % (tag, short(s["title"], 10)), "%s: %s says %s, the tracks start it at %s" % (
|
||||
result["name"], s["title"], s["pco"], s["tracks"]))
|
||||
if result["skipped"]:
|
||||
line.notes.append("%s: not in this campus's order, left alone: %s" % (result["name"], ", ".join(result["skipped"])))
|
||||
return line
|
||||
|
||||
|
||||
def eval_ltc(rc, plan, look, line):
|
||||
"""PCO's LTC note for every song must be the timecode the tracks play at that song's start."""
|
||||
for s in song_ltc(rc, plan, look):
|
||||
@@ -950,14 +993,14 @@ class Line:
|
||||
"""One checklist line: its faults [(level, msg, detail)] and, when it is fine, what to show."""
|
||||
|
||||
def __init__(self):
|
||||
self.faults, self.info = [], ""
|
||||
self.faults, self.info, self.notes = [], "", []
|
||||
|
||||
def fault(self, level, msg, detail=""):
|
||||
if all(m != msg for _, m, _ in self.faults):
|
||||
self.faults.append((level, msg, detail))
|
||||
|
||||
def as_dict(self):
|
||||
return {"faults": [[lv, m] for lv, m, _ in self.faults], "details": [d for _, _, d in self.faults if d],
|
||||
return {"faults": [[lv, m] for lv, m, _ in self.faults], "details": [d for _, _, d in self.faults if d] + list(self.notes),
|
||||
"info": self.info}
|
||||
|
||||
|
||||
@@ -966,7 +1009,7 @@ def look(cfg, now=None, data_dir=None, sources=None):
|
||||
`sources` replaces the readers (tests)."""
|
||||
rc = cfg.get("ready") or {}
|
||||
now = now or time.time()
|
||||
src = dict(pco=lambda: pco_plan(rc, now), prop=lambda plan: prop_look(rc, plan),
|
||||
src = dict(pco=lambda: pco_plan(rc, now), pco_campus=lambda st: pco_plan(rc, now, st, team=False), prop=lambda plan: prop_look(rc, plan),
|
||||
ableton=lambda plan: ableton_look(cfg, rc, plan),
|
||||
lineup=lambda: http_json(rc.get("lineup_url", "").rstrip("/") + "/state", timeout=6),
|
||||
resolume=lambda: http_json(rc.get("resolume_url", "").rstrip("/") + "/api/v1/composition", timeout=6),
|
||||
@@ -1023,9 +1066,23 @@ def look(cfg, now=None, data_dir=None, sources=None):
|
||||
lines.update(eval_ableton(rc, plan, al))
|
||||
if rc.get("ltc_note", "LTC"):
|
||||
eval_ltc(rc, plan, al, lines.setdefault("timecode", Line()))
|
||||
LAST["ltc"] = [dict(s, item={"id": s["item"]["id"], "note_ids": s["item"]["note_ids"]})
|
||||
for s in song_ltc(rc, plan, al)]
|
||||
na_songs = song_ltc(rc, plan, al)
|
||||
LAST["ltc"] = [dict(s, item={"id": s["item"]["id"], "note_ids": s["item"]["note_ids"]}) for s in na_songs]
|
||||
LAST["service_type"], LAST["plan_id"] = plan.get("service_type"), plan["id"]
|
||||
others = []
|
||||
for campus in rc.get("ltc_campuses") or []:
|
||||
if not campus.get("service_type"):
|
||||
continue
|
||||
try:
|
||||
cplan = src["pco_campus"](campus["service_type"])
|
||||
res = campus_ltc(rc, na_songs, campus, cplan)
|
||||
eval_campus_ltc(rc, res, lines["timecode"])
|
||||
others.append(dict(res, songs=[dict(s, item={"id": s["item"]["id"], "note_ids": s["item"]["note_ids"]}) for s in res["songs"]]))
|
||||
except Exception as e: # noqa: BLE001 - one campus's plan down must not hide the home one
|
||||
tag = campus.get("short") or (campus.get("name") or "?")[:2].upper()
|
||||
msg = str(e) if isinstance(e, ReadyError) else "PCO UNREACHABLE"
|
||||
lines["timecode"].fault(WARN, "%s %s" % (tag, msg), "%s: %s" % (campus.get("name") or tag, e))
|
||||
LAST["ltc_other"] = others
|
||||
if rc.get("locations"):
|
||||
lines["track_bundle"] = eval_track_bundle(rc, al)
|
||||
except Exception as e: # noqa: BLE001
|
||||
@@ -1140,7 +1197,12 @@ def run_action(cfg, action, chk=None, sleep=time.sleep):
|
||||
comp = http_json(base, timeout=6) or {}
|
||||
return True # an empty column is filled by hand in Resolume
|
||||
if action == "ltc_to_pco":
|
||||
return write_ltc(rc, LAST.get("ltc") or [], LAST.get("service_type"), LAST.get("plan_id"))
|
||||
result = write_ltc(rc, LAST.get("ltc") or [], LAST.get("service_type"), LAST.get("plan_id"))
|
||||
for other in LAST.get("ltc_other") or []:
|
||||
r = write_ltc(rc, other["songs"], other["service_type"], other["plan_id"])
|
||||
if r is not True and result is True:
|
||||
result = "%s: %s" % (other["short"], r)
|
||||
return result
|
||||
if action == "copy_tracks":
|
||||
plan = LAST.get("plan")
|
||||
if not plan:
|
||||
|
||||
Reference in New Issue
Block a user