From a0bb1011a0df0fac4b7773e0d53e7ffceed8790b Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Sat, 17 Jan 2026 20:35:55 +0100 Subject: [PATCH] notebookVB --- 10 Tests/2026-01-17 02.py | 140 ++++++++++++++++ 10 Tests/2026-01-17 03.py | 152 ++++++++++++++++++ .../kontrola_pojisteni_2026-01-16_17-34-51.pdf | Bin 0 -> 11841 bytes .../kontrola_pojisteni_2026-01-16_17-56-44.pdf | Bin 0 -> 12291 bytes .../kontrola_pojisteni_2026-01-17_13-51-46.pdf | Bin 0 -> 12578 bytes knihovny/EmailMessagingGraph.py | 91 +++++++++++ 6 files changed, 383 insertions(+) create mode 100644 10 Tests/2026-01-17 02.py create mode 100644 10 Tests/2026-01-17 03.py create mode 100644 20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-34-51.pdf create mode 100644 20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-16_17-56-44.pdf create mode 100644 20 Ověření proti Medicus/Output/kontrola_pojisteni_2026-01-17_13-51-46.pdf create mode 100644 knihovny/EmailMessagingGraph.py 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 0000000000000000000000000000000000000000..19337cec64c6c8a28c0a7c3894974fd0ced2fb3c GIT binary patch literal 11841 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
@0x_y%<7ZIiF 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 H7|j0xg>R+F literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9e9f2188488bce1b0f65cd27ef19b71fd5fef7f0 GIT binary patch literal 12578 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 literal 0 HcmV?d00001 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}" + )