Optagelsen sendes op som kladde, når man stopper, så STT kører mens man lytter; 'Send svaret' bekræfter. Næste spørgsmål (Claude + TTS) forberedes i baggrunden efter hvert svar og hentes på 0 s. Dagbogens samtale er datoen (vælg dag). 'Spørgsmål og svar' afspiller oplæsningen og svaret efter hinanden; /erindringer/alle viser alt dag for dag. Baggrundsjobs holdes med stærke referencer og logger fejl. portal.css: én standardregel, så tabeller på smalle skærme ruller i sig selv og lange ord brydes, i stedet for at siden får vandret scroll. Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu
86 lines
7.1 KiB
Python
86 lines
7.1 KiB
Python
# Røgtest af erindringer-modulet med FastAPI TestClient, uden OIDC: sessionen sættes direkte.
|
|
# Kør i CT151: PORTAL_DATA=/tmp/ef-test/data + env fra /etc/portal.env. Rører ikke prod-data.
|
|
import io, json, os, sys
|
|
from fastapi.testclient import TestClient
|
|
import main, erindringer
|
|
|
|
c = TestClient(main.app, base_url="https://weircon.dk")
|
|
adm = {"username": "agw", "name": "Asger", "groups": [main.config.ADMIN_GROUP, "Familie"]}
|
|
|
|
|
|
def login(client, user):
|
|
# Starlette-session: skriv via en lille rute er omstændeligt; monkeypatch current_user i stedet.
|
|
main.current_user = lambda request: user
|
|
|
|
|
|
login(c, adm)
|
|
r = c.get("/admin/erindringer"); assert r.status_code == 200 and "Erindringer" in r.text, r.status_code
|
|
r = c.post("/admin/erindringer/projekt", data={"slug": "test-ep", "titel": "Testemne", "beskrivelse": "b", "kontekst": "Personen er Asger. Vi ved at han bor i Sønderlund.", "start": "Hvad husker du fra Rørbæk?\nHvem var din morfar?"}, follow_redirects=False)
|
|
assert r.status_code == 303, r.text
|
|
assert erindringer.projekt("test-ep")["start"][0].startswith("Hvad husker")
|
|
r = c.post("/admin/erindringer/gaest", data={"navn": "Tina Test", "projekter": ["test-ep"]}, follow_redirects=False)
|
|
assert r.status_code == 303
|
|
pid = [p for p in erindringer.alle_personer() if p["navn"] == "Tina Test"][0]["pid"]; noegle = erindringer.person(pid)["noegle"]
|
|
print("gæst", pid)
|
|
|
|
# API som gæst (uden session)
|
|
login(c, None)
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": noegle}); assert r.status_code == 200, r.text
|
|
q1 = r.json(); print("Q1", q1["spoergsmaal"], "| lyd:", bool(q1["lyd_url"]))
|
|
if q1["lyd_url"]:
|
|
assert c.get(q1["lyd_url"]).status_code == 200
|
|
r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": q1["spoergsmaal"], "noegle": noegle, "tekst": "Jeg husker min morfar Gustav i Villa Monika i Hannerup. Han havde en stor have og en motorcykel, som han aldrig kørte på."})
|
|
assert r.status_code == 200, r.text; s1 = r.json(); print("svar1", s1["id"])
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle}); assert r.status_code == 200, r.text
|
|
q2 = r.json(); print("Q2 (Claude)", q2["spoergsmaal"], "| talt:", q2["talt"], "| kilde:", q2["kilde"], "| afslut:", q2["afslut"])
|
|
assert "[" not in q2["spoergsmaal"] and q2["lyd_sha"]
|
|
# spring over: det sprungne må ikke komme igen
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle, "sprunget": [q2["spoergsmaal"]]}); assert r.status_code == 200
|
|
assert r.json()["spoergsmaal"] != q2["spoergsmaal"]; print("Q3 efter spring", r.json()["spoergsmaal"], "| kilde:", r.json()["kilde"])
|
|
# lydsvar: 2 s tone som mp3 lavet med ffmpeg
|
|
os.system("ffmpeg -y -v error -f lavfi -i 'sine=frequency=440:duration=2' -c:a libmp3lame /tmp/ef-test/tone.mp3")
|
|
r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": q2["spoergsmaal"], "spoergsmaal_talt": q2["talt"], "lyd_sha": q2["lyd_sha"], "kilde": q2["kilde"], "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")})
|
|
assert r.status_code == 200, r.text; s2 = r.json(); print("svar2 lyd", s2["id"], "varighed", s2["varighed"], "tekst:", repr(s2["tekst"])[:60])
|
|
r = c.get(f"/erindringer/g/{noegle}/klip/{s2['id']}.mp3"); assert r.status_code == 200 and r.headers["content-type"].startswith("audio")
|
|
r = c.get(f"/erindringer/g/{noegle}/klip/{s2['id']}-sp.mp3"); assert r.status_code == 200, "spørgsmålets lyd mangler"
|
|
k2 = erindringer.klip(pid, s2["id"]); assert k2["spoergsmaal_lyd"] and k2["kilde"] == q2["kilde"] and k2["spoergsmaal_talt"] == q2["talt"]
|
|
r = c.post("/erindringer/api/ret", json={"id": s1["id"], "tekst": "Rettet tekst.", "noegle": noegle}); assert r.status_code == 200
|
|
# kladde-flow: optagelsen sendes op med det samme, afskrift i baggrunden, så bekræftes den
|
|
import time as _t
|
|
r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": "Hvad lavede du i haven?", "kladde": "1", "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")})
|
|
assert r.status_code == 200, r.text; s3 = r.json(); print("kladde", s3["id"], s3["status"], "svartid ok")
|
|
assert s3["kladde"] is True and not [k for k in erindringer.klip_for(pid) if k["id"] == s3["id"]], "kladde må ikke tælle med"
|
|
r = c.post("/erindringer/api/bekraeft", json={"id": s3["id"], "noegle": noegle}); assert r.status_code == 200
|
|
for _ in range(40):
|
|
st = c.post("/erindringer/api/status", json={"id": s3["id"], "noegle": noegle}).json()
|
|
if st["status"] != "transskriberes": break
|
|
_t.sleep(1)
|
|
print("status efter bekræft:", st["status"], repr(st["tekst"])[:40])
|
|
assert [k for k in erindringer.klip_for(pid) if k["id"] == s3["id"]], "bekræftet klip skal tælle med"
|
|
_t.sleep(8) # baggrunden forbereder næste spørgsmål
|
|
t0 = _t.time(); r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": q1["samtale"], "noegle": noegle}); dt = _t.time() - t0
|
|
assert r.status_code == 200; print("næste efter kladde: %.1fs" % dt, r.json()["spoergsmaal"][:60], "| lyd:", bool(r.json()["lyd_url"]))
|
|
# slet kladde
|
|
r = c.post("/erindringer/api/svar", data={"projekt": "test-ep", "samtale": q1["samtale"], "spoergsmaal": "x", "kladde": "1", "noegle": noegle}, files={"lyd": ("svar.mp3", open("/tmp/ef-test/tone.mp3", "rb"), "audio/mpeg")}); s4 = r.json()
|
|
r = c.post("/erindringer/api/slet", json={"id": s4["id"], "noegle": noegle}); assert r.status_code == 200 and erindringer.klip(pid, s4["id"]) is None
|
|
# dagbog: samtale = dato
|
|
login(c, adm)
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "dagbog", "samtale": None, "noegle": "", "dato": "2026-10-05"}); assert r.status_code == 200 and r.json()["samtale"] == "20261005", r.text
|
|
r = c.get("/erindringer/alle"); assert r.status_code == 200 and "Alle svar" in r.text
|
|
login(c, None)
|
|
assert erindringer.klip(pid, s1["id"])["tekst"] == "Rettet tekst."
|
|
# forkert nøgle -> 404
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": "x" * 40}); assert r.status_code == 404
|
|
# indlogget familie: dagbog findes, admin-person-side, eksport
|
|
login(c, adm)
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "dagbog", "samtale": None, "noegle": ""}); assert r.status_code == 200, r.text
|
|
print("dagbog Q", r.json()["spoergsmaal"])
|
|
r = c.get(f"/admin/erindringer/p/{pid}"); assert r.status_code == 200 and "Rettet tekst." in r.text
|
|
r = c.get(f"/admin/erindringer/p/{pid}/eksport.zip"); assert r.status_code == 200 and r.headers["content-type"] == "application/zip" and len(r.content) > 1000
|
|
r = c.post(f"/admin/erindringer/p/{pid}/klip/{s1['id']}", data={"handling": "favorit", "favorit": "1"}, follow_redirects=False); assert r.status_code == 303
|
|
assert erindringer.klip(pid, s1["id"])["favorit"] is True
|
|
r = c.post(f"/admin/erindringer/person/{pid}", data={"handling": "luk"}, follow_redirects=False); assert r.status_code == 303
|
|
login(c, None)
|
|
r = c.post("/erindringer/api/naeste", json={"projekt": "test-ep", "samtale": None, "noegle": noegle}); assert r.status_code == 404
|
|
print("ALT OK")
|