This commit is contained in:
2025-10-18 15:21:32 +02:00
parent 7332ee0734
commit 3038098f4e
6 changed files with 369 additions and 1 deletions

View File

@@ -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()

View File

@@ -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)

44
82 test.py Normal file
View File

@@ -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)

41
83_capture_graphql.py Normal file
View File

@@ -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}")

100
84 test.py Normal file
View File

@@ -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)

View File

@@ -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\"}"}]}]}