notebookvb

This commit is contained in:
Vladimir Buzalka
2026-05-31 07:51:29 +02:00
parent ac37a6e6db
commit a9ef60212d
17 changed files with 3590 additions and 0 deletions
+385
View File
@@ -0,0 +1,385 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Cteni agendy z Medevio kalendare.
Hlavni funkce: `list_agendu(start, end=None, calendar=None)`
start date | datetime | str 'YYYY-MM-DD' nebo 'YYYY-MM-DD HH:MM'
end to stejne; pokud None, bere se konec dne `start` (23:59:59)
calendar None = oba (vlado + manzelka), nebo "vlado" / "manzelka" / UUID,
nebo list techto hodnot
Vraci list dictu serazenych podle start. Kazdy dict obsahuje pole `calendar`
(jmeno) navic.
CLI:
python agenda_dne.py # interaktivne se zepta
python agenda_dne.py --od 2026-06-03 --do 2026-06-03 --kalendar vlado
python agenda_dne.py --od 2026-06-01 --do 2026-06-07 # tyden, oba
python agenda_dne.py --den 2026-06-03 # zkratka pro jeden den
python agenda_dne.py --den dnes
python agenda_dne.py --den +1 --kalendar manzelka
"""
import sys
import json
import argparse
from pathlib import Path
from datetime import datetime, date, timedelta
import requests
from dateutil import parser as dtparser, tz
try:
sys.stdout.reconfigure(encoding="utf-8")
except AttributeError:
pass
GRAPHQL_URL = "https://api.medevio.cz/graphql"
CLINIC_SLUG = "mudr-buzalkova"
PRAGUE_TZ = tz.gettz("Europe/Prague")
# Pojmenovane kalendare - viz pamet project-medevio-kalendar
CALENDARS = {
"vlado": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"manzelka": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
}
BASE_DIR = Path(__file__).resolve().parent
TOKEN_PATH = BASE_DIR / "token.txt"
DEBUG_DIR = BASE_DIR / "debug"
QUERY = """query Agenda_ListAll(
$calendarIds: [UUID!]!, $clinicSlug: String!,
$locale: Locale!, $since: DateTime!, $until: DateTime!
) {
reservations: listClinicReservations(
clinicSlug: $clinicSlug, calendarIds: $calendarIds,
since: $since, until: $until
) {
id start end note done color canceledAt calendarId
request {
id displayTitle(locale: $locale)
extendedPatient {
id name surname dob phone identificationNumber
insuranceCompanyObject { code shortName }
}
}
}
recurringReservations: listClinicRecurringReservations(
clinicSlug: $clinicSlug, calendarIds: $calendarIds,
since: $since, until: $until
) {
recurringReservation {
id calendarId color note
rrule { frequency interval dtstart tzid byweekday bymonthday byweekno }
}
instances { start end note color }
}
}"""
# ==================== Helpery ====================
def _load_token() -> str:
token = TOKEN_PATH.read_text(encoding="utf-8").strip()
if token.startswith("Bearer "):
token = token.split(" ", 1)[1]
return token
def _headers() -> dict:
return {
"content-type": "application/json",
"authorization": f"Bearer {_load_token()}",
"origin": "https://my.medevio.cz",
"referer": "https://my.medevio.cz/",
}
def _is_uuid(s: str) -> bool:
return isinstance(s, str) and len(s) == 36 and s.count("-") == 4
def _resolve_calendars(calendar) -> list[tuple[str, str]]:
"""Vraci list (jmeno, uuid). None = oba pojmenovane."""
if calendar is None:
return list(CALENDARS.items())
items = calendar if isinstance(calendar, list) else [calendar]
out = []
for c in items:
if c in CALENDARS:
out.append((c, CALENDARS[c]))
elif _is_uuid(c):
# zkusime najit jmeno
name = next((n for n, u in CALENDARS.items() if u == c), c)
out.append((name, c))
else:
raise ValueError(f"Neznamy kalendar: {c!r}")
return out
def _to_dt(value, end_of_day: bool = False) -> datetime:
"""Prevede date/datetime/str na datetime s Europe/Prague timezone."""
if isinstance(value, datetime):
dt = value
elif isinstance(value, date):
dt = datetime.combine(
value,
datetime.max.time().replace(microsecond=0) if end_of_day else datetime.min.time(),
)
elif isinstance(value, str):
s = value.strip().replace("T", " ")
if " " in s:
dt = datetime.strptime(s, "%Y-%m-%d %H:%M")
else:
d = datetime.strptime(s, "%Y-%m-%d").date()
dt = datetime.combine(
d,
datetime.max.time().replace(microsecond=0) if end_of_day else datetime.min.time(),
)
else:
raise TypeError(f"Nelze prevest na datetime: {value!r}")
if dt.tzinfo is None:
dt = dt.replace(tzinfo=PRAGUE_TZ)
return dt
def _to_utc_iso(dt: datetime) -> str:
return dt.astimezone(tz.UTC).strftime("%Y-%m-%dT%H:%M:%S.000Z")
# ==================== Hlavni funkce ====================
def list_agendu(
start,
end=None,
calendar=None,
save_debug: bool = True,
) -> list[dict]:
"""Vraci rezervace v zadanem rozmezi pro vybrane kalendare.
Pokud `end` neni zadan, pouzije se konec stejneho dne jako `start`.
Pokud `calendar` je None, vrati se oba pojmenovane kalendare slouceny.
"""
start_dt = _to_dt(start, end_of_day=False)
if end is None:
end_dt = _to_dt(start_dt.date(), end_of_day=True)
else:
end_dt = _to_dt(end, end_of_day=True)
cals = _resolve_calendars(calendar)
payload = {
"operationName": "Agenda_ListAll",
"variables": {
"calendarIds": [uuid for _, uuid in cals],
"clinicSlug": CLINIC_SLUG,
"since": _to_utc_iso(start_dt),
"until": _to_utc_iso(end_dt),
"locale": "cs",
},
"query": QUERY,
}
r = requests.post(GRAPHQL_URL, headers=_headers(), data=json.dumps(payload), timeout=30)
r.raise_for_status()
data = r.json()
if save_debug:
DEBUG_DIR.mkdir(exist_ok=True)
debug_path = DEBUG_DIR / f"agenda_{start_dt:%Y%m%d}_{end_dt:%Y%m%d}_{datetime.now():%H%M%S}.json"
debug_path.write_text(
json.dumps({"request": payload["variables"], "response": data}, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"[debug] {debug_path}")
if "errors" in data:
raise RuntimeError(f"GraphQL error: {data['errors']}")
uuid_to_name = {uuid: name for name, uuid in cals}
parsed = []
# 1) Jednorazove rezervace
for res in data["data"].get("reservations") or []:
if res.get("canceledAt"):
continue
s = dtparser.isoparse(res["start"]).astimezone(PRAGUE_TZ)
e = dtparser.isoparse(res["end"]).astimezone(PRAGUE_TZ)
req = res.get("request") or {}
pat = req.get("extendedPatient") or {}
ins = pat.get("insuranceCompanyObject") or {}
request_id = req.get("id") or ""
parsed.append({
"typ": "pacient" if request_id else "poznamka",
"calendar": uuid_to_name.get(res.get("calendarId"), res.get("calendarId")),
"calendar_id": res.get("calendarId"),
"start": s,
"end": e,
"title": req.get("displayTitle") or "",
"patient": f"{pat.get('surname','')} {pat.get('name','')}".strip(),
"dob": pat.get("dob") or "",
"rc": pat.get("identificationNumber") or "",
"phone": pat.get("phone") or "",
"insurance": ins.get("shortName") or "",
"insurance_code": ins.get("code") or "",
"note": (res.get("note") or "").strip(),
"done": bool(res.get("done")),
"color": res.get("color") or "",
"reservation_id": res["id"],
"request_id": request_id,
"patient_id": pat.get("id") or "",
"recurring": False,
})
# 2) Opakujici se - jednotlive instance v intervalu
for rr in data["data"].get("recurringReservations") or []:
rule = rr.get("recurringReservation") or {}
cal_id = rule.get("calendarId")
for inst in rr.get("instances") or []:
s = dtparser.isoparse(inst["start"]).astimezone(PRAGUE_TZ)
e = dtparser.isoparse(inst["end"]).astimezone(PRAGUE_TZ)
parsed.append({
"typ": "poznamka", # opakujici se vznikaji vzdy pres "Jina udalost"
"calendar": uuid_to_name.get(cal_id, cal_id),
"calendar_id": cal_id,
"start": s,
"end": e,
"title": "",
"patient": "",
"dob": "", "rc": "", "phone": "",
"insurance": "", "insurance_code": "",
"note": (inst.get("note") or rule.get("note") or "").strip(),
"done": False,
"color": inst.get("color") or rule.get("color") or "",
"reservation_id": rule.get("id"),
"request_id": "",
"patient_id": "",
"recurring": True,
"rrule": rule.get("rrule"),
})
parsed.sort(key=lambda x: (x["start"], x["calendar"]))
return parsed
# Zachovani zpetne kompatibility
def get_agenda(day, calendar=None) -> list[dict]:
"""Vraci agendu jednoho dne. Pro zpetnou kompat se starsim API."""
return list_agendu(day, end=day, calendar=calendar)
# ==================== Vystup ====================
def print_agenda(reservations: list[dict], header: str | None = None) -> None:
cz_dny = ["pondeli", "utery", "streda", "ctvrtek", "patek", "sobota", "nedele"]
if header is None:
if reservations:
dates = sorted({r["start"].date() for r in reservations})
if len(dates) == 1:
header = f"Agenda {dates[0].isoformat()} ({cz_dny[dates[0].weekday()]})"
else:
header = f"Agenda {dates[0].isoformat()} - {dates[-1].isoformat()}"
else:
header = "Agenda"
print(header)
print("=" * len(header))
if not reservations:
print("(zadne rezervace)")
return
last_date = None
for r in reservations:
if r["start"].date() != last_date:
if last_date is not None:
print()
print(f"--- {r['start'].date().isoformat()} ({cz_dny[r['start'].weekday()]}) ---")
last_date = r["start"].date()
time_str = f"{r['start']:%H:%M}-{r['end']:%H:%M}"
cal = f"[{r['calendar']}]" if r["calendar"] else ""
typ = f"<{r.get('typ','?')}>"
flags = []
if r["done"]:
flags.append("HOTOVO")
if r.get("recurring"):
flags.append("OPAKOVANE")
flag_str = f" [{', '.join(flags)}]" if flags else ""
label = r["patient"] if r.get("typ") == "pacient" else (r.get("note") or "(bez popisu)")
line = f"{time_str} {cal} {typ} {label}"
if r["dob"]:
line += f" *{r['dob']}"
if r["insurance"]:
line += f" {r['insurance']}"
line += flag_str
print(line)
if r["title"]:
print(f" {r['title']}")
if r["note"]:
for ln in r["note"].splitlines():
print(f" poznamka: {ln}")
print()
print(f"Celkem: {len(reservations)} rezervaci")
# ==================== CLI ====================
def _parse_day_arg(arg: str | None) -> date:
if not arg or arg in ("dnes", "today"):
return date.today()
if arg in ("zitra", "tomorrow"):
return date.today() + timedelta(days=1)
if arg in ("vcera", "yesterday"):
return date.today() - timedelta(days=1)
if arg.startswith(("+", "-")) and arg[1:].isdigit():
return date.today() + timedelta(days=int(arg))
return datetime.strptime(arg, "%Y-%m-%d").date()
def _prompt_interactive() -> tuple[date, date, str | None]:
cz_dny = ["pondeli", "utery", "streda", "ctvrtek", "patek", "sobota", "nedele"]
today = date.today()
print("Cteni agendy z Medevio")
print(f"Dnes je {today.isoformat()} ({cz_dny[today.weekday()]})")
print()
print("Od (YYYY-MM-DD / +N / -N / dnes / zitra / vcera, Enter = dnes):")
od = _parse_day_arg(input("> ").strip() or None)
print(f"Do (Enter = stejny den jako od = {od.isoformat()}):")
do_raw = input("> ").strip()
do = _parse_day_arg(do_raw) if do_raw else od
print(f"Kalendar (vlado / manzelka / Enter = oba):")
cal = input("> ").strip() or None
return od, do, cal
def main():
ap = argparse.ArgumentParser(description="Cteni agendy z Medevio.")
ap.add_argument("--od", help="Pocatecni datum 'YYYY-MM-DD' / +N / -N / dnes/zitra/vcera")
ap.add_argument("--do", dest="do_", help="Koncove datum (default = stejny jako --od)")
ap.add_argument("--den", help="Zkratka: --od i --do nastaveny na tento den")
ap.add_argument("--kalendar", default=None,
help=f"{'/'.join(CALENDARS)} nebo UUID. Bez parametru = oba.")
args = ap.parse_args()
if args.den:
od = do = _parse_day_arg(args.den)
cal = args.kalendar
elif args.od:
od = _parse_day_arg(args.od)
do = _parse_day_arg(args.do_) if args.do_ else od
cal = args.kalendar
else:
od, do, cal = _prompt_interactive()
reservations = list_agendu(od, do, calendar=cal)
print_agenda(reservations)
if __name__ == "__main__":
main()
@@ -0,0 +1,178 @@
{
"request": {
"calendarIds": [
"144c4e12-347c-49ca-9ec0-8ca965a4470d"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-28T22:00:00.000Z",
"until": "2026-05-29T22:00:00.000Z",
"locale": "cs",
"emptyCalendarIds": false
},
"response": {
"data": {
"reservations": [
{
"id": "6e02c736-6060-41bb-90f5-a9dcd309bee4",
"start": "2026-05-29T09:40:00.000Z",
"end": "2026-05-29T09:50:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "98b890a9-bc2b-42c8-859f-fe109f09e1f6",
"displayTitle": "Očkování - Černý kašel (kombinovaná vakcína Adacel [černý kašel, tetanus, záškrt])",
"extendedPatient": {
"id": "92e7be7e-e41b-4181-afc3-624a82bd94f2",
"name": "Nicol",
"surname": "Slaninová",
"dob": "1978-12-20",
"phone": "+420777145986",
"identificationNumber": "7862202920",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "e47d9c64-b84e-4a89-bb3e-77e96db55e84",
"start": "2026-05-29T08:45:00.000Z",
"end": "2026-05-29T09:30:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "9154f33e-6724-450c-af58-3abd0a4778da",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "17a5563e-4044-4c67-b9fd-928be546ea5a",
"name": "Martin",
"surname": "Štoček",
"dob": "1989-10-19",
"phone": "+420604131463",
"identificationNumber": "8910193336",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "8f0a84e9-1f88-4f94-b826-7364095284fc",
"start": "2026-05-29T10:00:00.000Z",
"end": "2026-05-29T10:05:00.000Z",
"note": "ukončit PN Pelcová E.",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"request": null
},
{
"id": "565cf3f0-ad44-485f-9433-18e271f429c1",
"start": "2026-05-29T09:30:00.000Z",
"end": "2026-05-29T09:50:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "4ae28457-ee0c-46f0-89b1-23075cfcbe81",
"displayTitle": "Konzultace zdravotního stavu ",
"extendedPatient": {
"id": "88a2c4f3-ad3e-472b-899c-c86b72d6f2e7",
"name": "Hana",
"surname": "Hlavsová",
"dob": "1941-09-06",
"phone": null,
"identificationNumber": "415906003",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "c87c1cfe-2360-478c-8820-a9d92a47511e",
"start": "2026-05-29T09:50:00.000Z",
"end": "2026-05-29T10:00:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "0067cd89-ccb0-4b23-9548-260e2efd2d45",
"displayTitle": "Odběry",
"extendedPatient": {
"id": "fbb78b75-1c61-4915-8d06-1d7ceb6f9450",
"name": "Petra",
"surname": "Zelenková",
"dob": "1983-01-11",
"phone": "+420731585469",
"identificationNumber": "8351112693",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "9b3ab4a3-47aa-4438-9dd7-5289d74a16a5",
"start": "2026-05-29T07:30:00.000Z",
"end": "2026-05-29T08:00:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "c2dd4666-4be3-4543-ad02-bafb05694bd3",
"displayTitle": "Předoperační vyšetření",
"extendedPatient": {
"id": "e6d965dd-b1ba-44c6-a0ba-915a83d45770",
"name": "Markéta",
"surname": "Bečicová",
"dob": "1967-08-12",
"phone": "+420736540111",
"identificationNumber": "6758120446",
"insuranceCompanyObject": {
"code": 205,
"shortName": "ČPZP"
}
}
}
},
{
"id": "86a379f8-3479-4c5e-86b8-550afc55491f",
"start": "2026-05-29T08:00:00.000Z",
"end": "2026-05-29T08:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "97e97780-1b9d-4c4d-be99-f05b25cad2fa",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "a72e32cb-752e-4b3b-bcce-fca9f142c76a",
"name": "Eva",
"surname": "Zemanová",
"dob": "1939-09-12",
"phone": "+420739555303",
"identificationNumber": "395912079",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
}
]
}
}
}
@@ -0,0 +1,268 @@
{
"request": {
"calendarIds": [
"144c4e12-347c-49ca-9ec0-8ca965a4470d"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-06-02T22:00:00.000Z",
"until": "2026-06-03T22:00:00.000Z",
"locale": "cs",
"emptyCalendarIds": false
},
"response": {
"data": {
"reservations": [
{
"id": "bc275236-970f-4b88-887e-b4c5f286e3a9",
"start": "2026-06-03T08:00:00.000Z",
"end": "2026-06-03T08:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "bebb68b3-f9e1-48e0-9e14-b72e5781b4c4",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "86931ca3-cfd4-4aed-af66-244cecc691ee",
"name": "Sylvie",
"surname": "Rejfířová",
"dob": "1980-06-01",
"phone": "+420602266614",
"identificationNumber": "8056010149",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "73844dfa-316f-47da-ac3d-2554ff5aec7f",
"start": "2026-06-03T08:45:00.000Z",
"end": "2026-06-03T09:00:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "97dd7ace-1668-479b-a09b-44d775e53227",
"displayTitle": "Kontrola INR (Quick)",
"extendedPatient": {
"id": "d8c2f2b6-fdf7-462e-91de-fce908aaf3de",
"name": "Jaroslav",
"surname": "Kameník",
"dob": "1940-05-10",
"phone": "+420776702345",
"identificationNumber": "400510088",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "c2c96faa-4129-4815-a213-ffdd887d42f8",
"start": "2026-06-03T07:00:00.000Z",
"end": "2026-06-03T07:30:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "4ee1cee1-5ccd-425d-85b1-3896d850a784",
"displayTitle": "Prohlídka při léčbě cukrovky",
"extendedPatient": {
"id": "0aeefc63-8599-4602-b731-b7c81e9106a2",
"name": "Miloslav",
"surname": "Hájek",
"dob": "1974-10-24",
"phone": "+420602262242",
"identificationNumber": "7410241014",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "906ca466-eb83-4a65-bc2e-90020714d4e9",
"start": "2026-06-03T09:00:00.000Z",
"end": "2026-06-03T09:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "6790af69-4416-4ef8-9101-7e3f7d668320",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "7bcf8cea-efe7-4f90-8e91-c8f88ba72ee6",
"name": "Denisa",
"surname": "Ryšavá",
"dob": "2007-10-24",
"phone": "+420606815415",
"identificationNumber": "0760245079",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "a91481ba-cf7c-4968-bd54-8424565cae3d",
"start": "2026-06-03T09:45:00.000Z",
"end": "2026-06-03T09:55:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "c0ac9fdb-5a82-4306-9d1c-ed22110205fe",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "ec4db98b-cce8-473c-a47e-ef24bf61d3d8",
"name": "Romana",
"surname": "Ratkiewiczová",
"dob": "1990-04-15",
"phone": "+420737261867",
"identificationNumber": "9054151128",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "b5f01f0c-0c9f-42bb-a7bd-f0a725638d38",
"start": "2026-06-03T10:00:00.000Z",
"end": "2026-06-03T10:10:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "48d490fa-0fd1-4a16-aba8-79f275043345",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "d8d36c4d-19fc-4405-9bf9-f9a733fb2dad",
"name": "Jana",
"surname": "Ptáčková",
"dob": "1947-09-17",
"phone": "+420604363791",
"identificationNumber": "475917011",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "44d6f66b-95b9-4bfb-b2c9-69338a7cae8f",
"start": "2026-06-03T10:10:00.000Z",
"end": "2026-06-03T10:20:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "30e1d256-eaaf-4a0b-aa13-cd511e4e309e",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "bb27b85a-8703-4768-86f4-2cbf4c196dd1",
"name": "Karel",
"surname": "Galus",
"dob": "1946-06-14",
"phone": "+420605230820",
"identificationNumber": "460614110",
"insuranceCompanyObject": {
"code": 211,
"shortName": "ZPMV"
}
}
}
},
{
"id": "b128a573-849d-4bf2-9317-01142147e61e",
"start": "2026-06-03T10:30:00.000Z",
"end": "2026-06-03T10:50:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "bbd0e36a-5148-43d2-8095-fcf746586778",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "5a0a9ff0-bbe8-4fc7-a27d-b7b475ee2189",
"name": "Lenka",
"surname": "Balousova",
"dob": "1972-03-28",
"phone": "+420603560064",
"identificationNumber": "7253282355",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "c0bbc7bc-2491-4143-a6b9-59d4fab9df63",
"start": "2026-06-03T10:50:00.000Z",
"end": "2026-06-03T11:05:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "b0f01a56-6ae6-47fa-94bf-e693977e127b",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "3e1090b7-d0c2-4dd8-bbfe-dc464d2f1671",
"name": "Lenka",
"surname": "Vaněčková",
"dob": "1943-03-17",
"phone": "+420776599498",
"identificationNumber": "435317067",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "9d2f969b-da52-418c-8465-d29021beab49",
"start": "2026-06-03T11:15:00.000Z",
"end": "2026-06-03T11:25:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"request": {
"id": "019ec07d-b454-4231-9b49-9389fe675faa",
"displayTitle": "Očkování - Klíšťová encefalitida",
"extendedPatient": {
"id": "865e0404-b2c8-4635-8d8d-df7a14275b14",
"name": "Jiří",
"surname": "Chomát",
"dob": "1938-03-14",
"phone": "+420737217617",
"identificationNumber": "380314026",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
}
]
}
}
}
@@ -0,0 +1,63 @@
{
"request": {
"calendarIds": [
"b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"144c4e12-347c-49ca-9ec0-8ca965a4470d"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-29T22:00:00.000Z",
"until": "2026-05-30T21:59:59.000Z",
"locale": "cs",
"emptyCalendarIds": false
},
"response": {
"data": {
"reservations": [
{
"id": "afe20509-c476-48e9-83eb-fb02e161c6d8",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "aee6f9c4-1487-4934-a32a-8a7b1517271b",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "c646d60b-40ca-4f32-8a11-052ab90f5d2c",
"start": "2026-05-30T07:00:00.000Z",
"end": "2026-05-30T07:05:00.000Z",
"note": "TEST poznamka z Claude Code",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "3ac6849a-12e2-4248-a99d-ec9a577ce820",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "Test 2 z Claude Code",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
}
]
}
}
}
@@ -0,0 +1,295 @@
{
"request": {
"calendarIds": [
"b6555c7e-4e95-4657-b441-87c2c9a7b2ca"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-29T22:00:00.000Z",
"until": "2026-05-30T21:59:59.000Z",
"locale": "cs"
},
"response": {
"data": {
"reservations": [
{
"id": "afe20509-c476-48e9-83eb-fb02e161c6d8",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "aee6f9c4-1487-4934-a32a-8a7b1517271b",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "c646d60b-40ca-4f32-8a11-052ab90f5d2c",
"start": "2026-05-30T07:00:00.000Z",
"end": "2026-05-30T07:05:00.000Z",
"note": "TEST poznamka z Claude Code",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "3ac6849a-12e2-4248-a99d-ec9a577ce820",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "Test 2 z Claude Code",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
}
],
"recurringReservations": [
{
"recurringReservation": {
"id": "ccadcdbd-d5b3-47ec-9b18-414c6dd0d105",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "TEST denne",
"rrule": {
"frequency": "DAILY",
"interval": 1,
"dtstart": "2026-05-30T14:40:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T14:40:00.000Z",
"end": "2026-05-30T14:45:00.000Z",
"note": "TEST denne",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "280cc437-2ff0-4d74-b16a-a6175d2b1020",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T2",
"rrule": {
"frequency": "DAILY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"MO",
"TU",
"WE",
"TH",
"FR"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "89f850f5-19dc-48b8-a24d-9173b80aa095",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T3",
"rrule": {
"frequency": "WEEKLY",
"interval": 1,
"dtstart": "2026-05-30T15:05:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T15:05:00.000Z",
"end": "2026-05-30T15:10:00.000Z",
"note": "T3",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "f38f7185-8050-46ee-a2c4-0ca590cc3ab9",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T4",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
1,
3,
5,
7,
9,
11,
13,
15,
17,
19,
21,
23,
25,
27,
29,
31,
33,
35,
37,
39,
41,
43,
45,
47,
49,
51,
53
]
}
},
"instances": []
},
{
"recurringReservation": {
"id": "357c63bc-6278-4867-acf6-e6d3124eec53",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T5",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
2,
4,
6,
8,
10,
12,
14,
16,
18,
20,
22,
24,
26,
28,
30,
32,
34,
36,
38,
40,
42,
44,
46,
48,
50,
52
]
}
},
"instances": [
{
"start": "2026-05-30T15:35:00.000Z",
"end": "2026-05-30T15:40:00.000Z",
"note": "T5",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "eee3904f-475d-4932-914a-fff9ada8fb6c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T6",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:45:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": [
30
],
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T15:45:00.000Z",
"end": "2026-05-30T15:50:00.000Z",
"note": "T6",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "95c5c675-68a2-4181-9d6d-1ebc6d2e6a4c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T7",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"-1SA"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T15:20:00.000Z",
"end": "2026-05-30T15:25:00.000Z",
"note": "T7",
"color": "CHARCOAL"
}
]
}
]
}
}
}
@@ -0,0 +1,232 @@
{
"request": {
"calendarIds": [
"b6555c7e-4e95-4657-b441-87c2c9a7b2ca"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-29T22:00:00.000Z",
"until": "2026-05-30T21:59:59.000Z",
"locale": "cs"
},
"response": {
"data": {
"reservations": [
{
"id": "afe20509-c476-48e9-83eb-fb02e161c6d8",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "aee6f9c4-1487-4934-a32a-8a7b1517271b",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "TEST",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
},
{
"id": "3ac6849a-12e2-4248-a99d-ec9a577ce820",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z",
"note": "Test 2 z Claude Code",
"done": null,
"color": "CHARCOAL",
"canceledAt": null,
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"request": null
}
],
"recurringReservations": [
{
"recurringReservation": {
"id": "280cc437-2ff0-4d74-b16a-a6175d2b1020",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T2",
"rrule": {
"frequency": "DAILY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"MO",
"TU",
"WE",
"TH",
"FR"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "f38f7185-8050-46ee-a2c4-0ca590cc3ab9",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T4",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
1,
3,
5,
7,
9,
11,
13,
15,
17,
19,
21,
23,
25,
27,
29,
31,
33,
35,
37,
39,
41,
43,
45,
47,
49,
51,
53
]
}
},
"instances": []
},
{
"recurringReservation": {
"id": "357c63bc-6278-4867-acf6-e6d3124eec53",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T5",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
2,
4,
6,
8,
10,
12,
14,
16,
18,
20,
22,
24,
26,
28,
30,
32,
34,
36,
38,
40,
42,
44,
46,
48,
50,
52
]
}
},
"instances": [
{
"start": "2026-05-30T15:35:00.000Z",
"end": "2026-05-30T15:40:00.000Z",
"note": "T5",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "eee3904f-475d-4932-914a-fff9ada8fb6c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T6",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:45:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": [
30
],
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T15:45:00.000Z",
"end": "2026-05-30T15:50:00.000Z",
"note": "T6",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "95c5c675-68a2-4181-9d6d-1ebc6d2e6a4c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T7",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"-1SA"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-30T15:20:00.000Z",
"end": "2026-05-30T15:25:00.000Z",
"note": "T7",
"color": "CHARCOAL"
}
]
}
]
}
}
}
@@ -0,0 +1,17 @@
{
"request": {
"calendarIds": [
"b6555c7e-4e95-4657-b441-87c2c9a7b2ca"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-30T22:00:00.000Z",
"until": "2026-05-31T21:59:59.000Z",
"locale": "cs",
"emptyCalendarIds": false
},
"response": {
"data": {
"reservations": []
}
}
}
@@ -0,0 +1,247 @@
{
"request": {
"calendarIds": [
"b6555c7e-4e95-4657-b441-87c2c9a7b2ca"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-05-30T22:00:00.000Z",
"until": "2026-05-31T21:59:59.000Z",
"locale": "cs"
},
"response": {
"data": {
"reservations": [],
"recurringReservations": [
{
"recurringReservation": {
"id": "ccadcdbd-d5b3-47ec-9b18-414c6dd0d105",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "TEST denne",
"rrule": {
"frequency": "DAILY",
"interval": 1,
"dtstart": "2026-05-30T14:40:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-31T14:40:00.000Z",
"end": "2026-05-31T14:45:00.000Z",
"note": "TEST denne",
"color": "CHARCOAL"
}
]
},
{
"recurringReservation": {
"id": "280cc437-2ff0-4d74-b16a-a6175d2b1020",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T2",
"rrule": {
"frequency": "DAILY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"MO",
"TU",
"WE",
"TH",
"FR"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "89f850f5-19dc-48b8-a24d-9173b80aa095",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T3",
"rrule": {
"frequency": "WEEKLY",
"interval": 1,
"dtstart": "2026-05-30T15:05:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "f38f7185-8050-46ee-a2c4-0ca590cc3ab9",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T4",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
1,
3,
5,
7,
9,
11,
13,
15,
17,
19,
21,
23,
25,
27,
29,
31,
33,
35,
37,
39,
41,
43,
45,
47,
49,
51,
53
]
}
},
"instances": []
},
{
"recurringReservation": {
"id": "357c63bc-6278-4867-acf6-e6d3124eec53",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T5",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-30T15:35:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"SA"
],
"bymonthday": null,
"byweekno": [
2,
4,
6,
8,
10,
12,
14,
16,
18,
20,
22,
24,
26,
28,
30,
32,
34,
36,
38,
40,
42,
44,
46,
48,
50,
52
]
}
},
"instances": []
},
{
"recurringReservation": {
"id": "eee3904f-475d-4932-914a-fff9ada8fb6c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T6",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:45:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": [
30
],
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "95c5c675-68a2-4181-9d6d-1ebc6d2e6a4c",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T7",
"rrule": {
"frequency": "MONTHLY",
"interval": 1,
"dtstart": "2026-05-30T15:20:00.000Z",
"tzid": "Europe/Prague",
"byweekday": [
"-1SA"
],
"bymonthday": null,
"byweekno": null
}
},
"instances": []
},
{
"recurringReservation": {
"id": "f675ca4f-1169-4b3b-8266-b171d23849c8",
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"color": "CHARCOAL",
"note": "T8",
"rrule": {
"frequency": "YEARLY",
"interval": 1,
"dtstart": "2026-05-31T15:05:00.000Z",
"tzid": "Europe/Prague",
"byweekday": null,
"bymonthday": null,
"byweekno": null
}
},
"instances": [
{
"start": "2026-05-31T15:05:00.000Z",
"end": "2026-05-31T15:10:00.000Z",
"note": "T8",
"color": "CHARCOAL"
}
]
}
]
}
}
}
@@ -0,0 +1,278 @@
{
"request": {
"calendarIds": [
"144c4e12-347c-49ca-9ec0-8ca965a4470d"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-06-02T22:00:00.000Z",
"until": "2026-06-03T21:59:59.000Z",
"locale": "cs",
"emptyCalendarIds": false
},
"response": {
"data": {
"reservations": [
{
"id": "bc275236-970f-4b88-887e-b4c5f286e3a9",
"start": "2026-06-03T08:00:00.000Z",
"end": "2026-06-03T08:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "bebb68b3-f9e1-48e0-9e14-b72e5781b4c4",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "86931ca3-cfd4-4aed-af66-244cecc691ee",
"name": "Sylvie",
"surname": "Rejfířová",
"dob": "1980-06-01",
"phone": "+420602266614",
"identificationNumber": "8056010149",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "73844dfa-316f-47da-ac3d-2554ff5aec7f",
"start": "2026-06-03T08:45:00.000Z",
"end": "2026-06-03T09:00:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "97dd7ace-1668-479b-a09b-44d775e53227",
"displayTitle": "Kontrola INR (Quick)",
"extendedPatient": {
"id": "d8c2f2b6-fdf7-462e-91de-fce908aaf3de",
"name": "Jaroslav",
"surname": "Kameník",
"dob": "1940-05-10",
"phone": "+420776702345",
"identificationNumber": "400510088",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "c2c96faa-4129-4815-a213-ffdd887d42f8",
"start": "2026-06-03T07:00:00.000Z",
"end": "2026-06-03T07:30:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "4ee1cee1-5ccd-425d-85b1-3896d850a784",
"displayTitle": "Prohlídka při léčbě cukrovky",
"extendedPatient": {
"id": "0aeefc63-8599-4602-b731-b7c81e9106a2",
"name": "Miloslav",
"surname": "Hájek",
"dob": "1974-10-24",
"phone": "+420602262242",
"identificationNumber": "7410241014",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "906ca466-eb83-4a65-bc2e-90020714d4e9",
"start": "2026-06-03T09:00:00.000Z",
"end": "2026-06-03T09:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "6790af69-4416-4ef8-9101-7e3f7d668320",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "7bcf8cea-efe7-4f90-8e91-c8f88ba72ee6",
"name": "Denisa",
"surname": "Ryšavá",
"dob": "2007-10-24",
"phone": "+420606815415",
"identificationNumber": "0760245079",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "a91481ba-cf7c-4968-bd54-8424565cae3d",
"start": "2026-06-03T09:45:00.000Z",
"end": "2026-06-03T09:55:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "c0ac9fdb-5a82-4306-9d1c-ed22110205fe",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "ec4db98b-cce8-473c-a47e-ef24bf61d3d8",
"name": "Romana",
"surname": "Ratkiewiczová",
"dob": "1990-04-15",
"phone": "+420737261867",
"identificationNumber": "9054151128",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "b5f01f0c-0c9f-42bb-a7bd-f0a725638d38",
"start": "2026-06-03T10:00:00.000Z",
"end": "2026-06-03T10:10:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "48d490fa-0fd1-4a16-aba8-79f275043345",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "d8d36c4d-19fc-4405-9bf9-f9a733fb2dad",
"name": "Jana",
"surname": "Ptáčková",
"dob": "1947-09-17",
"phone": "+420604363791",
"identificationNumber": "475917011",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "44d6f66b-95b9-4bfb-b2c9-69338a7cae8f",
"start": "2026-06-03T10:10:00.000Z",
"end": "2026-06-03T10:20:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "30e1d256-eaaf-4a0b-aa13-cd511e4e309e",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "bb27b85a-8703-4768-86f4-2cbf4c196dd1",
"name": "Karel",
"surname": "Galus",
"dob": "1946-06-14",
"phone": "+420605230820",
"identificationNumber": "460614110",
"insuranceCompanyObject": {
"code": 211,
"shortName": "ZPMV"
}
}
}
},
{
"id": "b128a573-849d-4bf2-9317-01142147e61e",
"start": "2026-06-03T10:30:00.000Z",
"end": "2026-06-03T10:50:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "bbd0e36a-5148-43d2-8095-fcf746586778",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "5a0a9ff0-bbe8-4fc7-a27d-b7b475ee2189",
"name": "Lenka",
"surname": "Balousova",
"dob": "1972-03-28",
"phone": "+420603560064",
"identificationNumber": "7253282355",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "c0bbc7bc-2491-4143-a6b9-59d4fab9df63",
"start": "2026-06-03T10:50:00.000Z",
"end": "2026-06-03T11:05:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "b0f01a56-6ae6-47fa-94bf-e693977e127b",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "3e1090b7-d0c2-4dd8-bbfe-dc464d2f1671",
"name": "Lenka",
"surname": "Vaněčková",
"dob": "1943-03-17",
"phone": "+420776599498",
"identificationNumber": "435317067",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "9d2f969b-da52-418c-8465-d29021beab49",
"start": "2026-06-03T11:15:00.000Z",
"end": "2026-06-03T11:25:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "019ec07d-b454-4231-9b49-9389fe675faa",
"displayTitle": "Očkování - Klíšťová encefalitida",
"extendedPatient": {
"id": "865e0404-b2c8-4635-8d8d-df7a14275b14",
"name": "Jiří",
"surname": "Chomát",
"dob": "1938-03-14",
"phone": "+420737217617",
"identificationNumber": "380314026",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
}
]
}
}
}
@@ -0,0 +1,278 @@
{
"request": {
"calendarIds": [
"144c4e12-347c-49ca-9ec0-8ca965a4470d"
],
"clinicSlug": "mudr-buzalkova",
"since": "2026-06-02T22:00:00.000Z",
"until": "2026-06-03T21:59:59.000Z",
"locale": "cs"
},
"response": {
"data": {
"reservations": [
{
"id": "bc275236-970f-4b88-887e-b4c5f286e3a9",
"start": "2026-06-03T08:00:00.000Z",
"end": "2026-06-03T08:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "bebb68b3-f9e1-48e0-9e14-b72e5781b4c4",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "86931ca3-cfd4-4aed-af66-244cecc691ee",
"name": "Sylvie",
"surname": "Rejfířová",
"dob": "1980-06-01",
"phone": "+420602266614",
"identificationNumber": "8056010149",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "73844dfa-316f-47da-ac3d-2554ff5aec7f",
"start": "2026-06-03T08:45:00.000Z",
"end": "2026-06-03T09:00:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "97dd7ace-1668-479b-a09b-44d775e53227",
"displayTitle": "Kontrola INR (Quick)",
"extendedPatient": {
"id": "d8c2f2b6-fdf7-462e-91de-fce908aaf3de",
"name": "Jaroslav",
"surname": "Kameník",
"dob": "1940-05-10",
"phone": "+420776702345",
"identificationNumber": "400510088",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "c2c96faa-4129-4815-a213-ffdd887d42f8",
"start": "2026-06-03T07:00:00.000Z",
"end": "2026-06-03T07:30:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "4ee1cee1-5ccd-425d-85b1-3896d850a784",
"displayTitle": "Prohlídka při léčbě cukrovky",
"extendedPatient": {
"id": "0aeefc63-8599-4602-b731-b7c81e9106a2",
"name": "Miloslav",
"surname": "Hájek",
"dob": "1974-10-24",
"phone": "+420602262242",
"identificationNumber": "7410241014",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "906ca466-eb83-4a65-bc2e-90020714d4e9",
"start": "2026-06-03T09:00:00.000Z",
"end": "2026-06-03T09:45:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "6790af69-4416-4ef8-9101-7e3f7d668320",
"displayTitle": "Preventivní prohlídka nebo vstupní prohlídka",
"extendedPatient": {
"id": "7bcf8cea-efe7-4f90-8e91-c8f88ba72ee6",
"name": "Denisa",
"surname": "Ryšavá",
"dob": "2007-10-24",
"phone": "+420606815415",
"identificationNumber": "0760245079",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "a91481ba-cf7c-4968-bd54-8424565cae3d",
"start": "2026-06-03T09:45:00.000Z",
"end": "2026-06-03T09:55:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "c0ac9fdb-5a82-4306-9d1c-ed22110205fe",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "ec4db98b-cce8-473c-a47e-ef24bf61d3d8",
"name": "Romana",
"surname": "Ratkiewiczová",
"dob": "1990-04-15",
"phone": "+420737261867",
"identificationNumber": "9054151128",
"insuranceCompanyObject": {
"code": 201,
"shortName": "VoZP"
}
}
}
},
{
"id": "b5f01f0c-0c9f-42bb-a7bd-f0a725638d38",
"start": "2026-06-03T10:00:00.000Z",
"end": "2026-06-03T10:10:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "48d490fa-0fd1-4a16-aba8-79f275043345",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "d8d36c4d-19fc-4405-9bf9-f9a733fb2dad",
"name": "Jana",
"surname": "Ptáčková",
"dob": "1947-09-17",
"phone": "+420604363791",
"identificationNumber": "475917011",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "44d6f66b-95b9-4bfb-b2c9-69338a7cae8f",
"start": "2026-06-03T10:10:00.000Z",
"end": "2026-06-03T10:20:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "30e1d256-eaaf-4a0b-aa13-cd511e4e309e",
"displayTitle": "Očkování - Žloutenka A",
"extendedPatient": {
"id": "bb27b85a-8703-4768-86f4-2cbf4c196dd1",
"name": "Karel",
"surname": "Galus",
"dob": "1946-06-14",
"phone": "+420605230820",
"identificationNumber": "460614110",
"insuranceCompanyObject": {
"code": 211,
"shortName": "ZPMV"
}
}
}
},
{
"id": "b128a573-849d-4bf2-9317-01142147e61e",
"start": "2026-06-03T10:30:00.000Z",
"end": "2026-06-03T10:50:00.000Z",
"note": null,
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "bbd0e36a-5148-43d2-8095-fcf746586778",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "5a0a9ff0-bbe8-4fc7-a27d-b7b475ee2189",
"name": "Lenka",
"surname": "Balousova",
"dob": "1972-03-28",
"phone": "+420603560064",
"identificationNumber": "7253282355",
"insuranceCompanyObject": {
"code": 207,
"shortName": "OZP"
}
}
}
},
{
"id": "c0bbc7bc-2491-4143-a6b9-59d4fab9df63",
"start": "2026-06-03T10:50:00.000Z",
"end": "2026-06-03T11:05:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "b0f01a56-6ae6-47fa-94bf-e693977e127b",
"displayTitle": "Zdravotní obtíže",
"extendedPatient": {
"id": "3e1090b7-d0c2-4dd8-bbfe-dc464d2f1671",
"name": "Lenka",
"surname": "Vaněčková",
"dob": "1943-03-17",
"phone": "+420776599498",
"identificationNumber": "435317067",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
},
{
"id": "9d2f969b-da52-418c-8465-d29021beab49",
"start": "2026-06-03T11:15:00.000Z",
"end": "2026-06-03T11:25:00.000Z",
"note": "",
"done": null,
"color": null,
"canceledAt": null,
"calendarId": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
"request": {
"id": "019ec07d-b454-4231-9b49-9389fe675faa",
"displayTitle": "Očkování - Klíšťová encefalitida",
"extendedPatient": {
"id": "865e0404-b2c8-4635-8d8d-df7a14275b14",
"name": "Jiří",
"surname": "Chomát",
"dob": "1938-03-14",
"phone": "+420737217617",
"identificationNumber": "380314026",
"insuranceCompanyObject": {
"code": 111,
"shortName": "VZP"
}
}
}
}
],
"recurringReservations": []
}
}
}
@@ -0,0 +1,20 @@
{
"request": {
"clinicSlug": "mudr-buzalkova",
"color": "CHARCOAL",
"note": "TEST poznamka z Claude Code",
"timeSlotInput": {
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"start": "2026-05-30T07:00:00.000Z",
"end": "2026-05-30T07:05:00.000Z"
}
},
"response": {
"data": {
"reservation": {
"id": "c646d60b-40ca-4f32-8a11-052ab90f5d2c",
"__typename": "Reservation"
}
}
}
}
@@ -0,0 +1,20 @@
{
"request": {
"clinicSlug": "mudr-buzalkova",
"color": "CHARCOAL",
"note": "Test 2 z Claude Code",
"timeSlotInput": {
"calendarId": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"start": "2026-05-30T08:00:00.000Z",
"end": "2026-05-30T08:05:00.000Z"
}
},
"response": {
"data": {
"reservation": {
"id": "3ac6849a-12e2-4248-a99d-ec9a577ce820",
"__typename": "Reservation"
}
}
}
}
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Smazani rezervace / poznamky v Medevio kalendari.
Defaultni chovani:
- jednorazova: smazat (cancel)
- opakujici se: smazat tuto a nasledujici (ThisAndFuture)
Pouziti jako modul:
from smaz_poznamku import smaz_jednorazovou, smaz_opakujici
smaz_jednorazovou("<reservation_uuid>")
smaz_opakujici("<recurring_id>", date="2026-05-30T14:40:00.000Z") # default ThisAndFuture
smaz_opakujici("<recurring_id>", date="...Z", update_type="All")
smaz_opakujici("<recurring_id>", date="...Z", update_type="Single")
CLI:
python smaz_poznamku.py --jednorazova <reservation_uuid>
python smaz_poznamku.py --opakujici <recurring_id> --datum 2026-05-30T14:40:00.000Z
python smaz_poznamku.py --opakujici <id> --datum ...Z --typ All
"""
import sys
import json
import argparse
from pathlib import Path
from datetime import datetime
import requests
from dateutil import parser as dtparser, tz
try:
sys.stdout.reconfigure(encoding="utf-8")
except AttributeError:
pass
GRAPHQL_URL = "https://api.medevio.cz/graphql"
CLINIC_SLUG = "mudr-buzalkova"
PRAGUE_TZ = tz.gettz("Europe/Prague")
BASE_DIR = Path(__file__).resolve().parent
TOKEN_PATH = BASE_DIR / "token.txt"
DEBUG_DIR = BASE_DIR / "debug"
VALID_UPDATE_TYPES = ("Single", "ThisAndFuture", "All")
MUTATION_SINGLE = """mutation UpdateReservation_CancelReservationByDoctor(
$clinicSlug: String!, $reservationId: UUID!
) {
reservation: cancelReservationByDoctor(
clinicSlug: $clinicSlug, reservationId: $reservationId
) { id __typename }
}"""
MUTATION_RECURRING = """mutation UpdateReservation_CancelRecurringReservationByDoctor(
$input: RemoveRecurringReservationInput!
) {
success: removeDateFromRecurringReservation(input: $input)
}"""
def _load_token() -> str:
token = TOKEN_PATH.read_text(encoding="utf-8").strip()
if token.startswith("Bearer "):
token = token.split(" ", 1)[1]
return token
def _headers() -> dict:
return {
"content-type": "application/json",
"authorization": f"Bearer {_load_token()}",
"origin": "https://my.medevio.cz",
"referer": "https://my.medevio.cz/",
}
def _to_utc_iso(value) -> str:
"""Prijme str/datetime/date a vrati '....000Z'."""
if isinstance(value, str):
if value.endswith("Z") and "T" in value:
return value
dt = dtparser.parse(value)
elif isinstance(value, datetime):
dt = value
else:
raise TypeError(f"Nelze prevest na datetime: {value!r}")
if dt.tzinfo is None:
dt = dt.replace(tzinfo=PRAGUE_TZ)
return dt.astimezone(tz.UTC).strftime("%Y-%m-%dT%H:%M:%S.000Z")
def _post(payload: dict, label: str, save_debug: bool = True) -> dict:
r = requests.post(GRAPHQL_URL, headers=_headers(), data=json.dumps(payload), timeout=30)
r.raise_for_status()
data = r.json()
if save_debug:
DEBUG_DIR.mkdir(exist_ok=True)
p = DEBUG_DIR / f"smaz_{label}_{datetime.now():%Y%m%d_%H%M%S}.json"
p.write_text(
json.dumps({"request": payload["variables"], "response": data}, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"[debug] {p}")
if "errors" in data:
raise RuntimeError(f"GraphQL error: {data['errors']}")
return data["data"]
def smaz_jednorazovou(reservation_id: str, clinic_slug: str = CLINIC_SLUG) -> dict:
"""Zrusi (cancel) jednorazovou rezervaci. Vraci {'id': ..., '__typename': ...}."""
payload = {
"operationName": "UpdateReservation_CancelReservationByDoctor",
"variables": {"clinicSlug": clinic_slug, "reservationId": reservation_id},
"query": MUTATION_SINGLE,
}
result = _post(payload, "single")["reservation"]
print(f"OK zruseno: {result['id']}")
return result
def smaz_opakujici(
recurring_id: str,
date,
update_type: str = "ThisAndFuture",
clinic_slug: str = CLINIC_SLUG,
) -> bool:
"""Smaze opakujici se rezervaci.
Args:
recurring_id: UUID `recurringReservation.id`
date: datum konkretni instance (ISO str s Z, nebo datetime, nebo 'YYYY-MM-DD HH:MM' v lokal cas)
update_type: 'Single' | 'ThisAndFuture' (default) | 'All'
"""
if update_type not in VALID_UPDATE_TYPES:
raise ValueError(f"update_type musi byt {VALID_UPDATE_TYPES}, dostal '{update_type}'")
payload = {
"operationName": "UpdateReservation_CancelRecurringReservationByDoctor",
"variables": {
"input": {
"clinicSlug": clinic_slug,
"recurringReservationId": recurring_id,
"date": _to_utc_iso(date),
"updateType": update_type,
}
},
"query": MUTATION_RECURRING,
}
success = _post(payload, f"recurring_{update_type}")["success"]
print(f"OK smazano ({update_type}): {recurring_id} @ {payload['variables']['input']['date']} -> success={success}")
return success
def main():
ap = argparse.ArgumentParser(description="Smazani rezervace v Medevio kalendari.")
g = ap.add_mutually_exclusive_group(required=True)
g.add_argument("--jednorazova", help="UUID jednorazove rezervace ke zruseni")
g.add_argument("--opakujici", help="UUID opakujiciho se pravidla")
ap.add_argument("--datum", help="Datum instance opakujici (ISO Z nebo 'YYYY-MM-DD HH:MM')")
ap.add_argument("--typ", choices=VALID_UPDATE_TYPES, default="ThisAndFuture",
help="Pro --opakujici. Default ThisAndFuture.")
args = ap.parse_args()
if args.jednorazova:
smaz_jednorazovou(args.jednorazova)
else:
if not args.datum:
ap.error("--opakujici vyzaduje --datum")
smaz_opakujici(args.opakujici, args.datum, update_type=args.typ)
if __name__ == "__main__":
main()
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Zapise poznamku lekare do Medevio kalendare (color=CHARCOAL).
Hlavni funkce: `zapis_poznamku()` - bere parametry:
calendar_id UUID kalendare (Vlado / manzelka / ...)
den date nebo str 'YYYY-MM-DD'
cas str 'HH:MM' nebo time
trvani_min int - delka v minutach
poznamka str - text
perioda None | str - "DAILY", "WEEKDAYS", atd. (zatim NotImplemented)
color ECRFIconColor enum, default CHARCOAL
clinic_slug default mudr-buzalkova
Vrati id vytvorene rezervace (str).
CLI:
python zapis_poznamky.py --den 2026-06-03 --cas 09:00 --trvani 5 --poznamka "Text"
python zapis_poznamky.py --den 2026-06-03 --cas 09:00 --trvani 5 --poznamka "Text" --kalendar manzelka
"""
import sys
import json
import argparse
from pathlib import Path
from datetime import datetime, date, time as dtime, timedelta
import requests
from dateutil import tz
try:
sys.stdout.reconfigure(encoding="utf-8")
except AttributeError:
pass
GRAPHQL_URL = "https://api.medevio.cz/graphql"
CLINIC_SLUG = "mudr-buzalkova"
PRAGUE_TZ = tz.gettz("Europe/Prague")
# Pojmenovane kalendare - viz pamet project-medevio-kalendar
CALENDARS = {
"vlado": "b6555c7e-4e95-4657-b441-87c2c9a7b2ca",
"manzelka": "144c4e12-347c-49ca-9ec0-8ca965a4470d",
}
DEFAULT_CALENDAR = "vlado"
BASE_DIR = Path(__file__).resolve().parent
TOKEN_PATH = BASE_DIR / "token.txt"
DEBUG_DIR = BASE_DIR / "debug"
MUTATION = """mutation CreateReservation_MakeReservationByDoctor(
$clinicSlug: String!,
$color: ECRFIconColor,
$note: String!,
$timeSlotInput: TimeSlotInput!
) {
reservation: makeReservationByDoctor(
clinicSlug: $clinicSlug
color: $color
note: $note
timeSlotInput: $timeSlotInput
) {
id
__typename
}
}"""
def _load_token() -> str:
token = TOKEN_PATH.read_text(encoding="utf-8").strip()
if token.startswith("Bearer "):
token = token.split(" ", 1)[1]
return token
def _headers() -> dict:
return {
"content-type": "application/json",
"authorization": f"Bearer {_load_token()}",
"origin": "https://my.medevio.cz",
"referer": "https://my.medevio.cz/",
}
def _to_utc_iso(dt: datetime) -> str:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=PRAGUE_TZ)
return dt.astimezone(tz.UTC).strftime("%Y-%m-%dT%H:%M:%S.000Z")
def _resolve_calendar(calendar) -> str:
"""Prijme UUID nebo nazev ('vlado', 'manzelka'). Vrati UUID."""
if calendar in CALENDARS:
return CALENDARS[calendar]
if isinstance(calendar, str) and len(calendar) == 36 and calendar.count("-") == 4:
return calendar
raise ValueError(f"Neznamy kalendar: {calendar!r}. Pouzij UUID nebo jeden z {list(CALENDARS)}.")
def _resolve_day(den) -> date:
if isinstance(den, date) and not isinstance(den, datetime):
return den
if isinstance(den, datetime):
return den.date()
return datetime.strptime(str(den), "%Y-%m-%d").date()
def _resolve_time(cas) -> dtime:
if isinstance(cas, dtime):
return cas
return datetime.strptime(str(cas), "%H:%M").time()
def zapis_poznamku(
calendar: str = DEFAULT_CALENDAR,
den=None,
cas=None,
trvani_min: int = 5,
poznamka: str = "",
perioda: str | None = None,
color: str = "CHARCOAL",
clinic_slug: str = CLINIC_SLUG,
save_debug: bool = True,
) -> str:
"""Vytvori poznamku lekare v Medevio kalendari. Vraci id rezervace."""
if perioda is not None:
raise NotImplementedError(
"Periodicita zatim neni implementovana - neznam format payloadu. "
"Zachyt jednu opakujici se rezervaci pres Claude in Chrome a doplnime."
)
if den is None or cas is None:
raise ValueError("Musis zadat 'den' a 'cas'.")
calendar_id = _resolve_calendar(calendar)
d = _resolve_day(den)
t = _resolve_time(cas)
start_dt = datetime.combine(d, t).replace(tzinfo=PRAGUE_TZ)
end_dt = start_dt + timedelta(minutes=int(trvani_min))
payload = {
"operationName": "CreateReservation_MakeReservationByDoctor",
"variables": {
"clinicSlug": clinic_slug,
"color": color,
"note": poznamka,
"timeSlotInput": {
"calendarId": calendar_id,
"start": _to_utc_iso(start_dt),
"end": _to_utc_iso(end_dt),
},
},
"query": MUTATION,
}
r = requests.post(GRAPHQL_URL, headers=_headers(), data=json.dumps(payload), timeout=30)
r.raise_for_status()
data = r.json()
if save_debug:
DEBUG_DIR.mkdir(exist_ok=True)
debug_path = DEBUG_DIR / f"poznamka_{datetime.now():%Y%m%d_%H%M%S}.json"
debug_path.write_text(
json.dumps({"request": payload["variables"], "response": data}, ensure_ascii=False, indent=2),
encoding="utf-8",
)
print(f"[debug] {debug_path}")
if "errors" in data:
raise RuntimeError(f"GraphQL error: {data['errors']}")
res_id = data["data"]["reservation"]["id"]
print(f"OK: {d} {t:%H:%M} +{trvani_min}min '{poznamka}' -> {res_id}")
return res_id
def main():
ap = argparse.ArgumentParser(description="Zapis poznamku lekare do Medevio kalendare.")
ap.add_argument("--kalendar", default=DEFAULT_CALENDAR,
help=f"Jmeno ({'/'.join(CALENDARS)}) nebo UUID. Default '{DEFAULT_CALENDAR}'.")
ap.add_argument("--den", required=True, help="YYYY-MM-DD")
ap.add_argument("--cas", required=True, help="HH:MM (lokalni cas Europe/Prague)")
ap.add_argument("--trvani", type=int, default=5, help="delka v minutach (default 5)")
ap.add_argument("--poznamka", required=True, help="text poznamky")
ap.add_argument("--perioda", default=None,
help="opakovani (zatim NotImplemented)")
ap.add_argument("--color", default="CHARCOAL")
args = ap.parse_args()
zapis_poznamku(
calendar=args.kalendar,
den=args.den,
cas=args.cas,
trvani_min=args.trvani,
poznamka=args.poznamka,
perioda=args.perioda,
color=args.color,
)
if __name__ == "__main__":
main()