#!/usr/bin/env python3 # -*- coding: utf-8 -*- import os,zlib import json import requests import pymysql from pathlib import Path from datetime import datetime import time import shutil # ============================== # 🔧 CONFIGURATION # ============================== TOKEN_PATH = Path("token.txt") CLINIC_SLUG = "mudr-buzalkova" BASE_DIR = Path(r"u:\Dropbox\ordinace\Dokumentace_ke_zpracování\Medevio_přílohy") BASE_DIR.mkdir(parents=True, exist_ok=True) 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 ClinicRequestDetail_GetPatientRequest2( $requestId: UUID!, ) { patientRequestMedicalRecords: listMedicalRecordsForPatientRequest( attachmentTypes: [ECRF_FILL_ATTACHMENT, MESSAGE_ATTACHMENT, PATIENT_REQUEST_ATTACHMENT] patientRequestId: $requestId pageInfo: {first: 100, offset: 0} ) { attachmentType id medicalRecord { contentType description downloadUrl id url visibleToPatient } } } """ def short_crc8(uuid_str: str) -> str: """Return deterministic 8-char hex string from any input string (CRC32).""" return f"{zlib.crc32(uuid_str.encode('utf-8')) & 0xffffffff:08x}" def extract_filename_from_url(url: str) -> str: """Extracts filename from S3-style URL (between last '/' and first '?').""" try: filename = url.split("/")[-1].split("?")[0] return filename except Exception: return "unknown_filename" def safe_rename(src: Path, dst: Path, retries: int = 5, delay: float = 3.0): """Rename a folder with retries to avoid Dropbox/OneDrive sync lock issues.""" for attempt in range(1, retries + 1): try: src.rename(dst) return # success except PermissionError as e: print(f" ⚠️ Rename attempt {attempt}/{retries} failed ({e}) — waiting {delay}s...") time.sleep(delay) except Exception as e: print(f" ❌ Unexpected rename error: {e}") break print(f" 🚫 Failed to rename '{src}' → '{dst}' after {retries} attempts.") # ============================== # 🔑 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 # ============================== # 💾 DOWNLOAD FILE # ============================== def download_file(url: str, out_path: Path): try: r = requests.get(url, timeout=30) r.raise_for_status() out_path.parent.mkdir(parents=True, exist_ok=True) with open(out_path, "wb") as f: f.write(r.content) print(f" 💾 Saved: {out_path.relative_to(BASE_DIR)}") except Exception as e: print(f" ⚠️ Failed to download {out_path.name}: {e}") # ============================== # 📡 FETCH ATTACHMENTS # ============================== def fetch_attachments(headers, request_id): variables = { "requestId": request_id, } payload = { "operationName": "ClinicRequestDetail_GetPatientRequest2", "query": GRAPHQL_QUERY, "variables": variables, } r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers) if r.status_code != 200: print(f"❌ HTTP {r.status_code}") return [] data = r.json().get("data", {}).get("patientRequestMedicalRecords", []) return data # ============================== # 🧠 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) with conn.cursor() as cur: cur.execute(""" SELECT id, displayTitle, pacient_prijmeni, pacient_jmeno, createdAt FROM pozadavky WHERE displayTitle = 'Odeslat lékařskou zprávu' """) rows = cur.fetchall() print(f"📋 Found {len(rows)} 'Odeslat lékařskou zprávu' requests") for i, row in enumerate(rows, 1): req_id = row["id"] print(req_id) prijmeni = row.get("pacient_prijmeni") or "Neznamy" jmeno = row.get("pacient_jmeno") or "" created = row.get("createdAt") created_date = None if created: try: created_date = datetime.strptime(str(created), "%Y-%m-%d %H:%M:%S").strftime("%Y-%m-%d") except Exception: created_date = "unknown" patient_dir = BASE_DIR / f"{prijmeni}, {jmeno}" / created_date print(f"\n[{i}/{len(rows)}] 📂 {patient_dir.relative_to(BASE_DIR)}") attachments = fetch_attachments(headers, req_id) # print(attachments) if not attachments: print(" ⚠️ No attachments") continue # vytvoř krátký CRC32 hash z UUID uuid_short = short_crc8(str(req_id)) # Dočasná složka bez počtu temp_dir = BASE_DIR / f"{prijmeni}, {jmeno}" / f"{created_date} {uuid_short}" temp_dir.mkdir(parents=True, exist_ok=True) for a in attachments: m = a.get("medicalRecord") or {} # fname = m.get("description") or f"{m.get('id')}.bin" url = m.get("downloadUrl") fname = extract_filename_from_url(url) if url: out_path = temp_dir / fname download_file(url, out_path) # Po stažení všech příloh spočítej skutečné soubory real_count = len([f for f in temp_dir.iterdir() if f.is_file()]) # Přejmenuj složku na finální název s počtem final_dir = temp_dir.parent / f"{temp_dir.name} ({real_count})" if real_count != 0: safe_rename(temp_dir, final_dir) print(f" 📎 Saved {real_count} attachments → {final_dir.relative_to(BASE_DIR)}") else: print(f" ⚠️ No attachments for {temp_dir.name}") temp_dir.rmdir() # smaž prázdnou složku conn.close() print("\n✅ Done!") if __name__ == "__main__": main()