From 3038c87b5df36182260a653d45a214ad3fde5f0a Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Wed, 12 Nov 2025 07:10:48 +0100 Subject: [PATCH] notebook --- 10ReadPozadavky/0701 test.py | 101 +++--- .../PRAVIDELNE_ReadPozadavkySaveMySql.py | 14 +- Testy/01 Test.py | 122 ++++++++ Testy/02 Test.py | 296 ++++++++++++++++++ token.txt | 1 + 5 files changed, 477 insertions(+), 57 deletions(-) create mode 100644 Testy/01 Test.py create mode 100644 Testy/02 Test.py create mode 100644 token.txt diff --git a/10ReadPozadavky/0701 test.py b/10ReadPozadavky/0701 test.py index 795b3cd..9a46fbb 100644 --- a/10ReadPozadavky/0701 test.py +++ b/10ReadPozadavky/0701 test.py @@ -1,59 +1,60 @@ -import requests -import json -from pathlib import Path +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- -TOKEN_PATH = Path("token.txt") -REQUEST_ID = "092a0c63-28be-4c6b-ab3b-204e1e2641d4" -CLINIC_SLUG = "mudr-buzalkova" +import pymysql +from datetime import datetime -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 - -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 - } - } +# ============================== +# โš™๏ธ CONFIGURATION +# ============================== +DB_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", } + +REQUEST_ID = "4476f9f8-d7b7-4381-b056-a44897413bcd" + +# ============================== +# ๐Ÿงฉ READ CONVERSATION +# ============================== +conn = pymysql.connect(**DB_CONFIG) +cur = conn.cursor(pymysql.cursors.DictCursor) + +sql = """ +SELECT + id, + sender_name, + text, + created_at, + attachment_url, + attachment_description, + attachment_content_type +FROM medevio_conversation +WHERE request_id = %s +ORDER BY created_at ASC; """ +cur.execute(sql, (REQUEST_ID,)) +rows = cur.fetchall() -variables = { - "requestId": REQUEST_ID, - } +print(f"๐Ÿ’ฌ Conversation for request {REQUEST_ID}:") +print("โ”€" * 100) -headers = { - "Authorization": f"Bearer {read_token(TOKEN_PATH)}", - "Content-Type": "application/json", - "Accept": "application/json", -} +for r in rows: + created = r["created_at"].strftime("%Y-%m-%d %H:%M") if r["created_at"] else "unknown" + sender = r["sender_name"] or "(unknown)" + text = (r["text"] or "").strip().replace("\n", " ") -payload = { - "operationName": "ClinicRequestDetail_GetPatientRequest2", - "query": GRAPHQL_QUERY, - "variables": variables, -} + print(f"[{created}] {sender}: {text}") -print("๐Ÿ“ก Querying Medevio API...\n") -r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers) -print(f"HTTP status: {r.status_code}\n") -print(json.dumps(r.json(), indent=2, ensure_ascii=False)) + if r["attachment_url"]: + print(f" ๐Ÿ“Ž Attachment: {r['attachment_description']} ({r['attachment_content_type']})") + print(f" URL: {r['attachment_url']}") + print() + +cur.close() +conn.close() diff --git a/10ReadPozadavky/PRAVIDELNE_ReadPozadavkySaveMySql.py b/10ReadPozadavky/PRAVIDELNE_ReadPozadavkySaveMySql.py index 42f4775..ed314c5 100644 --- a/10ReadPozadavky/PRAVIDELNE_ReadPozadavkySaveMySql.py +++ b/10ReadPozadavky/PRAVIDELNE_ReadPozadavkySaveMySql.py @@ -8,12 +8,12 @@ from datetime import datetime import time import time, socket -for _ in range(30): - try: - socket.create_connection(("127.0.0.1", 3307), timeout=3).close() - break - except OSError: - time.sleep(10) +# for _ in range(30): +# try: +# socket.create_connection(("127.0.0.1", 3307), timeout=3).close() +# break +# except OSError: +# time.sleep(10) # ================================ # ๐Ÿ”ง CONFIGURATION # ================================ @@ -23,7 +23,7 @@ BATCH_SIZE = 100 DONE_LIMIT = 200 # only last 200 DONE DB_CONFIG = { - "host": "127.0.0.1", + "host": "192.168.1.76", "port": 3307, "user": "root", "password": "Vlado9674+", diff --git a/Testy/01 Test.py b/Testy/01 Test.py new file mode 100644 index 0000000..cd421ff --- /dev/null +++ b/Testy/01 Test.py @@ -0,0 +1,122 @@ +#!/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 new file mode 100644 index 0000000..5f3d2d5 --- /dev/null +++ b/Testy/02 Test.py @@ -0,0 +1,296 @@ +#!/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 = "2025-1-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/token.txt b/token.txt new file mode 100644 index 0000000..d31188b --- /dev/null +++ b/token.txt @@ -0,0 +1 @@ +nYvrvgflIKcDiQg8Hhpud+qG8iGZ8eH8su4nyT/Mgcm7XQp65ygY9s39+O01wIpk/7sKd6fBHkiKvsqH \ No newline at end of file