Merge branch 'claude/heuristic-lichterman'
This commit is contained in:
@@ -30,7 +30,7 @@ CLINIC_SLUG = "mudr-buzalkova"
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3307,
|
||||
"port": 3306,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "medevio",
|
||||
@@ -40,26 +40,22 @@ DB_CONFIG = {
|
||||
|
||||
EXPORT_DIR = Path(r"u:\Dropbox\Ordinace\Reporty")
|
||||
EXPORT_DIR.mkdir(exist_ok=True, parents=True)
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
xlsx_path = EXPORT_DIR / f"{timestamp} Agenda + Pozadavky (Merged).xlsx"
|
||||
|
||||
# Delete previous reports
|
||||
for old in EXPORT_DIR.glob("* Agenda + Požadavky.xlsx"):
|
||||
old.unlink()
|
||||
print(f"🗑️ Deleted old report: {old.name}")
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
|
||||
xlsx_path = EXPORT_DIR / f"{timestamp} Agenda + Požadavky.xlsx"
|
||||
|
||||
# ==================== LOAD TOKEN ====================
|
||||
def load_gateway_token(storage_path="medevio_storage.json"):
|
||||
path = Path(storage_path)
|
||||
if not path.exists():
|
||||
raise SystemExit(f"❌ Storage file not found: {path}")
|
||||
with path.open("r", encoding="utf-8") as f:
|
||||
state = json.load(f)
|
||||
token = next(
|
||||
(c["value"] for c in state["cookies"] if c["name"] == "gateway-access-token"),
|
||||
None,
|
||||
)
|
||||
if not token:
|
||||
raise SystemExit("❌ gateway-access-token not found in storage file.")
|
||||
return token
|
||||
|
||||
|
||||
gateway_token = load_gateway_token()
|
||||
TOKEN_PATH = Path("token.txt")
|
||||
if not TOKEN_PATH.exists():
|
||||
TOKEN_PATH = Path(__file__).parent / "token.txt"
|
||||
if not TOKEN_PATH.exists():
|
||||
raise SystemExit(f"❌ token.txt not found")
|
||||
gateway_token = TOKEN_PATH.read_text(encoding="utf-8").strip()
|
||||
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
@@ -80,8 +76,18 @@ thin_border = Border(
|
||||
)
|
||||
|
||||
|
||||
REQUEST_URL_TEMPLATE = "https://my.medevio.cz/mudr-buzalkova/klinika/pozadavky?pozadavek={}"
|
||||
link_font = Font(color="0563C1", underline="single")
|
||||
|
||||
|
||||
def format_ws(ws, df):
|
||||
"""Apply unified formatting to a worksheet."""
|
||||
# Find Request_ID column index (1-based)
|
||||
req_id_col = None
|
||||
columns = list(df.columns)
|
||||
if "Request_ID" in columns:
|
||||
req_id_col = columns.index("Request_ID") + 1
|
||||
|
||||
for col_idx in range(1, len(df.columns) + 1):
|
||||
col_letter = get_column_letter(col_idx)
|
||||
cell = ws.cell(row=1, column=col_idx)
|
||||
@@ -96,6 +102,10 @@ def format_ws(ws, df):
|
||||
cell.border = thin_border
|
||||
if r_idx % 2 == 0:
|
||||
cell.fill = alt_fill
|
||||
# Add hyperlink to Request_ID cells
|
||||
if req_id_col and cell.column == req_id_col and cell.value:
|
||||
cell.hyperlink = REQUEST_URL_TEMPLATE.format(cell.value)
|
||||
cell.font = link_font
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
|
||||
@@ -138,7 +148,12 @@ payload = {
|
||||
|
||||
r = requests.post(GRAPHQL_URL, headers=headers, data=json.dumps(payload))
|
||||
r.raise_for_status()
|
||||
reservations = r.json()["data"]["reservations"]
|
||||
resp = r.json()
|
||||
if "errors" in resp or "data" not in resp:
|
||||
print("❌ API response:")
|
||||
print(json.dumps(resp, indent=2, ensure_ascii=False))
|
||||
raise SystemExit("API call failed - check token or query.")
|
||||
reservations = resp["data"]["reservations"]
|
||||
|
||||
rows = []
|
||||
for r in reservations:
|
||||
|
||||
33
40 agenda a požadavky/check_mysql.py
Normal file
33
40 agenda a požadavky/check_mysql.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Check one request in MySQL."""
|
||||
|
||||
import pymysql
|
||||
import json
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3306,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "medevio",
|
||||
"charset": "utf8mb4",
|
||||
"cursorclass": pymysql.cursors.DictCursor,
|
||||
}
|
||||
|
||||
REQUEST_ID = "6b46b5a8-b080-4821-86b0-39adabeec86b"
|
||||
|
||||
conn = pymysql.connect(**DB_CONFIG)
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM pozadavky WHERE id = %s", (REQUEST_ID,))
|
||||
row = cur.fetchone()
|
||||
conn.close()
|
||||
|
||||
if row:
|
||||
# Convert datetime objects to strings for JSON serialization
|
||||
for k, v in row.items():
|
||||
if hasattr(v, 'isoformat'):
|
||||
row[k] = v.isoformat()
|
||||
print(json.dumps(row, indent=2, ensure_ascii=False, default=str))
|
||||
else:
|
||||
print(f"Not found: {REQUEST_ID}")
|
||||
63
40 agenda a požadavky/check_request.py
Normal file
63
40 agenda a požadavky/check_request.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Quick check: fetch one request from Medevio API and print all fields."""
|
||||
|
||||
import json
|
||||
import requests
|
||||
from pathlib import Path
|
||||
|
||||
TOKEN_PATH = Path(__file__).parent / "token.txt"
|
||||
GRAPHQL_URL = "https://api.medevio.cz/graphql"
|
||||
CLINIC_SLUG = "mudr-buzalkova"
|
||||
REQUEST_ID = "6b46b5a8-b080-4821-86b0-39adabeec86b"
|
||||
|
||||
token = TOKEN_PATH.read_text(encoding="utf-8").strip()
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
"authorization": f"Bearer {token}",
|
||||
"origin": "https://my.medevio.cz",
|
||||
"referer": "https://my.medevio.cz/",
|
||||
}
|
||||
|
||||
# Query with as many fields as possible
|
||||
QUERY = """
|
||||
query GetPatientRequest2($requestId: UUID!, $clinicSlug: String!, $locale: Locale!) {
|
||||
request: getPatientRequest2(patientRequestId: $requestId, clinicSlug: $clinicSlug) {
|
||||
id
|
||||
displayTitle(locale: $locale)
|
||||
createdAt
|
||||
updatedAt
|
||||
doneAt
|
||||
removedAt
|
||||
userNote
|
||||
eventType
|
||||
extendedPatient(clinicSlug: $clinicSlug) {
|
||||
name
|
||||
surname
|
||||
dob
|
||||
identificationNumber
|
||||
insuranceCompanyObject { shortName }
|
||||
}
|
||||
ecrfFilledData(locale: $locale) {
|
||||
name
|
||||
groups {
|
||||
label
|
||||
fields { name label type value }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
payload = {
|
||||
"operationName": "GetPatientRequest2",
|
||||
"query": QUERY,
|
||||
"variables": {
|
||||
"requestId": REQUEST_ID,
|
||||
"clinicSlug": CLINIC_SLUG,
|
||||
"locale": "cs",
|
||||
},
|
||||
}
|
||||
|
||||
r = requests.post(GRAPHQL_URL, json=payload, headers=headers, timeout=30)
|
||||
print(json.dumps(r.json(), indent=2, ensure_ascii=False))
|
||||
178
40 agenda a požadavky/sync_open_requests.py
Normal file
178
40 agenda a požadavky/sync_open_requests.py
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Sync open requests: checks each request marked as open in MySQL (doneAt IS NULL
|
||||
AND removedAt IS NULL) against the Medevio API. If the API shows the request is
|
||||
closed (doneAt) or removed (removedAt), updates MySQL accordingly.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import pymysql
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
# ==============================
|
||||
# UTF-8 output (Windows friendly)
|
||||
# ==============================
|
||||
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")
|
||||
|
||||
# ==============================
|
||||
# DRY RUN - set to True to only print what would be updated, False to actually update
|
||||
# ==============================
|
||||
DRY_RUN = False
|
||||
|
||||
# ==============================
|
||||
# CONFIG
|
||||
# ==============================
|
||||
GRAPHQL_URL = "https://api.medevio.cz/graphql"
|
||||
CLINIC_SLUG = "mudr-buzalkova"
|
||||
|
||||
TOKEN_PATH = Path(__file__).parent / "token.txt"
|
||||
if not TOKEN_PATH.exists():
|
||||
raise SystemExit("❌ token.txt not found")
|
||||
|
||||
gateway_token = TOKEN_PATH.read_text(encoding="utf-8").strip()
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
"authorization": f"Bearer {gateway_token}",
|
||||
"origin": "https://my.medevio.cz",
|
||||
"referer": "https://my.medevio.cz/",
|
||||
}
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": "192.168.1.76",
|
||||
"port": 3306,
|
||||
"user": "root",
|
||||
"password": "Vlado9674+",
|
||||
"database": "medevio",
|
||||
"charset": "utf8mb4",
|
||||
"cursorclass": pymysql.cursors.DictCursor,
|
||||
}
|
||||
|
||||
GRAPHQL_QUERY = """
|
||||
query GetPatientRequest2($requestId: UUID!, $clinicSlug: String!) {
|
||||
request: getPatientRequest2(patientRequestId: $requestId, clinicSlug: $clinicSlug) {
|
||||
id
|
||||
doneAt
|
||||
removedAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def fix_datetime(dt_str):
|
||||
if not dt_str:
|
||||
return None
|
||||
try:
|
||||
return datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def fetch_request(request_id):
|
||||
payload = {
|
||||
"operationName": "GetPatientRequest2",
|
||||
"query": GRAPHQL_QUERY,
|
||||
"variables": {
|
||||
"requestId": request_id,
|
||||
"clinicSlug": CLINIC_SLUG,
|
||||
},
|
||||
}
|
||||
for attempt in range(3):
|
||||
try:
|
||||
r = requests.post(GRAPHQL_URL, json=payload, headers=headers, timeout=30)
|
||||
break
|
||||
except (requests.ConnectionError, requests.Timeout, requests.exceptions.RequestException) as e:
|
||||
print(f" ⚠️ Attempt {attempt+1}/3 failed: {e}")
|
||||
time.sleep(2)
|
||||
else:
|
||||
print(f" ❌ Connection failed after 3 attempts for {request_id}")
|
||||
return None
|
||||
if r.status_code != 200:
|
||||
print(f" ❌ HTTP {r.status_code} for {request_id}")
|
||||
return None
|
||||
data = r.json()
|
||||
if "errors" in data:
|
||||
print(f" ❌ API error for {request_id}: {data['errors']}")
|
||||
return None
|
||||
return data.get("data", {}).get("request")
|
||||
|
||||
|
||||
# ==============================
|
||||
# MAIN
|
||||
# ==============================
|
||||
conn = pymysql.connect(**DB_CONFIG)
|
||||
|
||||
# 1) Read all open requests from MySQL
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id, displayTitle, pacient_prijmeni, pacient_jmeno "
|
||||
"FROM pozadavky WHERE doneAt IS NULL AND removedAt IS NULL"
|
||||
)
|
||||
open_requests = cur.fetchall()
|
||||
|
||||
mode = "DRY RUN" if DRY_RUN else "LIVE"
|
||||
print(f"🔧 Mode: {mode}")
|
||||
print(f"📋 Found {len(open_requests)} open requests in MySQL.\n")
|
||||
|
||||
updated = 0
|
||||
errors = 0
|
||||
|
||||
for i, req in enumerate(open_requests, 1):
|
||||
rid = req["id"]
|
||||
name = f"{req.get('pacient_prijmeni', '')} {req.get('pacient_jmeno', '')}".strip()
|
||||
title = req.get("displayTitle", "")
|
||||
print(f"[{i}/{len(open_requests)}] {name} – {title} ({rid})")
|
||||
|
||||
api_data = fetch_request(rid)
|
||||
if api_data is None:
|
||||
errors += 1
|
||||
continue
|
||||
|
||||
api_done = api_data.get("doneAt")
|
||||
api_removed = api_data.get("removedAt")
|
||||
api_updated = api_data.get("updatedAt")
|
||||
|
||||
if api_done or api_removed:
|
||||
done_dt = fix_datetime(api_done)
|
||||
removed_dt = fix_datetime(api_removed)
|
||||
updated_dt = fix_datetime(api_updated)
|
||||
|
||||
status = "DONE" if api_done else "REMOVED"
|
||||
|
||||
if DRY_RUN:
|
||||
print(f" 🔍 Would update → {status} (doneAt={api_done}, removedAt={api_removed})")
|
||||
else:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"UPDATE pozadavky SET doneAt = %s, removedAt = %s, updatedAt = %s WHERE id = %s",
|
||||
(done_dt, removed_dt, updated_dt, rid),
|
||||
)
|
||||
conn.commit()
|
||||
print(f" ✅ Updated → {status}")
|
||||
|
||||
updated += 1
|
||||
else:
|
||||
print(f" ⏳ Still open")
|
||||
|
||||
# Be gentle with the API
|
||||
time.sleep(1)
|
||||
|
||||
conn.close()
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"📊 Total open in MySQL: {len(open_requests)}")
|
||||
print(f"✅ Updated (closed/removed): {updated}")
|
||||
print(f"⏳ Still open: {len(open_requests) - updated - errors}")
|
||||
print(f"❌ Errors: {errors}")
|
||||
Reference in New Issue
Block a user