#!/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 import sys # Force UTF-8 output even under Windows Task Scheduler import sys try: sys.stdout.reconfigure(encoding='utf-8') sys.stderr.reconfigure(encoding='utf-8') except AttributeError: # Python < 3.7 fallback (not needed for you, but safe) import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') # ============================== # ๐Ÿ›ก SAFE PRINT FOR CP1250 / EMOJI # ============================== def safe_print(text: str): enc = sys.stdout.encoding or "" if not enc or not enc.lower().startswith("utf"): # strip emoji + characters outside BMP text = ''.join(ch for ch in text if ord(ch) < 65536) try: print(text) except UnicodeEncodeError: # ASCII fallback text = ''.join(ch for ch in text if ord(ch) < 128) print(text) # ============================== # ๐Ÿ”ง 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: safe_print(f"โŒ HTTP {r.status_code} for request {request_id}") return [] return r.json().get("data", {}).get("patientRequestMedicalRecords", []) # ============================== # ๐Ÿ’พ SAVE TO MYSQL # ============================== def insert_download(cur, req_id, a, m, created_date, existing_ids): attachment_id = a.get("id") if attachment_id in existing_ids: safe_print(f" โญ๏ธ Already downloaded {attachment_id}") return False url = m.get("downloadUrl") if not url: safe_print(" โš ๏ธ Missing download URL") return False filename = extract_filename_from_url(url) try: r = requests.get(url, timeout=30) r.raise_for_status() content = r.content except Exception as e: safe_print(f" โš ๏ธ Download failed {url}: {e}") return False file_size = len(content) attachment_type = a.get("attachmentType") content_type = m.get("contentType") 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) safe_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 attachments with conn.cursor() as cur: cur.execute("SELECT attachment_id FROM medevio_downloads") existing_ids = {row["attachment_id"] for row in cur.fetchall()} safe_print(f"โœ… {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() safe_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() safe_print(f"\n[{i}/{len(req_rows)}] ๐Ÿงพ {prijmeni}, {jmeno} ({req_id})") attachments = fetch_attachments(headers, req_id) if not attachments: safe_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() with conn.cursor() as cur: cur.execute("UPDATE pozadavky SET attachmentsProcessed = NOW() WHERE id = %s", (req_id,)) conn.commit() safe_print(f" โœ… Done ({len(attachments)} attachments)") time.sleep(0.3) conn.close() safe_print("\n๐ŸŽฏ All attachments processed.") # ============================== if __name__ == "__main__": main()