notebookVB
This commit is contained in:
140
10 Tests/2026-01-17 02.py
Normal file
140
10 Tests/2026-01-17 02.py
Normal file
@@ -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()
|
||||||
152
10 Tests/2026-01-17 03.py
Normal file
152
10 Tests/2026-01-17 03.py
Normal file
@@ -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()
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
91
knihovny/EmailMessagingGraph.py
Normal file
91
knihovny/EmailMessagingGraph.py
Normal file
@@ -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}"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user