From a0cefe8428b49b8a3330e313c182dca7efa93f04 Mon Sep 17 00:00:00 2001 From: christian Date: Sun, 11 Oct 2026 07:13:19 -0400 Subject: [PATCH] Auto-fix day logs are deleted a calendar month after their date Only files named exactly ' Auto-Fix.log' in that folder; checked when a new day's file starts and when the watchdog starts. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- status_panel.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_status_panel.py | 15 +++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bef55a4..6d059b4 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ During that window, with ALARMS on, the panel fixes a problem by itself once it - At most 2 tries per problem, 90 s apart; then it is yours to hold. - It waits while the network is having a blip, and does nothing to Dante when several devices vanish at once (that is NA Ingest's view or the Dante network, not the Macs). - Never automatic: restarting a running ProPresenter, Bible app, ProdCom (or DVS on NA Ingest, which restarts ProdCom), captions or the bridge, power-cycling a switch port, or turning on a connection someone switched off. `load_config` refuses an auto fix that closes a program. -- **Daily log:** everything auto-fix does is written as it happens to `~/Desktop/Auto-Fix Logs/ Auto-Fix.log` on NA Ingest (e.g. `2026-10-11 Sunday Auto-Fix.log`): the problem, what it did, whether it worked, when the fault cleared, when it gave up or held back. Double-click it to open it in Console, which shows new lines live. +- **Daily log:** everything auto-fix does is written as it happens to `~/Desktop/Auto-Fix Logs/ Auto-Fix.log` on NA Ingest (e.g. `2026-10-11 Sunday Auto-Fix.log`): the problem, what it did, whether it worked, when the fault cleared, when it gave up or held back. Double-click it to open it in Console, which shows new lines live. One file per day; each is deleted a calendar month after its date (the 11 Oct file on 11 Nov). - Press ALARMS to mute the panel for maintenance: that also stops auto-fix. The window and the list are `"auto_fix"` in `config.json`. ## Restart page (page 20) diff --git a/status_panel.py b/status_panel.py index cc21c35..eb63edb 100644 --- a/status_panel.py +++ b/status_panel.py @@ -15,6 +15,7 @@ Stdlib only; runs on the macOS /usr/bin/python3 (3.9). status_panel.py --dry-run run, but never write to Companion """ import argparse +import calendar import http.client import json import logging @@ -585,10 +586,41 @@ class AutoLog: e.g. "~/Desktop/Auto-Fix Logs/2026-10-11 Sunday Auto-Fix.log" (a .log opens in Console, which shows new lines live).""" + NAME = re.compile(r"^(\d{4})-(\d{2})-(\d{2}) [A-Za-z]+ Auto-Fix\.log$") + def __init__(self, directory): self.dir = os.path.expanduser(directory) self.lock = threading.Lock() + def cleanup(self, now): + """Delete day files once they are a calendar month old (the 11 Oct file goes on 11 Nov). + Only files named exactly like these logs are ever touched.""" + today = datetime.fromtimestamp(now).date() + removed = [] + try: + names = os.listdir(self.dir) + except OSError: + return removed + for name in names: + m = self.NAME.match(name) + if not m: + continue + y, mo, d = (int(x) for x in m.groups()) + y2, mo2 = (y + 1, 1) if mo == 12 else (y, mo + 1) + try: + due = datetime(y2, mo2, min(d, calendar.monthrange(y2, mo2)[1])).date() + except ValueError: + continue + if today >= due: + try: + os.remove(os.path.join(self.dir, name)) + removed.append(name) + except OSError as e: + log.warning("auto-fix log cleanup %s: %s", name, e) + if removed: + log.info("auto-fix logs older than a month deleted: %s", ", ".join(sorted(removed))) + return removed + def path(self, now): return os.path.join(self.dir, datetime.fromtimestamp(now).strftime("%Y-%m-%d %A") + " Auto-Fix.log") @@ -600,6 +632,8 @@ class AutoLog: os.makedirs(self.dir, exist_ok=True) path = self.path(now) new = not os.path.exists(path) + if new: + self.cleanup(now) # a new day: drop files a month old with open(path, "a", encoding="utf-8") as f: if new: f.write("Status panel auto-fixes - %s\n" @@ -697,6 +731,8 @@ class Engine: self.auto_tries = {} # fix identity -> [tries, last try time] for the fault now going on self.auto_seen = {} # fix identity -> last time it was in an auto plan self.autolog = AutoLog(self.auto.get("log_dir", "~/Desktop/Auto-Fix Logs")) if self.auto else None + if self.autolog: + self.autolog.cleanup(time.time()) self.auto_watch = {} # "BUTTON FAULT" auto-fix went after -> when (to log when it clears) self.auto_paused = False self.auto_on = False # inside the window (for the "auto-fix on" line) diff --git a/tests/test_status_panel.py b/tests/test_status_panel.py index 35dffb1..d928f22 100644 --- a/tests/test_status_panel.py +++ b/tests/test_status_panel.py @@ -722,6 +722,21 @@ class TestFixes(unittest.TestCase): self.assertIn("-> done, checking it comes back", body[1]) self.assertIn("OK again: ABLETON NOT RUNNING (15 s after the auto-fix)", body[2]) + def test_auto_fix_logs_go_after_a_month(self): + d = tempfile.mkdtemp() + names = ["2026-09-10 Thursday Auto-Fix.log", "2026-09-11 Friday Auto-Fix.log", # a month old + "2026-09-12 Saturday Auto-Fix.log", "2026-10-04 Sunday Auto-Fix.log", # not yet + "notes.txt", "2026-01-01 Thursday Auto-Fix.log.bak"] # not ours + for n in names: + open(os.path.join(d, n), "w").close() + removed = sp.AutoLog(d).cleanup(SUNDAY) # Sunday 11 Oct 2026 + self.assertEqual(sorted(removed), names[:2]) + self.assertEqual(sorted(os.listdir(d)), sorted(names[2:])) + open(os.path.join(d, "2026-01-31 Saturday Auto-Fix.log"), "w").close() # month ends: 28 Feb + self.assertEqual(sp.AutoLog(d).cleanup(datetime(2026, 2, 27, 9).timestamp()), []) + self.assertEqual(sp.AutoLog(d).cleanup(datetime(2026, 2, 28, 9).timestamp()), + ["2026-01-31 Saturday Auto-Fix.log"]) + def test_auto_fix_tries_twice_then_leaves_it_to_you(self): ran, comp = self.auto_setup() self.snap.remote["tracks\0pgrep -xq Live"] = False