Files
Vladimir Buzalka 2bdac59676 notebookvb
2026-06-14 12:07:35 +02:00

186 lines
6.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
recept_resolver.py — vyřizuje "čekající" žádosti o recept přes Telegram.
Jediný proces, který mluví s Telegramem (kvůli getUpdates). Ve smyčce:
1) pošle otázky pro nové pending záznamy (které ještě otázku nemají),
2) long-polluje odpovědi; odpověď (reply na otázku) → podle obsahu:
• RČ (910 číslic) → definitivní volba pacienta → založí požadavek
• číslo kandidáta → vybere kandidáta → založí požadavek
• „ne“ → přeskočí (nezakládá)
Po založení označí původní e-mail kategorií ClaudeZpracovalRecept.
Telegram přenos je v recept_telegram.py (token/chat dodá uživatel).
Bez odpovědi záznam zůstává 'ceka' (čeká se libovolně dlouho).
Spuštění: python recept_resolver.py
"""
import re
import sys
import time
from pathlib import Path
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE.parent / "EmailAgent"))
sys.path.insert(0, str(HERE.parent))
import recept_pending as PEND # noqa: E402
import recept_dialog as DLG # noqa: E402
import recept_telegram as TG # noqa: E402
import graph_mail # noqa: E402 (značení mailu)
import mcp_medevio as MED # noqa: E402 (zaloz_pozadavek_recept)
from Knihovny.mysql_db import connect_mysql # noqa: E402
MAILBOX = "ordinace@buzalkova.cz"
PROCESSED_CATEGORY = "ClaudeZpracovalRecept"
SINCE_FILE = HERE / "_resolver_since.txt" # poslední zpracované message_id
def log(msg: str) -> None:
print(f"[{time.strftime('%H:%M:%S')}] {msg}", flush=True)
def _norm_rc(s: str) -> str:
return re.sub(r"\D", "", s or "")
def najdi_pacienta_dle_rc(rc: str):
"""RČ → (uuid, jmeno, prijmeni) z medevio_pacient, nebo None."""
rc = _norm_rc(rc)
if not rc:
return None
try:
conn = connect_mysql()
conn.ping(reconnect=True)
cur = conn.cursor()
cur.execute(
"SELECT patient_id, name, surname FROM medevio_pacient "
"WHERE REPLACE(identification_number,'/','') = %s LIMIT 1",
[rc],
)
row = cur.fetchone()
return row if row else None
except Exception as e:
log(f"[uuid lookup chyba] {type(e).__name__}: {e}")
return None
def _load_since():
try:
return int(SINCE_FILE.read_text(encoding="utf-8").strip())
except Exception:
return None
def _save_since(s: int) -> None:
SINCE_FILE.write_text(str(s), encoding="utf-8")
def posli_nove_otazky() -> None:
"""Pošle otázku do Telegramu pro každý pending záznam bez otázky."""
for rec in PEND.cekajici_bez_otazky():
try:
mid = TG.posli_otazku(DLG.format_otazka(rec))
PEND.aktualizuj(rec["id"], otazka_message_id=mid)
log(f"otázka odeslána (pending {rec['id'][:8]}, msg {mid})")
except Exception as e:
log(f"otázku nelze odeslat ({type(e).__name__}: {e}) — zkusím příště")
break # nejspíš výpadek / nenakonfigurováno → nech na příští kolo
def _zaloz_pro_rc(rec: dict, rc: str) -> None:
info = najdi_pacienta_dle_rc(rc)
if not info:
TG.posli_zpravu(f"⚠ RČ {rc} není v Medeviu — nezakládám. Zkus jiné RČ.")
return
uuid_, jmeno, prijmeni = info[0], info[1], info[2]
try:
res = MED.zaloz_pozadavek_recept(
uuid_, rec.get("leky_str", ""), rec.get("pozn_str", "")
)
except Exception as e:
TG.posli_zpravu(f"❌ Chyba při zakládání: {type(e).__name__}: {e}")
return
PEND.aktualizuj(rec["id"], stav="zalozeno",
vysledek={"request_id": res["request_id"], "rc": rc,
"pacient": f"{prijmeni} {jmeno}"})
try:
graph_mail.add_category(MAILBOX, rec["email_message_id"], PROCESSED_CATEGORY)
except Exception as e:
log(f"[mail označení] {type(e).__name__}: {e}")
TG.posli_zpravu(f"✅ Založeno: {prijmeni} {jmeno} (RČ {rc}) — "
f"{rec.get('leky_str', '')}")
log(f"založeno {res['request_id']} pro {prijmeni} {jmeno}")
def zpracuj_odpoved(u: dict) -> None:
"""Zpracuje jednu příchozí Telegram zprávu."""
rid = u.get("reply_to_message_id")
rec = PEND.najdi_dle_otazky(rid) if rid else None
if rec is None:
cekaji = PEND.cekajici()
if len(cekaji) == 1:
rec = cekaji[0] # jediný čekající → ber to na něj
else:
if cekaji:
TG.posli_zpravu("Odpověz prosím jako reply na konkrétní dotaz "
"(čeká jich víc).")
return
if rec.get("stav") != "ceka":
return
d = DLG.parse_odpoved(u.get("text", ""))
if d["akce"] == "preskoc":
PEND.aktualizuj(rec["id"], stav="preskoceno")
TG.posli_zpravu("OK, nezakládám.")
elif d["akce"] == "rc":
_zaloz_pro_rc(rec, d["rc"])
elif d["akce"] == "kandidat":
kand = rec.get("kandidati") or []
i = d["index"] - 1
if 0 <= i < len(kand):
_zaloz_pro_rc(rec, kand[i].get("rc", ""))
else:
TG.posli_zpravu(f"Kandidát {d['index']} neexistuje (mám {len(kand)}).")
else:
TG.posli_zpravu("Nerozumím. Pošli RČ pacienta, číslo kandidáta, nebo „ne“.")
def smycka(poll_s: int = 5) -> None:
try:
TG.priprav() # naprimuj entitu Vlada (jinak fresh session spadne)
except Exception as e:
log(f"[priprav] {type(e).__name__}: {e}")
since = _load_since()
if since is None:
# první start — vezmi aktuální stav jako základ, starou historii ignoruj
since = TG.baseline_since()
_save_since(since)
log(f"baseline since_id={since}")
log("Resolver běží (Ctrl+C ukončí).")
while True:
try:
posli_nove_otazky()
nove, since = TG.nacti_odpovedi(since)
for u in nove:
zpracuj_odpoved(u)
_save_since(since)
except KeyboardInterrupt:
log("Konec.")
break
except Exception as e:
log(f"[smyčka] {type(e).__name__}: {e}")
time.sleep(poll_s)
if __name__ == "__main__":
smycka()