From 2346ad77395cd064f5a205977f1eb7145a599d44 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Sat, 13 Jun 2026 21:46:11 +0200 Subject: [PATCH] notebookvb --- Medevio/Testy/90_recon_recept.py | 132 ++++++++++ Medevio/Testy/91_login_save_session.py | 72 ++++++ Medevio/Testy/recon_recept/01_card.png | Bin 0 -> 24815 bytes .../Testy/recon_recept/_SESSION_EXPIRED.txt | 1 + Medevio/medevio_api_notes.md | 45 ++++ OrdinaceAgentEmail/NOTES.md | 27 ++ OrdinaceAgentEmail/medevio_recept.py | 241 ++++++++++++++++++ 7 files changed, 518 insertions(+) create mode 100644 Medevio/Testy/90_recon_recept.py create mode 100644 Medevio/Testy/91_login_save_session.py create mode 100644 Medevio/Testy/recon_recept/01_card.png create mode 100644 Medevio/Testy/recon_recept/_SESSION_EXPIRED.txt create mode 100644 OrdinaceAgentEmail/medevio_recept.py diff --git a/Medevio/Testy/90_recon_recept.py b/Medevio/Testy/90_recon_recept.py new file mode 100644 index 0000000..2b1a6dd --- /dev/null +++ b/Medevio/Testy/90_recon_recept.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +RECON ONLY — nic nezakládá, nic neodesílá. +Otevře testovacího pacienta Vladko, otevře "Nový požadavek", +zachytí dostupné typy požadavků a podívá se na formulář "Recept". +Ukládá: screenshoty, HTML, plný GraphQL provoz (request + response). +""" +from pathlib import Path +from datetime import datetime +import sys, json, time +from playwright.sync_api import sync_playwright, TimeoutError as PWTimeout + +try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") +except Exception: + pass + +HERE = Path(__file__).resolve().parent +STATE_FILE = HERE.parent / "medevio_storage.json" +PATIENT_UUID = "0210db7b-8fb0-4b47-b1d8-ec7a10849a63" # Vladko - testovaci aplikace +PATIENT_URL = f"https://my.medevio.cz/mudr-buzalkova/klinika/pacienti?pacient={PATIENT_UUID}" + +OUT = HERE / "recon_recept" +OUT.mkdir(exist_ok=True) +GQL_LOG = OUT / f"graphql_{int(time.time())}.jsonl" + + +def log(msg): + print(f"[{datetime.now():%H:%M:%S}] {msg}", flush=True) + + +def main(): + with sync_playwright() as p: + browser = p.chromium.launch(headless=False, slow_mo=150) + context = browser.new_context(storage_state=str(STATE_FILE)) + page = context.new_page() + + # ---- capture GraphQL request + response bodies ---- + def on_response(resp): + try: + req = resp.request + if "graphql" in req.url and req.method == "POST": + rec = {"op": None, "request": None, "response": None, + "status": resp.status} + try: + rec["request"] = json.loads(req.post_data or "{}") + rec["op"] = rec["request"].get("operationName") + except Exception: + pass + try: + rec["response"] = resp.json() + except Exception: + pass + with open(GQL_LOG, "a", encoding="utf-8") as f: + f.write(json.dumps(rec, ensure_ascii=False) + "\n") + except Exception: + pass + + page.on("response", on_response) + + log(f"Otevírám kartu pacienta…") + page.goto(PATIENT_URL, wait_until="networkidle") + time.sleep(2) + page.screenshot(path=str(OUT / "01_card.png"), full_page=True) + + # ---- detect login / session expiry ---- + url_now = page.url + if "login" in url_now or "prihlaseni" in url_now or "auth" in url_now: + log(f"!!! Vypadá to na odhlášení / propadlou session. URL: {url_now}") + (OUT / "_SESSION_EXPIRED.txt").write_text(url_now, encoding="utf-8") + browser.close() + return + + # is the card actually visible? + card_ok = False + try: + page.get_by_text("Historie požadavků").wait_for(timeout=8000) + card_ok = True + log("Karta pacienta načtena (vidím 'Historie požadavků').") + except PWTimeout: + log("!!! Nevidím 'Historie požadavků' — možná jiný layout nebo session.") + + (OUT / "01_card.html").write_text(page.content(), encoding="utf-8") + + if not card_ok: + browser.close() + return + + # ---- open "Nový požadavek" ---- + try: + page.get_by_role("button", name="Nový požadavek").click() + time.sleep(1.0) + page.screenshot(path=str(OUT / "02_new_request_open.png"), full_page=True) + (OUT / "02_new_request_open.html").write_text(page.content(), encoding="utf-8") + log("Kliknuto 'Nový požadavek'.") + except Exception as e: + log(f"!!! Nepodařilo se kliknout 'Nový požadavek': {e}") + browser.close() + return + + # ---- capture all available request-type options (empty query) ---- + try: + opts = page.locator("[role='option']").all_text_contents() + (OUT / "03_all_options.txt").write_text( + "\n".join(opts), encoding="utf-8") + log(f"Dostupných typů (bez filtru): {len(opts)}") + except Exception as e: + log(f"options(all) chyba: {e}") + + # ---- type 'recept' and capture filtered options ---- + try: + page.keyboard.type("recept") + time.sleep(1.0) + opts2 = page.locator("[role='option']").all_text_contents() + (OUT / "04_recept_options.txt").write_text( + "\n".join(opts2), encoding="utf-8") + page.screenshot(path=str(OUT / "04_recept_options.png"), full_page=True) + (OUT / "04_recept_options.html").write_text(page.content(), encoding="utf-8") + log(f"Po napsání 'recept' nabízí: {opts2}") + except Exception as e: + log(f"options(recept) chyba: {e}") + + log("RECON hotovo — NIC nezaloženo. Zavírám za 3s.") + time.sleep(3) + browser.close() + log(f"Artefakty v: {OUT}") + log(f"GraphQL log: {GQL_LOG}") + + +if __name__ == "__main__": + main() diff --git a/Medevio/Testy/91_login_save_session.py b/Medevio/Testy/91_login_save_session.py new file mode 100644 index 0000000..54083ed --- /dev/null +++ b/Medevio/Testy/91_login_save_session.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Otevře přihlašovací okno Medevia. PŘIHLAŠ SE RUČNĚ. +Skript sám pozná, že už nejsi na přihlašovací stránce, počká na ustálení +a uloží session do medevio_storage.json. Žádné stisknutí Enter není třeba. +""" +from pathlib import Path +from datetime import datetime +import sys, time +from playwright.sync_api import sync_playwright + +try: + sys.stdout.reconfigure(encoding="utf-8", errors="replace") +except Exception: + pass + +HERE = Path(__file__).resolve().parent +STATE_FILE = HERE.parent / "medevio_storage.json" +LOGIN_URL = "https://my.medevio.cz/prihlaseni" +TIMEOUT_S = 300 # 5 minut na přihlášení + + +def log(msg): + print(f"[{datetime.now():%H:%M:%S}] {msg}", flush=True) + + +def is_logged_in(url: str) -> bool: + return ("medevio.cz" in url + and "prihlaseni" not in url + and "auth" not in url + and "login" not in url) + + +def main(): + with sync_playwright() as p: + browser = p.chromium.launch(headless=False, slow_mo=80) + context = browser.new_context() + page = context.new_page() + page.goto(LOGIN_URL, wait_until="load") + + log("=== PŘIHLAS SE v otevřeném okně Medevia ===") + log("Skript čeká, až opustíš přihlašovací stránku…") + + deadline = time.time() + TIMEOUT_S + logged = False + while time.time() < deadline: + try: + if is_logged_in(page.url): + # počkej na ustálení redirectů + time.sleep(4) + if is_logged_in(page.url): + logged = True + break + except Exception: + pass + time.sleep(2) + + if not logged: + log("!!! Nepřihlášeno do limitu. Session NEULOŽENA.") + browser.close() + return + + log(f"Přihlášeno (URL: {page.url}). Ukládám session…") + context.storage_state(path=str(STATE_FILE)) + log(f"Session uložena: {STATE_FILE}") + time.sleep(1) + browser.close() + + +if __name__ == "__main__": + main() diff --git a/Medevio/Testy/recon_recept/01_card.png b/Medevio/Testy/recon_recept/01_card.png new file mode 100644 index 0000000000000000000000000000000000000000..92706e5e8512f647ea392f5fa4a2130f85344a05 GIT binary patch literal 24815 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

