feat(tower): routines staging push and delta

Add compute_delta_named, routines_staging_files, compute_routines_delta
so routines.json goes last in the push delta. Generalize push_file with
subdir param (default podcasts, compat preserved). Fix test_delta.py:
compute_delta lives in lisael_content, not lisael_server.
This commit is contained in:
L'électron rare
2026-06-21 09:24:45 +02:00
parent e493f85203
commit 916d4d123f
3 changed files with 65 additions and 4 deletions
+20 -3
View File
@@ -385,9 +385,26 @@ def compute_delta(staging, have):
files.append("manifest.json")
return files
def push_file(ip, name):
data = open(os.path.join(STAGING, name), "rb").read()
url = f"http://{ip}:{BOX_PORT}/put?name=podcasts/{name}"
def compute_delta_named(staging, have, manifest_name):
haveset = set(have)
files = [f for f in staging if f != manifest_name and f not in haveset]
if files and manifest_name in staging:
files.append(manifest_name)
return files
def routines_staging_files():
if not os.path.isdir(STAGING_ROUTINES):
return []
return sorted(f for f in os.listdir(STAGING_ROUTINES)
if not f.startswith(".") and f != ".cache.json")
def compute_routines_delta(have):
return compute_delta_named(routines_staging_files(), have, "routines.json")
def push_file(ip, name, subdir="podcasts"):
src_dir = STAGING_ROUTINES if subdir == "routines" else STAGING
data = open(os.path.join(src_dir, name), "rb").read()
url = f"http://{ip}:{BOX_PORT}/put?name={subdir}/{name}"
with urllib.request.urlopen(urllib.request.Request(url, data=data, method="POST"),
timeout=300) as r:
return r.status
+1 -1
View File
@@ -1,4 +1,4 @@
from lisael_server import compute_delta
from lisael_content import compute_delta
def test_delta():
stg = ["oli_01.m4a", "oli.bin", "bestioles_01.mp3", "manifest.json"]
+44
View File
@@ -139,6 +139,50 @@ def test_build_routine_manifest_regen_on_emoji_change():
if os.path.exists(C.ROUTINES_JSON):
os.remove(C.ROUTINES_JSON)
def test_routines_delta_manifest_last():
os.makedirs(C.STAGING_ROUTINES, exist_ok=True)
for f in ("matin.bin", "matin_01.bin", "matin_01.mp3", "routines.json"):
open(os.path.join(C.STAGING_ROUTINES, f), "wb").close()
d = C.compute_routines_delta([])
assert d[-1] == "routines.json" # manifest always last
assert set(d[:-1]) == {"matin.bin", "matin_01.bin", "matin_01.mp3"}
# box already has assets -> only manifest moves if assets changed; here all present
assert C.compute_routines_delta(["matin.bin", "matin_01.bin", "matin_01.mp3"]) == []
def test_push_file_url_construction():
"""push_file builds the correct URL with subdir in the path."""
captured = {}
class FakeResp:
status = 200
def __enter__(self): return self
def __exit__(self, *a): pass
def fake_urlopen(req, timeout=None):
captured["url"] = req.full_url
return FakeResp()
orig = C.urllib.request.urlopen
try:
C.urllib.request.urlopen = fake_urlopen
# Create dummy files in each staging dir
os.makedirs(C.STAGING, exist_ok=True)
os.makedirs(C.STAGING_ROUTINES, exist_ok=True)
open(os.path.join(C.STAGING, "test.mp3"), "wb").close()
open(os.path.join(C.STAGING_ROUTINES, "routines.json"), "wb").close()
# default subdir = podcasts
C.push_file("127.0.0.1", "test.mp3")
assert "podcasts/test.mp3" in captured["url"]
# explicit routines subdir
C.push_file("127.0.0.1", "routines.json", subdir="routines")
assert "routines/routines.json" in captured["url"]
# explicit podcasts subdir same as default
C.push_file("127.0.0.1", "test.mp3", subdir="podcasts")
assert "podcasts/test.mp3" in captured["url"]
finally:
C.urllib.request.urlopen = orig
if __name__ == "__main__":
for name, fn in sorted(globals().items()):
if name.startswith("test_"):