#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Fetch Medevio pending (ACTIVE) patient requests and return a pandas DataFrame. Reads Bearer token from token.txt (single line, token only). """ import requests import pandas as pd import time from typing import List, Dict, Any # CONFIG --------------------------------------------------------------------- TOKEN_FILE = "token.txt" # file with token (no "Bearer " prefix) GRAPHQL_URL = "https://app.medevio.cz/graphql" CLINIC_SLUG = "mudr-buzalkova" # adjust if needed LOCALE = "cs" PAGE_SIZE = 50 # how many items to request per page REQUEST_WAIT = 0.2 # seconds between requests to be polite # --------------------------------------------------------------------------- GRAPHQL_QUERY = r""" query ClinicLegacyRequestList_ListPatientRequestsForClinic( $clinicSlug: String!, $queueId: String, $queueAssignment: QueueAssignmentFilter!, $state: PatientRequestState, $pageInfo: PageInfo!, $locale: Locale! ) { requests: listPatientRequestsForClinic( clinicSlug: $clinicSlug, queueId: $queueId, queueAssignment: $queueAssignment, state: $state, pageInfo: $pageInfo ) { id createdAt dueDate displayTitle(locale: $locale) doneAt removedAt priority evaluationResult(locale: $locale) { fields { name value } } clinicId extendedPatient { id identificationNumber kind name note owner { name surname } key status surname type user { id name surname } isUnknownPatient } lastMessage { createdAt id readAt sender { id name surname clinicId } text } queue { id name } reservations { id canceledAt done start } tags(onlyImportant: true) { id name color icon } priceWhenCreated currencyWhenCreated } } """ def read_token(path: str) -> str: with open(path, "r", encoding="utf-8") as f: t = f.read().strip() if t.startswith("Bearer "): t = t.split(" ", 1)[1] return t def fetch_requests(token: str, clinic_slug: str = CLINIC_SLUG, locale: str = LOCALE, page_size: int = PAGE_SIZE) -> List[Dict[str, Any]]: headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json", "Accept": "application/json", } all_items: List[Dict[str, Any]] = [] offset = 0 while True: variables = { "clinicSlug": clinic_slug, "queueId": None, "queueAssignment": "ANY", "state": "ACTIVE", "pageInfo": {"first": page_size, "offset": offset}, "locale": locale, } payload = {"query": GRAPHQL_QUERY, "variables": variables, "operationName": "ClinicLegacyRequestList_ListPatientRequestsForClinic"} r = requests.post(GRAPHQL_URL, json=payload, headers=headers, timeout=30) r.raise_for_status() js = r.json() # Basic error handling if "errors" in js: raise RuntimeError(f"GraphQL returned errors: {js['errors']}") items = js.get("data", {}).get("requests", []) if not items: break all_items.extend(items) # If fewer than requested, we are at the end if len(items) < page_size: break offset += page_size time.sleep(REQUEST_WAIT) return all_items def flatten_item(item: Dict[str, Any]) -> Dict[str, Any]: patient = item.get("extendedPatient") or {} last_msg = item.get("lastMessage") or {} queue = item.get("queue") or {} # evaluationResult fields -> map of name:value (if exists) eval_map = {} eval_block = item.get("evaluationResult") or {} for fld in (eval_block.get("fields") or []): name = fld.get("name") value = fld.get("value") if name: eval_map[name] = value flat = { "id": item.get("id"), "createdAt": item.get("createdAt"), "dueDate": item.get("dueDate"), "displayTitle": item.get("displayTitle"), "doneAt": item.get("doneAt"), "removedAt": item.get("removedAt"), "priority": item.get("priority"), "clinicId": item.get("clinicId"), "patient_id": patient.get("id"), "patient_identificationNumber": patient.get("identificationNumber"), "patient_name": patient.get("name"), "patient_surname": patient.get("surname"), "patient_status": patient.get("status"), "lastMessage_id": last_msg.get("id"), "lastMessage_createdAt": last_msg.get("createdAt"), "lastMessage_text": last_msg.get("text"), "queue_id": queue.get("id"), "queue_name": queue.get("name"), "priceWhenCreated": item.get("priceWhenCreated"), "currencyWhenCreated": item.get("currencyWhenCreated"), } # merge evaluation fields (if any) prefixed by "eval_" for k, v in eval_map.items(): flat[f"eval_{k}"] = v return flat def to_dataframe(items: List[Dict[str, Any]]) -> pd.DataFrame: rows = [flatten_item(it) for it in items] df = pd.DataFrame(rows) # try parsing dates for col in ("createdAt", "dueDate", "doneAt", "lastMessage_createdAt", "removedAt"): if col in df.columns: df[col] = pd.to_datetime(df[col], errors="coerce") return df def main(): token = read_token(TOKEN_FILE) print("Fetching pending (ACTIVE) requests from Medevio...") items = fetch_requests(token) print(f"Fetched {len(items)} items.") df = to_dataframe(items) pd.set_option("display.max_rows", 20) pd.set_option("display.max_colwidth", 160) print(df.head(50)) # optionally save df.to_excel("medevio_pending_requests.xlsx", index=False) print("Saved medevio_pending_requests.xlsx") if __name__ == "__main__": main()