Compare commits

..

5 Commits

Author SHA1 Message Date
46fbcdeff7 notebook 2026-03-10 17:39:47 +01:00
6478902172 Rename 871 test.py to Report_AgendaPozadavky.py
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 17:36:12 +01:00
68700ecd4d Merge branch 'claude/heuristic-lichterman' 2026-03-10 17:35:25 +01:00
e345e477d3 Rename report file and auto-delete previous reports
- Rename output to "YYYY-MM-DD HH-MM-SS Agenda + Požadavky.xlsx"
- Delete old reports before generating new one

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 17:34:29 +01:00
be0d41ad01 Fix agenda report and add MySQL sync for open requests
- 871 test.py: Switch auth from medevio_storage.json to token.txt,
  update MySQL port to 3306, add hyperlinks to Request_ID column,
  add better API error handling
- sync_open_requests.py: New script to sync doneAt/removedAt/updatedAt
  from Medevio API to MySQL for requests incorrectly marked as open
- check_request.py: Diagnostic script to inspect a single request via API
- check_mysql.py: Diagnostic script to inspect a single request in MySQL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 07:44:19 +01:00
5 changed files with 310 additions and 21 deletions

Submodule .claude/worktrees/heuristic-lichterman updated: be0d41ad01...e345e477d3

View File

@@ -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:

View 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}")

View 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))

View 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}")