notebookvb

This commit is contained in:
Vladimir Buzalka
2026-06-13 21:46:11 +02:00
parent ca39622ddd
commit 2346ad7739
7 changed files with 518 additions and 0 deletions
+132
View File
@@ -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()
+72
View File
@@ -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()
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

@@ -0,0 +1 @@
https://my.medevio.cz/prihlaseni
+45
View File
@@ -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: [<id z kroku 1>], 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 |