From 505076bbf8b5a2bbc8173ea14a831adfea0018ed Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Fri, 14 Nov 2025 07:18:30 +0100 Subject: [PATCH] notebook --- Testy/05 Testy.py | 112 ++++++++++++++ Testy/06 Testy.py | 203 ++++++++++++++++++++++++++ Testy/07 Testy.py | 251 +++++++++++++++++++++++++++++++ Testy/08 Testy.py | 365 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 931 insertions(+) create mode 100644 Testy/05 Testy.py create mode 100644 Testy/06 Testy.py create mode 100644 Testy/07 Testy.py create mode 100644 Testy/08 Testy.py diff --git a/Testy/05 Testy.py b/Testy/05 Testy.py new file mode 100644 index 0000000..284f7d0 --- /dev/null +++ b/Testy/05 Testy.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import requests +from pathlib import Path + +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" + +GRAPHQL_URL = "https://api.medevio.cz/graphql" # ← správná URL + +# 👉 nastav offset zde +OFFSET = 0 # 0 = první pacient, 1 = druhý, 100 = 101. pacient +FIRST = 1 # načteme jen jednoho + +QUERY = """ +query PatientGridImpl_ListClinicPatients( + $clinicSlug: String!, + $filter: ListPatientFilter!, + $pageInfo: PageInfo!, + $sort: [ListPatientsSort!] +) { + patientsList: listPatients( + clinicSlug: $clinicSlug + filter: $filter + pageInfo: $pageInfo + sort: $sort + ) { + count + patients { + id + identificationNumber + insuranceCompanyObject { + id + shortName + } + lastReservation + locale + name + nextReservation + key + phone + sex + status2 + surname + type + kind + isInClinic + isUnknownPatient + user { + id + name + surname + phone + registrationCompletedTime + } + owner { + name + surname + } + clinics { + id + name + slug + } + tags(onlyImportant: false) { + id + name + color + icon + } + } + } +} +""" + +variables = { + "clinicSlug": CLINIC_SLUG, + "filter": {}, + "pageInfo": { + "first": FIRST, + "offset": OFFSET + }, + "sort": [ + { + "field": "ReverseFullName", + "sort": "ASC" + } + ] +} + +token = Path("token.txt").read_text().strip() + +headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", +} + +print("⏳ Fetching patient...") +response = requests.post( + GRAPHQL_URL, + json={"query": QUERY, "variables": variables}, + headers=headers +) + +response.raise_for_status() +data = response.json() + +print("\n📌 RAW JSON RESPONSE:\n") +print(json.dumps(data, indent=2, ensure_ascii=False)) diff --git a/Testy/06 Testy.py b/Testy/06 Testy.py new file mode 100644 index 0000000..289af46 --- /dev/null +++ b/Testy/06 Testy.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import requests +import pymysql +from pathlib import Path + +# ============================== +# 🔧 KONFIGURACE +# ============================== +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" +GRAPHQL_URL = "https://api.medevio.cz/graphql" + +DB_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# počet pacientů na stránku +PAGE_SIZE = 100 + + +# ============================== +# 📌 GRAPHQL QUERY +# ============================== +QUERY = """ +query PatientGridImpl_ListClinicPatients( + $clinicSlug: String!, + $filter: ListPatientFilter!, + $pageInfo: PageInfo!, + $sort: [ListPatientsSort!] +) { + patientsList: listPatients( + clinicSlug: $clinicSlug + filter: $filter + pageInfo: $pageInfo + sort: $sort + ) { + count + patients { + id + identificationNumber + insuranceCompanyObject { + id + shortName + } + name + surname + phone + sex + status2 + type + kind + isInClinic + isUnknownPatient + user { + registrationCompletedTime + } + clinics { + id + name + slug + } + tags(onlyImportant: false) { + id + name + color + icon + } + } + } +} +""" +from datetime import datetime + +def normalize_dt(dt_str): + if not dt_str: + return None + # remove Z + dt_str = dt_str.replace("Z", "") + # replace T with space + dt_str = dt_str.replace("T", " ") + # remove fractional seconds + if "." in dt_str: + dt_str = dt_str.split(".")[0] + return dt_str # MySQL can accept "YYYY-MM-DD HH:MM:SS" + +# ============================== +# 💾 ULOŽENÍ PACIENTA +# ============================== +def save_patient_summary(cur, p): + sql = """ + REPLACE INTO medevio_pacienti ( + id, jmeno, prijmeni, rodne_cislo, telefon, pohlavi, + pojistovna_id, pojistovna_nazev, + status, typ, kind, + is_in_clinic, is_unknown, + registration_time, + tags_json, clinics_json, + last_update + ) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + + ins = p.get("insuranceCompanyObject") or {} + user = p.get("user") or {} + + cur.execute(sql, ( + p.get("id"), + p.get("name"), + p.get("surname"), + p.get("identificationNumber"), + p.get("phone"), + p.get("sex"), + ins.get("id"), + ins.get("shortName"), + p.get("status2"), + p.get("type"), + p.get("kind"), + 1 if p.get("isInClinic") else 0, + 1 if p.get("isUnknownPatient") else 0, + normalize_dt(user.get("registrationCompletedTime")), + json.dumps(p.get("tags"), ensure_ascii=False), + json.dumps(p.get("clinics"), ensure_ascii=False), + )) + + +# ============================== +# 🧠 MAIN +# ============================== +def main(): + token = TOKEN_PATH.read_text().strip() + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + conn = pymysql.connect(**DB_CONFIG) + cur = conn.cursor() + + offset = 0 + total = None + + print("⏳ Starting patient sync...\n") + + while True: + print(f"➡️ Fetching patients {offset}–{offset + PAGE_SIZE} ...") + + variables = { + "clinicSlug": CLINIC_SLUG, + "filter": {}, + "pageInfo": {"first": PAGE_SIZE, "offset": offset}, + "sort": [{"field": "ReverseFullName", "sort": "ASC"}], + } + + response = requests.post( + GRAPHQL_URL, + json={"query": QUERY, "variables": variables}, + headers=headers, + timeout=30 + ) + response.raise_for_status() + data = response.json() + + block = data["data"]["patientsList"] + if total is None: + total = block["count"] + print(f"📌 Total patients: {total}\n") + + patients = block["patients"] + + if not patients: + print("✅ No more patients. Finished.") + break + + # save each patient + for p in patients: + save_patient_summary(cur, p) + + conn.commit() + + print(f" ✓ Saved {len(patients)} patients.") + + offset += PAGE_SIZE + if offset >= total: + print("\n✅ All patients downloaded.") + break + + cur.close() + conn.close() + + +# ============================== +if __name__ == "__main__": + main() diff --git a/Testy/07 Testy.py b/Testy/07 Testy.py new file mode 100644 index 0000000..a39e8c2 --- /dev/null +++ b/Testy/07 Testy.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import requests +from pathlib import Path + +TOKEN_PATH = Path("token.txt") +GRAPHQL_URL = "https://api.medevio.cz/graphql" +CLINIC_SLUG = "mudr-buzalkova" + +PATIENT_ID = "5a0a9ff0-bbe8-4fc7-a27d-b7b475ee2189" + +QUERY = """ +query ClinicPatientDetailModal_GetData( + $clinicSlug: String!, + $patientId: String!, + $patientUuid: UUID!, + $challengesStatus: ECRFChallengeStatus!, + $locale: Locale! +) { + clinic: getClinic(clinicSlug: $clinicSlug) { + id + features + sslSUKLCertificateId + type + ais + slug + ...ClinicWithTypeAndFeatures_Clinic + ...PatientInfo_Clinic + __typename + } + patient: getPatientForClinic(clinicSlug: $clinicSlug, patientId: $patientId) { + ...ClinicPatientDetailModal_Patient + __typename + } + challenges: listPatientChallenges2( + clinicSlug: $clinicSlug + patientId: $patientId + status: $challengesStatus + ) { + ...ChallengeTableList_EcrfChallenge + __typename + } + patientRequestsResponse: filterPatientRequestsForClinic( + clinicSlug: $clinicSlug + filter: {patientId: $patientUuid} + pageInfo: {first: 1, offset: 0} + ) { + count + items { id __typename } + __typename + } + treatmentPlanPatients: listTreatmentPlanPatients( + clinicSlug: $clinicSlug + patientId: $patientUuid + ) { + ...ClinicPlanPatientList_PlanPatient + __typename + } + premiumPlans: listClinicPremiumPlans(clinicSlug: $clinicSlug) { + id + __typename + } + mergeSuggestions: findMergeSuggestions( + clinicSlug: $clinicSlug + input: {existingPatientId: $patientUuid} + ) { + ...MergeSuggestionAlert_MergeSuggestionResult + __typename + } + insuranceCards: getPatientDocuments( + patientId: $patientUuid + type: InsuranceCard + ) { + ...PatientInfo_InsuranceCard + __typename + } +} + +fragment ClinicWithTypeAndFeatures_Clinic on Clinic { + id + type + features + __typename +} + +fragment PatientInfo_Clinic on Clinic { + country + id + slug + ais + ...ClinicWithTypeAndFeatures_Clinic + __typename +} + +fragment ClinicPatientDetailModal_Patient on ExtendedPatient { + id + isInClinic + kind + name + isUnknownPatient + sex + surname + identificationNumber + editableByDoctor + type + key + user { id name surname __typename } + ...ClinicPatientDetail_Patient + ...PatientInfo_AccountPatient + ...ClinicPatientInfo_Patient + __typename +} + +fragment ClinicPatientDetail_Patient on ExtendedPatient { + name + surname + email + id + identificationNumber + isInClinic + key + phone + sex + type + dob + user { id __typename } + isUnknownPatient + hasMobileApp + __typename +} + +fragment PatientInfo_AccountPatient on ExtendedPatient { + id + createdAt + key + user { + registrationCompletedTime + deactivatedTime + __typename + } + __typename +} + +fragment ClinicPatientInfo_Patient on ExtendedPatient { + anamnesisShared + anamnesisStatusForClinic { updatedAt __typename } + clinics { id name slug __typename } + id + isInClinic + dob + city + familyMembers: family { __typename } + houseNumber + identificationNumber + insuranceCompanyObject { id code name shortName __typename } + kind + name + note + owner { name surname __typename } + key + status + street + surname + user { id email name phone surname __typename } + userRelationship + premiumPlanPatient { id __typename } + sex + tags(onlyImportant: false) { id name color icon __typename } + type + isUnknownPatient + hasMobileApp + __typename +} + +fragment ChallengeTableList_EcrfChallenge on ECRFChallenge { + id + createdAt + sentAt + issuedToPatient { + id + identificationNumber + name + surname + __typename + } + userECRF(locale: $locale) { id name __typename } + patientRequestId + status + __typename +} + +fragment MergeSuggestionAlert_MergeSuggestionResult on MergeSuggestionResult { + extendedPatient { id __typename } + matchResult + __typename +} + +fragment ClinicPlanPatientList_PlanPatient on TreatmentPlanPatient { + id + createdAt + listPatient { id identificationNumber name key status surname __typename } + treatmentPlan { id slug name __typename } + __typename +} + +fragment PatientInfo_InsuranceCard on PatientDocument { + id + contentType + url + downloadUrl + __typename +} +""" + + +def main(): + token = TOKEN_PATH.read_text().strip() + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + variables = { + "clinicSlug": CLINIC_SLUG, + "patientId": PATIENT_ID, + "patientUuid": PATIENT_ID, + "challengesStatus": "SENT", + "locale": "cs", + } + + print("⏳ Fetching patient detail…") + + r = requests.post( + GRAPHQL_URL, + json={"query": QUERY, "variables": variables}, + headers=headers, + timeout=30 + ) + + r.raise_for_status() + data = r.json() + + print("\n📌 RAW DETAIL JSON:\n") + print(json.dumps(data, indent=2, ensure_ascii=False)) + + +if __name__ == "__main__": + main() diff --git a/Testy/08 Testy.py b/Testy/08 Testy.py new file mode 100644 index 0000000..54c0ac8 --- /dev/null +++ b/Testy/08 Testy.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import requests +import pymysql +from pathlib import Path + +# ============================== +# CONFIG +# ============================== +TOKEN_PATH = Path("token.txt") +GRAPHQL_URL = "https://api.medevio.cz/graphql" +CLINIC_SLUG = "mudr-buzalkova" + +DB_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +PATIENT_ID = "5a0a9ff0-bbe8-4fc7-a27d-b7b475ee2189" + + +# ============================== +# HELPERS +# ============================== +def normalize_dt(dt_str): + if not dt_str: + return None + dt_str = dt_str.replace("Z", "").replace("T", " ") + if "." in dt_str: + dt_str = dt_str.split(".")[0] + return dt_str + + +def save_patient_detail(cur, p): + user = p.get("user") or {} + ins = p.get("insuranceCompanyObject") or {} + tags = p.get("tags") or [] + clinics = p.get("clinics") or [] + + sql = """ + UPDATE medevio_pacienti SET + email = %s, + telefon = %s, + dob = %s, + street = %s, + house_number = %s, + city = %s, + + user_id = %s, + user_email = %s, + user_name = %s, + user_surname = %s, + user_phone = %s, + user_reg_time = %s, + user_deactivated_time = %s, + + created_at = %s, + note = %s, + has_mobile_app = %s, + user_relationship = %s, + + pojistovna_code = %s, + tags_json = %s, + clinics_json = %s, + last_update = NOW() + + WHERE id = %s + """ + + cur.execute(sql, ( + p.get("email"), + p.get("phone"), + p.get("dob"), + + p.get("street"), + p.get("houseNumber"), + p.get("city"), + + user.get("id"), + user.get("email"), + user.get("name"), + user.get("surname"), + user.get("phone"), + normalize_dt(user.get("registrationCompletedTime")), + normalize_dt(user.get("deactivatedTime")), + + normalize_dt(p.get("createdAt")), + p.get("note"), + 1 if p.get("hasMobileApp") else 0, + p.get("userRelationship"), + + ins.get("code"), + json.dumps(tags, ensure_ascii=False), + json.dumps(clinics, ensure_ascii=False), + + p.get("id") + )) + + +# ============================== +# FULL EXACT WORKING GRAPHQL QUERY +# ============================== +QUERY = """ +query ClinicPatientDetailModal_GetData( + $clinicSlug: String!, + $patientId: String!, + $patientUuid: UUID!, + $challengesStatus: ECRFChallengeStatus!, + $locale: Locale! +) { + clinic: getClinic(clinicSlug: $clinicSlug) { + id + features + sslSUKLCertificateId + type + ais + slug + ...ClinicWithTypeAndFeatures_Clinic + ...PatientInfo_Clinic + __typename + } + patient: getPatientForClinic(clinicSlug: $clinicSlug, patientId: $patientId) { + ...ClinicPatientDetailModal_Patient + __typename + } + challenges: listPatientChallenges2( + clinicSlug: $clinicSlug + patientId: $patientId + status: $challengesStatus + ) { + ...ChallengeTableList_EcrfChallenge + __typename + } + patientRequestsResponse: filterPatientRequestsForClinic( + clinicSlug: $clinicSlug + filter: {patientId: $patientUuid} + pageInfo: {first: 1, offset: 0} + ) { + count + items { id __typename } + __typename + } + treatmentPlanPatients: listTreatmentPlanPatients( + clinicSlug: $clinicSlug + patientId: $patientUuid + ) { + ...ClinicPlanPatientList_PlanPatient + __typename + } + premiumPlans: listClinicPremiumPlans(clinicSlug: $clinicSlug) { + id + __typename + } + mergeSuggestions: findMergeSuggestions( + clinicSlug: $clinicSlug + input: {existingPatientId: $patientUuid} + ) { + ...MergeSuggestionAlert_MergeSuggestionResult + __typename + } + insuranceCards: getPatientDocuments( + patientId: $patientUuid + type: InsuranceCard + ) { + ...PatientInfo_InsuranceCard + __typename + } +} + +fragment ClinicWithTypeAndFeatures_Clinic on Clinic { + id + type + features + __typename +} + +fragment PatientInfo_Clinic on Clinic { + country + id + slug + ais + ...ClinicWithTypeAndFeatures_Clinic + __typename +} + +fragment ClinicPatientDetailModal_Patient on ExtendedPatient { + id + isInClinic + kind + name + isUnknownPatient + sex + surname + identificationNumber + editableByDoctor + type + key + user { id name surname __typename } + ...ClinicPatientDetail_Patient + ...PatientInfo_AccountPatient + ...ClinicPatientInfo_Patient + __typename +} + +fragment ClinicPatientDetail_Patient on ExtendedPatient { + name + surname + email + id + identificationNumber + isInClinic + key + phone + sex + type + dob + user { id __typename } + isUnknownPatient + hasMobileApp + __typename +} + +fragment PatientInfo_AccountPatient on ExtendedPatient { + id + createdAt + key + user { + registrationCompletedTime + deactivatedTime + __typename + } + __typename +} + +fragment ClinicPatientInfo_Patient on ExtendedPatient { + anamnesisShared + anamnesisStatusForClinic { updatedAt __typename } + clinics { id name slug __typename } + id + isInClinic + dob + city + familyMembers: family { __typename } + houseNumber + identificationNumber + insuranceCompanyObject { id code name shortName __typename } + kind + name + note + owner { name surname __typename } + key + status + street + surname + user { id email name phone surname __typename } + userRelationship + premiumPlanPatient { id __typename } + sex + tags(onlyImportant: false) { id name color icon __typename } + type + isUnknownPatient + hasMobileApp + __typename +} + +fragment ChallengeTableList_EcrfChallenge on ECRFChallenge { + id + createdAt + sentAt + issuedToPatient { + id + identificationNumber + name + surname + __typename + } + userECRF(locale: $locale) { id name __typename } + patientRequestId + status + __typename +} + +fragment MergeSuggestionAlert_MergeSuggestionResult on MergeSuggestionResult { + extendedPatient { id __typename } + matchResult + __typename +} + +fragment ClinicPlanPatientList_PlanPatient on TreatmentPlanPatient { + id + createdAt + listPatient { id identificationNumber name key status surname __typename } + treatmentPlan { id slug name __typename } + __typename +} + +fragment PatientInfo_InsuranceCard on PatientDocument { + id + contentType + url + downloadUrl + __typename +} +""" + + +# ============================== +# MAIN +# ============================== +def main(): + token = TOKEN_PATH.read_text().strip() + + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + variables = { + "clinicSlug": CLINIC_SLUG, + "patientId": PATIENT_ID, + "patientUuid": PATIENT_ID, + "challengesStatus": "SENT", + "locale": "cs", + } + + print(f"⏳ Fetching patient detail {PATIENT_ID}…") + + r = requests.post( + GRAPHQL_URL, + json={"query": QUERY, "variables": variables}, + headers=headers, + timeout=30 + ) + r.raise_for_status() + data = r.json() + + patient = data["data"]["patient"] + if not patient: + print("❌ Patient not found in API response!") + return + + print("📥 Patient detail downloaded.") + + conn = pymysql.connect(**DB_CONFIG) + cur = conn.cursor() + + save_patient_detail(cur, patient) + conn.commit() + + print("✅ Patient detail saved to DB.") + + cur.close() + conn.close() + + +if __name__ == "__main__": + main()