From 30d64680bed637b856e9deccdbd9b19466ecfb14 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Wed, 28 Jan 2026 07:26:08 +0100 Subject: [PATCH] notebook --- 12 Tower1/10 ReadAllActive.py | 214 +++++++++++++++++++++++++++ 12 Tower1/12 ReadAllinBatches.py | 239 +++++++++++++++++++++++++++++++ 12 Tower1/20 ReadPoznamky.py | 217 ++++++++++++++++++++++++++++ 12 Tower1/30 ReadConversation.py | 147 +++++++++++++++++++ 12 Tower1/40 DownloadPrilohy.py | 177 +++++++++++++++++++++++ 12 Tower1/token.txt | 1 + 6 files changed, 995 insertions(+) create mode 100644 12 Tower1/10 ReadAllActive.py create mode 100644 12 Tower1/12 ReadAllinBatches.py create mode 100644 12 Tower1/20 ReadPoznamky.py create mode 100644 12 Tower1/30 ReadConversation.py create mode 100644 12 Tower1/40 DownloadPrilohy.py create mode 100644 12 Tower1/token.txt diff --git a/12 Tower1/10 ReadAllActive.py b/12 Tower1/10 ReadAllActive.py new file mode 100644 index 0000000..483581e --- /dev/null +++ b/12 Tower1/10 ReadAllActive.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import pymysql +import requests +from pathlib import Path +from datetime import datetime, timezone +import time +from dateutil import parser +import sys + +# Force UTF-8 output +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') + +# ================================ +# 🔧 CONFIGURATION +# ================================ +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" +BATCH_SIZE = 100 + +DB_CONFIG = { + "host": "192.168.1.50", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# ⭐ NOVÝ TESTOVANÝ DOTAZ – obsahuje lastMessage.createdAt +GRAPHQL_QUERY = r""" +query ClinicRequestList2( + $clinicSlug: String!, + $queueId: String, + $queueAssignment: QueueAssignmentFilter!, + $state: PatientRequestState, + $pageInfo: PageInfo!, + $locale: Locale! +) { + requestsResponse: listPatientRequestsForClinic2( + clinicSlug: $clinicSlug, + queueId: $queueId, + queueAssignment: $queueAssignment, + state: $state, + pageInfo: $pageInfo + ) { + count + patientRequests { + id + displayTitle(locale: $locale) + createdAt + updatedAt + doneAt + removedAt + extendedPatient { + name + surname + identificationNumber + } + lastMessage { + createdAt + } + } + } +} +""" + +# ================================ +# 🧿 SAFE DATETIME PARSER (ALWAYS UTC → LOCAL) +# ================================ +def to_mysql_dt_utc(iso_str): + if not iso_str: + return None + try: + dt = parser.isoparse(iso_str) + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + dt_local = dt.astimezone() + return dt_local.strftime("%Y-%m-%d %H:%M:%S") + except: + return None + +# ================================ +# 🔑 TOKEN +# ================================ +def read_token(path: Path) -> str: + tok = path.read_text(encoding="utf-8").strip() + if tok.startswith("Bearer "): + return tok.split(" ", 1)[1] + return tok + +# ================================ +# 💾 UPSERT +# ================================ +def upsert(conn, r): + p = r.get("extendedPatient") or {} + api_updated = to_mysql_dt_utc(r.get("updatedAt")) + last_msg = r.get("lastMessage") or {} + msg_updated = to_mysql_dt_utc(last_msg.get("createdAt")) + + def max_dt(a, b): + if a and b: + return max(a, b) + return a or b + + final_updated = max_dt(api_updated, msg_updated) + + sql = """ + INSERT INTO pozadavky ( + id, displayTitle, createdAt, updatedAt, doneAt, removedAt, + pacient_jmeno, pacient_prijmeni, pacient_rodnecislo + ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) + ON DUPLICATE KEY UPDATE + displayTitle=VALUES(displayTitle), + updatedAt=VALUES(updatedAt), + doneAt=VALUES(doneAt), + removedAt=VALUES(removedAt), + pacient_jmeno=VALUES(pacient_jmeno), + pacient_prijmeni=VALUES(pacient_prijmeni), + pacient_rodnecislo=VALUES(pacient_rodnecislo) + """ + + vals = ( + r.get("id"), + r.get("displayTitle"), + to_mysql_dt_utc(r.get("createdAt")), + final_updated, + to_mysql_dt_utc(r.get("doneAt")), + to_mysql_dt_utc(r.get("removedAt")), + p.get("name"), + p.get("surname"), + p.get("identificationNumber"), + ) + + with conn.cursor() as cur: + cur.execute(sql, vals) + conn.commit() + +# ================================ +# 📡 FETCH ACTIVE PAGE +# ================================ +def fetch_active(headers, offset): + variables = { + "clinicSlug": CLINIC_SLUG, + "queueId": None, + "queueAssignment": "ANY", + "pageInfo": {"first": BATCH_SIZE, "offset": offset}, + "locale": "cs", + "state": "ACTIVE", + } + + payload = { + "operationName": "ClinicRequestList2", + "query": GRAPHQL_QUERY, + "variables": variables, + } + + r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers) + r.raise_for_status() + data = r.json().get("data", {}).get("requestsResponse", {}) + return data.get("patientRequests", []), data.get("count", 0) + +# ================================ +# 🧠 MAIN +# ================================ +def main(): + token = read_token(TOKEN_PATH) + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + conn = pymysql.connect(**DB_CONFIG) + print(f"\n=== Sync ACTIVE požadavků @ {datetime.now():%Y-%m-%d %H:%M:%S} ===") + + offset = 0 + total_processed = 0 + total_count = None + + while True: + batch, count = fetch_active(headers, offset) + if total_count is None: + total_count = count + print(f"📡 Celkem ACTIVE v Medevio: {count}") + + if not batch: + break + + for r in batch: + upsert(conn, r) + + total_processed += len(batch) + print(f" • {total_processed}/{total_count} ACTIVE processed") + + if offset + BATCH_SIZE >= count: + break + + offset += BATCH_SIZE + time.sleep(0.4) + + conn.close() + print("\n✅ ACTIVE sync hotovo!\n") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/12 Tower1/12 ReadAllinBatches.py b/12 Tower1/12 ReadAllinBatches.py new file mode 100644 index 0000000..b88ebdc --- /dev/null +++ b/12 Tower1/12 ReadAllinBatches.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import pymysql +import requests +from pathlib import Path +from datetime import datetime +from dateutil import parser +import time +import sys + +# ================================ +# UTF-8 SAFE OUTPUT (Windows friendly) +# ================================ +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') + + +def safe_print(text: str): + enc = sys.stdout.encoding or "" + if not enc.lower().startswith("utf"): + text = ''.join(ch for ch in text if ord(ch) < 65536) + try: + print(text) + except UnicodeEncodeError: + text = ''.join(ch for ch in text if ord(ch) < 128) + print(text) + + +# ================================ +# 🔧 CONFIG +# ================================ +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" + +BATCH_SIZE = 500 +STATES = ["ACTIVE", "DONE"] # explicitně – jinak API vrací jen ACTIVE + +DB_CONFIG = { + "host": "192.168.1.50", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +GRAPHQL_QUERY = r""" +query ClinicRequestList2( + $clinicSlug: String!, + $queueId: String, + $queueAssignment: QueueAssignmentFilter!, + $state: PatientRequestState, + $pageInfo: PageInfo!, + $locale: Locale! +) { + requestsResponse: listPatientRequestsForClinic2( + clinicSlug: $clinicSlug, + queueId: $queueId, + queueAssignment: $queueAssignment, + state: $state, + pageInfo: $pageInfo + ) { + count + patientRequests { + id + displayTitle(locale: $locale) + createdAt + updatedAt + doneAt + removedAt + extendedPatient { + name + surname + identificationNumber + } + lastMessage { + createdAt + } + } + } +} +""" + + +# ================================ +# TOKEN +# ================================ +def read_token(path: Path) -> str: + tok = path.read_text(encoding="utf-8").strip() + if tok.startswith("Bearer "): + return tok.split(" ", 1)[1] + return tok + + +# ================================ +# DATETIME PARSER +# ================================ +def to_mysql_dt(iso_str): + if not iso_str: + return None + try: + dt = parser.isoparse(iso_str) + if dt.tzinfo is None: + dt = dt.replace(tzinfo=datetime.now().astimezone().tzinfo) + return dt.astimezone().strftime("%Y-%m-%d %H:%M:%S") + except Exception: + return None + + +# ================================ +# UPSERT +# ================================ +def upsert(conn, r): + p = r.get("extendedPatient") or {} + + api_updated = to_mysql_dt(r.get("updatedAt")) + msg_updated = to_mysql_dt((r.get("lastMessage") or {}).get("createdAt")) + + final_updated = max(filter(None, [api_updated, msg_updated]), default=None) + + sql = """ + INSERT INTO pozadavky ( + id, displayTitle, createdAt, updatedAt, doneAt, removedAt, + pacient_jmeno, pacient_prijmeni, pacient_rodnecislo + ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s) + ON DUPLICATE KEY UPDATE + displayTitle=VALUES(displayTitle), + updatedAt=VALUES(updatedAt), + doneAt=VALUES(doneAt), + removedAt=VALUES(removedAt), + pacient_jmeno=VALUES(pacient_jmeno), + pacient_prijmeni=VALUES(pacient_prijmeni), + pacient_rodnecislo=VALUES(pacient_rodnecislo) + """ + + vals = ( + r.get("id"), + r.get("displayTitle"), + to_mysql_dt(r.get("createdAt")), + final_updated, + to_mysql_dt(r.get("doneAt")), + to_mysql_dt(r.get("removedAt")), + p.get("name"), + p.get("surname"), + p.get("identificationNumber"), + ) + + with conn.cursor() as cur: + cur.execute(sql, vals) + conn.commit() + + +# ================================ +# FETCH PAGE (per state) +# ================================ +def fetch_state(headers, state, offset): + variables = { + "clinicSlug": CLINIC_SLUG, + "queueId": None, + "queueAssignment": "ANY", + "state": state, + "pageInfo": {"first": BATCH_SIZE, "offset": offset}, + "locale": "cs", + } + + payload = { + "operationName": "ClinicRequestList2", + "query": GRAPHQL_QUERY, + "variables": variables, + } + + r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers) + r.raise_for_status() + + data = r.json()["data"]["requestsResponse"] + return data.get("patientRequests", []), data.get("count", 0) + + +# ================================ +# MAIN +# ================================ +def main(): + token = read_token(TOKEN_PATH) + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + conn = pymysql.connect(**DB_CONFIG) + + safe_print(f"\n=== FULL Medevio READ-ALL sync @ {datetime.now():%Y-%m-%d %H:%M:%S} ===") + + grand_total = 0 + + for state in STATES: + safe_print(f"\n🔁 STATE = {state}") + offset = 0 + total = None + processed = 0 + + while True: + batch, count = fetch_state(headers, state, offset) + + if total is None: + total = count + safe_print(f"📡 {state}: celkem {total}") + + if not batch: + break + + for r in batch: + upsert(conn, r) + + processed += len(batch) + safe_print(f" • {processed}/{total}") + + offset += BATCH_SIZE + if offset >= count: + break + + time.sleep(0.4) + + grand_total += processed + + conn.close() + safe_print(f"\n✅ HOTOVO – celkem zpracováno {grand_total} požadavků\n") + + +# ================================ +if __name__ == "__main__": + main() diff --git a/12 Tower1/20 ReadPoznamky.py b/12 Tower1/20 ReadPoznamky.py new file mode 100644 index 0000000..63ba4b8 --- /dev/null +++ b/12 Tower1/20 ReadPoznamky.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Download and store Medevio questionnaires (userNote + eCRF) for all patient requests. +Uses the verified working query "GetPatientRequest2". +""" + +import json +import requests +import pymysql +from datetime import datetime +from pathlib import Path +import time +import sys + +# Force UTF-8 output even under Windows Task Scheduler +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') + + +# ============================== +# 🛡 SAFE PRINT FOR CP1250 / EMOJI +# ============================== +def safe_print(text: str): + enc = sys.stdout.encoding or "" + if not enc.lower().startswith("utf"): + text = ''.join(ch for ch in text if ord(ch) < 65536) + try: + print(text) + except UnicodeEncodeError: + text = ''.join(ch for ch in text if ord(ch) < 128) + print(text) + + +# ============================== +# 🔧 CONFIGURATION (UPDATED TO 192.168.1.50) +# ============================== +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" +GRAPHQL_URL = "https://api.medevio.cz/graphql" + +DB_CONFIG = { + "host": "192.168.1.50", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + + +# ============================== +# 🕒 DATETIME FIXER +# ============================== +def fix_datetime(dt_str): + if not dt_str: + return None + try: + return datetime.fromisoformat(dt_str.replace("Z", "").replace("+00:00", "")) + except Exception: + return None + + +# Optional filter +CREATED_AFTER = "2025-01-01" + + +# ============================== +# 🧮 HELPERS +# ============================== +def read_token(p: Path) -> str: + tok = p.read_text(encoding="utf-8").strip() + if tok.startswith("Bearer "): + return tok.split(" ", 1)[1] + return tok + + +GRAPHQL_QUERY = r""" +query GetPatientRequest2($requestId: UUID!, $clinicSlug: String!, $locale: Locale!) { + request: getPatientRequest2(patientRequestId: $requestId, clinicSlug: $clinicSlug) { + id + displayTitle(locale: $locale) + createdAt + updatedAt + userNote + eventType + extendedPatient(clinicSlug: $clinicSlug) { + name + surname + identificationNumber + } + ecrfFilledData(locale: $locale) { + name + groups { + label + fields { + name + label + type + value + } + } + } + } +} +""" + + +def fetch_questionnaire(headers, request_id, clinic_slug): + payload = { + "operationName": "GetPatientRequest2", + "query": GRAPHQL_QUERY, + "variables": { + "requestId": request_id, + "clinicSlug": clinic_slug, + "locale": "cs", + }, + } + r = requests.post(GRAPHQL_URL, json=payload, headers=headers, timeout=40) + if r.status_code != 200: + safe_print(f"❌ HTTP {r.status_code} for {request_id}: {r.text}") + return None + return r.json().get("data", {}).get("request") + + +def insert_questionnaire(cur, req): + if not req: + return + + patient = req.get("extendedPatient") or {} + ecrf_data = req.get("ecrfFilledData") + created_at = fix_datetime(req.get("createdAt")) + updated_at = fix_datetime(req.get("updatedAt")) + + cur.execute(""" + INSERT INTO medevio_questionnaires ( + request_id, created_at, updated_at, user_note, ecrf_json + ) + VALUES (%s,%s,%s,%s,%s) + ON DUPLICATE KEY UPDATE + updated_at = VALUES(updated_at), + user_note = VALUES(user_note), + ecrf_json = VALUES(ecrf_json), + updated_local = NOW() + """, ( + req.get("id"), + created_at, + updated_at, + req.get("userNote"), + json.dumps(ecrf_data, ensure_ascii=False), + )) + + safe_print(f" 💾 Stored questionnaire for {patient.get('surname','')} {patient.get('name','')}") + + +# ============================== +# 🧠 MAIN +# ============================== +def main(): + token = read_token(TOKEN_PATH) + headers = { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + conn = pymysql.connect(**DB_CONFIG) + + # load list of requests from the table we just filled + with conn.cursor() as cur: + sql = """ + SELECT id, pacient_jmeno, pacient_prijmeni, createdAt, updatedAt, questionnaireprocessed + FROM pozadavky + WHERE (questionnaireprocessed IS NULL OR questionnaireprocessed < updatedAt) + """ + if CREATED_AFTER: + sql += " AND createdAt >= %s" + cur.execute(sql, (CREATED_AFTER,)) + else: + cur.execute(sql) + + rows = cur.fetchall() + + safe_print(f"📋 Found {len(rows)} requests needing questionnaire check.") + + for i, row in enumerate(rows, 1): + req_id = row["id"] + safe_print(f"\n[{i}/{len(rows)}] 🔍 Fetching questionnaire for {req_id} ...") + + req = fetch_questionnaire(headers, req_id, CLINIC_SLUG) + if not req: + safe_print(" ⚠️ No questionnaire data found.") + continue + + with conn.cursor() as cur: + insert_questionnaire(cur, req) + cur.execute( + "UPDATE pozadavky SET questionnaireprocessed = NOW() WHERE id = %s", + (req_id,) + ) + conn.commit() + + time.sleep(0.6) + + conn.close() + safe_print("\n✅ Done! All questionnaires stored in MySQL table `medevio_questionnaires`.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/12 Tower1/30 ReadConversation.py b/12 Tower1/30 ReadConversation.py new file mode 100644 index 0000000..d1e4371 --- /dev/null +++ b/12 Tower1/30 ReadConversation.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import requests +import pymysql +from pathlib import Path +from datetime import datetime +import time +import sys + +# UTF-8 SAFE OUTPUT +try: + sys.stdout.reconfigure(encoding='utf-8') + sys.stderr.reconfigure(encoding='utf-8') +except AttributeError: + pass + +# ============================== +# CONFIG (.50) +# ============================== +TOKEN_PATH = Path("token.txt") + +DB_CONFIG = { + "host": "192.168.1.50", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +GRAPHQL_QUERY_MESSAGES = r""" +query UseMessages_ListMessages($requestId: String!, $updatedSince: DateTime) { + messages: listMessages(patientRequestId: $requestId, updatedSince: $updatedSince) { + id createdAt updatedAt readAt text type + sender { id name surname clinicId } + medicalRecord { id description contentType url downloadUrl createdAt updatedAt } + } +} +""" + +def parse_dt(s): + if not s: return None + try: return datetime.fromisoformat(s.replace("Z", "+00:00")) + except: return None + +def read_token(path: Path) -> str: + return path.read_text(encoding="utf-8").strip().replace("Bearer ", "") + +def main(): + token = read_token(TOKEN_PATH) + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + conn = pymysql.connect(**DB_CONFIG) + + # 1. Seznam již stažených příloh (prevence duplicit) + with conn.cursor() as cur: + cur.execute("SELECT attachment_id FROM medevio_downloads") + existing_ids = {r["attachment_id"] for r in cur.fetchall()} + + # 2. Seznam požadavků k synchronizaci + with conn.cursor() as cur: + cur.execute(""" + SELECT id, messagesProcessed FROM pozadavky + WHERE messagesProcessed IS NULL OR messagesProcessed < updatedAt + """) + rows = cur.fetchall() + + print(f"📋 Počet požadavků k synchronizaci zpráv: {len(rows)}") + + for i, row in enumerate(rows, 1): + req_id = row["id"] + updated_since = row["messagesProcessed"] + if updated_since: + updated_since = updated_since.replace(microsecond=0).isoformat() + "Z" + + print(f"[{i}/{len(rows)}] Synchronizuji: {req_id}") + + payload = { + "operationName": "UseMessages_ListMessages", + "query": GRAPHQL_QUERY_MESSAGES, + "variables": {"requestId": req_id, "updatedSince": updated_since} + } + + try: + r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) + messages = r.json().get("data", {}).get("messages", []) or [] + + if messages: + with conn.cursor() as cur: + for msg in messages: + # Uložení zprávy + sender = msg.get("sender") or {} + sender_name = " ".join(filter(None, [sender.get("name"), sender.get("surname")])) + mr = msg.get("medicalRecord") or {} + + cur.execute(""" + INSERT INTO medevio_conversation ( + id, request_id, sender_name, sender_id, sender_clinic_id, + text, created_at, read_at, updated_at, + attachment_url, attachment_description, attachment_content_type + ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + ON DUPLICATE KEY UPDATE + text = VALUES(text), updated_at = VALUES(updated_at), read_at = VALUES(read_at) + """, ( + msg.get("id"), req_id, sender_name, sender.get("id"), sender.get("clinicId"), + msg.get("text"), parse_dt(msg.get("createdAt")), parse_dt(msg.get("readAt")), + parse_dt(msg.get("updatedAt")), mr.get("downloadUrl") or mr.get("url"), + mr.get("description"), mr.get("contentType") + )) + + # Uložení přílohy (pokud existuje a nemáme ji) + attachment_id = mr.get("id") + if attachment_id and attachment_id not in existing_ids: + url = mr.get("downloadUrl") or mr.get("url") + if url: + att_r = requests.get(url, timeout=30) + if att_r.status_code == 200: + cur.execute(""" + INSERT INTO medevio_downloads ( + request_id, attachment_id, attachment_type, + filename, content_type, file_size, created_at, file_content + ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) + """, ( + req_id, attachment_id, "MESSAGE_ATTACHMENT", + url.split("/")[-1].split("?")[0], mr.get("contentType"), + len(att_r.content), parse_dt(msg.get("createdAt")), att_r.content + )) + existing_ids.add(attachment_id) + + cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,)) + conn.commit() + else: + with conn.cursor() as cur: + cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,)) + conn.commit() + + time.sleep(0.3) + except Exception as e: + print(f" ❌ Chyba u {req_id}: {e}") + + conn.close() + print("\n🎉 Delta sync zpráv a příloh DOKONČEN") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/12 Tower1/40 DownloadPrilohy.py b/12 Tower1/40 DownloadPrilohy.py new file mode 100644 index 0000000..2507099 --- /dev/null +++ b/12 Tower1/40 DownloadPrilohy.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Download all attachments for pozadavky where attachmentsProcessed IS NULL +Store them in MySQL table `medevio_downloads` on 192.168.1.50. +""" + +import zlib +import json +import requests +import pymysql +from pathlib import Path +from datetime import datetime +import time +import sys + +# Force UTF-8 output +try: + sys.stdout.reconfigure(encoding='utf-8') + sys.stderr.reconfigure(encoding='utf-8') +except AttributeError: + pass + + +# ============================== +# 🛡 SAFE PRINT +# ============================== +def safe_print(text: str): + enc = sys.stdout.encoding or "" + if not enc or not enc.lower().startswith("utf"): + text = ''.join(ch for ch in text if ord(ch) < 65536) + try: + print(text) + except UnicodeEncodeError: + text = ''.join(ch for ch in text if ord(ch) < 128) + print(text) + + +# ============================== +# 🔧 CONFIGURATION (.50) +# ============================== +TOKEN_PATH = Path("token.txt") +CLINIC_SLUG = "mudr-buzalkova" + +DB_CONFIG = { + "host": "192.168.1.50", + "port": 3306, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +CREATED_AFTER = "2024-12-01" + +GRAPHQL_QUERY = r""" +query ClinicRequestDetail_GetPatientRequest2($requestId: UUID!) { + patientRequestMedicalRecords: listMedicalRecordsForPatientRequest( + attachmentTypes: [ECRF_FILL_ATTACHMENT, MESSAGE_ATTACHMENT, PATIENT_REQUEST_ATTACHMENT] + patientRequestId: $requestId + pageInfo: {first: 100, offset: 0} + ) { + attachmentType + id + medicalRecord { + contentType + description + downloadUrl + id + url + visibleToPatient + } + } +} +""" + + +def extract_filename_from_url(url: str) -> str: + try: + return url.split("/")[-1].split("?")[0] + except: + return "unknown_filename" + + +def read_token(p: Path) -> str: + tok = p.read_text(encoding="utf-8").strip() + return tok.split(" ", 1)[1] if tok.startswith("Bearer ") else tok + + +def main(): + token = read_token(TOKEN_PATH) + headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"} + conn = pymysql.connect(**DB_CONFIG) + + # 1. Načtení ID již stažených příloh + with conn.cursor() as cur: + cur.execute("SELECT attachment_id FROM medevio_downloads") + existing_ids = {row["attachment_id"] for row in cur.fetchall()} + + safe_print(f"✅ V databázi již máme {len(existing_ids)} příloh.") + + # 2. Výběr požadavků ke zpracování + sql = "SELECT id, pacient_prijmeni, pacient_jmeno, createdAt FROM pozadavky WHERE attachmentsProcessed IS NULL" + params = [] + if CREATED_AFTER: + sql += " AND createdAt >= %s" + params.append(CREATED_AFTER) + + with conn.cursor() as cur: + cur.execute(sql, params) + req_rows = cur.fetchall() + + safe_print(f"📋 Počet požadavků ke stažení příloh: {len(req_rows)}") + + for i, row in enumerate(req_rows, 1): + req_id = row["id"] + prijmeni = row.get("pacient_prijmeni") or "Neznamy" + created_date = row.get("createdAt") or datetime.now() + + safe_print(f"\n[{i}/{len(req_rows)}] 🧾 {prijmeni} ({req_id})") + + payload = { + "operationName": "ClinicRequestDetail_GetPatientRequest2", + "query": GRAPHQL_QUERY, + "variables": {"requestId": req_id}, + } + + try: + r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) + attachments = r.json().get("data", {}).get("patientRequestMedicalRecords", []) + + if attachments: + with conn.cursor() as cur: + for a in attachments: + m = a.get("medicalRecord") or {} + att_id = a.get("id") + + if att_id in existing_ids: + continue + + url = m.get("downloadUrl") + if url: + att_r = requests.get(url, timeout=30) + if att_r.status_code == 200: + content = att_r.content + filename = extract_filename_from_url(url) + + cur.execute(""" + INSERT INTO medevio_downloads ( + request_id, attachment_id, attachment_type, + filename, content_type, file_size, + created_at, file_content + ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s) + """, (req_id, att_id, a.get("attachmentType"), filename, + m.get("contentType"), len(content), created_date, content)) + existing_ids.add(att_id) + safe_print(f" 💾 Uloženo: {filename} ({len(content) / 1024:.1f} kB)") + + conn.commit() + + # Označíme jako zpracované i když nebyly nalezeny žádné přílohy + with conn.cursor() as cur: + cur.execute("UPDATE pozadavky SET attachmentsProcessed = NOW() WHERE id = %s", (req_id,)) + conn.commit() + + time.sleep(0.3) + except Exception as e: + print(f" ❌ Chyba u {req_id}: {e}") + + conn.close() + safe_print("\n🎯 Všechny přílohy byly zpracovány.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/12 Tower1/token.txt b/12 Tower1/token.txt new file mode 100644 index 0000000..d31188b --- /dev/null +++ b/12 Tower1/token.txt @@ -0,0 +1 @@ +nYvrvgflIKcDiQg8Hhpud+qG8iGZ8eH8su4nyT/Mgcm7XQp65ygY9s39+O01wIpk/7sKd6fBHkiKvsqH \ No newline at end of file