diff --git a/8 medevio read calendar.py b/8 medevio read calendar.py new file mode 100644 index 0000000..798919f --- /dev/null +++ b/8 medevio read calendar.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Open the Medevio daily agenda calendar, +inspect the rendered HTML, and probe JS memory +to see what data is exposed. +""" + +from playwright.sync_api import sync_playwright + +STATE_FILE = "medevio_storage.json" +AGENDA_URL = ( + "https://my.medevio.cz/mudr-buzalkova/klinika/kalendar/agenda-dne/" + "?kalendar=144c4e12-347c-49ca-9ec0-8ca965a4470d&datum=2025-10-17" +) + +def main(): + with sync_playwright() as pw: + browser = pw.chromium.launch(headless=False, slow_mo=150) + context = browser.new_context(storage_state=STATE_FILE) + page = context.new_page() + + print("πŸ”— Opening agenda-day calendar...") + page.goto(AGENDA_URL, wait_until="networkidle", timeout=90_000) + + # -------- Check login -------- + body = (page.text_content("body") or "").lower() + if any(x in body for x in ["pΕ™ihlΓ‘Ε‘enΓ­", "pΕ™ihlΓ‘sit", "sign in", "login"]): + raise SystemExit("❌ Not logged in – refresh medevio_storage.json.") + + # -------- Wait for appointments to render -------- + page.wait_for_timeout(4000) + + # -------- Dump a few appointment blocks -------- + blocks = page.locator("div.rbc-event-inner-content, div[data-testid='Reservation']").evaluate_all( + "(els) => els.map(e => e.outerHTML)" + ) + print(f"\nβœ… Found {len(blocks)} appointment blocks. Showing first 3:\n") + for snippet in blocks[:3]: + print(snippet) + print("-" * 80) + + # -------- Explore window memory -------- + print("\nπŸ” Inspecting global JS variables...") + keys = page.evaluate("Object.keys(window)") + interesting = [k for k in keys if any(w in k.lower() for w in ["mede", "cal", "react", "state", "reserv"])] + print("Interesting keys:", interesting[:20]) + + for candidate in [ + "window.__INITIAL_STATE__", + "window.__INITIAL_DATA__", + "window.__REACT_DEVTOOLS_GLOBAL_HOOK__", + "window.medevioCalendar", + "window.calendarStore", + "window.reduxStore", + "window.reactProps", + ]: + try: + data = page.evaluate(f"JSON.stringify({candidate}, null, 2)") + if data and len(data) > 200: + print(f"\n===== {candidate} =====\n{data[:1000]}...\n") + except Exception: + pass + + # -------- Optionally: listen for network requests while you click -------- + def log_request(req): + url = req.url + if any(x in url for x in ["pozadavek", "request", "api"]): + print("πŸ“‘", url) + + page.on("request", log_request) + print("\nπŸ‘‰ Now click manually on a few agenda items to open their detail cards.") + print(" Any backend calls will appear below.\n") + + page.wait_for_timeout(40000) # give yourself ~40s to click around + browser.close() + +if __name__ == "__main__": + main() diff --git a/81 Read agenda and give 1.py b/81 Read agenda and give 1.py new file mode 100644 index 0000000..ca35802 --- /dev/null +++ b/81 Read agenda and give 1.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from playwright.sync_api import sync_playwright +import json +import requests + +STATE_FILE = "medevio_storage.json" +AGENDA_URL = ( + "https://my.medevio.cz/mudr-buzalkova/klinika/kalendar/agenda-dne/" + "?kalendar=144c4e12-347c-49ca-9ec0-8ca965a4470d&datum=2025-10-17" +) +GRAPHQL_URL = "https://api.medevio.cz/graphql" + + +def extract_agenda_rows(page): + rows = page.locator("div[data-testid='reservation-row']") + if rows.count() == 0: + raise SystemExit("❌ No rows found β€” check selector or login session.") + + results = [] + print(f"\nFound {rows.count()} rows, showing sample structure:") + for row in rows.all()[:3]: + print("-" * 80) + print(row.evaluate("el => el.outerHTML")[:500], "...\n") + + # Now extract data safely + for row in rows.all(): + rid = row.get_attribute("data-id") or "" + # Try to read each cell dynamically + cells = row.locator("div.MuiDataGrid-cell") + record = {"id": rid} + for c in cells.all(): + field = c.get_attribute("data-field") or "unknown" + text = (c.text_content() or "").strip() + record[field] = text + results.append(record) + return results + + +def step1_extract_appointments(): + with sync_playwright() as pw: + browser = pw.chromium.launch(headless=False, slow_mo=150) + context = browser.new_context(storage_state=STATE_FILE) + page = context.new_page() + print("πŸ”— Opening Medevio agenda-day page...") + page.goto(AGENDA_URL, wait_until="networkidle", timeout=90_000) + page.wait_for_selector("div[data-testid='reservation-row']", timeout=30_000) + + appointments = extract_agenda_rows(page) + browser.close() + print(f"βœ… Extracted {len(appointments)} appointments") + for a in appointments: + print(f"β€” {a.get('StartDateTime','?')} {a.get('Patient','?')}: {a.get('Reason','?')} ({a['id']})") + return appointments + + +def step2_fetch_detail(session_cookies, reservation_id): + headers = { + "content-type": "application/json", + "origin": "https://my.medevio.cz", + "referer": "https://my.medevio.cz/", + } + + query = { + "operationName": "ReservationDetail", + "variables": {"id": reservation_id}, + "query": """ + query ReservationDetail($id: ID!) { + reservation(id: $id) { + id + reason + startDateTime + endDateTime + status + note + patient { id name age } + doctor { id name } + location { name } + } + } + """, + } + + print(f"\nπŸ“‘ Fetching GraphQL detail for {reservation_id}...") + response = requests.post(GRAPHQL_URL, headers=headers, cookies=session_cookies, data=json.dumps(query)) + print("Status:", response.status_code) + print(json.dumps(response.json(), indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + appointments = step1_extract_appointments() + if not appointments: + raise SystemExit("No appointments found.") + + # Use first appointment for detail fetch + reservation_id = appointments[0]["id"] + + # Load session cookies from storage + with open(STATE_FILE, "r", encoding="utf-8") as f: + state = json.load(f) + cookies = {c["name"]: c["value"] for c in state.get("cookies", []) if "medevio" in c["domain"]} + + step2_fetch_detail(cookies, reservation_id) diff --git a/82 test.py b/82 test.py new file mode 100644 index 0000000..c46f431 --- /dev/null +++ b/82 test.py @@ -0,0 +1,44 @@ +import json, requests + +GRAPHQL_URL = "https://api.medevio.cz/graphql" + +FULL_INTROSPECTION_QUERY = """ +query IntrospectionQuery { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + } +} +fragment FullType on __Type { + kind + name + fields(includeDeprecated: true) { + name + } +} +""" + +headers = { + "content-type": "application/json", + "origin": "https://my.medevio.cz", + "referer": "https://my.medevio.cz/", +} + +# Load cookies from storage +state = json.load(open("medevio_storage.json", encoding="utf-8")) +cookies = {c["name"]: c["value"] for c in state["cookies"] if "medevio" in c["domain"]} + +payload = {"operationName": "IntrospectionQuery", "query": FULL_INTROSPECTION_QUERY} + +r = requests.post(GRAPHQL_URL, headers=headers, cookies=cookies, data=json.dumps(payload)) +print("Status:", r.status_code) +try: + data = r.json() + print(json.dumps(data, indent=2, ensure_ascii=False)[:2000]) +except Exception as e: + print("Could not decode response:", e) + print(r.text) diff --git a/83_capture_graphql.py b/83_capture_graphql.py new file mode 100644 index 0000000..3170bc2 --- /dev/null +++ b/83_capture_graphql.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from playwright.sync_api import sync_playwright +import json, os, time + +STATE_FILE = "medevio_storage.json" +GRAPHQL_LOG = f"graphql_capture_{int(time.time())}.jsonl" + +with sync_playwright() as pw: + browser = pw.chromium.launch(headless=False, slow_mo=200) + context = browser.new_context(storage_state=STATE_FILE) + page = context.new_page() + + def log_graphql(req): + if "graphql" in req.url and req.method == "POST": + try: + body = req.post_data or "" + data = json.loads(body) + with open(GRAPHQL_LOG, "a", encoding="utf-8") as f: + f.write(json.dumps(data, ensure_ascii=False) + "\n") + print(f"πŸ“‘ {data.get('operationName')} saved") + except Exception: + pass + + page.on("request", log_graphql) + + print("πŸ”— Opening Medevio main page...") + page.goto("https://my.medevio.cz/mudr-buzalkova/klinika/kalendar/agenda-dne/" + "?kalendar=144c4e12-347c-49ca-9ec0-8ca965a4470d", wait_until="networkidle") + + print("\nπŸ‘‰ Click various items in Medevio (calendar, reservations, requests, etc.).") + print(" Every GraphQL call will be saved to", GRAPHQL_LOG) + print(" Press Ctrl+C or close the browser when done.\n") + + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + browser.close() + print(f"\nβœ… Finished β€” GraphQL calls saved to {GRAPHQL_LOG}") diff --git a/84 test.py b/84 test.py new file mode 100644 index 0000000..0aab053 --- /dev/null +++ b/84 test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Query Medevio for the full agenda of 17 Oct 2025 and print raw API response. +""" + +import json +import requests + +GRAPHQL_URL = "https://api.medevio.cz/graphql" + +CALENDAR_ID = "144c4e12-347c-49ca-9ec0-8ca965a4470d" +CLINIC_SLUG = "mudr-buzalkova" + +def load_gateway_token(storage_path="medevio_storage.json"): + """Return Medevio gateway-access-token from saved Playwright storage.""" + import json + from pathlib import Path + + path = Path(storage_path) + if not path.exists(): + raise SystemExit(f"❌ Storage file not found: {path}") + + with path.open("r", encoding="utf-8") as f: + state = json.load(f) + + token = next( + (c["value"] for c in state["cookies"] + if c["name"] == "gateway-access-token"), None + ) + + if not token: + raise SystemExit("❌ gateway-access-token not found in storage file.") + + return token + +gateway_token = load_gateway_token() + +headers = { + "content-type": "application/json", + "origin": "https://my.medevio.cz", + "referer": "https://my.medevio.cz/", + "authorization": f"Bearer {gateway_token}", +} + +payload = { + "operationName": "ClinicAgenda_ListClinicReservations", + "variables": { + "calendarIds": [CALENDAR_ID], + "clinicSlug": CLINIC_SLUG, + "since": "2025-10-16T22:00:00.000Z", + "until": "2025-10-17T21:59:59.999Z", + "locale": "cs", + "emptyCalendarIds": False, + }, + "query": """query ClinicAgenda_ListClinicReservations( + $calendarIds: [UUID!], + $clinicSlug: String!, + $locale: Locale!, + $since: DateTime!, + $until: DateTime!, + $emptyCalendarIds: Boolean! + ) { + reservations: listClinicReservations( + clinicSlug: $clinicSlug, + calendarIds: $calendarIds, + since: $since, + until: $until + ) @skip(if: $emptyCalendarIds) { + id + start + end + note + done + color + request { + id + displayTitle(locale: $locale) + extendedPatient { + name + surname + dob + insuranceCompanyObject { shortName } + } + } + } + }""", +} + +print("πŸ“‘ Querying Medevio API for agenda of 17 Oct 2025...") +r = requests.post(GRAPHQL_URL, headers=headers, data=json.dumps(payload)) +print("Status:", r.status_code) + +try: + data = r.json() + print(json.dumps(data, indent=2, ensure_ascii=False)) +except Exception as e: + print("❌ Could not parse JSON:", e) + print(r.text) diff --git a/medevio_storage.json b/medevio_storage.json deleted file mode 100644 index 0841a74..0000000 --- a/medevio_storage.json +++ /dev/null @@ -1 +0,0 @@ -{"cookies": [{"name": "gateway-access-token", "value": "siiQdoYzEwMy3QUPQtHkUoJr6KGkdMJWX2xP47Bwr1SH8Tin4sROlJV/KpBlKl/bVViG9aktQOXmcmcY", "domain": "my.medevio.cz", "path": "/", "expires": 1761076304, "httpOnly": false, "secure": false, "sameSite": "Lax"}, {"name": "aws-waf-token", "value": "d1c38c75-3a6a-458d-b446-67df055372e9:CgoAliiKsQdKAAAA:9TJuIJZ01Qb0+kOyO8Ts9H7zh4kHnIldQcWNkJRmwzQJNDwQ/J/Jpw67r76cKM9jDfFXRYYbJtBPb/QJ7kbMcWckSZKB0fL2TKJhib9xLarYjvm2Eu+wTll08nisMUOa7LaroeMJ7nnE0m8jCRmoCz3c2+EWMjRHRJsJ9e+fDE4uXfYrALyBZryvX+7048AY17JW73UuNK21bus3ODXjOviZCFBh3OCHwC9IX0ClW4xrBCzN5uqtXT9OGm2aJKSWGs3IlZQ=", "domain": ".my.medevio.cz", "path": "/", "expires": 1758829907, "httpOnly": false, "secure": true, "sameSite": "Lax"}], "origins": [{"origin": "https://my.medevio.cz", "localStorage": [{"name": "awswaf_token_refresh_timestamp", "value": "1758484300098"}, {"name": "awswaf_session_storage", "value": "d1c38c75-3a6a-458d-b446-67df055372e9:CgoAliiKsQhKAAAA:FW9mIZy/Za1OyAHnRHgnpQBZWQz3rrOPnh2MOZ/83oVVADSY5xWChgKzF9ZZAg0Vd19PnOaE8AlAsw1KHD6xlmrBvlAlL/qzqxgWO8fmJmtrO3ZZbUrMyaqlQRLab0G85japL6jJKHQWcMtdj1lKnE17RqnoeVHX6FJvK6kvhIeVLp88j1TNTBkMcMJShnXMLn4F/l/GC91TcgnE1k4G6VWAbLzOqcRcxjWLe/boFkAWRiF5EhqO6By635TryNmgGiXQT5kXiGsHzxhUg17xewy+IHe6prGaq8UIpMXD7LMRFjlc8+RbQJAUwieztpmc1urc+GCqb3O9fGBLWiqekHPS+95bmr9x0lLAJOVNma11waGApkTusHADPWJ4c+eBfw+5OgGBQesBCtW/HG3Obj6Ou0dfTiUKG2XmXHpEsN6Xc2w3nTmyDzioSSMhfFmBCv9AJ8G9iVOyRIfknsLH8Wl8zgFkaJI7d5awiC3W/a1BV0HPONLQywrVq5l5TG3QpZs3GbQXUs1gHZjdI8hCoLeFJPSrxis3CwezKCs4qexfkyDRvzuqGS8Qy7s6hL/JqU3RiprpzzXOWNLVcDC+TpYArmH35yET1neb7k/iinozEjjW7Kp3cB4XXJpvR0dK+5NTva7+lw5HovGPs9UNj08a1LrfvdtzisI09vaSTVnYPiaWi3j4bUc+7FXEU2qfKoA51ZabIDkFwdbLkX6156KQls4fDEnj/1+/hWkCkBa/LfRg/KU9iDex96HxZi95pDqdVSe5Rgyt6PGDqCXXw8/tgw7v8qYMOr9zAQRqQc3zMHZvC4PqVVAfuXwy3mCJ9KY1gH1ZkofuR77TDbtn//vL2XAbSm1WhVOP8MF12O6C6q9sCx6nuypwCOY7v/Ix1JqgeZIWfSi0KkQ99EzRyFyTbcJO4A=="}, {"name": "Application.Intl.locale", "value": "cs"}, {"name": "Password.prefill", "value": "{\"username\":\"vladimir.buzalka@buzalka.cz\",\"type\":\"email\"}"}]}]} \ No newline at end of file