#!/usr/bin/env python3 """Phase 4 — the morning after. 1. B1's held app AS A HOUSEHOLD WOULD FIND IT AT BREAKFAST: the app page, the dashboard, the backups page, the event list, the mail — in BOTH languages, quoted. Is there ONE sentence that says what happened, since when, which copy holds what, and what to press? Scored against `09` §3b Q4's recommended option. 2. Every app still on 9202 healthy through its own front door; every badge true AFTER a rescan (R-607 — a badge read before a rescan is not a measurement). """ import json, os, re, sys, time HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) import walk as w # noqa: E402 OUT = os.path.join(HERE, "bad-days", "P4-morning-after") HELD = "glance" SUBS = {"glance": "dashboard", "uptime-kuma": "status", "wishlist": "wishes", "vikunja": "tasks", "bentopdf": "pdf", "privatebin": "paste", "opengist": "gist", "docmost": "docs", "tandoor": "recipes", "zipline": "img", "gitea": "git", "vaultwarden": "vault", "romm": "arcade", "mealie": "mealie"} def text_of(html): t = re.sub(r"", " ", html, flags=re.S) t = re.sub(r"", " ", t, flags=re.S) return re.sub(r"\s+", " ", re.sub(r"<[^>]+>", " ", t)).strip() def quote(page_path, keys, label): got = {} for lang, sfx in (("hu", ""), ("en", "?lang=en")): sep = "&" if "?" in page_path else "?" p = page_path + (sfx.replace("?", sep) if sfx else "") html = w.page(p) t = text_of(html) hits = [s.strip() for s in re.split(r"(?<=[.!?]) ", t) if any(k in s.lower() for k in keys)] got[lang] = hits[:10] open(f"{OUT}/{label}-{lang}.html", "w").write(html) return got def main(): os.makedirs(OUT, exist_ok=True) w.login() w.say("==== Phase 4: the morning after") rec = {} st = w.stack(HELD) rec["held_app_state"] = { "name": HELD, "state": st.get("state"), "updating": st.get("updating"), "update_phase": st.get("update_phase"), "update_phase_label": st.get("update_phase_label"), "update_error": st.get("update_error"), "hold_reason": st.get("hold_reason"), "observables": w.observables(HELD)} w.say(f" [1] {HELD}: state={st.get('state')} phase={st.get('update_phase')} " f"hold={st.get('hold_reason')!r}") w.say(f" error = {st.get('update_error')!r}") KEYS = ("friss", "update", "vissza", "restore", "ment", "backup", "hib", "error", "megsz", "nem ", "masolat", "másolat", "copy", "meghajt", "drive") w.say(" [1] the four surfaces a household would look at, both languages") rec["app_page"] = quote(f"/apps/{HELD}", KEYS, "app-page") rec["dashboard"] = quote("/dashboard", KEYS, "dashboard") rec["launcher"] = quote("/", KEYS, "launcher") rec["backups_page"] = quote("/backups", KEYS, "backups") rec["backups_apps_page"] = quote("/backups/apps", KEYS, "backups-apps") for k in ("app_page", "dashboard", "launcher", "backups_page", "backups_apps_page"): w.say(f" {k:18} HU {rec[k]['hu'][:3]}") w.say(f" {k:18} EN {rec[k]['en'][:3]}") # THE FOUR THINGS Q4's recommended option promises the household are told page_hu = " ".join(rec["app_page"]["hu"]).lower() page_en = " ".join(rec["app_page"]["en"]).lower() rec["q4_score"] = { "says WHAT happened": any(k in page_hu for k in ("friss", "hib", "megsz")), "says SINCE WHEN": bool(re.search(r"\d", " ".join(rec["app_page"]["hu"]))), "says WHICH COPY HOLDS WHAT": any(k in page_hu for k in ("meghajt", "masolat", "másolat", "tavoli", "távoli", "sajat", "saját", "helyi")), "says WHAT TO PRESS": any(k in page_hu for k in ("vissza", "ment", "nyomd", "gomb")), "the same in ENGLISH": bool(page_en) and any(k in page_en for k in ("update", "restore", "backup", "copy")), "IS THE SENTENCE ENGLISH ON THE ENGLISH PAGE (R-606)": None, } # R-606: the update sentence is a finished Hungarian string, not a key. Look for Hungarian # accented words or ASCII Hungarian fragments surviving on the EN page — positive and negative. hu_frags = ["friss", "megszak", "alkalmaz", "verzio", "verzió", "vissza"] en_only = ["update", "restore", "backup", "version"] hu_on_en = [f for f in hu_frags if f in page_en] en_on_en = [f for f in en_only if f in page_en] rec["q4_score"]["IS THE SENTENCE ENGLISH ON THE ENGLISH PAGE (R-606)"] = { "hungarian_fragments_found_on_the_english_page": hu_on_en, "english_fragments_found (control)": en_on_en, } for k, v in rec["q4_score"].items(): w.say(f" Q4 · {k}: {v}") # 2. every app still on the box, through its own front door, and every badge after a rescan w.say(" [2] every app still on the box — front door and badge, after a rescan") w.ctl("POST", "/api/sync") time.sleep(2) w.ctl("POST", "/api/stacks/rescan") time.sleep(3) _, d = w.ctl("GET", "/api/stacks") apps = [] for s in (d.get("data") or []): if not s.get("deployed"): continue n = s["name"] sub = SUBS.get(n, n) rc, code, _ = w.app_curl(sub, "/", timeout=20) inst = {k: (v.get("ref") if isinstance(v, dict) else v) for k, v in ((s.get("app_config") or {}).get("installed_images") or {}).items()} cat = s.get("catalog_images") or {} bdg = w.badges(n) truthful = (inst == cat) == any("naprak" in (x["text"] or "").lower() for x in bdg["hu"]) if bdg["hu"] else None apps.append({"name": n, "state": s.get("state"), "front_door_http": code, "installed": inst, "catalog": cat, "badge_hu": bdg["hu"], "badge_en": bdg["en"], "badge_matches_the_refs": truthful}) w.say(f" {n:16} state={str(s.get('state')):10} door={code:4} " f"badge={[x['text'] for x in bdg['hu']]} refs_equal={inst==cat}") rec["apps_on_the_box"] = apps json.dump(rec, open(f"{OUT}/result.json", "w"), indent=2, ensure_ascii=False) open(f"{OUT}/log.txt", "w").write("\n".join(w.LOG) + "\n") w.say(f" written -> {OUT}/result.json") if __name__ == "__main__": main()