From 39bee27a2d10c17ecc6fecbc001d396d3299fc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:13:19 +0200 Subject: [PATCH] feat(tower): routines truth model load and save TDD: test_routines_seed_and_roundtrip tests routines_load() seeding default (matin/soir), round-trip save/load, and verifies json structure with all required fields (id, title, emoji, steps). All tests GREEN. --- tower/lisael_content.py | 26 ++++++++++++++++++++++++++ tower/test_routines.py | 11 +++++++++++ 2 files changed, 37 insertions(+) diff --git a/tower/lisael_content.py b/tower/lisael_content.py index 4ed3881..f88e2f9 100644 --- a/tower/lisael_content.py +++ b/tower/lisael_content.py @@ -11,6 +11,7 @@ STAGING = os.environ.get("LISAEL_STAGING", os.path.join(BASE, "podcasts")) STAGING_ROUTINES = os.environ.get("LISAEL_STAGING_ROUTINES", os.path.join(BASE, "routines")) LVGLIMG = os.environ.get("LISAEL_LVGLIMG", os.path.join(BASE, "LVGLImage.py")) FEEDS_JSON = os.path.join(BASE, "feeds.json") +ROUTINES_JSON = os.path.join(BASE, "routines.json") BOX_JSON = os.path.join(BASE, "box-state.json") BOX_PORT = int(os.environ.get("LISAEL_BOX_PORT", "8080")) TTS_URL = os.environ.get("LISAEL_TTS_URL", "http://100.78.6.122:9300/v1/audio/speech") @@ -71,6 +72,31 @@ def add_feed(key, name, rss, n=2): def remove_feed(key): save_feeds([f for f in load_feeds() if f["key"] != key]) +# ---- routines (truth) ---- +DEFAULT_ROUTINES = [ + {"id": "matin", "title": "Le matin", "emoji": "☀️", "steps": [ + {"text": "Habille-toi", "emoji": "\U0001F455"}, + {"text": "Petit-déjeuner", "emoji": "\U0001F950"}, + {"text": "Brosse tes dents", "emoji": "\U0001FAA5"}, + {"text": "Prépare ton cartable", "emoji": "\U0001F392"}]}, + {"id": "soir", "title": "Le soir", "emoji": "\U0001F319", "steps": [ + {"text": "Mets ton pyjama", "emoji": "\U0001F476"}, + {"text": "Brosse tes dents", "emoji": "\U0001FAA5"}, + {"text": "Lis une histoire", "emoji": "\U0001F4D6"}]}, +] + +def routines_load(): + if os.path.exists(ROUTINES_JSON): + try: + return json.load(open(ROUTINES_JSON, encoding="utf-8")).get("routines", []) + except Exception: + pass + routines_save([dict(r) for r in DEFAULT_ROUTINES]) + return [dict(r) for r in DEFAULT_ROUTINES] + +def routines_save(routines): + _atomic_write_json(ROUTINES_JSON, {"routines": routines}) + # ---- RSS ---- def cover_url(root): el = root.find(".//channel/itunes:image", NS) diff --git a/tower/test_routines.py b/tower/test_routines.py index 4f41d33..f04a11f 100644 --- a/tower/test_routines.py +++ b/tower/test_routines.py @@ -12,6 +12,17 @@ def test_emoji_cp_strips_vs16(): # multi-codepoint (no VS16): joined by '-' — full ZWJ sequence check assert C.emoji_cp("\U0001F468‍\U0001F4BB") == "1f468-200d-1f4bb" +def test_routines_seed_and_roundtrip(): + r = C.routines_load() # seeds default if missing + ids = [x["id"] for x in r] + assert ids == ["matin", "soir"] + assert os.path.exists(C.ROUTINES_JSON) + assert all("emoji" in x and "title" in x and "steps" in x for x in r) + r[0]["steps"].append({"text": "Cartable", "emoji": "\U0001F392"}) + C.routines_save(r) + r2 = C.routines_load() + assert r2[0]["steps"][-1]["text"] == "Cartable" + if __name__ == "__main__": for name, fn in sorted(globals().items()): if name.startswith("test_"):