203 lines
6.1 KiB
Python
203 lines
6.1 KiB
Python
#!/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()
|