diff --git a/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/log_podani.json b/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/log_podani.json index fd37a19..f87705a 100644 --- a/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/log_podani.json +++ b/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/log_podani.json @@ -73,5 +73,10 @@ "datum": "31.05.2026", "ref_cislo": "178213777", "podano_kdy": "2026-05-13 07:09:12" + }, + { + "datum": "30.06.2026", + "ref_cislo": "178258393", + "podano_kdy": "2026-05-13 21:03:20" } ] \ No newline at end of file diff --git a/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/stav.json b/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/stav.json index 4a524da..08b7b5e 100644 --- a/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/stav.json +++ b/Insurance/StahováníSeznamuPojištěnců/209 ZPŠ/stav.json @@ -1 +1 @@ -{"mesic": 5, "rok": 2026} \ No newline at end of file +{"mesic": 6, "rok": 2026} \ No newline at end of file diff --git a/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json b/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json index c6a0703..b72d0b4 100644 --- a/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json +++ b/Insurance/StahováníZpráv/209 ZPŠ/zps_cookies.json @@ -1,7 +1,7 @@ [ { "name": "SID", - "value": "cfdefd7ad7d093aeeadee6402dff0fa8", + "value": "0589c59247aa8fa221c380eec74c9cef", "domain": ".portal.zpskoda.cz", "path": "/", "expires": -1, @@ -14,7 +14,7 @@ "value": "CERT", "domain": ".portal.zpskoda.cz", "path": "/", - "expires": 1810184951, + "expires": 1810234998, "secure": true, "httpOnly": false, "sameSite": "Lax" diff --git a/Medevio/80 Pacienti/create_table.sql b/Medevio/80 Pacienti/create_table.sql new file mode 100644 index 0000000..1545d5a --- /dev/null +++ b/Medevio/80 Pacienti/create_table.sql @@ -0,0 +1,25 @@ +CREATE TABLE IF NOT EXISTS medevio.medevio_pacient ( + patient_id VARCHAR(36) NOT NULL PRIMARY KEY, + name VARCHAR(100), + surname VARCHAR(100), + identification_number VARCHAR(20) COMMENT 'Rodne cislo bez lomitka', + sex VARCHAR(10) COMMENT 'Male / Female', + dob DATE COMMENT 'Datum narozeni', + email VARCHAR(200), + phone VARCHAR(50), + insurance_code INT COMMENT 'Kod pojistovny (111=VZP, 207=OZP, ...)', + insurance_name VARCHAR(100) COMMENT 'Nazev pojistovny', + status VARCHAR(20) COMMENT 'ACTIVE / INACTIVE', + is_in_clinic TINYINT(1) DEFAULT 1, + has_mobile_app TINYINT(1) DEFAULT 0, + anamnesis_shared TINYINT(1) DEFAULT 0, + note TEXT COMMENT 'Interni poznamka lekare', + city VARCHAR(200), + street VARCHAR(200), + house_number VARCHAR(50), + user_id VARCHAR(36) COMMENT 'ID uzivatelskeho uctu (PatientUser)', + user_email VARCHAR(200) COMMENT 'Email spravujici osoby', + user_phone VARCHAR(50) COMMENT 'Telefon spravujici osoby', + created_at DATETIME COMMENT 'Kdy byl pacient vytvoren v Medeviu', + synced_at DATETIME NOT NULL COMMENT 'Cas posledni synchronizace' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/Medevio/80 Pacienti/sync_patients_to_mysql.py b/Medevio/80 Pacienti/sync_patients_to_mysql.py new file mode 100644 index 0000000..2399c9d --- /dev/null +++ b/Medevio/80 Pacienti/sync_patients_to_mysql.py @@ -0,0 +1,337 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Sync Medevio patients to MySQL table medevio.medevio_pacient. + +Stahuje seznam pacientů přes GraphQL API (stránkovaně po 50), +pro každého pacienta stáhne detail a uloží/aktualizuje v MySQL. +""" + +import sys +try: + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") +except AttributeError: + import io + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") + sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8") + +import json +import time +import pymysql +import requests +from pathlib import Path +from datetime import datetime +from dateutil import parser as dtparser, tz + +# ==================== CONFIG ==================== +GRAPHQL_URL = "https://api.medevio.cz/graphql" +CLINIC_SLUG = "mudr-buzalkova" +PAGE_SIZE = 50 +DELAY_BETWEEN_REQUESTS = 0.3 + +TOKEN_PATH = Path(__file__).resolve().parent.parent / "token.txt" + +DB_CONFIG = { + "host": "192.168.1.76", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +PRAGUE_TZ = tz.gettz("Europe/Prague") + +# ==================== TABLE ==================== +CREATE_TABLE_SQL = """ +CREATE TABLE IF NOT EXISTS medevio_pacient ( + patient_id VARCHAR(36) NOT NULL PRIMARY KEY, + name VARCHAR(100), + surname VARCHAR(100), + identification_number VARCHAR(20) COMMENT 'Rodne cislo bez lomitka', + sex VARCHAR(10) COMMENT 'Male / Female', + dob DATE COMMENT 'Datum narozeni', + email VARCHAR(200), + phone VARCHAR(50), + insurance_code INT COMMENT 'Kod pojistovny (111=VZP, 207=OZP, ...)', + insurance_name VARCHAR(100) COMMENT 'Nazev pojistovny', + status VARCHAR(20) COMMENT 'ACTIVE / INACTIVE', + is_in_clinic TINYINT(1) DEFAULT 1, + has_mobile_app TINYINT(1) DEFAULT 0, + anamnesis_shared TINYINT(1) DEFAULT 0, + note TEXT COMMENT 'Interni poznamka lekare', + city VARCHAR(200), + street VARCHAR(200), + house_number VARCHAR(50), + user_id VARCHAR(36) COMMENT 'ID uzivatelskeho uctu (PatientUser)', + user_email VARCHAR(200) COMMENT 'Email spravujici osoby', + user_phone VARCHAR(50) COMMENT 'Telefon spravujici osoby', + created_at DATETIME COMMENT 'Kdy byl pacient vytvoren v Medeviu', + synced_at DATETIME NOT NULL COMMENT 'Cas posledni synchronizace' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +""" + +# ==================== UPSERT ==================== +UPSERT_SQL = """ +INSERT INTO medevio_pacient ( + patient_id, name, surname, identification_number, + sex, dob, email, phone, + insurance_code, insurance_name, + status, is_in_clinic, has_mobile_app, anamnesis_shared, + note, city, street, house_number, + user_id, user_email, user_phone, + created_at, synced_at +) VALUES ( + %s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s +) +ON DUPLICATE KEY UPDATE + name = VALUES(name), + surname = VALUES(surname), + identification_number = VALUES(identification_number), + sex = VALUES(sex), + dob = VALUES(dob), + email = VALUES(email), + phone = VALUES(phone), + insurance_code = VALUES(insurance_code), + insurance_name = VALUES(insurance_name), + status = VALUES(status), + is_in_clinic = VALUES(is_in_clinic), + has_mobile_app = VALUES(has_mobile_app), + anamnesis_shared = VALUES(anamnesis_shared), + note = VALUES(note), + city = VALUES(city), + street = VALUES(street), + house_number = VALUES(house_number), + user_id = VALUES(user_id), + user_email = VALUES(user_email), + user_phone = VALUES(user_phone), + created_at = VALUES(created_at), + synced_at = VALUES(synced_at) +""" + +# ==================== GRAPHQL QUERIES ==================== + +LIST_PATIENTS_QUERY = """ +query SyncListPatients($clinicSlug: String!, $pageInfo: PageInfo!, $filter: ListPatientFilter!) { + patientsList: listPatients(clinicSlug: $clinicSlug, filter: $filter, pageInfo: $pageInfo) { + count + patients { + id + identificationNumber + name + surname + sex + phone + status2 + isInClinic + locale + insuranceCompanyObject { id shortName } + user { id phone name surname } + } + } +} +""" + +DETAIL_PATIENT_QUERY = """ +query GetPatientDetail($clinicSlug: String!, $patientId: String!) { + patient: getPatientForClinic(clinicSlug: $clinicSlug, patientId: $patientId) { + id + name + surname + identificationNumber + sex + dob + email + phone + status + isInClinic + hasMobileApp + anamnesisShared + note + city + street + houseNumber + createdAt + insuranceCompanyObject { + code + name + } + user { + id + email + phone + } + } +} +""" + + +def parse_dt(iso_str): + if not iso_str: + return None + try: + return dtparser.isoparse(iso_str).astimezone(PRAGUE_TZ).replace(tzinfo=None) + except Exception: + return None + + +def make_headers(token): + return { + "content-type": "application/json", + "authorization": f"Bearer {token}", + "origin": "https://my.medevio.cz", + "referer": "https://my.medevio.cz/", + } + + +def gql_request(headers, query, variables): + payload = {"query": query, "variables": variables} + resp = requests.post(GRAPHQL_URL, headers=headers, json=payload, timeout=30) + resp.raise_for_status() + data = resp.json() + if "errors" in data: + print(f" GQL errors: {data['errors']}") + return None + return data.get("data") + + +def fetch_patients_list(headers): + """Stáhne seznam všech patient IDs stránkovaně přes listPatients.""" + all_patients = [] + offset = 0 + total = None + + while True: + data = gql_request(headers, LIST_PATIENTS_QUERY, { + "clinicSlug": CLINIC_SLUG, + "filter": {}, + "pageInfo": {"first": PAGE_SIZE, "offset": offset}, + }) + if not data or "patientsList" not in data: + print(f" Chyba pri stahovani seznamu (offset={offset}).") + return None + + result = data["patientsList"] + if total is None: + total = result.get("count", 0) + print(f" Celkem pacientu v Medevio: {total}") + + patients = result.get("patients") or [] + if not patients: + break + + all_patients.extend(patients) + offset += PAGE_SIZE + print(f" Stazeno {len(all_patients)}/{total}...") + + if offset >= total: + break + time.sleep(DELAY_BETWEEN_REQUESTS) + + return all_patients + + +def fetch_patient_detail(headers, patient_id): + """Stáhne detail jednoho pacienta.""" + data = gql_request(headers, DETAIL_PATIENT_QUERY, { + "clinicSlug": CLINIC_SLUG, + "patientId": patient_id, + }) + if data and "patient" in data: + return data["patient"] + return None + + +def patient_to_row(p): + """Převede patient dict na tuple pro UPSERT.""" + insurance = p.get("insuranceCompanyObject") or {} + user = p.get("user") or {} + now = datetime.now().replace(microsecond=0) + + return ( + p.get("id"), + p.get("name"), + p.get("surname"), + p.get("identificationNumber"), + p.get("sex"), + p.get("dob"), + p.get("email"), + p.get("phone"), + insurance.get("code"), + insurance.get("name"), + p.get("status") or p.get("status2"), + 1 if p.get("isInClinic") else 0, + 1 if p.get("hasMobileApp") else 0, + 1 if p.get("anamnesisShared") else 0, + p.get("note"), + p.get("city"), + p.get("street"), + p.get("houseNumber"), + user.get("id"), + user.get("email"), + user.get("phone"), + parse_dt(p.get("createdAt")), + now, + ) + + +def upsert_patients(conn, patients): + rows = [patient_to_row(p) for p in patients] + with conn.cursor() as cur: + cur.executemany(UPSERT_SQL, rows) + conn.commit() + return len(rows) + + +def main(): + token = TOKEN_PATH.read_text(encoding="utf-8").strip() + if token.startswith("Bearer "): + token = token.split(" ", 1)[1] + + headers = make_headers(token) + + conn = pymysql.connect(**DB_CONFIG) + with conn.cursor() as cur: + cur.execute(CREATE_TABLE_SQL) + conn.commit() + print("Tabulka medevio_pacient pripravena.") + + # --- Krok 1: stáhni seznam patient IDs --- + print("\nStahuji seznam pacientu...") + patients = fetch_patients_list(headers) + + if patients is None or len(patients) == 0: + print("Nepodařilo se stáhnout seznam pacientů.") + conn.close() + return + + # --- Krok 2: pro každého stáhni detail --- + print(f"\nStahuji detaily pro {len(patients)} pacientu...") + detailed = [] + errors = 0 + for i, p in enumerate(patients): + detail = fetch_patient_detail(headers, p["id"]) + if detail: + detailed.append(detail) + else: + errors += 1 + + if (i + 1) % 50 == 0: + print(f" Detail {i + 1}/{len(patients)}...") + time.sleep(DELAY_BETWEEN_REQUESTS) + + # --- Krok 3: upsert do MySQL --- + count = upsert_patients(conn, detailed) + print(f"\nHotovo — {count} pacientu upsertovano.") + if errors: + print(f" ({errors} pacientu se nepodařilo stáhnout)") + + conn.close() + print("\nDokonceno.") + + +if __name__ == "__main__": + main() diff --git a/Medevio/medevio_api_notes.md b/Medevio/medevio_api_notes.md index 42fa5d6..9030197 100644 --- a/Medevio/medevio_api_notes.md +++ b/Medevio/medevio_api_notes.md @@ -1,11 +1,11 @@ -# 🧭 MEDEVIO API – Working Notes +# MEDEVIO API -- Working Notes ## General Architecture -- **Frontend:** React + Material‑UI (MUI) + Apollo GraphQL -- **Backend:** GraphQL API → `https://api.medevio.cz/graphql` -- **Authentication:** cookies/tokens stored in `medevio_storage.json` (Playwright session) -- **Frontend base:** `https://my.medevio.cz/mudr-buzalkova/klinika/...` -- **Session handling:** token refreshed through `AccessToken_AuthSelf` calls +- **Frontend:** React + Material-UI (MUI) + Apollo GraphQL +- **Backend:** GraphQL API -> `https://api.medevio.cz/graphql` +- **Authentication:** cookies/tokens stored in `medevio_storage.json` (Playwright session) +- **Frontend base:** `https://my.medevio.cz/mudr-buzalkova/klinika/...` +- **Session handling:** token refreshed through `AccessToken_AuthSelf` calls --- @@ -21,51 +21,6 @@ Use these cookies in every `requests.post(GRAPHQL_URL, headers, cookies, data=.. --- -## Key GraphQL Operations - -| Operation name | Purpose | -|----------------|----------| -| `AccessToken_AuthSelf` | Verify or refresh session | -| `ClinicNavigation_GetClinic` | Get basic clinic info | -| `ClinicAgenda_GetCalendarsForClinic` | List calendars within clinic | -| **`ClinicAgenda_ListClinicReservations`** | **List reservations (appointments)** | -| **`UpdateReservation_GetReservation`** | **Get full detail of one reservation** | -| **`ClinicRequestDetail_GetPatientRequest2`** | **Fetch detailed “Požadavek” (request card)** | -| `ClinicRequestNotes_Get` | Load Požadavek notes | -| `UseMessages_ListMessages` | Load chat messages | -| `Communication_GetClinicFooter` | UI footer text | - ---- - -## Workflow for Automation - -### 1️⃣ Extract reservations (agenda overview) -```graphql -operationName: "ClinicAgenda_ListClinicReservations" -variables: { - "clinicId": "", - "dateFrom": "YYYY-MM-DDT00:00:00Z", - "dateTo": "YYYY-MM-DDT23:59:59Z" -} -``` -→ returns `id`, `startDateTime`, `endDateTime`, `patient {name, age}`, `reason`, `status`. - -### 2️⃣ Get detail of a single reservation -```graphql -operationName: "UpdateReservation_GetReservation" -variables: {"id": ""} -``` -→ returns reason, time, doctor, patient contact, note, etc. - -### 3️⃣ Get detail of Požadavek -```graphql -operationName: "ClinicRequestDetail_GetPatientRequest2" -variables: {"id": ""} -``` -→ returns text, attachments, communication, etc. - ---- - ## HTTP Setup (Python) ```python @@ -80,56 +35,260 @@ response = requests.post(GRAPHQL_URL, headers=headers, cookies=cookies, data=jso --- -## Reservation Data Example +## Key IDs -```json -{ - "id": "0e3a4f5c-1c15-40e6-b1b1-325de9269ef5", - "reason": "Očkování - Chřipka", - "startDateTime": "2025-10-17T09:10:00Z", - "endDateTime": "2025-10-17T09:20:00Z", - "status": "confirmed", - "note": "přijde i s taťkou", - "patient": {"id": "...", "name": "Černík Pavel", "age": 53}, - "doctor": {"id": "...", "name": "MUDr. Buzalková"}, - "location": {"name": "Ordinace Prosek"} +| Entity | ID | +|--------|----| +| Clinic | `25f24970-dae3-4f80-9337-d3616e53fb10` | +| Clinic slug | `mudr-buzalkova` | +| Calendar MUDr. Buzalkova | `144c4e12-347c-49ca-9ec0-8ca965a4470d` | +| AIS entity (Medicus) | `ef1549a5-d266-4f52-9a4d-7275e79ac82e` | + +--- + +## Complete GraphQL Operations Catalog + +### Session & Auth + +| Operation | Purpose | +|-----------|---------| +| `AccessToken_AuthSelf` | Verify or refresh session | + +### Search + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `Search` | `clinicSlug`, `locale`, `query` | `clinic`, `results` (patients + requests) | + +### Patient Card (modal) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `ClinicPatientDetailModal_GetData` | `clinicSlug`, `patientId`, `patientUuid`, `locale`, `challengesStatus` | `clinic`, `calendars`, `patient` (full detail), `challenges`, `patientRequestsResponse`, `treatmentPlanPatients`, `premiumPlans`, `insuranceCards`, `pinnedRequests`, `canBeRemoved` | +| `ClinicPatientRequestHistory_FilterPatientRequestsForClinic` | `clinicSlug`, `locale`, `patientId`, `patientRequestFilter`, `pageInfo {first, offset}` | `filterResponse`, `clinic`, `queues`, `calendars`, `userECRFs`, `substates`, `tags` | +| `ClinicPatientDetailModal_FindMergeSuggestions` | `clinicSlug`, `patientUuid` | `mergeSuggestions` | + +#### Patient object structure (from GetData) +``` +patient { + id, name, surname, identificationNumber (rodne cislo bez lomitka), + sex (Male/Female), dob (YYYY-MM-DD), email, phone, + kind (Human), type (Human), status (ACTIVE), + isInClinic, isInOrganizationOnly, hasMobileApp, + anamnesisShared, anamnesisStatusForClinic { updatedAt }, + insuranceCompanyObject { id, code, name, shortName }, + tags [], familyMembers [], clinics [], + note, organizationNotes [], + city, street, houseNumber, + user { id, name, surname, email, phone, registrationCompletedTime }, + createdAt, editableByDoctor, userRelationship, premiumPlanPatient } ``` +### Patient Edit + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `EditPatientModal_GetClinic` | `clinicSlug` | `clinic` | +| `ClinicEditPatient_GetPatientForClinic` | `clinicSlug`, `patientId`, `includePatient` | `patient`, `clinic` | +| `ClinicEditPatient_ListInsuranceCompanies` | `country` | `insuranceCompanies []` (id, code, name) | +| `UseSupportedCountryCodes_Codes` | (none) | `codes` (phone country codes) | + +### Patient List (Kartoteka) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `LegacyClinicPatientListPage_ListClinicPatients` | `clinicSlug`, `criteria {premiumPlanId, tagId}`, `pageInfo {first: 50, offset}` | `count`, `patients`, `clinic` | + +### Requests (Pozadavky) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `ClinicLegacyRequestList_ListPatientRequestsForClinic` | `clinicSlug`, `locale`, `pageInfo {first: 30, offset}`, `queueAssignment`, `queueId`, `state` (ACTIVE/DONE) | `requests`, `clinic` | + +#### Request list item structure +``` +request { + id, createdAt, dueDate, displayTitle, doneAt, removedAt, + priority, evaluationResult, clinicId, + extendedPatient { id, identificationNumber, kind, name, surname, + note, owner, key, premiumPlanPatient, status, tags [] }, + queue { id, name }, substate, pinType, + ecrf { id, name, iconMaskColor { name } }, + flags [], hasMobileApp +} +``` + +### Request Detail + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `ClinicRequestDetail_GetPatientRequest2` | `clinicSlug`, `isDoctor`, `requestId`, `locale` | `request` (full detail) | +| `UseMessages_ListMessages` | `requestId`, `updatedSince` | `messages` | +| `Communication_GetClinicFooter` | `clinicSlug` | `footer` | +| `ClinicRequestNotes_Get` | `patientRequestId` | `notes` | + +#### Request detail structure +``` +request { + id, doneAt, doneBy { id, name, surname }, + removedAt, queue, isInClinic, + clinic { id, paymentEnabled, hasInvoicingEnabled, slug, features }, + clinicMedicalRecord, clinicMedicalRecordVisibleToPatient, + extendedPatient { ... (same as patient) }, + ecrf, ecrfFilledData, eventType (PATIENT_REQUEST), + flags [], invoice, userNote, paymentData, + recording, reservations [], evaluationResult, tags [], + createdAt, createdBy, customTitle, createdByDoctor, + displayTitle, referringClinic, pinType, + dueDate, substate, hasMobileApp, + priceWhenCreated, currencyWhenCreated +} +``` + +### Calendar (Kalendar) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `ClinicCalendar_ListClinicReservations` | `calendarIds []`, `clinicCountry`, `clinicSlug`, `locale`, `since` (ISO), `until` (ISO), `showTimeSlots`, `schedulePatientId`, `emptyCalendarIds` | `holidays`, `reservations`, `vacations` | +| `ClinicCalendar_GetWindows` | `calendarIds []`, `clinicSlug`, `locale`, `since`, `until` | `calendarWindows` (ordinacni hodiny) | + +#### Reservation structure +``` +reservation { + id, calendarId, + clinic { id, slug, aisEntity { id, slug } }, + color, done, canceledAt, + end (ISO), note, + request { + displayTitle, id, + ecrf { id, name, iconMaskColor { name } }, + extendedPatient { dob, email, id, identificationNumber, + kind, name, surname, note, phone, sex, status, tags [], + insuranceCompanyObject { code, name } } + } +} +``` + +### References (Referovani) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `ClinicReferences_GetClinic` | `clinicSlug` | `clinic`, `substates` | +| `ClinicReferences_ListReferencesSentByClinic` | `clinicSlug`, `locale`, `pageInfo {first: 51, offset}` | `references` | + +### Treatment Plans (Lecebne plany) + +| Operation | Variables | Response | +|-----------|-----------|----------| +| `PlanPatientsPage_GetClinic` | `clinicSlug` | `clinic` | +| `PlanPatientsPage_ListPlanPatients` | `clinicSlug`, `pageInfo {first: 51, offset}` | `planPatients` | + +### Settings (Nastaveni) + +| Operation | Purpose | +|-----------|---------| +| `ClinicSettingsSubNavigation_GetClinic` | Navigation state for settings | +| `ClinicRequestSettings_GetClinic` | Request settings | +| `UserEcrfListing_GetClinicECRFMenu` | List request types (ECRF forms) | +| `ClinicEcrfSet_ListECRFSets` | List request type groups | +| `ClinicOpeningHoursEdit_GetClinicOpeningHours2` | Opening hours settings | +| `ClinicPaymentsSettings_GetClinic` | Payment settings | +| `StripeVerificationContext_GetClinicCompanyInfo` | Stripe payment verification | +| `SettingsPage` | General settings | +| `InviteSettings` | Invite settings | +| `InviteSettings_GetInviteContent` | Invite content/templates | +| `ImportAisSettings_GetClinic` | AIS integration settings | +| `ImportAisSettings_GetAises` | Available AIS systems | +| `ClinicUserGrid_Columns` | User grid columns (queues, calendars) | +| `ClinicUserGrid_Rows` | User list for clinic | + +### Clinic Info + +| Operation | Purpose | +|-----------|---------| +| `ClinicNavigation_GetClinic` | Basic clinic info | +| `ClinicAgenda_GetCalendarsForClinic` | List calendars within clinic | + --- ## Playwright Selectors | Purpose | Selector | -|----------|-----------| +|---------|----------| | Agenda rows | `div[data-testid='reservation-row']` | | Row ID | `data-id` | | Agenda fields | `div[data-field='Time']`, `div[data-field='Patient']`, `div[data-field='Reason']` | --- -## Recommended Automation Pipeline +## URL Patterns -1. **Agenda scrape (Playwright)** → extract `data-id`, patient, time, reason. -2. **GraphQL fetch (Python)** → use `ClinicAgenda_ListClinicReservations`. -3. **Detail enrichment** → call `UpdateReservation_GetReservation` and `ClinicRequestDetail_GetPatientRequest2`. -4. **Storage/reporting** → save to MySQL/Excel for vaccine planning. +| Page | URL | +|------|-----| +| Search | `/mudr-buzalkova/klinika/hledat/?q={query}` | +| Patient card | `/mudr-buzalkova/klinika/hledat/?q={query}&pacient={uuid}` | +| Request detail | `/mudr-buzalkova/klinika/hledat/?q={query}&pozadavek={uuid}` | +| Requests (active) | `/mudr-buzalkova/klinika/pozadavky/` | +| Requests (done) | `/mudr-buzalkova/klinika/pozadavky?vyrizene=1` | +| Calendar | `/mudr-buzalkova/klinika/kalendar/?kalendar={calendarId}&pohled=pracovni-tyden` | +| Patient list | `/mudr-buzalkova/klinika/kartoteka/` | +| References | `/mudr-buzalkova/klinika/reference` | +| Treatment plans | `/mudr-buzalkova/klinika/lecebne-plany/pacienti` | +| Settings - requests | `/mudr-buzalkova/klinika/nastaveni/pozadavky` | +| Settings - groups | `/mudr-buzalkova/klinika/nastaveni/skupiny-pozadavku` | +| Settings - invites | `/mudr-buzalkova/klinika/nastaveni/pozvanky` | +| Settings - integrations | `/mudr-buzalkova/klinika/nastaveni/propojky` | +| Settings - hours | `/mudr-buzalkova/klinika/nastaveni/ordinacni-doba` | +| Settings - payments | `/mudr-buzalkova/klinika/nastaveni/platby` | +| Users | `/mudr-buzalkova/klinika/uzivatele/` | + +--- + +## Insurance Companies (CZ) + +| Code | Name | +|------|------| +| 0 | Ministerstvo zdravotnictvi CR | +| 111 | VZP | +| 201 | VoZP | +| 205 | CPZP | +| 207 | OZP | +| 209 | ZPS | +| 211 | ZPMV | +| 213 | RBP | +| 999 | Jina | + +--- + +## Clinic Features (enabled) +RESERVATION_REMINDERS, PUSH_NOTIFICATIONS, INVITES, ON_DEMAND_SLOT_GENERATOR, +REQUEST_TYPES_ACCESS_RULES, CAN_REFER_PATIENTS, TREATMENT_PLANS_V2, +MEDICAL_RECORDS, CAMPAIGNS, PATIENT_SEGMENTATION, ADVANCED_CALENDAR_FUNCTIONS, +PATIENT_CLINIC_STATUS, STANDARD_TARIFF_FUNCTIONS, CLINIC_CLOSED_WARNINGS, +ADVANCED_SWITCHBOARD, REQUEST_AUTOMATIONS, REQUEST_PINNING, +AI_SWITCHBOARD_PROMO, PATIENT_REQUEST_SUBSTATES --- ## Security / Maintenance Notes -- GraphQL introspection disabled. -- Refresh session cookies periodically. +- GraphQL introspection disabled. +- Refresh session cookies periodically. - Keep data GDPR-compliant. --- ## Future Tasks -- Automate cookie renewal. -- Build helper functions: - - `get_reservations(date)` using `ClinicAgenda_ListClinicReservations` - - `get_reservation_detail(reservation_id)` -- Map vaccine keywords (`Očkování – Chřipka`, `COVID`, `Hepatitida`, …). +- Automate cookie renewal. +- Build helper functions: + - `get_reservations(date)` using `ClinicCalendar_ListClinicReservations` + - `get_reservation_detail(reservation_id)` + - `get_patient(patient_id)` using `ClinicPatientDetailModal_GetData` + - `list_patients(offset)` using `LegacyClinicPatientListPage_ListClinicPatients` + - `search_patients(query)` using `Search` + - `get_requests(state, offset)` using `ClinicLegacyRequestList_ListPatientRequestsForClinic` + - `get_request_detail(request_id)` using `ClinicRequestDetail_GetPatientRequest2` +- Map vaccine keywords. - Export to Excel/MySQL for vaccine order planning.