#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Stáhne konverzaci pro požadavky, kde: messagesProcessed IS NULL OR messagesProcessed < updatedAt. Vloží do medevio_conversation a přílohy do medevio_downloads. """ 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, } 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 } } } """ # ============================== # ⏱ DATETIME PARSER # ============================== def parse_dt(s): if not s: return None try: return datetime.fromisoformat(s.replace("Z", "+00:00")) except: pass try: return datetime.strptime(s[:19], "%Y-%m-%dT%H:%M:%S") except: return None # ============================== # 🔐 TOKEN # ============================== def read_token(path: Path) -> str: tok = path.read_text(encoding="utf-8").strip() return tok.replace("Bearer ", "") # ============================== # 📡 FETCH MESSAGES # ============================== def fetch_messages(headers, request_id): payload = { "operationName": "UseMessages_ListMessages", "query": GRAPHQL_QUERY_MESSAGES, "variables": {"requestId": request_id, "updatedSince": None}, } r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30) if r.status_code != 200: print("❌ HTTP", r.status_code, "for request", request_id) return [] 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 # ============================== # 💾 SAVE MESSAGE # ============================== 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 ) or None 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) """ mr = msg.get("medicalRecord") or {} cur.execute(sql, ( msg.get("id"), req_id, sender_name, sender.get("id"), sender.get("clinicId"), msg.get("text"), parse_dt(msg.get("createdAt")), parse_dt(msg.get("readAt")), parse_dt(msg.get("updatedAt")), mr.get("downloadUrl") or mr.get("url"), mr.get("description"), mr.get("contentType") )) # ============================== # 💾 DOWNLOAD MESSAGE ATTACHMENT # ============================== def insert_download(cur, req_id, msg, existing_ids): mr = msg.get("medicalRecord") or {} attachment_id = mr.get("id") if not attachment_id: return if attachment_id in existing_ids: return # skip duplicates url = mr.get("downloadUrl") or mr.get("url") if not url: return try: r = requests.get(url, timeout=30) r.raise_for_status() data = r.content except Exception as e: print("⚠️ Failed to download:", e) return filename = url.split("/")[-1].split("?")[0] 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, "MESSAGE_ATTACHMENT", filename, mr.get("contentType"), len(data), parse_dt(msg.get("createdAt")), data )) existing_ids.add(attachment_id) # ============================== # 🧠 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 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"📦 Already downloaded attachments: {len(existing_ids)}\n") # ---- Select 10 oldest pozadavky (regardless of messagesProcessed) sql = """ SELECT id FROM pozadavky ORDER BY updatedAt ASC LIMIT 10 """ with conn.cursor() as cur: cur.execute(sql) requests_to_process = cur.fetchall() print(f"📋 Will process {len(requests_to_process)} oldest pozadavků.\n") # ---- Process each pozadavek for idx, row in enumerate(requests_to_process, 1): req_id = row["id"] print(f"[{idx}/{len(requests_to_process)}] Processing {req_id} …") messages = fetch_messages(headers, req_id) with conn.cursor() as cur: for msg in messages: insert_message(cur, req_id, msg) insert_download(cur, req_id, msg, existing_ids) conn.commit() with conn.cursor() as cur: cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,)) conn.commit() print(f" ✅ {len(messages)} messages saved\n") time.sleep(0.25) conn.close() print("🎉 Done!") if __name__ == "__main__": main()