HmiBE zMH|aXQX{pSZ_DaA$%76n0<%m0*XGDw*)!D`{^!qs$hf~7&Ktld8nj~QCP-{tD}Pz6 ze^ay%Lc78+IXQWNiwn>`f0Bs2yCrkG5w!)lGTE64*#V{xJinodN1?+y4DNOl;1R}! zkaqqSo3Bu5#fX8Oxp_(&!5FgBpu_7SaEPj@sewVMq>N0ZOrNO%BH}JYgZ)n)S}e(Pp@M~Ru1+l+zqr-`cfkvCDV#@rT0j)GVbp} zhbz;iK9g*#E0-9xVHyo7D<8R5VDfA{Em*XY+;0trDbkXX`w*-xXo@tWMdcUX@aTf& zheq3%*6eMr4q|X!241ui3*4@v@UQwHc(gmA=JWl7?I)V`$$!6>60^Jn`VJt6*wP3c znMtab+_CJ0h4P9T+Y!%|+1XhpNcE842JEV4r}B#wK0W`Cw6*>**=5*`reAMcqK_s3 ztxAy)Dl?S>X&YJfCZ>OGTEicF();IDS689q_#VMt z6G(EkaB*>AyKN4E5_ScQC`*!-pGe;#u5Yg3n9vF1 zWyr;Qj1iNAEg*?Iy-e`u&Mgtr*c zyu$ijp-Pzf$3`t)v1S)Aya-V>e?+R{fqpY%D+eF9Pjr@sy)3VW3fZJP?nXUj0jb5G;M6?w$xl#D$n5H{~YXF3>~@6k5WCYPR~Ek zee98!pFh3%We!t{J$u+UO5*`4@sSli+JW1Ao0+6n!|e6hug&f4Ee(a`F=lU*HTslH zsGs%t*vyOJ*ojtuegy`GHDFdW0I}Q~{9iXd2c9-7x9PLKv>1oB5UeZZVKb*PG|-^m z_IV6JSXLg|Amki%Ie(53eLJSx698S^HWCT@$Mgb$w+2jNVHj}4UGw2^FI_1cgj@z` z2*yRVYcLH`gBm_vij3Iu{i{u36T$tfwIFRr$r6Z?08w0YpUk9s^{EJf2ko~!ZOGxn zBarCS`NyEQ;`T9-T@WC1GkBYZc}Eeo-vv<^F>|SujYQ}wxOL|l!uizBNr28q8WLzg zkb;=K~!}bbn-!kAkXrMA8;C3%=pZu$q#m?_L zbR@rk04N`b>^rmtsQLB5g9l*XOlkO;dI1Cc#vPiR`H+lAfxPPuQVkk}<+G3<2s-HWYDo5!`uchr#6krp{Mh?CJ$&xn3CQiR zNf6|!GLAptgiv_L+t9vsp_j(2JUpt4ii#>Kq>wr? zhN%N{Cd4Bvlln(hvd1W?M5oOeY&1b|hlKINB|3`yp3N#_E`-4s7E$4LQ7s1?Pek>< zZLh!EQvc`8`robw*#=$qR{CQ&Dc@XY{zU3~P9{Qp0fmV8gn|SG&86b7)TNJMyCHbv z>7iEG*SY}WUdK0fgzHA;GqCrB+-f5dbJzCh)%s1%-w4@r4(*LHJ#J$9_< zyo@Z)ELO@JZ&pGt{sDX-GooPl{?4#?X98maJzgBl(=PN;)Gcn{`GT)SO+$0JeHB%Q zmy%wq4lka)qm+%zDF57yS3AkMwFjACg66>uikC2z^1aEkg~I) zMU7@h^UWTMY5Mf1D=0DTMg00000 literal 0 HcmV?d00001 diff --git a/Medevio/Testy/recon_recept/_SESSION_EXPIRED.txt b/Medevio/Testy/recon_recept/_SESSION_EXPIRED.txt new file mode 100644 index 0000000..3099ba9 --- /dev/null +++ b/Medevio/Testy/recon_recept/_SESSION_EXPIRED.txt @@ -0,0 +1 @@ +https://my.medevio.cz/prihlaseni \ No newline at end of file diff --git a/Medevio/medevio_api_notes.md b/Medevio/medevio_api_notes.md index 2cc8632..1245743 100644 --- a/Medevio/medevio_api_notes.md +++ b/Medevio/medevio_api_notes.md @@ -241,6 +241,51 @@ request { } ``` +### Request Creation (Vytvoření požadavku lékařem) — ODCHYCENO 2026-06-13 + +Lékařský účet (klinický token) **NEumí vyplnit pacientský dotazník** smysluplně — formulář +„Recept na léky" má z lékařské strany (`sid: ERECEPT_SIMPLEST_BEZ_DAVKOVANI`) jen jedno +pole `nazev-leku`, kdežto pacient v appce vyplní dvě pole („Název léků" + „Poznámka"). +**Proto: obsah z e-mailu zapisujeme do INTERNÍ POZNÁMKY, ne do dotazníku.** + +Vytvoření prázdného požadavku „Recept na léky" je **dvoukrok**: + +```graphql +# 1) vytvoř (prázdný) ECRF fill → vrátí ecrfFill.id +mutation ClinicRequestCreateModal_FillECRFForm($input: FillECRFFormInput!) { + ecrfFill: fillECRFForm(input: $input) { id } +} +# input: { byDoctor: true, fields: [], patientId, sid: "ERECEPT_SIMPLEST_BEZ_DAVKOVANI", stepId: "erecept-gp-request" } + +# 2) vytvoř požadavek s odkazem na ecrfFill +mutation ClinicRequestCreateModal_CreateRequest($clinicSlug: String!, $input: CreatePatientRequestWithoutReservationInput!) { + patientRequest: createPatientRequestWithoutReservation(clinicSlug: $clinicSlug, input: $input) { id } +} +# input: { patientId, userECRFId, ecrfFillIds: [], createdByDoctor: true, shouldInvitePatient: false } +``` + +| Klíč | Hodnota | +|------|---------| +| ECRF „Recept na léky" `userECRFId` | `79488e86-e9e5-47e3-8b19-7e5229427f23` | +| ECRF `sid` | `ERECEPT_SIMPLEST_BEZ_DAVKOVANI` | +| ECRF `stepId` | `erecept-gp-request` | + +Seznam typů požadavků: `UserEcrfAutocomplete_ListUserECRFsByClinic`. + +### Tagy / štítky požadavku — ODCHYCENO 2026-06-13 + +```graphql +query TagRequestEditModal_ListTags($clinicSlug: String!, $requestId: UUID!) { ... } # seznam štítků + zda jsou přiřazené +mutation TagRequestEditModal_AssignTagToRequest($clinicSlug: String!, $requestId: UUID!, $tagId: UUID!) { + tagRequest: assignTagToPatientRequest(clinicSlug: $clinicSlug, patientRequestId: $requestId, tagId: $tagId) { id } +} +``` + +| Štítek | tagId | barva | +|--------|-------|-------| +| `CLAUDE` | `c136aeca-0625-4c43-b81f-fc3949ec6ba6` | ORCHID | +| `NEZAPOMENOUT` | `5bced917-83d2-46db-896c-c8e615de1a69` | GREY | + ### Request Detail | Operation | Variables | Response | diff --git a/OrdinaceAgentEmail/NOTES.md b/OrdinaceAgentEmail/NOTES.md index da70dc4..5e4c88a 100644 --- a/OrdinaceAgentEmail/NOTES.md +++ b/OrdinaceAgentEmail/NOTES.md @@ -57,6 +57,33 @@ přesuny, odpovědi), žádný `state.json`. počítače, na Z230 → `reporter:c:\medicus\medicus.fdb`). - `ANTHROPIC_API_KEY` z `Medevio/.env`. +## Vytvoření požadavku v Medeviu — `medevio_recept.py` + +Funkce pro agenta: jakmile je pacient + léky správně identifikován, založí mu +v Medeviu požadavek **„Recept na léky"**, aby ho lékař viděl (Medevio kontrolujeme +průběžně, e-mail zřídka). + +```python +from medevio_recept import vytvor_recept +rid = vytvor_recept(rodne_cislo="730920/8104", + nazev_leku="Euthyrox 100 µg", + poznamka="docházejí mi léky") +``` + +Co se stane (vše odchyceno z webu Medevia 2026-06-13, ověřeno na testovacím pacientovi): +1. **RČ → patient UUID** přes MySQL `medevio.medevio_pacient` (`identification_number` → `patient_id`). +2. `fillECRFForm` (prázdný) → `createPatientRequestWithoutReservation` → založí „Recept na léky". +3. `createClinicPatientRequestNote` → obě pole do **interní poznámky** (formátováno „Název léků / Poznámka"). +4. `assignTagToPatientRequest` → štítek **CLAUDE** (`pridat_stitek=False` vypne). + +**Proč interní poznámka, ne dotazník:** lékařský přístup neumí vyplnit pacientský +ECRF dotazník smysluplně (z lékařské strany má jen 1 pole `nazev-leku`), proto obsah +jde do interní poznámky (viditelná jen pro ordinaci). + +Auth: Bearer token z `Medevio/token.txt` (dlouhodobý). Test: `python medevio_recept.py` +(založí testovací Recept na Vladkovi `0210db7b-…`). Pro test bez DB lookup je parametr +`patient_uuid=`. Mutace + konstanty jsou v `Medevio/medevio_api_notes.md`. + ## Známé limity / TODO - E-mailových kontaktů je v kartotéce málo (~70 z 6300 pacientů) — párování diff --git a/OrdinaceAgentEmail/medevio_recept.py b/OrdinaceAgentEmail/medevio_recept.py new file mode 100644 index 0000000..eb4a5b8 --- /dev/null +++ b/OrdinaceAgentEmail/medevio_recept.py @@ -0,0 +1,241 @@ +#!/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"RČ {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}")