diff --git a/10 Tests/2026-01-17 02.py b/10 Tests/2026-01-17 02.py new file mode 100644 index 0000000..9959425 --- /dev/null +++ b/10 Tests/2026-01-17 02.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +REPORT – ZMĚNA STAVU POJIŠTĚNÍ (POUZE REGISTROVANÍ PACIENTI) + +Výstup: +pacient XY, současný stav pojištění je "X", +ke změně došlo dne YYYY-MM-DD (ze "1" na "X") + +Zdroj: +- změna stavu: MySQL (vzp_stav_pojisteni) +- filtr pacientů: Medicus (aktivně registrovaní) +""" + +import sys +from pathlib import Path +import pymysql + +# ========================================== +# PROJECT ROOT +# ========================================== +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from knihovny.medicus_db import MedicusDB + +# ========================================== +# MYSQL CONFIG +# ========================================== +MYSQL_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# ========================================== +# MEDICUS CONFIG +# ========================================== +HOST = "192.168.1.4" +DB_PATH = r"z:\Medicus 3\data\MEDICUS.FDB" + +# ========================================== +# SQL – ZMĚNY 1 → X (BEZ OHLEDU NA REGISTRACI) +# ========================================== +SQL = """ +SELECT + cur.rc, + cur.prijmeni, + cur.jmeno, + cur.stav AS current_stav, + chg.k_datu AS change_date, + chg.stav AS change_stav +FROM + ( + SELECT p1.* + FROM vzp_stav_pojisteni p1 + JOIN ( + SELECT rc, MAX(k_datu) AS max_date + FROM vzp_stav_pojisteni + GROUP BY rc + ) x + ON x.rc = p1.rc + AND x.max_date = p1.k_datu + WHERE p1.stav <> '1' + ) cur +JOIN + ( + SELECT rc, MAX(k_datu) AS last_ok + FROM vzp_stav_pojisteni + WHERE stav = '1' + GROUP BY rc + ) ok + ON ok.rc = cur.rc +JOIN + vzp_stav_pojisteni chg + ON chg.id = ( + SELECT MIN(id) + FROM vzp_stav_pojisteni z + WHERE z.rc = cur.rc + AND z.k_datu = ( + SELECT MIN(k_datu) + FROM vzp_stav_pojisteni zz + WHERE zz.rc = cur.rc + AND zz.k_datu > ok.last_ok + ) + ) +ORDER BY cur.prijmeni, cur.jmeno; +""" + +# ========================================== +# MAIN +# ========================================== +def main(): + + # 1️⃣ Načíst aktivně registrované pacienty z Medicusu + db = MedicusDB(HOST, DB_PATH) + registered = db.get_active_registered_patients() + db.close() + + # vytvoříme množinu RC (rychlé filtrování) + registered_rc = {rc for rc, *_ in registered} + + print(f"Registrovaných pacientů: {len(registered_rc)}") + + # 2️⃣ Načíst změny z MySQL + mysql = pymysql.connect(**MYSQL_CONFIG) + + with mysql.cursor() as cur: + cur.execute(SQL) + rows = cur.fetchall() + + mysql.close() + + # 3️⃣ Průnik množin + filtered = [r for r in rows if r["rc"] in registered_rc] + + print(f"Změny pojištění (registrovaní): {len(filtered)}\n") + + # 4️⃣ Výpis reportu + print("REPORT – ZMĚNA STAVU POJIŠTĚNÍ (REGISTROVANÍ)\n") + + for r in filtered: + pacient = f"{r['prijmeni']} {r['jmeno']}".strip() + print( + f"pacient {pacient}, " + f"současný stav pojištění je \"{r['current_stav']}\", " + f"ke změně došlo dne {r['change_date']} " + f"(ze \"1\" na \"{r['change_stav']}\")" + ) + + print("\nKONEC REPORTU.") + +# ========================================== +if __name__ == "__main__": + main() diff --git a/10 Tests/2026-01-17 03.py b/10 Tests/2026-01-17 03.py new file mode 100644 index 0000000..950b9f1 --- /dev/null +++ b/10 Tests/2026-01-17 03.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +REPORT – ZMĚNA STAVU POJIŠTĚNÍ (POUZE REGISTROVANÍ PACIENTI) + +- změna definována jako přechod 1 → X +- zdroj změn: MySQL (vzp_stav_pojisteni) +- filtr pacientů: Medicus (aktivně registrovaní) +- email se odešle pouze pokud existuje alespoň 1 změna +""" + +import sys +from pathlib import Path +import pymysql + +# ========================================== +# PROJECT ROOT (import fix) +# ========================================== +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from knihovny.medicus_db import MedicusDB +from knihovny.EmailMessagingGraph import send_mail + +# ========================================== +# MYSQL CONFIG +# ========================================== +MYSQL_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# ========================================== +# MEDICUS CONFIG +# ========================================== +HOST = "192.168.1.4" +DB_PATH = r"z:\Medicus 3\data\MEDICUS.FDB" + +# ========================================== +# EMAIL CONFIG +# ========================================== +EMAIL_TO = "vladimir.buzalka@buzalka.cz" # ← sem případně uprav adresu +EMAIL_SUBJECT = "VZP – změna pojištění u registrovaných pacientů" + +# ========================================== +# SQL – ZMĚNA 1 → X (HISTORIE) +# ========================================== +SQL = """ +SELECT + cur.rc, + cur.prijmeni, + cur.jmeno, + cur.stav AS current_stav, + chg.k_datu AS change_date, + chg.stav AS change_stav +FROM + ( + SELECT p1.* + FROM vzp_stav_pojisteni p1 + JOIN ( + SELECT rc, MAX(k_datu) AS max_date + FROM vzp_stav_pojisteni + GROUP BY rc + ) x + ON x.rc = p1.rc + AND x.max_date = p1.k_datu + WHERE p1.stav <> '1' + ) cur +JOIN + ( + SELECT rc, MAX(k_datu) AS last_ok + FROM vzp_stav_pojisteni + WHERE stav = '1' + GROUP BY rc + ) ok + ON ok.rc = cur.rc +JOIN + vzp_stav_pojisteni chg + ON chg.id = ( + SELECT MIN(id) + FROM vzp_stav_pojisteni z + WHERE z.rc = cur.rc + AND z.k_datu = ( + SELECT MIN(k_datu) + FROM vzp_stav_pojisteni zz + WHERE zz.rc = cur.rc + AND zz.k_datu > ok.last_ok + ) + ) +ORDER BY cur.prijmeni, cur.jmeno; +""" + +# ========================================== +# MAIN +# ========================================== +def main(): + + # 1️⃣ Registrovaní pacienti (Medicus) + db = MedicusDB(HOST, DB_PATH) + registered = db.get_active_registered_patients() + db.close() + + registered_rc = {rc for rc, *_ in registered} + + # 2️⃣ Načíst změny z MySQL + mysql = pymysql.connect(**MYSQL_CONFIG) + with mysql.cursor() as cur: + cur.execute(SQL) + rows = cur.fetchall() + mysql.close() + + # 3️⃣ Průnik množin + filtered = [r for r in rows if r["rc"] in registered_rc] + + # 4️⃣ Pokud není co hlásit → konec + if not filtered: + print("Žádná změna pojištění u registrovaných pacientů – e-mail se neposílá.") + return + + # 5️⃣ Sestavení reportu (TEXT) + lines = [] + lines.append("Změna stavu pojištění u registrovaných pacientů:\n") + + for r in filtered: + pacient = f"{r['prijmeni']} {r['jmeno']}".strip() + lines.append( + f"- pacient {pacient}, " + f"současný stav \"{r['current_stav']}\", " + f"změna dne {r['change_date']} (ze \"1\" na \"{r['change_stav']}\")" + ) + + body = "\n".join(lines) + + # 6️⃣ Odeslání e-mailu + send_mail( + to=EMAIL_TO, + subject=f"{EMAIL_SUBJECT} ({len(filtered)})", + body=body, + html=False, + ) + + print(f"Odeslán e-mail: {len(filtered)} pacientů") + +# ========================================== +if __name__ == "__main__": + main() diff --git a/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-34-51.pdf b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-34-51.pdf new file mode 100644 index 0000000..19337ce Binary files /dev/null and b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-34-51.pdf differ diff --git a/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-56-44.pdf b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-56-44.pdf new file mode 100644 index 0000000..b37ad2f Binary files /dev/null and b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-56-44.pdf differ diff --git a/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-17_13-51-46.pdf b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-17_13-51-46.pdf new file mode 100644 index 0000000..9e9f218 Binary files /dev/null and b/20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-17_13-51-46.pdf differ diff --git a/knihovny/EmailMessagingGraph.py b/knihovny/EmailMessagingGraph.py new file mode 100644 index 0000000..6e5ea25 --- /dev/null +++ b/knihovny/EmailMessagingGraph.py @@ -0,0 +1,91 @@ +""" +EmailMessagingGraph.py +---------------------- +Private Microsoft Graph mail sender +Application permissions, shared mailbox +""" + +import msal +import requests +from functools import lru_cache +from typing import Union, List + + +# ========================= +# PRIVATE CONFIG (ONLY YOU) +# ========================= +TENANT_ID = "7d269944-37a4-43a1-8140-c7517dc426e9" +CLIENT_ID = "4b222bfd-78c9-4239-a53f-43006b3ed07f" +CLIENT_SECRET = "Txg8Q~MjhocuopxsJyJBhPmDfMxZ2r5WpTFj1dfk" +SENDER = "reports@buzalka.cz" + + +AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}" +SCOPE = ["https://graph.microsoft.com/.default"] + + +@lru_cache(maxsize=1) +def _get_token() -> str: + app = msal.ConfidentialClientApplication( + CLIENT_ID, + authority=AUTHORITY, + client_credential=CLIENT_SECRET, + ) + + token = app.acquire_token_for_client(scopes=SCOPE) + + if "access_token" not in token: + raise RuntimeError(f"Graph auth failed: {token}") + + return token["access_token"] + + +def send_mail( + to: Union[str, List[str]], + subject: str, + body: str, + *, + html: bool = False, +): + """ + Send email via Microsoft Graph. + + :param to: email or list of emails + :param subject: subject + :param body: email body + :param html: True = HTML, False = plain text + """ + + if isinstance(to, str): + to = [to] + + payload = { + "message": { + "subject": subject, + "body": { + "contentType": "HTML" if html else "Text", + "content": body, + }, + "toRecipients": [ + {"emailAddress": {"address": addr}} for addr in to + ], + }, + "saveToSentItems": "true", + } + + headers = { + "Authorization": f"Bearer {_get_token()}", + "Content-Type": "application/json", + } + + r = requests.post( + f"https://graph.microsoft.com/v1.0/users/{SENDER}/sendMail", + headers=headers, + json=payload, + timeout=30, + ) + + if r.status_code != 202: + raise RuntimeError( + f"sendMail failed [{r.status_code}]: {r.text}" + )