From ac16eedde946f0c86f36c25ec1b7dbb4208a65f0 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Fri, 21 Nov 2025 07:14:49 +0100 Subject: [PATCH] notebook --- .idea/Medevio.iml | 2 +- .idea/misc.xml | 2 +- Testy/000 Testy.py | 229 ----------- Testy/{17 test.py => 01 Test} | 22 +- Testy/01 Test.py | 122 ------ Testy/02 Test.py | 296 -------------- Testy/03 Test.py | 105 ----- Testy/03 Testt.py | 96 ----- Testy/04 Test.py | 216 ----------- Testy/05 Testy.py | 112 ------ Testy/06 Testy.py | 203 ---------- Testy/07 Testy.py | 251 ------------ Testy/08 Testy.py | 365 ------------------ Testy/09 Testy pt details all.py | 183 --------- .../10 Testy stahni pouze active pozadavky.py | 197 ---------- Testy/11 Testy.py | 87 ----- Testy/12 Testy.py | 168 -------- Testy/13 testy report pacienti.py | 226 ----------- Testy/14 Testy updateat.py | 227 ----------- Testy/15 test.py | 136 ------- Testy/16 test.py | 228 ----------- Testy/18 Test.py | 191 --------- Testy/19 Test 2.py | 146 ------- Testy/19 Test download at once.py | 102 ----- Testy/19 Test.py | 173 --------- 25 files changed, 19 insertions(+), 4066 deletions(-) delete mode 100644 Testy/000 Testy.py rename Testy/{17 test.py => 01 Test} (92%) delete mode 100644 Testy/01 Test.py delete mode 100644 Testy/02 Test.py delete mode 100644 Testy/03 Test.py delete mode 100644 Testy/03 Testt.py delete mode 100644 Testy/04 Test.py delete mode 100644 Testy/05 Testy.py delete mode 100644 Testy/06 Testy.py delete mode 100644 Testy/07 Testy.py delete mode 100644 Testy/08 Testy.py delete mode 100644 Testy/09 Testy pt details all.py delete mode 100644 Testy/10 Testy stahni pouze active pozadavky.py delete mode 100644 Testy/11 Testy.py delete mode 100644 Testy/12 Testy.py delete mode 100644 Testy/13 testy report pacienti.py delete mode 100644 Testy/14 Testy updateat.py delete mode 100644 Testy/15 test.py delete mode 100644 Testy/16 test.py delete mode 100644 Testy/18 Test.py delete mode 100644 Testy/19 Test 2.py delete mode 100644 Testy/19 Test download at once.py delete mode 100644 Testy/19 Test.py diff --git a/.idea/Medevio.iml b/.idea/Medevio.iml index b6731d8..3cd7809 100644 --- a/.idea/Medevio.iml +++ b/.idea/Medevio.iml @@ -4,7 +4,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 912bb5e..7a3c570 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + \ No newline at end of file diff --git a/Testy/000 Testy.py b/Testy/000 Testy.py deleted file mode 100644 index 1114bf5..0000000 --- a/Testy/000 Testy.py +++ /dev/null @@ -1,229 +0,0 @@ -import os -import shutil -import pymysql -import re -from pathlib import Path -from datetime import datetime -import time - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -DB_CONFIG = { - "host": "127.0.0.1", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", -} - -BASE_DIR = Path(r"z:\Dropbox\Ordinace\Dokumentace_ke_zpracování\MP") -BASE_DIR.mkdir(parents=True, exist_ok=True) - - -# ---- helper function for timing ---- -def log_section(name): - print(f"\n=== ⏱ {name} ===") - return time.time() - - -def log_done(start): - print(f" -> done in {time.time() - start:0.2f} sec") - - -def sanitize_name(name: str) -> str: - return re.sub(r'[<>:"/\\|?*\x00-\x1F]', "_", name).strip() - - -def make_abbrev(title: str) -> str: - if not title: - return "" - words = re.findall(r"[A-Za-zÁ-Žá-ž0-9]+", title) - abbr = "".join(w if w.isdigit() else w[0] for w in words) - return abbr.upper() - - -# ============================== -# 🧹 DELETE UNEXPECTED FILES -# ============================== -def clean_folder(folder: Path, valid_files: set): - start = log_section(f"Cleaning folder: {folder.name}") - if not folder.exists(): - log_done(start) - return - - for f in folder.iterdir(): - if f.is_file(): - - if f.name.startswith("▲"): - continue - - sanitized = sanitize_name(f.name) - if sanitized not in valid_files: - print(f"🗑 Removing unexpected: {f.name}") - try: - f.unlink() - except Exception as e: - print(f"⚠ Could not delete {f}: {e}") - - log_done(start) - - -# ============================== -# 📦 DB CONNECTION -# ============================== -print("\n🔌 Connecting to DB…") -start_db = time.time() -conn = pymysql.connect(**DB_CONFIG) -cur_meta = conn.cursor(pymysql.cursors.DictCursor) -cur_blob = conn.cursor() -print(f" -> connected in {time.time() - start_db:0.2f} sec") - -print("\n🔍 Loading metadata from DB…") -start_sql = time.time() - -cur_meta.execute(""" - SELECT d.id AS download_id, - d.request_id, - d.filename, - d.created_at, - p.updatedAt AS req_updated_at, - p.pacient_jmeno AS jmeno, - p.pacient_prijmeni AS prijmeni, - p.displayTitle - FROM medevio_downloads d - JOIN pozadavky p ON d.request_id = p.id - ORDER BY p.updatedAt DESC -""") - -rows = cur_meta.fetchall() - -print(f"📋 Loaded {len(rows)} attachment rows in {time.time() - start_sql:0.2f} sec.\n") - - -# ============================== -# 🧠 MAIN LOOP -# ============================== -processed_requests = set() - -for r in rows: - req_id = r["request_id"] - - if req_id in processed_requests: - continue - processed_requests.add(req_id) - - section = f"Processing request {req_id}" - sec_start = log_section(section) - - # ========== FETCH ALL VALID FILES ========== - start_valid = log_section("Loading valid filenames") - cur_meta.execute( - "SELECT filename FROM medevio_downloads WHERE request_id=%s", - (req_id,) - ) - valid_files = {sanitize_name(row["filename"]) for row in cur_meta.fetchall()} - log_done(start_valid) - - # ========== PREPARE FOLDER NAME ========== - updated_at = r["req_updated_at"] or datetime.now() - date_str = updated_at.strftime("%Y-%m-%d") - - prijmeni = sanitize_name(r["prijmeni"] or "Unknown") - jmeno = sanitize_name(r["jmeno"] or "") - title = r.get("displayTitle") or "" - abbr = make_abbrev(title) - - clean_folder_name = sanitize_name( - f"{date_str} {prijmeni}, {jmeno} [{abbr}] {req_id}" - ) - - # ========== DETECT EXISTING FOLDER ========== - start_detect = log_section("Detecting existing folder(s)") - existing_folder = None - folder_has_flag = False - - for f in BASE_DIR.iterdir(): - if f.is_dir() and req_id in f.name: - existing_folder = f - folder_has_flag = ("▲" in f.name) - break - log_done(start_detect) - - main_folder = existing_folder if existing_folder else BASE_DIR / clean_folder_name - - # ========== MERGE DUPLICATES ========== - start_merge = log_section("Scanning for duplicate folders") - possible_dups = [ - f for f in BASE_DIR.iterdir() - if f.is_dir() and req_id in f.name and f != main_folder - ] - - for dup in possible_dups: - print(f"♻ Merging duplicate folder: {dup.name}") - clean_folder(dup, valid_files) - main_folder.mkdir(parents=True, exist_ok=True) - - for f in dup.iterdir(): - if f.is_file(): - target = main_folder / f.name - if not target.exists(): - f.rename(target) - shutil.rmtree(dup, ignore_errors=True) - - log_done(start_merge) - - # ========== CLEAN MAIN FOLDER ========== - clean_folder(main_folder, valid_files) - - # ========== DOWNLOAD MISSING FILES ========== - start_dl = log_section("Downloading missing files") - added_new_file = False - main_folder.mkdir(parents=True, exist_ok=True) - - for filename in valid_files: - dest_plain = main_folder / filename - dest_marked = main_folder / ("▲" + filename) - - if dest_plain.exists() or dest_marked.exists(): - continue - - added_new_file = True - - cur_blob.execute( - "SELECT file_content FROM medevio_downloads " - "WHERE request_id=%s AND filename=%s", - (req_id, filename) - ) - row = cur_blob.fetchone() - - if not row or not row[0]: - continue - - with open(dest_plain, "wb") as f: - f.write(row[0]) - - print(f"💾 wrote: {dest_plain.name}") - - log_done(start_dl) - - # ========== REMOVE FOLDER FLAG ========== - if added_new_file and "▲" in main_folder.name: - try: - new_name = main_folder.name.replace("▲", "").strip() - new_path = main_folder.parent / new_name - main_folder.rename(new_path) - print(f"🔄 Folder flag removed → {new_name}") - main_folder = new_path - except Exception as e: - print(f"⚠ Could not rename folder: {e}") - - log_done(sec_start) - - -print("\n🎯 Export complete.\n") - -cur_blob.close() -cur_meta.close() -conn.close() diff --git a/Testy/17 test.py b/Testy/01 Test similarity index 92% rename from Testy/17 test.py rename to Testy/01 Test index 2b05def..9da1805 100644 --- a/Testy/17 test.py +++ b/Testy/01 Test @@ -96,7 +96,18 @@ def fetch_messages(headers, request_id): if r.status_code != 200: print("❌ HTTP", r.status_code, "for request", request_id) return [] - return r.json().get("data", {}).get("messages", []) or [] + + try: + data = r.json() + except Exception as e: + print(f"❌ Failed to parse JSON for {request_id}: {e}") + print(" Response text:", r.text[:500]) + return [] + + messages = data.get("data", {}).get("messages", []) or [] + print(f" 🌐 API returned {len(messages)} messages for {request_id}") + return messages + # ============================== @@ -218,18 +229,19 @@ def main(): print(f"📦 Already downloaded attachments: {len(existing_ids)}\n") - # ---- Select pozadavky needing message sync + # ---- Select 10 oldest pozadavky (regardless of messagesProcessed) sql = """ SELECT id FROM pozadavky - WHERE messagesProcessed IS NULL - OR messagesProcessed < updatedAt + ORDER BY updatedAt ASC + LIMIT 10 """ with conn.cursor() as cur: cur.execute(sql) requests_to_process = cur.fetchall() - print(f"📋 Found {len(requests_to_process)} pozadavků requiring message sync.\n") + print(f"📋 Will process {len(requests_to_process)} oldest pozadavků.\n") + # ---- Process each pozadavek for idx, row in enumerate(requests_to_process, 1): diff --git a/Testy/01 Test.py b/Testy/01 Test.py deleted file mode 100644 index cd421ff..0000000 --- a/Testy/01 Test.py +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Test: Read conversation messages for one request_id and save full JSON. -Uses same logic as the production messages downloader. -""" - -import json -import requests -from pathlib import Path -from datetime import datetime - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -TOKEN_PATH = Path("token.txt") # same token file as your working script -REQUEST_ID = "092a0c63-28be-4c6b-ab3b-204e1e2641d4" # 🧾 replace as needed -OUTPUT_DIR = Path(r"u:\Dropbox\!!!Days\Downloads Z230") # where to save the JSON - -GRAPHQL_QUERY = 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 - token - createdAt - updatedAt - } - } -} -""" - -# ============================== -# 🔑 READ TOKEN -# ============================== -def read_token(p: Path) -> str: - tok = p.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - - -# ============================== -# 🚀 FETCH FROM API -# ============================== -def fetch_messages(headers, request_id): - variables = {"requestId": request_id, "updatedSince": None} - payload = { - "operationName": "UseMessages_ListMessages", - "query": GRAPHQL_QUERY, - "variables": variables, - } - - r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) - print("HTTP status:", r.status_code) - if r.status_code != 200: - print("❌ Response preview:", r.text[:500]) - return None - return r.json() - - -# ============================== -# 🧠 MAIN -# ============================== -def main(): - token = read_token(TOKEN_PATH) - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "Accept": "application/json", - } - - data = fetch_messages(headers, REQUEST_ID) - if not data: - print("⚠️ No data returned.") - return - - # Save full JSON to file - OUTPUT_DIR.mkdir(parents=True, exist_ok=True) - output_path = OUTPUT_DIR / f"messages_{REQUEST_ID}.json" - output_path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") - - print(f"✅ JSON saved to {output_path}") - - # Optional: print summary - messages = data.get("data", {}).get("messages", []) - print(f"💬 {len(messages)} messages found:") - print("─" * 100) - - for msg in messages: - sender = msg.get("sender") or {} - sender_name = " ".join(x for x in [sender.get("name"), sender.get("surname")] if x).strip() or "(unknown)" - text = (msg.get("text") or "").strip().replace("\n", " ") - created = msg.get("createdAt", "")[:16].replace("T", " ") - print(f"[{created}] {sender_name}: {text}") - if msg.get("medicalRecord"): - mr = msg["medicalRecord"] - print(f" 📎 {mr.get('description') or '(no description)'} ({mr.get('contentType')})") - print(f" URL: {mr.get('downloadUrl') or mr.get('url')}") - print("─" * 100) - - -# ============================== -if __name__ == "__main__": - main() diff --git a/Testy/02 Test.py b/Testy/02 Test.py deleted file mode 100644 index c9266df..0000000 --- a/Testy/02 Test.py +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Read conversation messages for pozadavky where messagesProcessed IS NULL -(Optionally filtered by createdAt), insert them into `medevio_conversation`, -and if a message has an attachment (medicalRecord), download it and save into -`medevio_downloads` (same logic as your attachments script). -Finally, mark pozadavky.messagesProcessed = NOW(). -""" - -import zlib -import json -import requests -import pymysql -from pathlib import Path -from datetime import datetime -import time - -# ============================== -# 🔧 CONFIGURATION -# ============================== -TOKEN_PATH = Path("token.txt") -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", - "cursorclass": pymysql.cursors.DictCursor, -} - -# ✅ Optional: Only process requests created after this date ("" = no limit) -CREATED_AFTER = "2024-12-01" - -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 - token - createdAt - updatedAt - } - } -} -""" - -# ============================== -# 🧮 HELPERS -# ============================== -def short_crc8(uuid_str: str) -> str: - return f"{zlib.crc32(uuid_str.encode('utf-8')) & 0xffffffff:08x}" - -def extract_filename_from_url(url: str) -> str: - try: - return url.split("/")[-1].split("?")[0] - except Exception: - return "unknown_filename" - -def read_token(p: Path) -> str: - tok = p.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - -def parse_dt(s): - if not s: - return None - # handle both "YYYY-mm-ddTHH:MM:SS" and "YYYY-mm-dd HH:MM:SS" - s = s.replace("T", " ") - fmts = ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M") - for f in fmts: - try: - return datetime.strptime(s[:19], f) - except Exception: - pass - return None - -# ============================== -# 📡 FETCH MESSAGES -# ============================== -def fetch_messages(headers, request_id): - variables = {"requestId": request_id, "updatedSince": None} - payload = { - "operationName": "UseMessages_ListMessages", - "query": GRAPHQL_QUERY_MESSAGES, - "variables": variables, - } - r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) - if r.status_code != 200: - print(f"❌ HTTP {r.status_code} for messages of request {request_id}") - return [] - data = r.json().get("data", {}).get("messages", []) - return data or [] - -# ============================== -# 💾 SAVE: conversation row -# ============================== -def insert_message(cur, req_id, msg): - sender = msg.get("sender") or {} - sender_name = " ".join(x for x in [sender.get("name"), sender.get("surname")] if x).strip() or None - sender_id = sender.get("id") - sender_clinic_id = sender.get("clinicId") - - text = msg.get("text") - created_at = parse_dt(msg.get("createdAt")) - read_at = parse_dt(msg.get("readAt")) - updated_at = parse_dt(msg.get("updatedAt")) - - mr = msg.get("medicalRecord") or {} - attachment_url = mr.get("downloadUrl") or mr.get("url") - attachment_description = mr.get("description") - attachment_content_type = mr.get("contentType") - - sql = """ - 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 - sender_name = VALUES(sender_name), - sender_id = VALUES(sender_id), - sender_clinic_id = VALUES(sender_clinic_id), - text = VALUES(text), - created_at = VALUES(created_at), - read_at = VALUES(read_at), - updated_at = VALUES(updated_at), - attachment_url = VALUES(attachment_url), - attachment_description = VALUES(attachment_description), - attachment_content_type = VALUES(attachment_content_type) - """ - cur.execute(sql, ( - msg.get("id"), - req_id, - sender_name, - sender_id, - sender_clinic_id, - text, - created_at, - read_at, - updated_at, - attachment_url, - attachment_description, - attachment_content_type - )) - -# ============================== -# 💾 SAVE: download attachment (from message) -# ============================== -def insert_download_from_message(cur, req_id, msg, existing_ids): - mr = msg.get("medicalRecord") or {} - attachment_id = mr.get("id") - if not attachment_id: - return False - if attachment_id in existing_ids: - print(f" ⏭️ Skipping already downloaded message-attachment {attachment_id}") - return False - - url = mr.get("downloadUrl") or mr.get("url") - if not url: - return False - - try: - r = requests.get(url, timeout=30) - r.raise_for_status() - content = r.content - except Exception as e: - print(f" ⚠️ Failed to download message attachment {attachment_id}: {e}") - return False - - filename = extract_filename_from_url(url) - content_type = mr.get("contentType") - file_size = len(content) - created_date = parse_dt(msg.get("createdAt")) - - # We don't have patient names on the message level here; keep NULLs. - cur.execute(""" - INSERT INTO medevio_downloads ( - request_id, attachment_id, attachment_type, filename, - content_type, file_size, pacient_jmeno, pacient_prijmeni, - created_at, file_content - ) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) - ON DUPLICATE KEY UPDATE - file_content = VALUES(file_content), - file_size = VALUES(file_size), - downloaded_at = NOW() - """, ( - req_id, - attachment_id, - "MESSAGE_ATTACHMENT", - filename, - content_type, - file_size, - None, - None, - created_date, - content - )) - existing_ids.add(attachment_id) - print(f" 💾 Saved msg attachment {filename} ({file_size/1024:.1f} kB)") - return True - -# ============================== -# 🧠 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 existing download IDs to skip duplicates (same logic as your script) - print("📦 Loading list of already downloaded attachments...") - with conn.cursor() as cur: - cur.execute("SELECT attachment_id FROM medevio_downloads") - existing_ids = {row["attachment_id"] for row in cur.fetchall()} - print(f"✅ Found {len(existing_ids)} attachments already saved.") - - # Pull pozadavky where messagesProcessed IS NULL (optionally by createdAt) - sql = """ - SELECT id, displayTitle, pacient_prijmeni, pacient_jmeno, createdAt - FROM pozadavky - WHERE messagesProcessed IS NULL - """ - params = [] - if CREATED_AFTER: - sql += " AND createdAt >= %s" - params.append(CREATED_AFTER) - - with conn.cursor() as cur: - cur.execute(sql, params) - rows = cur.fetchall() - - print(f"📋 Found {len(rows)} pozadavky to process (messagesProcessed IS NULL" - + (f", created >= {CREATED_AFTER}" if CREATED_AFTER else "") + ")") - - for i, row in enumerate(rows, 1): - req_id = row["id"] - prijmeni = row.get("pacient_prijmeni") or "Neznamy" - jmeno = row.get("pacient_jmeno") or "" - print(f"\n[{i}/{len(rows)}] 💬 {prijmeni}, {jmeno} ({req_id})") - - messages = fetch_messages(headers, req_id) - if not messages: - print(" ⚠️ No messages found") - with conn.cursor() as cur: - cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,)) - conn.commit() - continue - - inserted = 0 - with conn.cursor() as cur: - for msg in messages: - insert_message(cur, req_id, msg) - # also pull any message attachments into downloads table - insert_download_from_message(cur, req_id, msg, existing_ids) - inserted += 1 - conn.commit() - - # mark processed - with conn.cursor() as cur: - cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,)) - conn.commit() - - print(f" ✅ {inserted} messages processed for {prijmeni}, {jmeno}") - time.sleep(0.3) # polite API delay - - conn.close() - print("\n✅ Done! All new conversations processed and pozadavky updated.") - -# ============================== -if __name__ == "__main__": - main() diff --git a/Testy/03 Test.py b/Testy/03 Test.py deleted file mode 100644 index 6f1996e..0000000 --- a/Testy/03 Test.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -import zlib -import pymysql -import re -from pathlib import Path -from datetime import datetime - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", -} - -BASE_DIR = Path(r"u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\MP1") -BASE_DIR.mkdir(parents=True, exist_ok=True) - - -def sanitize_name(name: str) -> str: - """Replace invalid filename characters with underscore.""" - return re.sub(r'[<>:"/\\|?*\x00-\x1F]', "_", name).strip() - - -# ============================== -# 📦 STREAMING EXPORT WITH TRIANGLE CHECK -# ============================== -conn = pymysql.connect(**DB_CONFIG) -cur_meta = conn.cursor(pymysql.cursors.DictCursor) -cur_blob = conn.cursor() - -cur_meta.execute(""" - SELECT id, request_id, attachment_id, filename, pacient_jmeno, - pacient_prijmeni, created_at, downloaded_at - FROM medevio_downloads - WHERE file_content IS NOT NULL; -""") - -rows = cur_meta.fetchall() -print(f"📋 Found {len(rows)} records to check/export") - -skipped, exported = 0, 0 - -for r in rows: - try: - created = r["created_at"] or r["downloaded_at"] or datetime.now() - date_str = created.strftime("%Y-%m-%d") - - prijmeni = sanitize_name(r["pacient_prijmeni"] or "Unknown") - jmeno = sanitize_name(r["pacient_jmeno"] or "") - - # 🔥 NEW: use full request_id instead of CRC32 - full_req_id = sanitize_name(r["request_id"]) - - # Base (non-triangle) and processed (triangle) folder variants - base_folder = f"{date_str} {prijmeni}, {jmeno} {full_req_id}" - tri_folder = f"{date_str}▲ {prijmeni}, {jmeno} {full_req_id}" - - base_folder = sanitize_name(base_folder) - tri_folder = sanitize_name(tri_folder) - - base_path = BASE_DIR / base_folder - tri_path = BASE_DIR / tri_folder - - filename = sanitize_name(r["filename"] or f"unknown_{r['id']}.bin") - file_path_base = base_path / filename - file_path_tri = tri_path / filename - - # 🟡 Skip if exists in either version - if file_path_base.exists() or file_path_tri.exists(): - skipped += 1 - found_in = "▲" if file_path_tri.exists() else "" - print(f"⏭️ Skipping existing{found_in}: {filename}") - continue - - # Make sure base folder exists before saving - base_path.mkdir(parents=True, exist_ok=True) - - # 2️⃣ Fetch blob - cur_blob.execute("SELECT file_content FROM medevio_downloads WHERE id = %s", (r["id"],)) - blob = cur_blob.fetchone()[0] - - if blob: - with open(file_path_base, "wb") as f: - f.write(blob) - exported += 1 - print(f"✅ Saved: {file_path_base.relative_to(BASE_DIR)}") - else: - print(f"⚠️ No content for id={r['id']}") - - except Exception as e: - print(f"❌ Error for id={r['id']}: {e}") - -cur_blob.close() -cur_meta.close() -conn.close() - -print(f"\n🎯 Export complete — {exported} new files saved, {skipped} skipped.\n") diff --git a/Testy/03 Testt.py b/Testy/03 Testt.py deleted file mode 100644 index 78f0820..0000000 --- a/Testy/03 Testt.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import json -import requests -from pathlib import Path - -# ============================ -# CONFIG -# ============================ -TOKEN_PATH = Path("token.txt") - -# vlož libovolný existující request ID -REQUEST_ID = "3fc9b28c-ada2-4d21-ab2d-fe60ad29fd8f" - -GRAPHQL_NOTES_QUERY = r""" -query ClinicRequestNotes_Get($patientRequestId: String!) { - notes: getClinicPatientRequestNotes(requestId: $patientRequestId) { - id - content - createdAt - updatedAt - createdBy { - id - name - surname - } - } -} -""" - -# ============================ -# TOKEN -# ============================ -def read_token(p: Path) -> str: - tok = p.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - -# ============================ -# FETCH -# ============================ -def fetch_notes(request_id, token): - url = "https://api.medevio.cz/graphql" - - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "Accept": "application/json", - } - - variables = {"patientRequestId": request_id} - - payload = { - "operationName": "ClinicRequestNotes_Get", - "query": GRAPHQL_NOTES_QUERY, - "variables": variables, - } - - r = requests.post(url, json=payload, headers=headers) - r.raise_for_status() - return r.json() - -# ============================ -# MAIN -# ============================ -def main(): - token = read_token(TOKEN_PATH) - - print(f"\n🔍 Fetching NOTES for request:\n ID = {REQUEST_ID}\n") - - data = fetch_notes(REQUEST_ID, token) - - print("📄 FULL RAW JSON:\n") - print(json.dumps(data, indent=2, ensure_ascii=False)) - - print("\n📝 Parsed notes:\n") - notes = data.get("data", {}).get("notes") or [] - if not notes: - print(" (no notes found)") - return - - for n in notes: - author = n.get("createdBy") - print(f"--- Note {n.get('id')} ---") - print(f"Created: {n.get('createdAt')}") - print(f"Updated: {n.get('updatedAt')}") - if author: - print(f"Author: {author.get('name')} {author.get('surname')}") - print("Content:") - print(n.get("content")) - print() - -if __name__ == "__main__": - main() diff --git a/Testy/04 Test.py b/Testy/04 Test.py deleted file mode 100644 index 1a7d578..0000000 --- a/Testy/04 Test.py +++ /dev/null @@ -1,216 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -Download all attachments for pozadavky where attachmentsProcessed IS NULL -and (optionally) createdAt is newer than a cutoff date. -Store them in MySQL table `medevio_downloads`, and update pozadavky.attachmentsProcessed. -""" - -import zlib -import json -import requests -import pymysql -from pathlib import Path -from datetime import datetime -import time - -# ============================== -# 🔧 CONFIGURATION -# ============================== -TOKEN_PATH = Path("token.txt") -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, -} - -CREATED_AFTER = "2024-12-01" # optional filter - -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 - } - } -} -""" - -# ============================== -# 🧮 HELPERS -# ============================== -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 - -# ============================== -# 📡 FETCH ATTACHMENTS -# ============================== -def fetch_attachments(headers, request_id): - payload = { - "operationName": "ClinicRequestDetail_GetPatientRequest2", - "query": GRAPHQL_QUERY, - "variables": {"requestId": request_id}, - } - r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) - if r.status_code != 200: - print(f"❌ HTTP {r.status_code} for request {request_id}") - return [] - return r.json().get("data", {}).get("patientRequestMedicalRecords", []) - - -# ============================== -# 💾 SAVE TO MYSQL (clean version) -# ============================== -def insert_download(cur, req_id, a, m, created_date, existing_ids): - - attachment_id = a.get("id") - if attachment_id in existing_ids: - print(f" ⏭️ Already downloaded {attachment_id}") - return False - - url = m.get("downloadUrl") - if not url: - print(" ⚠️ Missing download URL") - return False - - filename = extract_filename_from_url(url) - - # Download file - try: - r = requests.get(url, timeout=30) - r.raise_for_status() - content = r.content - except Exception as e: - print(f" ⚠️ Download failed {url}: {e}") - return False - - file_size = len(content) - attachment_type = a.get("attachmentType") - content_type = m.get("contentType") - - # 🚨 CLEAN INSERT — no patient_jmeno/no patient_prijmeni - 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) - ON DUPLICATE KEY UPDATE - file_content = VALUES(file_content), - file_size = VALUES(file_size), - downloaded_at = NOW() - """, ( - req_id, - attachment_id, - attachment_type, - filename, - content_type, - file_size, - created_date, - content, - )) - - existing_ids.add(attachment_id) - print(f" 💾 Saved {filename} ({file_size/1024:.1f} kB)") - return True - - -# ============================== -# 🧠 MAIN -# ============================== -def main(): - token = read_token(TOKEN_PATH) - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - } - - conn = pymysql.connect(**DB_CONFIG) - - # Load existing IDs - with conn.cursor() as cur: - cur.execute("SELECT attachment_id FROM medevio_downloads") - existing_ids = {row["attachment_id"] for row in cur.fetchall()} - print(f"✅ {len(existing_ids)} attachments already saved.") - - # Build query for pozadavky - 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() - - print(f"📋 Found {len(req_rows)} pozadavky to process.") - - # Process each pozadavek - for i, row in enumerate(req_rows, 1): - req_id = row["id"] - prijmeni = row.get("pacient_prijmeni") or "Neznamy" - jmeno = row.get("pacient_jmeno") or "" - created_date = row.get("createdAt") or datetime.now() - - print(f"\n[{i}/{len(req_rows)}] 🧾 {prijmeni}, {jmeno} ({req_id})") - - attachments = fetch_attachments(headers, req_id) - - if not attachments: - print(" ⚠️ No attachments found") - with conn.cursor() as cur: - cur.execute("UPDATE pozadavky SET attachmentsProcessed = NOW() WHERE id = %s", (req_id,)) - conn.commit() - continue - - with conn.cursor() as cur: - for a in attachments: - m = a.get("medicalRecord") or {} - insert_download(cur, req_id, a, m, created_date, existing_ids) - conn.commit() - - # Mark processed - with conn.cursor() as cur: - cur.execute("UPDATE pozadavky SET attachmentsProcessed = NOW() WHERE id = %s", (req_id,)) - conn.commit() - - print(f" ✅ Done ({len(attachments)} attachments)") - - time.sleep(0.3) - - conn.close() - print("\n🎯 All attachments processed.") - -# ============================== -if __name__ == "__main__": - main() diff --git a/Testy/05 Testy.py b/Testy/05 Testy.py deleted file mode 100644 index 284f7d0..0000000 --- a/Testy/05 Testy.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/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 deleted file mode 100644 index 289af46..0000000 --- a/Testy/06 Testy.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/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 deleted file mode 100644 index a39e8c2..0000000 --- a/Testy/07 Testy.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/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 deleted file mode 100644 index 54c0ac8..0000000 --- a/Testy/08 Testy.py +++ /dev/null @@ -1,365 +0,0 @@ -#!/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() diff --git a/Testy/09 Testy pt details all.py b/Testy/09 Testy pt details all.py deleted file mode 100644 index 7933114..0000000 --- a/Testy/09 Testy pt details all.py +++ /dev/null @@ -1,183 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import json -import time -import random -import requests -import pymysql -from pathlib import Path - -from datetime import datetime - -# Patients with details_updated_at older than this date will be refreshed -# UpdateOlderThan = datetime(2025, 2, 20) # example date -UpdateOlderThan = None #když chceš všechny aktualizovat - -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, -} - -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(), - details_updated_at = 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") - )) - -# === PLACEHOLDER: Insert your full GraphQL query here === -QUERY = Path("patient_detail.graphql").read_text(encoding="utf-8") - -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() - - if UpdateOlderThan: - print(f"🔎 Updating patients with details_updated_at NULL or < {UpdateOlderThan}") - cur.execute(""" - SELECT id, prijmeni, jmeno, details_updated_at - FROM medevio_pacienti - WHERE details_updated_at IS NULL OR details_updated_at < %s - ORDER BY prijmeni, jmeno - """, (UpdateOlderThan,)) - else: - print("🔎 Updating only patients with details_updated_at NULL") - cur.execute(""" - SELECT id, prijmeni, jmeno, details_updated_at - FROM medevio_pacienti - WHERE details_updated_at IS NULL - ORDER BY prijmeni, jmeno - """) - patients = cur.fetchall() - - total = len(patients) - print(f"⏳ Starting full patient detail sync for {total} patients...") - - for idx, row in enumerate(patients, start=1): - pid = row["id"] - name = f"{row.get('prijmeni','')}, {row.get('jmeno','')}" - print(f"[{idx}/{total}] Updating: {name} ({pid})") - - variables = { - "clinicSlug": CLINIC_SLUG, - "patientId": pid, - "patientUuid": pid, - "challengesStatus": "SENT", - "locale": "cs", - } - - try: - r = requests.post( - GRAPHQL_URL, - json={"query": QUERY, "variables": variables}, - headers=headers, - timeout=30 - ) - r.raise_for_status() - js = r.json() - - p = js["data"]["patient"] - if p: - save_patient_detail(cur, p) - conn.commit() - print(" ✔ saved") - else: - print(" ⚠ no patient data returned") - - except Exception as e: - print(f" ❌ ERROR: {e}") - time.sleep(2) - continue - - time.sleep(random.uniform(0.5, 1.5)) - - conn.close() - print("✅ DONE – full detail sync completed.") - -if __name__ == "__main__": - main() diff --git a/Testy/10 Testy stahni pouze active pozadavky.py b/Testy/10 Testy stahni pouze active pozadavky.py deleted file mode 100644 index 43c709a..0000000 --- a/Testy/10 Testy stahni pouze active pozadavky.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pymysql -import requests -from pathlib import Path -from datetime import datetime -import time - -# ================================ -# 🔧 CONFIGURATION -# ================================ -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" -BATCH_SIZE = 100 - -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", - "cursorclass": pymysql.cursors.DictCursor, -} - -GRAPHQL_QUERY = r""" -query ClinicRequestGrid_ListPatientRequestsForClinic2( - $clinicSlug: String!, - $queueId: String, - $queueAssignment: QueueAssignmentFilter!, - $pageInfo: PageInfo!, - $locale: Locale!, - $state: PatientRequestState -) { - requestsResponse: listPatientRequestsForClinic2( - clinicSlug: $clinicSlug, - queueId: $queueId, - queueAssignment: $queueAssignment, - pageInfo: $pageInfo, - state: $state - ) { - count - patientRequests { - id - displayTitle(locale: $locale) - createdAt - updatedAt - doneAt - removedAt - extendedPatient { - name - surname - identificationNumber - } - } - } -} -""" - -# ================================ -# 🔑 TOKEN -# ================================ -def read_token(path: Path) -> str: - tok = path.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - - -# ================================ -# 🕒 DATETIME FORMAT -# ================================ -def to_mysql_dt(iso_str): - if not iso_str: - return None - try: - dt = datetime.fromisoformat(iso_str.replace("Z", "+00:00")) - return dt.strftime("%Y-%m-%d %H:%M:%S") - except: - return None - - -# ================================ -# 💾 UPSERT -# ================================ -def upsert(conn, r): - p = r.get("extendedPatient") or {} - - 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")), - to_mysql_dt(r.get("updatedAt")), - 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 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": "ClinicRequestGrid_ListPatientRequestsForClinic2", - "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} ===") - - # ------------------------------- - # 🚀 FETCH ALL ACTIVE REQUESTS - # ------------------------------- - 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() diff --git a/Testy/11 Testy.py b/Testy/11 Testy.py deleted file mode 100644 index b653a38..0000000 --- a/Testy/11 Testy.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/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" - -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 - displayTitle(locale: $locale) - createdAt - doneAt - removedAt - extendedPatient { - name - surname - } - } -} -""" - -def main(): - token = TOKEN_PATH.read_text().strip() - - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "Accept": "application/json", - } - - variables = { - "clinicSlug": CLINIC_SLUG, - "queueId": None, - "queueAssignment": "ANY", - "state": "ACTIVE", - "pageInfo": {"first": 200, "offset": 100}, - "locale": "cs", - } - - print("⏳ Testing ACTIVE request fetch (LEGACY API)…") - - r = requests.post( - GRAPHQL_URL, - json={"query": QUERY, "variables": variables}, - headers=headers, - timeout=30 - ) - r.raise_for_status() - - js = r.json() - - # extract list - requests_list = js.get("data", {}).get("requests", []) - - print("\n📌 Number of ACTIVE requests returned:", len(requests_list)) - - print("\n📌 First 5 request IDs:") - for item in requests_list[:5]: - print(" •", item.get("id")) - - # debug dump if needed - # print(json.dumps(js, indent=2, ensure_ascii=False)) - - print("\n✅ Test completed.\n") - - -if __name__ == "__main__": - main() diff --git a/Testy/12 Testy.py b/Testy/12 Testy.py deleted file mode 100644 index 9b94413..0000000 --- a/Testy/12 Testy.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pymysql -import requests -from pathlib import Path -from datetime import datetime - -# ================================ -# 🔧 CONFIGURATION -# ================================ -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" - -LIMIT = 300 # download the latest 300 requests - -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", - "cursorclass": pymysql.cursors.DictCursor, -} - -GRAPHQL_QUERY = r""" -query ClinicRequestGrid_ListPatientRequestsForClinic2( - $clinicSlug: String!, - $queueId: String, - $queueAssignment: QueueAssignmentFilter!, - $pageInfo: PageInfo!, - $locale: Locale!, - $state: PatientRequestState -) { - requestsResponse: listPatientRequestsForClinic2( - clinicSlug: $clinicSlug, - queueId: $queueId, - queueAssignment: $queueAssignment, - pageInfo: $pageInfo, - state: $state - ) { - count - patientRequests { - id - displayTitle(locale: $locale) - createdAt - updatedAt - doneAt - removedAt - extendedPatient { - name - surname - identificationNumber - } - } - } -} -""" - -# ================================ -# TOKEN -# ================================ -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 - -# ================================ -# DATE PARSER -# ================================ -def to_mysql_dt(iso_str): - if not iso_str: - return None - try: - dt = datetime.fromisoformat(iso_str.replace("Z", "+00:00")) - return dt.strftime("%Y-%m-%d %H:%M:%S") - except: - return None - -# ================================ -# UPSERT -# ================================ -def upsert(conn, r): - p = (r.get("extendedPatient") or {}) - 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")), - to_mysql_dt(r.get("updatedAt")), - 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 LATEST 300 REQUESTS -# ================================ -def fetch_latest_requests(headers): - vars = { - "clinicSlug": CLINIC_SLUG, - "queueId": None, - "queueAssignment": "ANY", - "pageInfo": {"first": LIMIT, "offset": 0}, - "locale": "cs", - "state": "DONE" # ALL STATES (ACTIVE, DONE, REMOVED) - } - - payload = { - "operationName": "ClinicRequestGrid_ListPatientRequestsForClinic2", - "query": GRAPHQL_QUERY, - "variables": vars, - } - - 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", []) - -# ================================ -# 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=== Downloading last {LIMIT} requests @ {datetime.now():%Y-%m-%d %H:%M:%S} ===") - - requests_list = fetch_latest_requests(headers) - - print(f"📌 Requests returned: {len(requests_list)}") - - for r in requests_list: - upsert(conn, r) - - conn.close() - print("\n✅ Done. Latest requests synced.\n") - - -if __name__ == "__main__": - main() diff --git a/Testy/13 testy report pacienti.py b/Testy/13 testy report pacienti.py deleted file mode 100644 index ce99d91..0000000 --- a/Testy/13 testy report pacienti.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pymysql -import openpyxl -from openpyxl.styles import PatternFill, Font -from openpyxl.utils import get_column_letter - -from datetime import datetime - -timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S") -OUTPUT_PATH = fr"U:\Dropbox\!!!Days\Downloads Z230\{timestamp} medevio_patients_report.xlsx" - -# ============================ -# CONFIGURATION -# ============================ -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", - "cursorclass": pymysql.cursors.DictCursor, -} - -# ============================ -# FUNCTIONS -# ============================ - -from openpyxl.styles import Border, Side - -thin_border = Border( - left=Side(style='thin'), - right=Side(style='thin'), - top=Side(style='thin'), - bottom=Side(style='thin') -) - -def apply_thin_borders(ws): - """Apply thin borders to all cells in the worksheet.""" - for row in ws.iter_rows(): - for cell in row: - cell.border = thin_border - -def autofit_columns(ws): - """Auto-adjust column widths based on longest cell content.""" - for col in ws.columns: - max_length = 0 - col_letter = get_column_letter(col[0].column) - for cell in col: - try: - if cell.value: - max_length = max(max_length, len(str(cell.value))) - except: - pass - ws.column_dimensions[col_letter].width = max_length + 2 - -def apply_header_style(ws): - """Make header BRIGHT YELLOW and bold.""" - fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") - font = Font(bold=True) - - for cell in ws[1]: - cell.fill = fill - cell.font = font - -def create_compact_row(row): - """Produce compact record with merged pojistovna, with user_relationship after prijmeni.""" - - # insurance merged - code = row.get("pojistovna_code") or "" - naz = row.get("pojistovna_nazev") or "" - - if code and naz: - poj = f"{code} ({naz})" - elif code: - poj = code - elif naz: - poj = naz - else: - poj = "" - - return { - "id": row["id"], - "jmeno": row["jmeno"], - "prijmeni": row["prijmeni"], - - # 🔹 inserted here - "user_relationship": row.get("user_relationship"), - - "rodne_cislo": row["rodne_cislo"], - "dob": row["dob"], - "telefon": row["telefon"], - "email": row["email"], - "pojistovna": poj, - "status": row["status"], - "has_mobile_app": row["has_mobile_app"], - "registration_time": row["registration_time"], - "last_update": row["last_update"], - } - -def create_pozadavky_rows(rows): - """Convert raw pozadavky SQL rows into rows for the Excel sheet.""" - output = [] - for r in rows: - output.append({ - # 🔹 First the ID - "id": r["id"], - - # 🔹 Your 3 patient columns immediately after ID - "pacient_jmeno": r["pacient_jmeno"], - "pacient_prijmeni": r["pacient_prijmeni"], - "pacient_rodnecislo": r["pacient_rodnecislo"], - - # 🔹 Then all other fields in any order you prefer - "displayTitle": r["displayTitle"], - "createdAt": r["createdAt"], - "updatedAt": r["updatedAt"], - "doneAt": r["doneAt"], - "removedAt": r["removedAt"], - "attachmentsProcessed": r["attachmentsProcessed"], - "messagesProcessed": r["messagesProcessed"], - "communicationprocessed": r["communicationprocessed"], - "questionnaireprocessed": r["questionnaireprocessed"], - "lastSync": r["lastSync"], - }) - return output - - -# ============================ -# MAIN -# ============================ - -def main(): - print("📥 Connecting to MySQL...") - conn = pymysql.connect(**DB_CONFIG) - - with conn: - with conn.cursor() as cur: - cur.execute("SELECT * FROM medevio_pacienti ORDER BY prijmeni, jmeno") - patients = cur.fetchall() - - print(f"📊 Loaded {len(patients)} patients.") - - # Load pozadavky - - with conn.cursor() as cur: - cur.execute("SELECT * FROM pozadavky ORDER BY createdAt DESC") - pozadavky_rows = cur.fetchall() - - print(f"📄 Loaded {len(pozadavky_rows)} pozadavky.") - - wb = openpyxl.Workbook() - - # --------------------------------- - # 1) FULL SHEET - # --------------------------------- - ws_full = wb.active - ws_full.title = "Patients FULL" - - if patients: - headers = list(patients[0].keys()) - ws_full.append(headers) - - for row in patients: - ws_full.append([row.get(h) for h in headers]) - - apply_header_style(ws_full) - ws_full.freeze_panes = "A2" - ws_full.auto_filter.ref = ws_full.dimensions - autofit_columns(ws_full) - apply_thin_borders(ws_full) - - # --------------------------------- - # 2) COMPACT SHEET - # --------------------------------- - ws_compact = wb.create_sheet("Patients COMPACT") - - compact_rows = [create_compact_row(r) for r in patients] - compact_headers = list(compact_rows[0].keys()) - - ws_compact.append(compact_headers) - for row in compact_rows: - ws_compact.append([row.get(h) for h in compact_headers]) - - apply_header_style(ws_compact) - ws_compact.freeze_panes = "A2" - ws_compact.auto_filter.ref = ws_compact.dimensions - autofit_columns(ws_compact) - - # >>> ADD THIS <<< - ur_col_index = compact_headers.index("user_relationship") + 1 - col_letter = get_column_letter(ur_col_index) - ws_compact.column_dimensions[col_letter].width = 7.14 - apply_thin_borders(ws_compact) - - # --------------------------------- - # 3) POZADAVKY SHEET - # --------------------------------- - ws_p = wb.create_sheet("Pozadavky") - - poz_list = create_pozadavky_rows(pozadavky_rows) - headers_p = list(poz_list[0].keys()) if poz_list else [] - - if headers_p: - ws_p.append(headers_p) - for row in poz_list: - ws_p.append([row.get(h) for h in headers_p]) - - apply_header_style(ws_p) - ws_p.freeze_panes = "A2" - ws_p.auto_filter.ref = ws_p.dimensions - autofit_columns(ws_p) - apply_thin_borders(ws_p) - - - # --------------------------------- - # SAVE - # --------------------------------- - - wb.save(OUTPUT_PATH) - print(f"✅ Excel report saved to:\n{OUTPUT_PATH}") - -if __name__ == "__main__": - main() diff --git a/Testy/14 Testy updateat.py b/Testy/14 Testy updateat.py deleted file mode 100644 index d215ca2..0000000 --- a/Testy/14 Testy updateat.py +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import requests -from pathlib import Path -import json - -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" -BATCH_SIZE = 100 - -# přesně tvůj původní dotaz, beze změn -# GRAPHQL_QUERY = r""" -# query ClinicRequestGrid_ListPatientRequestsForClinic2( -# $clinicSlug: String!, -# $queueId: String, -# $queueAssignment: QueueAssignmentFilter!, -# $pageInfo: PageInfo!, -# $locale: Locale!, -# $state: PatientRequestState -# ) { -# requestsResponse: listPatientRequestsForClinic2( -# clinicSlug: $clinicSlug, -# queueId: $queueId, -# queueAssignment: $queueAssignment, -# pageInfo: $pageInfo, -# state: $state -# ) { -# count -# patientRequests { -# id -# displayTitle(locale: $locale) -# createdAt -# updatedAt -# doneAt -# removedAt -# extendedPatient { -# name -# surname -# identificationNumber -# } -# } -# } -# } -# """ -GRAPHQL_QUERY = r""" -query ClinicRequestGrid_ListPatientRequestsForClinic2( - $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) - - ### TIME FIELDS ADDED - createdAt - updatedAt - doneAt - removedAt - - extendedPatient { - id - identificationNumber - name - surname - kind - key - type - user { - id - name - surname - } - owner { - name - surname - } - dob - premiumPlanPatient { - id - premiumPlan { - id - } - } - status2 - tags(onlyImportant: true) { - id - } - isUnknownPatient - } - - invoice { - id - status - amount - currency - dueAmount - isOverdue - refundedAmount - settledAmount - } - - lastMessage { - createdAt - id - readAt - sender { - id - name - surname - clinicId - } - text - } - - priority - - queue { - id - name - clinicPatientRequestQueueUsers { - accountable { - id - name - surname - } - id - } - } - - reservations { - calendar { - id - internalName - name - } - id - canceledAt - done - start - } - - tags(onlyImportant: true) { - id - } - - userECRF(locale: $locale) { - id - sid - icon { - color - id - urlSvg - } - ecrfSet { - id - name - } - } - - priceWhenCreated - currencyWhenCreated - createdByDoctor - eventType - clinicNotes { - id - } - clinicMedicalRecord - } - } -} -""" - -def read_token(path: Path) -> str: - tok = path.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - - -def main(): - token = read_token(TOKEN_PATH) - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json", - "Accept": "application/json", - } - - variables = { - "clinicSlug": CLINIC_SLUG, - "queueId": None, - "queueAssignment": "ANY", - "pageInfo": {"first": BATCH_SIZE, "offset": 0}, - "locale": "cs", - "state": "ACTIVE", - } - - payload = { - "operationName": "ClinicRequestGrid_ListPatientRequestsForClinic2", - "query": GRAPHQL_QUERY, - "variables": variables, - } - - print("\n===== ČISTÁ ODPOVĚĎ SERVERU =====\n") - - r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) - - print(f"HTTP {r.status_code}\n") - print(r.text) # <-- TISK NEUPRAVENÉHO JSONU - - print("\n===== KONEC ČISTÉ ODPOVĚDI =====\n") - - -if __name__ == "__main__": - main() diff --git a/Testy/15 test.py b/Testy/15 test.py deleted file mode 100644 index d71905e..0000000 --- a/Testy/15 test.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import requests -from pathlib import Path - -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" -BATCH_SIZE = 100 -TARGET_ID = "cbf6000d-a6ca-4059-88b7-dfdc27220762" # ← sem tvoje ID - -# ⭐ Updated GraphQL with lastMessage included -GRAPHQL_QUERY = r""" -query ClinicRequestGrid_ListPatientRequestsForClinic2( - $clinicSlug: String!, - $queueId: String, - $queueAssignment: QueueAssignmentFilter!, - $pageInfo: PageInfo!, - $locale: Locale!, - $state: PatientRequestState -) { - requestsResponse: listPatientRequestsForClinic2( - clinicSlug: $clinicSlug, - queueId: $queueId, - queueAssignment: $queueAssignment, - pageInfo: $pageInfo, - state: $state - ) { - count - patientRequests { - id - displayTitle(locale: $locale) - createdAt - updatedAt - doneAt - removedAt - lastMessage { - id - createdAt - updatedAt - } - extendedPatient { - name - surname - identificationNumber - } - } - } -} -""" - -def read_token(path: Path) -> str: - tok = path.read_text(encoding="utf-8").strip() - if tok.startswith("Bearer "): - tok = tok.split(" ", 1)[1] - return tok - -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": "ClinicRequestGrid_ListPatientRequestsForClinic2", - "query": GRAPHQL_QUERY, - "variables": variables, - } - - r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) - - if r.status_code != 200: - print("HTTP status:", r.status_code) - print(r.text) - r.raise_for_status() - - data = r.json().get("data", {}).get("requestsResponse", {}) - return data.get("patientRequests", []), data.get("count", 0) - - -def main(): - token = read_token(TOKEN_PATH) - headers = { - "Authorization": f"Bearer {token}", - "Content-Type": "application/json" - } - - print(f"=== Hledám updatedAt a lastMessage pro pozadavek {TARGET_ID} ===\n") - - offset = 0 - total_count = None - found = False - - while True: - batch, count = fetch_active(headers, offset) - - if total_count is None: - total_count = count - - if not batch: - break - - for r in batch: - if r["id"] == TARGET_ID: - - print("Nalezeno!\n") - print(f"id: {r['id']}") - print(f"updatedAt: {r['updatedAt']}") - - lm = r.get("lastMessage") or {} - print(f"lastMessage.createdAt: {lm.get('createdAt')}") - print(f"lastMessage.updatedAt: {lm.get('updatedAt')}") - - found = True - break - - if found: - break - - if offset + BATCH_SIZE >= count: - break - - offset += BATCH_SIZE - - if not found: - print("❌ Požadavek nebyl nalezen mezi ACTIVE.") - - print("\n=== HOTOVO ===") - - -if __name__ == "__main__": - main() diff --git a/Testy/16 test.py b/Testy/16 test.py deleted file mode 100644 index cbabdcf..0000000 --- a/Testy/16 test.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/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 - -# ================================ -# 🔧 CONFIGURATION -# ================================ -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" -BATCH_SIZE = 100 - -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "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): - """ - Parse Medevio timestamps safely. - Treat timestamps WITHOUT timezone as UTC. - Convert to local time before saving to MySQL. - """ - if not iso_str: - return None - try: - dt = parser.isoparse(iso_str) - - # If tz is missing → assume UTC - if dt.tzinfo is None: - dt = dt.replace(tzinfo=timezone.utc) - - # Convert to local timezone - 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 (včetně správného updatedAt) -# ================================ -def upsert(conn, r): - p = r.get("extendedPatient") or {} - - # raw timestamps z API – nyní přes nový parser - 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")) - - # nejnovější změna - 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() diff --git a/Testy/18 Test.py b/Testy/18 Test.py deleted file mode 100644 index f7d4590..0000000 --- a/Testy/18 Test.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pymysql -import requests -from pathlib import Path -from datetime import datetime -from dateutil import parser - -# ================================ -# 🔧 CONFIGURATION -# ================================ -TOKEN_PATH = Path("token.txt") -CLINIC_SLUG = "mudr-buzalkova" -LIMIT = 300 # stáhneme posledních 300 ukončených požadavků - -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", - "cursorclass": pymysql.cursors.DictCursor, -} - -# ⭐ Ověřený dotaz s lastMessage -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 (UTC → MySQL) -# ================================ -def to_mysql_dt(iso_str): - if not iso_str: - return None - try: - dt = parser.isoparse(iso_str) # ISO8601 → aware datetime (UTC) - dt = dt.astimezone() # převede na lokální čas (CET/CEST) - return dt.strftime("%Y-%m-%d %H:%M:%S") - except: - return None - -# ================================ -# UPSERT WITH MERGED UPDATED TIME -# ================================ -def upsert(conn, r): - p = r.get("extendedPatient") or {} - - # API pole - api_updated = to_mysql_dt(r.get("updatedAt")) - - # poslední zpráva - last_msg = r.get("lastMessage") or {} - msg_at = to_mysql_dt(last_msg.get("createdAt")) - - # vybereme novější čas - def max_dt(a, b): - if a and b: - return max(a, b) - return a or b - - final_updated = max_dt(api_updated, msg_at) - - 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 LAST 300 DONE REQUESTS -# ================================ -def fetch_done(headers): - vars = { - "clinicSlug": CLINIC_SLUG, - "queueId": None, - "queueAssignment": "ANY", - "pageInfo": {"first": LIMIT, "offset": 0}, - "locale": "cs", - "state": "DONE", - } - - payload = { - "operationName": "ClinicRequestList2", - "query": GRAPHQL_QUERY, - "variables": vars, - } - - 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", []) - -# ================================ -# 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=== Downloading last {LIMIT} DONE requests @ {datetime.now():%Y-%m-%d %H:%M:%S} ===") - - requests_list = fetch_done(headers) - print(f"📌 Requests returned: {len(requests_list)}") - - for r in requests_list: - upsert(conn, r) - - conn.close() - print("\n✅ DONE - latest closed requests synced.\n") - - -if __name__ == "__main__": - main() diff --git a/Testy/19 Test 2.py b/Testy/19 Test 2.py deleted file mode 100644 index dba54c1..0000000 --- a/Testy/19 Test 2.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -import shutil -import pymysql -import re -from pathlib import Path -from datetime import datetime - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", -} - -BASE_DIR = Path(r"u:\Dropbox\Ordinace\Dokumentace_ke_zpracování\MP") -BASE_DIR.mkdir(parents=True, exist_ok=True) - - -def sanitize_name(name: str) -> str: - return re.sub(r'[<>:"/\\|?*\x00-\x1F]', "_", name).strip() - - -def clean_folder(folder: Path, valid_files: set): - """Remove files that do NOT exist in MySQL for this request.""" - if not folder.exists(): - return - - for f in folder.iterdir(): - if f.is_file() and sanitize_name(f.name) not in valid_files: - print(f"🗑️ Removing unexpected file: {f.name}") - try: - f.unlink() - except Exception as e: - print(f"⚠️ Cannot delete {f}: {e}") - - -# ============================== -# 📥 LOAD EVERYTHING IN ONE QUERY -# ============================== -conn = pymysql.connect(**DB_CONFIG) -cur = conn.cursor(pymysql.cursors.DictCursor) - -print("📥 Loading ALL metadata + BLOBs with ONE MySQL query…") - -cur.execute(""" - SELECT - d.id AS download_id, - d.request_id, - d.filename, - d.file_content, - p.updatedAt AS req_updated_at, - p.pacient_jmeno AS jmeno, - p.pacient_prijmeni AS prijmeni - FROM medevio_downloads d - JOIN pozadavky p ON d.request_id = p.id - ORDER BY p.updatedAt DESC, d.created_at ASC -""") - -rows = cur.fetchall() -print(f"📦 Loaded {len(rows)} total file rows.\n") - -conn.close() - -# ============================== -# 🔄 ORGANIZE ROWS PER REQUEST -# ============================== -requests = {} # req_id → list of file dicts - -for r in rows: - req_id = r["request_id"] - if req_id not in requests: - requests[req_id] = [] - requests[req_id].append(r) - -print(f"📌 Unique requests: {len(requests)}\n") - -# ============================== -# 🧠 MAIN LOOP – SAME LOGIC AS BEFORE -# ============================== -for req_id, filelist in requests.items(): - - # ========== GET UPDATEDAT (same logic) ========== - any_row = filelist[0] - updated_at = any_row["req_updated_at"] or datetime.now() - date_str = updated_at.strftime("%Y-%m-%d") - - prijmeni = sanitize_name(any_row["prijmeni"] or "Unknown") - jmeno = sanitize_name(any_row["jmeno"] or "") - - folder_name = sanitize_name(f"{date_str} {prijmeni}, {jmeno} {req_id}") - main_folder = BASE_DIR / folder_name - - # ========== VALID FILES ========== - valid_files = {sanitize_name(r["filename"]) for r in filelist} - - # ========== FIND OLD FOLDERS ========== - possible_dups = [ - f for f in BASE_DIR.iterdir() - if f.is_dir() and req_id in f.name and f != main_folder - ] - - # ========== MERGE OLD FOLDERS ========== - for dup in possible_dups: - print(f"♻️ Merging folder: {dup.name}") - - clean_folder(dup, valid_files) - main_folder.mkdir(parents=True, exist_ok=True) - - for f in dup.iterdir(): - if f.is_file(): - target = main_folder / f.name - if not target.exists(): - f.rename(target) - - shutil.rmtree(dup, ignore_errors=True) - - # ========== CLEAN MAIN FOLDER ========== - main_folder.mkdir(parents=True, exist_ok=True) - clean_folder(main_folder, valid_files) - - # ========== SAVE FILES (fast now) ========== - for r in filelist: - filename = sanitize_name(r["filename"]) - dest = main_folder / filename - - if dest.exists(): - continue - - content = r["file_content"] - if not content: - continue - - with open(dest, "wb") as f: - f.write(content) - - print(f"💾 Saved: {dest.relative_to(BASE_DIR)}") - -print("\n🎯 Export complete.\n") diff --git a/Testy/19 Test download at once.py b/Testy/19 Test download at once.py deleted file mode 100644 index f586a42..0000000 --- a/Testy/19 Test download at once.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import pymysql -from pathlib import Path -import os -import re - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", -} - -OUTPUT = Path(r"d:\Dropbox\Ordinace\Dokumentace_ke_zpracování\mp1") -OUTPUT.mkdir(exist_ok=True) - - -def sanitize(name: str) -> str: - return re.sub(r'[<>:"/\\|?*\x00-\x1F]', "_", name).strip() - - -# ============================== -# 📥 LOAD EVERYTHING IN ONE QUERY -# ============================== -def load_all_files(): - conn = pymysql.connect(**DB_CONFIG) - cur = conn.cursor(pymysql.cursors.DictCursor) - - print("📥 Loading ALL medevio_downloads including BLOBs… (can take a few seconds)") - - cur.execute(""" - SELECT - d.id AS download_id, - d.request_id, - d.attachment_id, - d.filename, - d.content_type, - d.file_size, - d.created_at, - p.pacient_jmeno, - p.pacient_prijmeni, - d.file_content - FROM medevio_downloads d - JOIN pozadavky p ON d.request_id = p.id - ORDER BY d.created_at - """) - - rows = cur.fetchall() - conn.close() - - print(f"📦 Loaded {len(rows)} BLOB records.") - return rows - - -# ============================== -# 💾 SAVE ALL TO FILESYSTEM -# ============================== -def save_all(rows): - saved = 0 - - for r in rows: - req_id = r["request_id"] - jmeno = sanitize(r["pacient_jmeno"] or "") - prijmeni = sanitize(r["pacient_prijmeni"] or "") - filename = sanitize(r["filename"] or f"{r['download_id']}.bin") - - # Folder for each request - folder = OUTPUT / f"{prijmeni}, {jmeno} {req_id}" - folder.mkdir(exist_ok=True) - - dest = folder / filename - - # Skip existing - if dest.exists(): - continue - - data = r["file_content"] - if not data: - continue - - with open(dest, "wb") as f: - f.write(data) - - print(f"💾 Saved {dest}") - saved += 1 - - print(f"\n🎯 Done — {saved} files saved.") - - -# ============================== -# MAIN -# ============================== -if __name__ == "__main__": - rows = load_all_files() # ONE query - save_all(rows) # then write to disk diff --git a/Testy/19 Test.py b/Testy/19 Test.py deleted file mode 100644 index 2d4d5eb..0000000 --- a/Testy/19 Test.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import os -import shutil -import pymysql -import re -from pathlib import Path -from datetime import datetime -import time - -# ============================== -# ⚙️ CONFIGURATION -# ============================== -DB_CONFIG = { - "host": "192.168.1.76", - "port": 3307, - "user": "root", - "password": "Vlado9674+", - "database": "medevio", - "charset": "utf8mb4", -} - -BASE_DIR = Path(r"d:\Dropbox\Ordinace\Dokumentace_ke_zpracování\MP") -BASE_DIR.mkdir(parents=True, exist_ok=True) - - -def sanitize_name(name: str) -> str: - """Replace invalid filename characters with underscore.""" - return re.sub(r'[<>:"/\\|?*\x00-\x1F]', "_", name).strip() - - -# ============================== -# 🧹 DELETE UNEXPECTED FILES -# ============================== -def clean_folder(folder: Path, valid_files: set): - """Remove all files in folder that are NOT present in valid_files.""" - if not folder.exists(): - return - - for f in folder.iterdir(): - if f.is_file(): - if sanitize_name(f.name) not in valid_files: - print(f"🗑️ Removing unexpected file: {f.name}") - try: - f.unlink() - except Exception as e: - print(f"⚠️ Could not delete {f}: {e}") - - -# ============================== -# 📦 DB CONNECTION -# ============================== -conn = pymysql.connect(**DB_CONFIG) - -cur_meta = conn.cursor(pymysql.cursors.DictCursor) -cur_blob = conn.cursor() - -print("🔍 Loading metadata from DB (FAST)…") - -cur_meta.execute(""" - SELECT d.id AS download_id, - d.request_id, - d.filename, - d.created_at, - p.updatedAt AS req_updated_at, - p.pacient_jmeno AS jmeno, - p.pacient_prijmeni AS prijmeni - FROM medevio_downloads d - JOIN pozadavky p ON d.request_id = p.id - ORDER BY p.updatedAt DESC -""") - -rows = cur_meta.fetchall() -print(f"📋 Found {len(rows)} attachment records.\n") - -# ============================== -# 🧠 MAIN LOOP -# ============================== -processed_requests = set() - -for r in rows: - req_id = r["request_id"] - - if req_id in processed_requests: - continue - processed_requests.add(req_id) - - # ========== FETCH ALL VALID FILES FOR THIS REQUEST ========== - cur_meta.execute( - "SELECT filename FROM medevio_downloads WHERE request_id=%s", - (req_id,) - ) - valid_files = {sanitize_name(row["filename"]) for row in cur_meta.fetchall()} - - # ========== FOLDER NAME BASED ON UPDATEDAT ========== - updated_at = r["req_updated_at"] or datetime.now() - date_str = updated_at.strftime("%Y-%m-%d") - - prijmeni = sanitize_name(r["prijmeni"] or "Unknown") - jmeno = sanitize_name(r["jmeno"] or "") - - folder_name = f"{date_str} {prijmeni}, {jmeno} {req_id}" - folder_name = sanitize_name(folder_name) - main_folder = BASE_DIR / folder_name - - # ========== FIND OLD FOLDER (DUPLICATE) ========== - # Any folder that contains "_" and is not main_folder is duplicate - possible_dups = [ - f for f in BASE_DIR.iterdir() - if f.is_dir() and req_id in f.name and f != main_folder - ] - - # ========== MERGE DUPLICATES ========== - for dup in possible_dups: - print(f"♻️ Merging duplicate folder: {dup.name}") - - # 1) Clean unexpected files in dup - clean_folder(dup, valid_files) - - # 2) Move files from dup to main folder - main_folder.mkdir(parents=True, exist_ok=True) - - for f in dup.iterdir(): - if f.is_file(): - target = main_folder / f.name - if not target.exists(): - f.rename(target) - - # 3) Remove the duplicate folder - try: - shutil.rmtree(dup, ignore_errors=True) - except Exception as e: - print(f"⚠️ Could not delete duplicate folder {dup}: {e}") - - # ========== CLEAN MAIN FOLDER ========== - clean_folder(main_folder, valid_files) - - # ========== DOWNLOAD MISSING FILES ========== - main_folder.mkdir(parents=True, exist_ok=True) - - for filename in valid_files: - dest = main_folder / filename - if dest.exists(): - continue - - # fetch blob only now - start = time.perf_counter() - cur_blob.execute( - "SELECT file_content FROM medevio_downloads " - "WHERE request_id=%s AND filename=%s", - (req_id, filename) - ) - row = cur_blob.fetchone() - if not row: - continue - end = time.perf_counter() - print(f"⏱ Took {end - start:.4f} seconds") - - content = row[0] - if not content: - continue - - with open(dest, "wb") as f: - f.write(content) - - print(f"💾 Wrote: {dest.relative_to(BASE_DIR)}") - -print("\n🎯 Export complete.\n") - -cur_blob.close() -cur_meta.close() -conn.close()