notebook
This commit is contained in:
2
.idea/Medevio.iml
generated
2
.idea/Medevio.iml
generated
@@ -4,7 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.12 virtualenv at U:\Medevio\.venv" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.12 (Medevio)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,5 +3,5 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.12 (Medevio)" />
|
<option name="sdkName" value="Python 3.12 (Medevio)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 virtualenv at U:\Medevio\.venv" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (Medevio)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
import pymysql
|
import pymysql
|
||||||
import requests
|
import requests
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
import time
|
import time
|
||||||
|
from dateutil import parser
|
||||||
|
|
||||||
# ================================
|
# ================================
|
||||||
# 🔧 CONFIGURATION
|
# 🔧 CONFIGURATION
|
||||||
@@ -24,21 +25,22 @@ DB_CONFIG = {
|
|||||||
"cursorclass": pymysql.cursors.DictCursor,
|
"cursorclass": pymysql.cursors.DictCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ⭐ NOVÝ TESTOVANÝ DOTAZ – obsahuje lastMessage.createdAt
|
||||||
GRAPHQL_QUERY = r"""
|
GRAPHQL_QUERY = r"""
|
||||||
query ClinicRequestGrid_ListPatientRequestsForClinic2(
|
query ClinicRequestList2(
|
||||||
$clinicSlug: String!,
|
$clinicSlug: String!,
|
||||||
$queueId: String,
|
$queueId: String,
|
||||||
$queueAssignment: QueueAssignmentFilter!,
|
$queueAssignment: QueueAssignmentFilter!,
|
||||||
|
$state: PatientRequestState,
|
||||||
$pageInfo: PageInfo!,
|
$pageInfo: PageInfo!,
|
||||||
$locale: Locale!,
|
$locale: Locale!
|
||||||
$state: PatientRequestState
|
|
||||||
) {
|
) {
|
||||||
requestsResponse: listPatientRequestsForClinic2(
|
requestsResponse: listPatientRequestsForClinic2(
|
||||||
clinicSlug: $clinicSlug,
|
clinicSlug: $clinicSlug,
|
||||||
queueId: $queueId,
|
queueId: $queueId,
|
||||||
queueAssignment: $queueAssignment,
|
queueAssignment: $queueAssignment,
|
||||||
pageInfo: $pageInfo,
|
state: $state,
|
||||||
state: $state
|
pageInfo: $pageInfo
|
||||||
) {
|
) {
|
||||||
count
|
count
|
||||||
patientRequests {
|
patientRequests {
|
||||||
@@ -53,40 +55,71 @@ query ClinicRequestGrid_ListPatientRequestsForClinic2(
|
|||||||
surname
|
surname
|
||||||
identificationNumber
|
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
|
# 🔑 TOKEN
|
||||||
# ================================
|
# ================================
|
||||||
def read_token(path: Path) -> str:
|
def read_token(path: Path) -> str:
|
||||||
tok = path.read_text(encoding="utf-8").strip()
|
tok = path.read_text(encoding="utf-8").strip()
|
||||||
if tok.startswith("Bearer "):
|
if tok.startswith("Bearer "):
|
||||||
tok = tok.split(" ", 1)[1]
|
return tok.split(" ", 1)[1]
|
||||||
return tok
|
return tok
|
||||||
|
|
||||||
|
|
||||||
# ================================
|
# ================================
|
||||||
# 🕒 DATETIME FORMAT
|
# 💾 UPSERT (včetně správného updatedAt)
|
||||||
# ================================
|
|
||||||
def to_mysql_dt(iso_str):
|
|
||||||
if not iso_str:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
dt = datetime.fromisoformat(iso_str.replace("Z", "+00:00"))
|
|
||||||
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
# ================================
|
|
||||||
# 💾 UPSERT
|
|
||||||
# ================================
|
# ================================
|
||||||
def upsert(conn, r):
|
def upsert(conn, r):
|
||||||
p = r.get("extendedPatient") or {}
|
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 = """
|
sql = """
|
||||||
INSERT INTO pozadavky (
|
INSERT INTO pozadavky (
|
||||||
id, displayTitle, createdAt, updatedAt, doneAt, removedAt,
|
id, displayTitle, createdAt, updatedAt, doneAt, removedAt,
|
||||||
@@ -105,10 +138,10 @@ def upsert(conn, r):
|
|||||||
vals = (
|
vals = (
|
||||||
r.get("id"),
|
r.get("id"),
|
||||||
r.get("displayTitle"),
|
r.get("displayTitle"),
|
||||||
to_mysql_dt(r.get("createdAt")),
|
to_mysql_dt_utc(r.get("createdAt")),
|
||||||
to_mysql_dt(r.get("updatedAt")),
|
final_updated,
|
||||||
to_mysql_dt(r.get("doneAt")),
|
to_mysql_dt_utc(r.get("doneAt")),
|
||||||
to_mysql_dt(r.get("removedAt")),
|
to_mysql_dt_utc(r.get("removedAt")),
|
||||||
p.get("name"),
|
p.get("name"),
|
||||||
p.get("surname"),
|
p.get("surname"),
|
||||||
p.get("identificationNumber"),
|
p.get("identificationNumber"),
|
||||||
@@ -133,15 +166,15 @@ def fetch_active(headers, offset):
|
|||||||
}
|
}
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"operationName": "ClinicRequestGrid_ListPatientRequestsForClinic2",
|
"operationName": "ClinicRequestList2",
|
||||||
"query": GRAPHQL_QUERY,
|
"query": GRAPHQL_QUERY,
|
||||||
"variables": variables,
|
"variables": variables,
|
||||||
}
|
}
|
||||||
|
|
||||||
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers)
|
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
data = r.json().get("data", {}).get("requestsResponse", {})
|
|
||||||
|
|
||||||
|
data = r.json().get("data", {}).get("requestsResponse", {})
|
||||||
return data.get("patientRequests", []), data.get("count", 0)
|
return data.get("patientRequests", []), data.get("count", 0)
|
||||||
|
|
||||||
|
|
||||||
@@ -160,9 +193,6 @@ def main():
|
|||||||
|
|
||||||
print(f"\n=== Sync ACTIVE požadavků @ {datetime.now():%Y-%m-%d %H:%M:%S} ===")
|
print(f"\n=== Sync ACTIVE požadavků @ {datetime.now():%Y-%m-%d %H:%M:%S} ===")
|
||||||
|
|
||||||
# -------------------------------
|
|
||||||
# 🚀 FETCH ALL ACTIVE REQUESTS
|
|
||||||
# -------------------------------
|
|
||||||
offset = 0
|
offset = 0
|
||||||
total_processed = 0
|
total_processed = 0
|
||||||
total_count = None
|
total_count = None
|
||||||
@@ -193,5 +223,6 @@ def main():
|
|||||||
print("\n✅ ACTIVE sync hotovo!\n")
|
print("\n✅ ACTIVE sync hotovo!\n")
|
||||||
|
|
||||||
|
|
||||||
|
# ================================
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Read conversation messages for pozadavky where messagesProcessed IS NULL
|
Stáhne konverzaci pro požadavky, kde:
|
||||||
(Optionally filtered by createdAt), insert them into `medevio_conversation`,
|
messagesProcessed IS NULL OR messagesProcessed < updatedAt.
|
||||||
and if a message has an attachment (medicalRecord), download it and save into
|
|
||||||
`medevio_downloads` (same logic as your attachments script).
|
Vloží do medevio_conversation a přílohy do medevio_downloads.
|
||||||
Finally, mark pozadavky.messagesProcessed = NOW().
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import zlib
|
import zlib
|
||||||
@@ -21,6 +20,7 @@ import time
|
|||||||
# 🔧 CONFIGURATION
|
# 🔧 CONFIGURATION
|
||||||
# ==============================
|
# ==============================
|
||||||
TOKEN_PATH = Path("token.txt")
|
TOKEN_PATH = Path("token.txt")
|
||||||
|
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
"host": "192.168.1.76",
|
"host": "192.168.1.76",
|
||||||
"port": 3307,
|
"port": 3307,
|
||||||
@@ -31,9 +31,6 @@ DB_CONFIG = {
|
|||||||
"cursorclass": pymysql.cursors.DictCursor,
|
"cursorclass": pymysql.cursors.DictCursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
# ✅ Optional: Only process requests created after this date ("" = no limit)
|
|
||||||
CREATED_AFTER = "2024-01-01"
|
|
||||||
|
|
||||||
GRAPHQL_QUERY_MESSAGES = r"""
|
GRAPHQL_QUERY_MESSAGES = r"""
|
||||||
query UseMessages_ListMessages($requestId: String!, $updatedSince: DateTime) {
|
query UseMessages_ListMessages($requestId: String!, $updatedSince: DateTime) {
|
||||||
messages: listMessages(patientRequestId: $requestId, updatedSince: $updatedSince) {
|
messages: listMessages(patientRequestId: $requestId, updatedSince: $updatedSince) {
|
||||||
@@ -64,75 +61,58 @@ query UseMessages_ListMessages($requestId: String!, $updatedSince: DateTime) {
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# 🧮 HELPERS
|
# ⏱ DATETIME PARSER
|
||||||
# ==============================
|
# ==============================
|
||||||
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):
|
def parse_dt(s):
|
||||||
if not s:
|
if not s:
|
||||||
return None
|
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:
|
try:
|
||||||
return datetime.strptime(s[:19], f)
|
return datetime.fromisoformat(s.replace("Z", "+00:00"))
|
||||||
except Exception:
|
except:
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
return datetime.strptime(s[:19], "%Y-%m-%dT%H:%M:%S")
|
||||||
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🔐 TOKEN
|
||||||
|
# ==============================
|
||||||
|
def read_token(path: Path) -> str:
|
||||||
|
tok = path.read_text(encoding="utf-8").strip()
|
||||||
|
return tok.replace("Bearer ", "")
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# 📡 FETCH MESSAGES
|
# 📡 FETCH MESSAGES
|
||||||
# ==============================
|
# ==============================
|
||||||
def fetch_messages(headers, request_id):
|
def fetch_messages(headers, request_id):
|
||||||
variables = {"requestId": request_id, "updatedSince": None}
|
|
||||||
payload = {
|
payload = {
|
||||||
"operationName": "UseMessages_ListMessages",
|
"operationName": "UseMessages_ListMessages",
|
||||||
"query": GRAPHQL_QUERY_MESSAGES,
|
"query": GRAPHQL_QUERY_MESSAGES,
|
||||||
"variables": variables,
|
"variables": {"requestId": request_id, "updatedSince": None},
|
||||||
}
|
}
|
||||||
|
|
||||||
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30)
|
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
print(f"❌ HTTP {r.status_code} for messages of request {request_id}")
|
print("❌ HTTP", r.status_code, "for request", request_id)
|
||||||
return []
|
return []
|
||||||
data = r.json().get("data", {}).get("messages", [])
|
return r.json().get("data", {}).get("messages", []) or []
|
||||||
return data or []
|
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# 💾 SAVE: conversation row
|
# 💾 SAVE MESSAGE
|
||||||
# ==============================
|
# ==============================
|
||||||
def insert_message(cur, req_id, msg):
|
def insert_message(cur, req_id, msg):
|
||||||
|
|
||||||
sender = msg.get("sender") or {}
|
sender = msg.get("sender") or {}
|
||||||
sender_name = " ".join(x for x in [sender.get("name"), sender.get("surname")] if x).strip() or None
|
sender_name = " ".join(
|
||||||
sender_id = sender.get("id")
|
x for x in [sender.get("name"), sender.get("surname")] if x
|
||||||
sender_clinic_id = sender.get("clinicId")
|
) or None
|
||||||
|
|
||||||
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 = """
|
sql = """
|
||||||
INSERT INTO medevio_conversation (
|
INSERT INTO medevio_conversation (
|
||||||
id, request_id, sender_name, sender_id, sender_clinic_id,
|
id, request_id,
|
||||||
|
sender_name, sender_id, sender_clinic_id,
|
||||||
text, created_at, read_at, updated_at,
|
text, created_at, read_at, updated_at,
|
||||||
attachment_url, attachment_description, attachment_content_type
|
attachment_url, attachment_description, attachment_content_type
|
||||||
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
||||||
@@ -148,57 +128,57 @@ def insert_message(cur, req_id, msg):
|
|||||||
attachment_description = VALUES(attachment_description),
|
attachment_description = VALUES(attachment_description),
|
||||||
attachment_content_type = VALUES(attachment_content_type)
|
attachment_content_type = VALUES(attachment_content_type)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
mr = msg.get("medicalRecord") or {}
|
||||||
|
|
||||||
cur.execute(sql, (
|
cur.execute(sql, (
|
||||||
msg.get("id"),
|
msg.get("id"),
|
||||||
req_id,
|
req_id,
|
||||||
sender_name,
|
sender_name,
|
||||||
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")),
|
||||||
attachment_url,
|
mr.get("downloadUrl") or mr.get("url"),
|
||||||
attachment_description,
|
mr.get("description"),
|
||||||
attachment_content_type
|
mr.get("contentType")
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# 💾 SAVE: download attachment (from message)
|
# 💾 DOWNLOAD MESSAGE ATTACHMENT
|
||||||
# ==============================
|
# ==============================
|
||||||
def insert_download_from_message(cur, req_id, msg, existing_ids):
|
def insert_download(cur, req_id, msg, existing_ids):
|
||||||
|
|
||||||
mr = msg.get("medicalRecord") or {}
|
mr = msg.get("medicalRecord") or {}
|
||||||
attachment_id = mr.get("id")
|
attachment_id = mr.get("id")
|
||||||
if not attachment_id:
|
if not attachment_id:
|
||||||
return False
|
return
|
||||||
|
|
||||||
if attachment_id in existing_ids:
|
if attachment_id in existing_ids:
|
||||||
print(f" ⏭️ Skipping already downloaded message-attachment {attachment_id}")
|
return # skip duplicates
|
||||||
return False
|
|
||||||
|
|
||||||
url = mr.get("downloadUrl") or mr.get("url")
|
url = mr.get("downloadUrl") or mr.get("url")
|
||||||
if not url:
|
if not url:
|
||||||
return False
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, timeout=30)
|
r = requests.get(url, timeout=30)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
content = r.content
|
data = r.content
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" ⚠️ Failed to download message attachment {attachment_id}: {e}")
|
print("⚠️ Failed to download:", e)
|
||||||
return False
|
return
|
||||||
|
|
||||||
filename = extract_filename_from_url(url)
|
filename = url.split("/")[-1].split("?")[0]
|
||||||
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("""
|
cur.execute("""
|
||||||
INSERT INTO medevio_downloads (
|
INSERT INTO medevio_downloads (
|
||||||
request_id, attachment_id, attachment_type, filename,
|
request_id, attachment_id, attachment_type,
|
||||||
content_type, file_size, pacient_jmeno, pacient_prijmeni,
|
filename, content_type, file_size, created_at, file_content
|
||||||
created_at, file_content
|
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
|
||||||
) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
file_content = VALUES(file_content),
|
file_content = VALUES(file_content),
|
||||||
file_size = VALUES(file_size),
|
file_size = VALUES(file_size),
|
||||||
@@ -208,21 +188,20 @@ def insert_download_from_message(cur, req_id, msg, existing_ids):
|
|||||||
attachment_id,
|
attachment_id,
|
||||||
"MESSAGE_ATTACHMENT",
|
"MESSAGE_ATTACHMENT",
|
||||||
filename,
|
filename,
|
||||||
content_type,
|
mr.get("contentType"),
|
||||||
file_size,
|
len(data),
|
||||||
None,
|
parse_dt(msg.get("createdAt")),
|
||||||
None,
|
data
|
||||||
created_date,
|
|
||||||
content
|
|
||||||
))
|
))
|
||||||
|
|
||||||
existing_ids.add(attachment_id)
|
existing_ids.add(attachment_id)
|
||||||
print(f" 💾 Saved msg attachment {filename} ({file_size/1024:.1f} kB)")
|
|
||||||
return True
|
|
||||||
|
|
||||||
# ==============================
|
# ==============================
|
||||||
# 🧠 MAIN
|
# 🧠 MAIN
|
||||||
# ==============================
|
# ==============================
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
token = read_token(TOKEN_PATH)
|
token = read_token(TOKEN_PATH)
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": f"Bearer {token}",
|
"Authorization": f"Bearer {token}",
|
||||||
@@ -232,65 +211,49 @@ def main():
|
|||||||
|
|
||||||
conn = pymysql.connect(**DB_CONFIG)
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
|
||||||
# Load existing download IDs to skip duplicates (same logic as your script)
|
# ---- Load existing attachments
|
||||||
print("📦 Loading list of already downloaded attachments...")
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute("SELECT attachment_id FROM medevio_downloads")
|
cur.execute("SELECT attachment_id FROM medevio_downloads")
|
||||||
existing_ids = {row["attachment_id"] for row in cur.fetchall()}
|
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)
|
print(f"📦 Already downloaded attachments: {len(existing_ids)}\n")
|
||||||
|
|
||||||
|
# ---- Select pozadavky needing message sync
|
||||||
sql = """
|
sql = """
|
||||||
SELECT id, displayTitle, pacient_prijmeni, pacient_jmeno, createdAt
|
SELECT id
|
||||||
FROM pozadavky
|
FROM pozadavky
|
||||||
WHERE messagesProcessed IS NULL
|
WHERE messagesProcessed IS NULL
|
||||||
|
OR messagesProcessed < updatedAt
|
||||||
"""
|
"""
|
||||||
params = []
|
|
||||||
if CREATED_AFTER:
|
|
||||||
sql += " AND createdAt >= %s"
|
|
||||||
params.append(CREATED_AFTER)
|
|
||||||
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute(sql, params)
|
cur.execute(sql)
|
||||||
rows = cur.fetchall()
|
requests_to_process = cur.fetchall()
|
||||||
|
|
||||||
print(f"📋 Found {len(rows)} pozadavky to process (messagesProcessed IS NULL"
|
print(f"📋 Found {len(requests_to_process)} pozadavků requiring message sync.\n")
|
||||||
+ (f", created >= {CREATED_AFTER}" if CREATED_AFTER else "") + ")")
|
|
||||||
|
|
||||||
for i, row in enumerate(rows, 1):
|
# ---- Process each pozadavek
|
||||||
|
for idx, row in enumerate(requests_to_process, 1):
|
||||||
req_id = row["id"]
|
req_id = row["id"]
|
||||||
prijmeni = row.get("pacient_prijmeni") or "Neznamy"
|
print(f"[{idx}/{len(requests_to_process)}] Processing {req_id} …")
|
||||||
jmeno = row.get("pacient_jmeno") or ""
|
|
||||||
print(f"\n[{i}/{len(rows)}] 💬 {prijmeni}, {jmeno} ({req_id})")
|
|
||||||
|
|
||||||
messages = fetch_messages(headers, 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:
|
with conn.cursor() as cur:
|
||||||
for msg in messages:
|
for msg in messages:
|
||||||
insert_message(cur, req_id, msg)
|
insert_message(cur, req_id, msg)
|
||||||
# also pull any message attachments into downloads table
|
insert_download(cur, req_id, msg, existing_ids)
|
||||||
insert_download_from_message(cur, req_id, msg, existing_ids)
|
|
||||||
inserted += 1
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
# mark processed
|
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,))
|
cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
print(f" ✅ {inserted} messages processed for {prijmeni}, {jmeno}")
|
print(f" ✅ {len(messages)} messages saved\n")
|
||||||
time.sleep(0.3) # polite API delay
|
time.sleep(0.25)
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
print("\n✅ Done! All new conversations processed and pozadavky updated.")
|
print("🎉 Done!")
|
||||||
|
|
||||||
|
|
||||||
# ==============================
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
226
Testy/13 testy report pacienti.py
Normal file
226
Testy/13 testy report pacienti.py
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import pymysql
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import PatternFill, Font
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
timestamp = datetime.now().strftime("%Y-%m-%d %H-%M-%S")
|
||||||
|
OUTPUT_PATH = fr"U:\Dropbox\!!!Days\Downloads Z230\{timestamp} medevio_patients_report.xlsx"
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# CONFIGURATION
|
||||||
|
# ============================
|
||||||
|
DB_CONFIG = {
|
||||||
|
"host": "192.168.1.76",
|
||||||
|
"port": 3307,
|
||||||
|
"user": "root",
|
||||||
|
"password": "Vlado9674+",
|
||||||
|
"database": "medevio",
|
||||||
|
"charset": "utf8mb4",
|
||||||
|
"cursorclass": pymysql.cursors.DictCursor,
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# FUNCTIONS
|
||||||
|
# ============================
|
||||||
|
|
||||||
|
from openpyxl.styles import Border, Side
|
||||||
|
|
||||||
|
thin_border = Border(
|
||||||
|
left=Side(style='thin'),
|
||||||
|
right=Side(style='thin'),
|
||||||
|
top=Side(style='thin'),
|
||||||
|
bottom=Side(style='thin')
|
||||||
|
)
|
||||||
|
|
||||||
|
def apply_thin_borders(ws):
|
||||||
|
"""Apply thin borders to all cells in the worksheet."""
|
||||||
|
for row in ws.iter_rows():
|
||||||
|
for cell in row:
|
||||||
|
cell.border = thin_border
|
||||||
|
|
||||||
|
def autofit_columns(ws):
|
||||||
|
"""Auto-adjust column widths based on longest cell content."""
|
||||||
|
for col in ws.columns:
|
||||||
|
max_length = 0
|
||||||
|
col_letter = get_column_letter(col[0].column)
|
||||||
|
for cell in col:
|
||||||
|
try:
|
||||||
|
if cell.value:
|
||||||
|
max_length = max(max_length, len(str(cell.value)))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
ws.column_dimensions[col_letter].width = max_length + 2
|
||||||
|
|
||||||
|
def apply_header_style(ws):
|
||||||
|
"""Make header BRIGHT YELLOW and bold."""
|
||||||
|
fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
|
||||||
|
font = Font(bold=True)
|
||||||
|
|
||||||
|
for cell in ws[1]:
|
||||||
|
cell.fill = fill
|
||||||
|
cell.font = font
|
||||||
|
|
||||||
|
def create_compact_row(row):
|
||||||
|
"""Produce compact record with merged pojistovna, with user_relationship after prijmeni."""
|
||||||
|
|
||||||
|
# insurance merged
|
||||||
|
code = row.get("pojistovna_code") or ""
|
||||||
|
naz = row.get("pojistovna_nazev") or ""
|
||||||
|
|
||||||
|
if code and naz:
|
||||||
|
poj = f"{code} ({naz})"
|
||||||
|
elif code:
|
||||||
|
poj = code
|
||||||
|
elif naz:
|
||||||
|
poj = naz
|
||||||
|
else:
|
||||||
|
poj = ""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": row["id"],
|
||||||
|
"jmeno": row["jmeno"],
|
||||||
|
"prijmeni": row["prijmeni"],
|
||||||
|
|
||||||
|
# 🔹 inserted here
|
||||||
|
"user_relationship": row.get("user_relationship"),
|
||||||
|
|
||||||
|
"rodne_cislo": row["rodne_cislo"],
|
||||||
|
"dob": row["dob"],
|
||||||
|
"telefon": row["telefon"],
|
||||||
|
"email": row["email"],
|
||||||
|
"pojistovna": poj,
|
||||||
|
"status": row["status"],
|
||||||
|
"has_mobile_app": row["has_mobile_app"],
|
||||||
|
"registration_time": row["registration_time"],
|
||||||
|
"last_update": row["last_update"],
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_pozadavky_rows(rows):
|
||||||
|
"""Convert raw pozadavky SQL rows into rows for the Excel sheet."""
|
||||||
|
output = []
|
||||||
|
for r in rows:
|
||||||
|
output.append({
|
||||||
|
# 🔹 First the ID
|
||||||
|
"id": r["id"],
|
||||||
|
|
||||||
|
# 🔹 Your 3 patient columns immediately after ID
|
||||||
|
"pacient_jmeno": r["pacient_jmeno"],
|
||||||
|
"pacient_prijmeni": r["pacient_prijmeni"],
|
||||||
|
"pacient_rodnecislo": r["pacient_rodnecislo"],
|
||||||
|
|
||||||
|
# 🔹 Then all other fields in any order you prefer
|
||||||
|
"displayTitle": r["displayTitle"],
|
||||||
|
"createdAt": r["createdAt"],
|
||||||
|
"updatedAt": r["updatedAt"],
|
||||||
|
"doneAt": r["doneAt"],
|
||||||
|
"removedAt": r["removedAt"],
|
||||||
|
"attachmentsProcessed": r["attachmentsProcessed"],
|
||||||
|
"messagesProcessed": r["messagesProcessed"],
|
||||||
|
"communicationprocessed": r["communicationprocessed"],
|
||||||
|
"questionnaireprocessed": r["questionnaireprocessed"],
|
||||||
|
"lastSync": r["lastSync"],
|
||||||
|
})
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
# ============================
|
||||||
|
# MAIN
|
||||||
|
# ============================
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("📥 Connecting to MySQL...")
|
||||||
|
conn = pymysql.connect(**DB_CONFIG)
|
||||||
|
|
||||||
|
with conn:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT * FROM medevio_pacienti ORDER BY prijmeni, jmeno")
|
||||||
|
patients = cur.fetchall()
|
||||||
|
|
||||||
|
print(f"📊 Loaded {len(patients)} patients.")
|
||||||
|
|
||||||
|
# Load pozadavky
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT * FROM pozadavky ORDER BY createdAt DESC")
|
||||||
|
pozadavky_rows = cur.fetchall()
|
||||||
|
|
||||||
|
print(f"📄 Loaded {len(pozadavky_rows)} pozadavky.")
|
||||||
|
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
|
||||||
|
# ---------------------------------
|
||||||
|
# 1) FULL SHEET
|
||||||
|
# ---------------------------------
|
||||||
|
ws_full = wb.active
|
||||||
|
ws_full.title = "Patients FULL"
|
||||||
|
|
||||||
|
if patients:
|
||||||
|
headers = list(patients[0].keys())
|
||||||
|
ws_full.append(headers)
|
||||||
|
|
||||||
|
for row in patients:
|
||||||
|
ws_full.append([row.get(h) for h in headers])
|
||||||
|
|
||||||
|
apply_header_style(ws_full)
|
||||||
|
ws_full.freeze_panes = "A2"
|
||||||
|
ws_full.auto_filter.ref = ws_full.dimensions
|
||||||
|
autofit_columns(ws_full)
|
||||||
|
apply_thin_borders(ws_full)
|
||||||
|
|
||||||
|
# ---------------------------------
|
||||||
|
# 2) COMPACT SHEET
|
||||||
|
# ---------------------------------
|
||||||
|
ws_compact = wb.create_sheet("Patients COMPACT")
|
||||||
|
|
||||||
|
compact_rows = [create_compact_row(r) for r in patients]
|
||||||
|
compact_headers = list(compact_rows[0].keys())
|
||||||
|
|
||||||
|
ws_compact.append(compact_headers)
|
||||||
|
for row in compact_rows:
|
||||||
|
ws_compact.append([row.get(h) for h in compact_headers])
|
||||||
|
|
||||||
|
apply_header_style(ws_compact)
|
||||||
|
ws_compact.freeze_panes = "A2"
|
||||||
|
ws_compact.auto_filter.ref = ws_compact.dimensions
|
||||||
|
autofit_columns(ws_compact)
|
||||||
|
|
||||||
|
# >>> ADD THIS <<<
|
||||||
|
ur_col_index = compact_headers.index("user_relationship") + 1
|
||||||
|
col_letter = get_column_letter(ur_col_index)
|
||||||
|
ws_compact.column_dimensions[col_letter].width = 7.14
|
||||||
|
apply_thin_borders(ws_compact)
|
||||||
|
|
||||||
|
# ---------------------------------
|
||||||
|
# 3) POZADAVKY SHEET
|
||||||
|
# ---------------------------------
|
||||||
|
ws_p = wb.create_sheet("Pozadavky")
|
||||||
|
|
||||||
|
poz_list = create_pozadavky_rows(pozadavky_rows)
|
||||||
|
headers_p = list(poz_list[0].keys()) if poz_list else []
|
||||||
|
|
||||||
|
if headers_p:
|
||||||
|
ws_p.append(headers_p)
|
||||||
|
for row in poz_list:
|
||||||
|
ws_p.append([row.get(h) for h in headers_p])
|
||||||
|
|
||||||
|
apply_header_style(ws_p)
|
||||||
|
ws_p.freeze_panes = "A2"
|
||||||
|
ws_p.auto_filter.ref = ws_p.dimensions
|
||||||
|
autofit_columns(ws_p)
|
||||||
|
apply_thin_borders(ws_p)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------
|
||||||
|
# SAVE
|
||||||
|
# ---------------------------------
|
||||||
|
|
||||||
|
wb.save(OUTPUT_PATH)
|
||||||
|
print(f"✅ Excel report saved to:\n{OUTPUT_PATH}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
227
Testy/14 Testy updateat.py
Normal file
227
Testy/14 Testy updateat.py
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
import json
|
||||||
|
|
||||||
|
TOKEN_PATH = Path("token.txt")
|
||||||
|
CLINIC_SLUG = "mudr-buzalkova"
|
||||||
|
BATCH_SIZE = 100
|
||||||
|
|
||||||
|
# přesně tvůj původní dotaz, beze změn
|
||||||
|
# GRAPHQL_QUERY = r"""
|
||||||
|
# query ClinicRequestGrid_ListPatientRequestsForClinic2(
|
||||||
|
# $clinicSlug: String!,
|
||||||
|
# $queueId: String,
|
||||||
|
# $queueAssignment: QueueAssignmentFilter!,
|
||||||
|
# $pageInfo: PageInfo!,
|
||||||
|
# $locale: Locale!,
|
||||||
|
# $state: PatientRequestState
|
||||||
|
# ) {
|
||||||
|
# requestsResponse: listPatientRequestsForClinic2(
|
||||||
|
# clinicSlug: $clinicSlug,
|
||||||
|
# queueId: $queueId,
|
||||||
|
# queueAssignment: $queueAssignment,
|
||||||
|
# pageInfo: $pageInfo,
|
||||||
|
# state: $state
|
||||||
|
# ) {
|
||||||
|
# count
|
||||||
|
# patientRequests {
|
||||||
|
# id
|
||||||
|
# displayTitle(locale: $locale)
|
||||||
|
# createdAt
|
||||||
|
# updatedAt
|
||||||
|
# doneAt
|
||||||
|
# removedAt
|
||||||
|
# extendedPatient {
|
||||||
|
# name
|
||||||
|
# surname
|
||||||
|
# identificationNumber
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# """
|
||||||
|
GRAPHQL_QUERY = r"""
|
||||||
|
query ClinicRequestGrid_ListPatientRequestsForClinic2(
|
||||||
|
$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)
|
||||||
|
|
||||||
|
### TIME FIELDS ADDED
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
doneAt
|
||||||
|
removedAt
|
||||||
|
|
||||||
|
extendedPatient {
|
||||||
|
id
|
||||||
|
identificationNumber
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
kind
|
||||||
|
key
|
||||||
|
type
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
}
|
||||||
|
owner {
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
}
|
||||||
|
dob
|
||||||
|
premiumPlanPatient {
|
||||||
|
id
|
||||||
|
premiumPlan {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
status2
|
||||||
|
tags(onlyImportant: true) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
isUnknownPatient
|
||||||
|
}
|
||||||
|
|
||||||
|
invoice {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
amount
|
||||||
|
currency
|
||||||
|
dueAmount
|
||||||
|
isOverdue
|
||||||
|
refundedAmount
|
||||||
|
settledAmount
|
||||||
|
}
|
||||||
|
|
||||||
|
lastMessage {
|
||||||
|
createdAt
|
||||||
|
id
|
||||||
|
readAt
|
||||||
|
sender {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
clinicId
|
||||||
|
}
|
||||||
|
text
|
||||||
|
}
|
||||||
|
|
||||||
|
priority
|
||||||
|
|
||||||
|
queue {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
clinicPatientRequestQueueUsers {
|
||||||
|
accountable {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reservations {
|
||||||
|
calendar {
|
||||||
|
id
|
||||||
|
internalName
|
||||||
|
name
|
||||||
|
}
|
||||||
|
id
|
||||||
|
canceledAt
|
||||||
|
done
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
tags(onlyImportant: true) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
userECRF(locale: $locale) {
|
||||||
|
id
|
||||||
|
sid
|
||||||
|
icon {
|
||||||
|
color
|
||||||
|
id
|
||||||
|
urlSvg
|
||||||
|
}
|
||||||
|
ecrfSet {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
priceWhenCreated
|
||||||
|
currencyWhenCreated
|
||||||
|
createdByDoctor
|
||||||
|
eventType
|
||||||
|
clinicNotes {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
clinicMedicalRecord
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def read_token(path: Path) -> str:
|
||||||
|
tok = path.read_text(encoding="utf-8").strip()
|
||||||
|
if tok.startswith("Bearer "):
|
||||||
|
tok = tok.split(" ", 1)[1]
|
||||||
|
return tok
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
token = read_token(TOKEN_PATH)
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
"clinicSlug": CLINIC_SLUG,
|
||||||
|
"queueId": None,
|
||||||
|
"queueAssignment": "ANY",
|
||||||
|
"pageInfo": {"first": BATCH_SIZE, "offset": 0},
|
||||||
|
"locale": "cs",
|
||||||
|
"state": "ACTIVE",
|
||||||
|
}
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"operationName": "ClinicRequestGrid_ListPatientRequestsForClinic2",
|
||||||
|
"query": GRAPHQL_QUERY,
|
||||||
|
"variables": variables,
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\n===== ČISTÁ ODPOVĚĎ SERVERU =====\n")
|
||||||
|
|
||||||
|
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30)
|
||||||
|
|
||||||
|
print(f"HTTP {r.status_code}\n")
|
||||||
|
print(r.text) # <-- TISK NEUPRAVENÉHO JSONU
|
||||||
|
|
||||||
|
print("\n===== KONEC ČISTÉ ODPOVĚDI =====\n")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
136
Testy/15 test.py
Normal file
136
Testy/15 test.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
TOKEN_PATH = Path("token.txt")
|
||||||
|
CLINIC_SLUG = "mudr-buzalkova"
|
||||||
|
BATCH_SIZE = 100
|
||||||
|
TARGET_ID = "cbf6000d-a6ca-4059-88b7-dfdc27220762" # ← sem tvoje ID
|
||||||
|
|
||||||
|
# ⭐ Updated GraphQL with lastMessage included
|
||||||
|
GRAPHQL_QUERY = r"""
|
||||||
|
query ClinicRequestGrid_ListPatientRequestsForClinic2(
|
||||||
|
$clinicSlug: String!,
|
||||||
|
$queueId: String,
|
||||||
|
$queueAssignment: QueueAssignmentFilter!,
|
||||||
|
$pageInfo: PageInfo!,
|
||||||
|
$locale: Locale!,
|
||||||
|
$state: PatientRequestState
|
||||||
|
) {
|
||||||
|
requestsResponse: listPatientRequestsForClinic2(
|
||||||
|
clinicSlug: $clinicSlug,
|
||||||
|
queueId: $queueId,
|
||||||
|
queueAssignment: $queueAssignment,
|
||||||
|
pageInfo: $pageInfo,
|
||||||
|
state: $state
|
||||||
|
) {
|
||||||
|
count
|
||||||
|
patientRequests {
|
||||||
|
id
|
||||||
|
displayTitle(locale: $locale)
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
doneAt
|
||||||
|
removedAt
|
||||||
|
lastMessage {
|
||||||
|
id
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
extendedPatient {
|
||||||
|
name
|
||||||
|
surname
|
||||||
|
identificationNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def read_token(path: Path) -> str:
|
||||||
|
tok = path.read_text(encoding="utf-8").strip()
|
||||||
|
if tok.startswith("Bearer "):
|
||||||
|
tok = tok.split(" ", 1)[1]
|
||||||
|
return tok
|
||||||
|
|
||||||
|
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": "ClinicRequestGrid_ListPatientRequestsForClinic2",
|
||||||
|
"query": GRAPHQL_QUERY,
|
||||||
|
"variables": variables,
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30)
|
||||||
|
|
||||||
|
if r.status_code != 200:
|
||||||
|
print("HTTP status:", r.status_code)
|
||||||
|
print(r.text)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
|
data = r.json().get("data", {}).get("requestsResponse", {})
|
||||||
|
return data.get("patientRequests", []), data.get("count", 0)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
token = read_token(TOKEN_PATH)
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
print(f"=== Hledám updatedAt a lastMessage pro pozadavek {TARGET_ID} ===\n")
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
total_count = None
|
||||||
|
found = False
|
||||||
|
|
||||||
|
while True:
|
||||||
|
batch, count = fetch_active(headers, offset)
|
||||||
|
|
||||||
|
if total_count is None:
|
||||||
|
total_count = count
|
||||||
|
|
||||||
|
if not batch:
|
||||||
|
break
|
||||||
|
|
||||||
|
for r in batch:
|
||||||
|
if r["id"] == TARGET_ID:
|
||||||
|
|
||||||
|
print("Nalezeno!\n")
|
||||||
|
print(f"id: {r['id']}")
|
||||||
|
print(f"updatedAt: {r['updatedAt']}")
|
||||||
|
|
||||||
|
lm = r.get("lastMessage") or {}
|
||||||
|
print(f"lastMessage.createdAt: {lm.get('createdAt')}")
|
||||||
|
print(f"lastMessage.updatedAt: {lm.get('updatedAt')}")
|
||||||
|
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if found:
|
||||||
|
break
|
||||||
|
|
||||||
|
if offset + BATCH_SIZE >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
offset += BATCH_SIZE
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
print("❌ Požadavek nebyl nalezen mezi ACTIVE.")
|
||||||
|
|
||||||
|
print("\n=== HOTOVO ===")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
228
Testy/16 test.py
Normal file
228
Testy/16 test.py
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#!/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": "192.168.1.76",
|
||||||
|
"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()
|
||||||
259
Testy/17 test.py
Normal file
259
Testy/17 test.py
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
Stáhne konverzaci pro požadavky, kde:
|
||||||
|
messagesProcessed IS NULL OR messagesProcessed < updatedAt.
|
||||||
|
|
||||||
|
Vloží do medevio_conversation a přílohy do medevio_downloads.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# ⏱ DATETIME PARSER
|
||||||
|
# ==============================
|
||||||
|
def parse_dt(s):
|
||||||
|
if not s:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return datetime.fromisoformat(s.replace("Z", "+00:00"))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
return datetime.strptime(s[:19], "%Y-%m-%dT%H:%M:%S")
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🔐 TOKEN
|
||||||
|
# ==============================
|
||||||
|
def read_token(path: Path) -> str:
|
||||||
|
tok = path.read_text(encoding="utf-8").strip()
|
||||||
|
return tok.replace("Bearer ", "")
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 📡 FETCH MESSAGES
|
||||||
|
# ==============================
|
||||||
|
def fetch_messages(headers, request_id):
|
||||||
|
payload = {
|
||||||
|
"operationName": "UseMessages_ListMessages",
|
||||||
|
"query": GRAPHQL_QUERY_MESSAGES,
|
||||||
|
"variables": {"requestId": request_id, "updatedSince": None},
|
||||||
|
}
|
||||||
|
|
||||||
|
r = requests.post("https://api.medevio.cz/graphql", json=payload, headers=headers, timeout=30)
|
||||||
|
if r.status_code != 200:
|
||||||
|
print("❌ HTTP", r.status_code, "for request", request_id)
|
||||||
|
return []
|
||||||
|
return r.json().get("data", {}).get("messages", []) or []
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 💾 SAVE MESSAGE
|
||||||
|
# ==============================
|
||||||
|
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
|
||||||
|
) or None
|
||||||
|
|
||||||
|
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)
|
||||||
|
"""
|
||||||
|
|
||||||
|
mr = msg.get("medicalRecord") or {}
|
||||||
|
|
||||||
|
cur.execute(sql, (
|
||||||
|
msg.get("id"),
|
||||||
|
req_id,
|
||||||
|
sender_name,
|
||||||
|
sender.get("id"),
|
||||||
|
sender.get("clinicId"),
|
||||||
|
msg.get("text"),
|
||||||
|
parse_dt(msg.get("createdAt")),
|
||||||
|
parse_dt(msg.get("readAt")),
|
||||||
|
parse_dt(msg.get("updatedAt")),
|
||||||
|
mr.get("downloadUrl") or mr.get("url"),
|
||||||
|
mr.get("description"),
|
||||||
|
mr.get("contentType")
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 💾 DOWNLOAD MESSAGE ATTACHMENT
|
||||||
|
# ==============================
|
||||||
|
def insert_download(cur, req_id, msg, existing_ids):
|
||||||
|
|
||||||
|
mr = msg.get("medicalRecord") or {}
|
||||||
|
attachment_id = mr.get("id")
|
||||||
|
if not attachment_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
if attachment_id in existing_ids:
|
||||||
|
return # skip duplicates
|
||||||
|
|
||||||
|
url = mr.get("downloadUrl") or mr.get("url")
|
||||||
|
if not url:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
r = requests.get(url, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
data = r.content
|
||||||
|
except Exception as e:
|
||||||
|
print("⚠️ Failed to download:", e)
|
||||||
|
return
|
||||||
|
|
||||||
|
filename = url.split("/")[-1].split("?")[0]
|
||||||
|
|
||||||
|
cur.execute("""
|
||||||
|
INSERT INTO medevio_downloads (
|
||||||
|
request_id, attachment_id, attachment_type,
|
||||||
|
filename, content_type, file_size, created_at, file_content
|
||||||
|
) VALUES (%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,
|
||||||
|
mr.get("contentType"),
|
||||||
|
len(data),
|
||||||
|
parse_dt(msg.get("createdAt")),
|
||||||
|
data
|
||||||
|
))
|
||||||
|
|
||||||
|
existing_ids.add(attachment_id)
|
||||||
|
|
||||||
|
|
||||||
|
# ==============================
|
||||||
|
# 🧠 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 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"📦 Already downloaded attachments: {len(existing_ids)}\n")
|
||||||
|
|
||||||
|
# ---- Select pozadavky needing message sync
|
||||||
|
sql = """
|
||||||
|
SELECT id
|
||||||
|
FROM pozadavky
|
||||||
|
WHERE messagesProcessed IS NULL
|
||||||
|
OR messagesProcessed < updatedAt
|
||||||
|
"""
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute(sql)
|
||||||
|
requests_to_process = cur.fetchall()
|
||||||
|
|
||||||
|
print(f"📋 Found {len(requests_to_process)} pozadavků requiring message sync.\n")
|
||||||
|
|
||||||
|
# ---- Process each pozadavek
|
||||||
|
for idx, row in enumerate(requests_to_process, 1):
|
||||||
|
req_id = row["id"]
|
||||||
|
print(f"[{idx}/{len(requests_to_process)}] Processing {req_id} …")
|
||||||
|
|
||||||
|
messages = fetch_messages(headers, req_id)
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
for msg in messages:
|
||||||
|
insert_message(cur, req_id, msg)
|
||||||
|
insert_download(cur, req_id, msg, existing_ids)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("UPDATE pozadavky SET messagesProcessed = NOW() WHERE id = %s", (req_id,))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
print(f" ✅ {len(messages)} messages saved\n")
|
||||||
|
time.sleep(0.25)
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
print("🎉 Done!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user