Files
ordinaceprojekt/Medevio/smaz_poznamku.py
T
Vladimir Buzalka a9ef60212d notebookvb
2026-05-31 07:51:29 +02:00

177 lines
5.7 KiB
Python

#!/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()