Files
ordinaceprojekt/OrdinaceAgentEmail/medevio_recept.py
T
Vladimir Buzalka 2346ad7739 notebookvb
2026-06-13 21:46:11 +02:00

242 lines
8.7 KiB
Python
Raw 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 -*-
"""
medevio_recept.py — vytvoření požadavku "Recept na léky" v Medeviu pro pacienta.
Určeno k volání z e-mailového agenta (recepty_agent.py): jakmile agent správně
identifikuje pacienta a co chce, založí mu v Medeviu požadavek, aby ho lékař viděl
(e-mail kontrolujeme zřídka, Medevio pořád).
Hlavní funkce:
vytvor_recept(rodne_cislo, nazev_leku, poznamka) -> request_id
Co dělá (vše ověřeno/odchyceno 2026-06-13 na testovacím pacientovi Vladko):
1. RČ -> patient UUID (MySQL medevio.medevio_pacient)
2. fillECRFForm -> ecrfFill.id (prázdný formulář)
3. createPatientRequestWithoutReservation -> založí "Recept na léky"
4. createClinicPatientRequestNote -> obě pole do INTERNÍ POZNÁMKY
5. assignTagToPatientRequest -> štítek CLAUDE
POZOR: lékařský (klinický) přístup neumí vyplnit pacientský dotazník smysluplně,
proto obsah ("Název léků" + "Poznámka") jde do interní poznámky, ne do dotazníku.
Auth: Bearer token z Medevio/token.txt (dlouhodobý API token).
"""
import sys
import re
from pathlib import Path
import requests
import pymysql
from pymysql.cursors import DictCursor
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
# ============================================================
# Konstanty odchycené z Medevia (klinika mudr-buzalkova)
# ============================================================
CLINIC_SLUG = "mudr-buzalkova"
GRAPHQL_URL = "https://api.medevio.cz/graphql"
RECEPT_USER_ECRF_ID = "79488e86-e9e5-47e3-8b19-7e5229427f23" # typ "Recept na léky"
RECEPT_SID = "ERECEPT_SIMPLEST_BEZ_DAVKOVANI"
RECEPT_STEP_ID = "erecept-gp-request"
CLAUDE_TAG_ID = "c136aeca-0625-4c43-b81f-fc3949ec6ba6" # štítek CLAUDE
TOKEN_PATH = Path(__file__).resolve().parent.parent / "Medevio" / "token.txt"
# MySQL je na různých strojích na různém portu — zkusíme kandidáty po řadě.
_MYSQL_BASE = dict(user="root", password="Vlado9674+", database="medevio")
_MYSQL_CANDIDATES = [
dict(host="192.168.1.76", port=3306),
dict(host="192.168.1.76", port=3307),
dict(host="127.0.0.1", port=3307),
dict(host="127.0.0.1", port=3306),
]
# ============================================================
# GraphQL operace (přesně jak je posílá web Medevia)
# ============================================================
M_FILL_ECRF = r"""
mutation ClinicRequestCreateModal_FillECRFForm($input: FillECRFFormInput!) {
ecrfFill: fillECRFForm(input: $input) { id }
}
"""
M_CREATE_REQUEST = r"""
mutation ClinicRequestCreateModal_CreateRequest($clinicSlug: String!, $input: CreatePatientRequestWithoutReservationInput!) {
patientRequest: createPatientRequestWithoutReservation(clinicSlug: $clinicSlug, input: $input) { id }
}
"""
M_CREATE_NOTE = r"""
mutation ClinicRequestNotes_Create($noteInput: CreateClinicPatientRequestNoteInput!) {
createClinicPatientRequestNote(noteInput: $noteInput) { id }
}
"""
M_ASSIGN_TAG = r"""
mutation TagRequestEditModal_AssignTagToRequest($clinicSlug: String!, $requestId: UUID!, $tagId: UUID!) {
tagRequest: assignTagToPatientRequest(clinicSlug: $clinicSlug, patientRequestId: $requestId, tagId: $tagId) { id }
}
"""
# ============================================================
# Pomocné funkce
# ============================================================
def _read_token() -> str:
t = TOKEN_PATH.read_text(encoding="utf-8").strip()
return t[7:].strip() if t.lower().startswith("bearer ") else t
def _gql(query: str, variables: dict, token: str) -> dict:
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
r = requests.post(GRAPHQL_URL, json={"query": query, "variables": variables},
headers=headers, timeout=30)
r.raise_for_status()
data = r.json()
if data.get("errors"):
raise RuntimeError(f"GraphQL chyba: {data['errors']}")
return data["data"]
def _mysql():
last = None
for cand in _MYSQL_CANDIDATES:
try:
return pymysql.connect(**cand, **_MYSQL_BASE,
cursorclass=DictCursor, connect_timeout=4)
except Exception as e:
last = e
raise RuntimeError(f"MySQL (medevio) nedostupné na žádném kandidátovi: {last}")
def najdi_uuid_dle_rc(rodne_cislo: str) -> dict:
"""RČ (s lomítkem i bez) -> řádek pacienta z medevio_pacient.
Vyhazuje LookupError když pacient není nalezen nebo je nejednoznačný."""
rc = re.sub(r"\D", "", rodne_cislo or "")
if not rc:
raise ValueError("Prázdné / neplatné rodné číslo.")
conn = _mysql()
try:
with conn.cursor() as cur:
cur.execute(
"SELECT patient_id, name, surname, status, user_id "
"FROM medevio_pacient "
"WHERE REPLACE(identification_number, '/', '') = %s",
(rc,),
)
rows = cur.fetchall()
finally:
conn.close()
if not rows:
raise LookupError(f"Pacient s RČ {rc} není v Medevio databázi (medevio_pacient).")
if len(rows) > 1:
raise LookupError(f"{rc} odpovídá více pacientům — nejednoznačné, řeš ručně.")
return rows[0]
def _format_note(nazev_leku: str, poznamka: str) -> str:
return (
"Žádost o recept (zpracováno e-mailovým agentem).\n\n"
f"Název léků:\n{(nazev_leku or '').strip() or ''}\n\n"
f"Poznámka:\n{(poznamka or '').strip() or ''}"
)
# ============================================================
# Hlavní funkce
# ============================================================
def vytvor_recept(rodne_cislo: str = None, nazev_leku: str = "", poznamka: str = "",
*, patient_uuid: str = None, pridat_stitek: bool = True,
token: str = None, verbose: bool = True) -> str:
"""
Založí pacientovi požadavek "Recept na léky" + interní poznámku + štítek CLAUDE.
Parametry:
rodne_cislo RČ pacienta (s lomítkem i bez); přeloží se na UUID přes MySQL.
nazev_leku text 1. pole ("Název léků").
poznamka text 2. pole ("Poznámka").
patient_uuid volitelně rovnou UUID pacienta (obejde lookup dle RČ; pro testy).
pridat_stitek True = přiřadí štítek CLAUDE.
token volitelně Bearer token; jinak se načte z token.txt.
Vrací: request_id založeného požadavku (str).
"""
token = token or _read_token()
if patient_uuid:
uuid = patient_uuid
kdo = f"UUID {uuid}"
else:
pac = najdi_uuid_dle_rc(rodne_cislo)
uuid = pac["patient_id"]
kdo = f"{pac['surname']} {pac['name']} (RČ {rodne_cislo}, status {pac['status']})"
if verbose:
print(f"→ Pacient: {kdo}")
# 1) prázdný ECRF fill
fill = _gql(M_FILL_ECRF, {"input": {
"byDoctor": True,
"fields": [],
"patientId": uuid,
"sid": RECEPT_SID,
"stepId": RECEPT_STEP_ID,
}}, token)
ecrf_fill_id = fill["ecrfFill"]["id"]
# 2) vytvoř požadavek "Recept na léky"
created = _gql(M_CREATE_REQUEST, {"clinicSlug": CLINIC_SLUG, "input": {
"patientId": uuid,
"userECRFId": RECEPT_USER_ECRF_ID,
"ecrfFillIds": [ecrf_fill_id],
"createdByDoctor": True,
"shouldInvitePatient": False,
}}, token)
request_id = created["patientRequest"]["id"]
if verbose:
print(f"✓ Požadavek vytvořen: {request_id}")
# 3) interní poznámka s oběma poli
_gql(M_CREATE_NOTE, {"noteInput": {
"requestId": request_id,
"content": _format_note(nazev_leku, poznamka),
}}, token)
if verbose:
print("✓ Interní poznámka zapsána")
# 4) štítek CLAUDE
if pridat_stitek:
_gql(M_ASSIGN_TAG, {
"clinicSlug": CLINIC_SLUG,
"requestId": request_id,
"tagId": CLAUDE_TAG_ID,
}, token)
if verbose:
print("✓ Štítek CLAUDE přiřazen")
return request_id
# ============================================================
# Test (na testovacím pacientovi Vladko)
# ============================================================
if __name__ == "__main__":
VLADKO_UUID = "0210db7b-8fb0-4b47-b1d8-ec7a10849a63" # Vladko - testovací aplikace
rid = vytvor_recept(
patient_uuid=VLADKO_UUID,
nazev_leku="Euthyrox 100 µg (TEST z medevio_recept.py)",
poznamka="Testovací požadavek z funkce vytvor_recept — možno zavřít.",
)
print(f"\nHOTOVO. request_id = {rid}")
print(f"https://my.medevio.cz/mudr-buzalkova/klinika/pacienti?pozadavek={rid}")