229 lines
5.5 KiB
Python
229 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import pymysql
|
||
import requests
|
||
from pathlib import Path
|
||
from datetime import datetime, timezone
|
||
import time
|
||
from dateutil import parser
|
||
|
||
# ================================
|
||
# 🔧 CONFIGURATION
|
||
# ================================
|
||
TOKEN_PATH = Path("token.txt")
|
||
CLINIC_SLUG = "mudr-buzalkova"
|
||
BATCH_SIZE = 100
|
||
|
||
DB_CONFIG = {
|
||
"host": "127.0.0.1",
|
||
"port": 3307,
|
||
"user": "root",
|
||
"password": "Vlado9674+",
|
||
"database": "medevio",
|
||
"charset": "utf8mb4",
|
||
"cursorclass": pymysql.cursors.DictCursor,
|
||
}
|
||
|
||
# ⭐ NOVÝ TESTOVANÝ DOTAZ – obsahuje lastMessage.createdAt
|
||
GRAPHQL_QUERY = r"""
|
||
query ClinicRequestList2(
|
||
$clinicSlug: String!,
|
||
$queueId: String,
|
||
$queueAssignment: QueueAssignmentFilter!,
|
||
$state: PatientRequestState,
|
||
$pageInfo: PageInfo!,
|
||
$locale: Locale!
|
||
) {
|
||
requestsResponse: listPatientRequestsForClinic2(
|
||
clinicSlug: $clinicSlug,
|
||
queueId: $queueId,
|
||
queueAssignment: $queueAssignment,
|
||
state: $state,
|
||
pageInfo: $pageInfo
|
||
) {
|
||
count
|
||
patientRequests {
|
||
id
|
||
displayTitle(locale: $locale)
|
||
createdAt
|
||
updatedAt
|
||
doneAt
|
||
removedAt
|
||
extendedPatient {
|
||
name
|
||
surname
|
||
identificationNumber
|
||
}
|
||
lastMessage {
|
||
createdAt
|
||
}
|
||
}
|
||
}
|
||
}
|
||
"""
|
||
|
||
|
||
# ================================
|
||
# 🧿 SAFE DATETIME PARSER (ALWAYS UTC → LOCAL)
|
||
# ================================
|
||
def to_mysql_dt_utc(iso_str):
|
||
"""
|
||
Parse Medevio timestamps safely.
|
||
Treat timestamps WITHOUT timezone as UTC.
|
||
Convert to local time before saving to MySQL.
|
||
"""
|
||
if not iso_str:
|
||
return None
|
||
try:
|
||
dt = parser.isoparse(iso_str)
|
||
|
||
# If tz is missing → assume UTC
|
||
if dt.tzinfo is None:
|
||
dt = dt.replace(tzinfo=timezone.utc)
|
||
|
||
# Convert to local timezone
|
||
dt_local = dt.astimezone()
|
||
|
||
return dt_local.strftime("%Y-%m-%d %H:%M:%S")
|
||
except:
|
||
return None
|
||
|
||
|
||
# ================================
|
||
# 🔑 TOKEN
|
||
# ================================
|
||
def read_token(path: Path) -> str:
|
||
tok = path.read_text(encoding="utf-8").strip()
|
||
if tok.startswith("Bearer "):
|
||
return tok.split(" ", 1)[1]
|
||
return tok
|
||
|
||
|
||
# ================================
|
||
# 💾 UPSERT (včetně správného updatedAt)
|
||
# ================================
|
||
def upsert(conn, r):
|
||
p = r.get("extendedPatient") or {}
|
||
|
||
# raw timestamps z API – nyní přes nový parser
|
||
api_updated = to_mysql_dt_utc(r.get("updatedAt"))
|
||
|
||
last_msg = r.get("lastMessage") or {}
|
||
msg_updated = to_mysql_dt_utc(last_msg.get("createdAt"))
|
||
|
||
# nejnovější změna
|
||
def max_dt(a, b):
|
||
if a and b:
|
||
return max(a, b)
|
||
return a or b
|
||
|
||
final_updated = max_dt(api_updated, msg_updated)
|
||
|
||
sql = """
|
||
INSERT INTO pozadavky (
|
||
id, displayTitle, createdAt, updatedAt, doneAt, removedAt,
|
||
pacient_jmeno, pacient_prijmeni, pacient_rodnecislo
|
||
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||
ON DUPLICATE KEY UPDATE
|
||
displayTitle=VALUES(displayTitle),
|
||
updatedAt=VALUES(updatedAt),
|
||
doneAt=VALUES(doneAt),
|
||
removedAt=VALUES(removedAt),
|
||
pacient_jmeno=VALUES(pacient_jmeno),
|
||
pacient_prijmeni=VALUES(pacient_prijmeni),
|
||
pacient_rodnecislo=VALUES(pacient_rodnecislo)
|
||
"""
|
||
|
||
vals = (
|
||
r.get("id"),
|
||
r.get("displayTitle"),
|
||
to_mysql_dt_utc(r.get("createdAt")),
|
||
final_updated,
|
||
to_mysql_dt_utc(r.get("doneAt")),
|
||
to_mysql_dt_utc(r.get("removedAt")),
|
||
p.get("name"),
|
||
p.get("surname"),
|
||
p.get("identificationNumber"),
|
||
)
|
||
|
||
with conn.cursor() as cur:
|
||
cur.execute(sql, vals)
|
||
conn.commit()
|
||
|
||
|
||
# ================================
|
||
# 📡 FETCH ACTIVE PAGE
|
||
# ================================
|
||
def fetch_active(headers, offset):
|
||
variables = {
|
||
"clinicSlug": CLINIC_SLUG,
|
||
"queueId": None,
|
||
"queueAssignment": "ANY",
|
||
"pageInfo": {"first": BATCH_SIZE, "offset": offset},
|
||
"locale": "cs",
|
||
"state": "ACTIVE",
|
||
}
|
||
|
||
payload = {
|
||
"operationName": "ClinicRequestList2",
|
||
"query": GRAPHQL_QUERY,
|
||
"variables": variables,
|
||
}
|
||
|
||
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers)
|
||
r.raise_for_status()
|
||
|
||
data = r.json().get("data", {}).get("requestsResponse", {})
|
||
return data.get("patientRequests", []), data.get("count", 0)
|
||
|
||
|
||
# ================================
|
||
# 🧠 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)
|
||
|
||
print(f"\n=== Sync ACTIVE požadavků @ {datetime.now():%Y-%m-%d %H:%M:%S} ===")
|
||
|
||
offset = 0
|
||
total_processed = 0
|
||
total_count = None
|
||
|
||
while True:
|
||
batch, count = fetch_active(headers, offset)
|
||
|
||
if total_count is None:
|
||
total_count = count
|
||
print(f"📡 Celkem ACTIVE v Medevio: {count}")
|
||
|
||
if not batch:
|
||
break
|
||
|
||
for r in batch:
|
||
upsert(conn, r)
|
||
|
||
total_processed += len(batch)
|
||
print(f" • {total_processed}/{total_count} ACTIVE processed")
|
||
|
||
if offset + BATCH_SIZE >= count:
|
||
break
|
||
|
||
offset += BATCH_SIZE
|
||
time.sleep(0.4)
|
||
|
||
conn.close()
|
||
print("\n✅ ACTIVE sync hotovo!\n")
|
||
|
||
|
||
# ================================
|
||
if __name__ == "__main__":
|
||
main()
|