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.
This commit is contained in:
L'électron rare
2026-06-21 09:13:19 +02:00
parent 38141bff69
commit 39bee27a2d
2 changed files with 37 additions and 0 deletions
+26
View File
@@ -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)
+11
View File
@@ -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_"):