#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Delta sync Medevio communication. Stáhne pouze zprávy změněné po messagesProcessed pro každý požadavek. """ import json import requests import pymysql from pathlib import Path from datetime import datetime import time import sys # ============================== # UTF-8 SAFE OUTPUT # ============================== try: sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') except AttributeError: import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') def safe_print(text: str): enc = sys.stdout.encoding or "" if not enc.lower().startswith("utf"): text = ''.join(ch for ch in text if ord(ch) < 65536) try: print(text) except UnicodeEncodeError: text = ''.join(ch for ch in text if ord(ch) < 128) print(text) # ============================== # CONFIG # ============================== TOKEN_PATH = Path("token.txt") 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 createdAt updatedAt } } } """ # ============================== # HELPERS # ============================== def parse_dt(s): if not s: return None try: return datetime.fromisoformat(s.replace("Z", "+00:00")) except Exception: return None def read_token(path: Path) -> str: tok = path.read_text(encoding="utf-8").strip() return tok.replace("Bearer ", "") # ============================== # FETCH MESSAGES (DELTA) # ============================== def fetch_messages(headers, request_id, updated_since): payload = { "operationName": "UseMessages_ListMessages", "query": GRAPHQL_QUERY_MESSAGES, "variables": { "requestId": request_id, "updatedSince": updated_since, }, } r = requests.post( "https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30 ) if r.status_code != 200: safe_print(f"❌ HTTP {r.status_code} for request {request_id}") return [] j = r.json() if "errors" in j: safe_print(f"❌ GraphQL error for {request_id}: {j['errors']}") return [] return j.get("data", {}).get("messages", []) or [] # ============================== # INSERT 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 mr = msg.get("medicalRecord") or {} 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.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") )) # ============================== # INSERT ATTACHMENT (DEDUP) # ============================== def insert_download(cur, req_id, msg, existing_ids): mr = msg.get("medicalRecord") or {} attachment_id = mr.get("id") if not attachment_id or attachment_id in existing_ids: return 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: safe_print(f"⚠️ Attachment download failed: {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) # existing attachments with conn.cursor() as cur: cur.execute("SELECT attachment_id FROM medevio_downloads") existing_ids = {r["attachment_id"] for r in cur.fetchall()} # select requests needing sync with conn.cursor() as cur: cur.execute(""" SELECT id, messagesProcessed FROM pozadavky WHERE messagesProcessed IS NULL OR messagesProcessed < updatedAt """) rows = cur.fetchall() safe_print(f"📋 Found {len(rows)} requests for message delta-sync\n") for i, row in enumerate(rows, 1): req_id = row["id"] updated_since = row["messagesProcessed"] if updated_since: updated_since = updated_since.replace(microsecond=0).isoformat() + "Z" safe_print(f"[{i}/{len(rows)}] {req_id}") messages = fetch_messages(headers, req_id, updated_since) if not messages: safe_print(" ⏭ No new messages") else: 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() safe_print(f" ✅ {len(messages)} new/updated messages") with conn.cursor() as cur: cur.execute( "UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,) ) conn.commit() time.sleep(0.25) conn.close() safe_print("\n🎉 Delta message sync DONE") # ============================== if __name__ == "__main__": main()