diff --git a/01 testik.py b/01 testik.py index 8c333e1..f018082 100644 --- a/01 testik.py +++ b/01 testik.py @@ -8,7 +8,7 @@ from pymysql.cursors import DictCursor from pprint import pprint # ========== CONFIG ========== -PFX_PATH = r"mbcert.pfx" +PFX_PATH = r"Certificates/MBcert.pfx" PFX_PASS = "Vlado7309208104++" VERIFY = True diff --git a/02 testík.py b/02 testík.py index 700b29a..b0bf855 100644 --- a/02 testík.py +++ b/02 testík.py @@ -21,7 +21,7 @@ import time, random,socket # ------------------- CONFIG ------------------- ENDPOINT = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/RegistracePojistencePZSB2B" # case-sensitive -PFX_PATH = r"mbcert.pfx" # <-- your .pfx path +PFX_PATH = r"Certificates/MBcert.pfx" # <-- your .pfx path PFX_PASS = "Vlado7309208104++" # <-- your export password VERIFY = True # or path to CA PEM, e.g. r"C:\certs\vzp_ca.pem" diff --git a/02.1 Test moje rodne cislo.py b/02.1 Test moje rodne cislo.py index 4d6269d..b5a92d2 100644 --- a/02.1 Test moje rodne cislo.py +++ b/02.1 Test moje rodne cislo.py @@ -21,7 +21,7 @@ import time, random,socket # ------------------- CONFIG ------------------- ENDPOINT = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/RegistracePojistencePZSB2B" # case-sensitive -PFX_PATH = r"mbcert.pfx" # <-- your .pfx path +PFX_PATH = r"Certificates/MBcert.pfx" # <-- your .pfx path PFX_PASS = "Vlado7309208104++" # <-- your export password VERIFY = True # or path to CA PEM, e.g. r"C:\certs\vzp_ca.pem" diff --git a/02.2 Testík.py b/02.2 Testík.py index 51ed961..c768fa4 100644 --- a/02.2 Testík.py +++ b/02.2 Testík.py @@ -22,7 +22,7 @@ import time, random,socket # ------------------- CONFIG ------------------- ENDPOINT = "https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/RegistracePojistencePZSB2B" # case-sensitive -PFX_PATH = r"mbcert.pfx" # <-- your .pfx path +PFX_PATH = r"Certificates/MBcert.pfx" # <-- your .pfx path PFX_PASS = "Vlado7309208104++" # <-- your export password VERIFY = True # or path to CA PEM, e.g. r"C:\certs\vzp_ca.pem" diff --git a/10 Tests/2026-01-16 1.py b/10 Tests/2026-01-16 1.py new file mode 100644 index 0000000..b897cc2 --- /dev/null +++ b/10 Tests/2026-01-16 1.py @@ -0,0 +1,34 @@ +import pymysql + +mysql = pymysql.connect( + host="192.168.1.76", + port=3307, + user="root", + password="Vlado9674+", + database="medevio", + charset="utf8mb4", + cursorclass=pymysql.cursors.DictCursor +) + +sql = """ +SELECT rc +FROM ( + SELECT rc, stav, + ROW_NUMBER() OVER (PARTITION BY rc ORDER BY k_datu DESC) AS rn + FROM vzp_stav_pojisteni +) t +WHERE rn = 1 + AND stav <> '1' +""" + +with mysql.cursor() as cur: + cur.execute(sql) + rows = cur.fetchall() + +# výsledná množina rodných čísel +rc_not_insured = {row["rc"] for row in rows} + +mysql.close() + +print(f"Nalezeno {len(rc_not_insured)} nepojištěných pacientů") +# print(rc_not_insured) # volitelně diff --git a/10 Tests/2026-01-16 2.py b/10 Tests/2026-01-16 2.py new file mode 100644 index 0000000..7d1102b --- /dev/null +++ b/10 Tests/2026-01-16 2.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Najde přesné datum změny stavu pojištění pomocí binary search. +Používá existující VZP B2B klient + ukládá výsledky do MySQL. + +Cíl: +- minimalizovat počet dotazů na VZP +- přesně určit první den, kdy stav != '1' +""" + +import sys +import time +import logging +from pathlib import Path +from datetime import date, timedelta +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.vzpb2b_client import VZPB2BClient + +# ========================================== +# LOGGING +# ========================================== +logging.basicConfig( + filename="insurance_binary_search.log", + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", + encoding="utf-8" +) + +console = logging.getLogger("console") +console.setLevel(logging.INFO) +handler = logging.StreamHandler() +handler.setFormatter(logging.Formatter("%(message)s")) +console.addHandler(handler) + +def log(msg): + logging.info(msg) + console.info(msg) + +# ========================================== +# MYSQL +# ========================================== +mysql = pymysql.connect( + host="192.168.1.76", + port=3307, + user="root", + password="Vlado9674+", + database="medevio", + charset="utf8mb4", + autocommit=True, + cursorclass=pymysql.cursors.DictCursor +) + +# ========================================== +# SAVE RESULT +# ========================================== +def save_insurance_status(mysql_conn, rc, prijmeni, jmeno, k_datu, result, xml): + sql = """ + INSERT INTO vzp_stav_pojisteni + (rc, prijmeni, jmeno, k_datu, + stav, kod_pojistovny, nazev_pojistovny, + pojisteni_kod, stav_vyrizeni, response_xml) + VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + """ + with mysql_conn.cursor() as cur: + cur.execute(sql, ( + rc, + prijmeni, + jmeno, + k_datu, + result["stav"], + result["kodPojistovny"], + result["nazevPojistovny"], + result["pojisteniKod"], + result["stavVyrizeni"], + xml + )) + +# ========================================== +# VZP CONFIG +# ========================================== +HOST = "192.168.1.4" +DB_PATH = r"z:\Medicus 3\data\MEDICUS.FDB" + +PFX_PATH = PROJECT_ROOT / "certificates" / "MBcert.pfx" +PFX_PASSWORD = "Vlado7309208104++" + +ENV = "prod" +ICZ = "00000000" +DIC = "00000000" + +# ========================================== +# INIT +# ========================================== +db = MedicusDB(HOST, DB_PATH) +vzp = VZPB2BClient( + ENV, + str(PFX_PATH), + PFX_PASSWORD, + icz=ICZ, + dic=DIC +) + +# ========================================== +# HELPER FUNCTIONS +# ========================================== +def vzp_query(rc, prijmeni, jmeno, d): + xml = vzp.stav_pojisteni(rc=rc, k_datu=d.isoformat()) + result = vzp.parse_stav_pojisteni(xml) + save_insurance_status(mysql, rc, prijmeni, jmeno, d, result, xml) + time.sleep(2) # VZP rate limit + return result["stav"] + +def binary_search_change(rc, prijmeni, jmeno, start_date, end_date): + """ + Najde PRVNÍ den, kdy stav != '1' + """ + low = start_date + high = end_date + found = None + + while low <= high: + mid = low + (high - low) // 2 + log(f" 🔍 {rc} probing {mid}") + + stav = vzp_query(rc, prijmeni, jmeno, mid) + + if stav == '1': + low = mid + timedelta(days=1) + else: + found = mid + high = mid - timedelta(days=1) + + return found + +# ========================================== +# LOAD CANDIDATES +# ========================================== +with mysql.cursor() as cur: + cur.execute(""" + SELECT + x.rc, + p.prijmeni, + p.jmeno, + x.last_ok +FROM ( + SELECT + rc, + MAX(CASE WHEN stav = '1' THEN k_datu END) AS last_ok, + MAX(k_datu) AS last_seen + FROM vzp_stav_pojisteni + GROUP BY rc +) x +JOIN vzp_stav_pojisteni p + ON p.rc = x.rc + AND p.k_datu = x.last_seen +WHERE + x.last_ok IS NOT NULL + AND x.last_seen = ( + SELECT MAX(k_datu) + FROM vzp_stav_pojisteni z + WHERE z.rc = x.rc AND z.stav <> '1' + ); + + """) + patients = cur.fetchall() + +log(f"Loaded {len(patients)} patients for binary search\n") + +# ========================================== +# MAIN LOOP +# ========================================== +today = date.today() + +for p in patients: + rc = p["rc"] + prijmeni = p["prijmeni"] + jmeno = p["jmeno"] + last_ok = p["last_ok"] + + log(f"\n📌 {prijmeni} {jmeno} ({rc}) – searching change") + + change_date = binary_search_change( + rc, + prijmeni, + jmeno, + last_ok, + today + ) + + if change_date: + log(f" ✅ změna nastala: {change_date}") + else: + log(" ⚠️ změna nenalezena") + +# ========================================== +# CLEANUP +# ========================================== +db.close() +mysql.close() + +log("\nDONE – binary search insurance check finished.") diff --git a/10 Tests/2026-01-16 3.py b/10 Tests/2026-01-16 3.py new file mode 100644 index 0000000..7f178e2 --- /dev/null +++ b/10 Tests/2026-01-16 3.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +REPORT – změna stavu pojištění + +Výstup: +pacient XY, současný (dnešní) stav pojištění je "X", +ke změně došlo dne Y, kdy stav byl "Z" + +Používá pouze tabulku vzp_stav_pojisteni +Kompatibilní s ONLY_FULL_GROUP_BY +""" + +import pymysql + +# ========================================== +# MYSQL CONFIG +# ========================================== +MYSQL_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# ========================================== +# SQL – REPORT +# ========================================== +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 + -- aktuální (poslední) stav pacienta + ( + 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 + -- první den změny po posledním stavu = '1' + ( + SELECT p2.rc, p2.k_datu, p2.stav + FROM vzp_stav_pojisteni p2 + JOIN ( + SELECT rc, MIN(k_datu) AS change_date + FROM vzp_stav_pojisteni + WHERE stav <> '1' + AND k_datu > ( + SELECT MAX(k_datu) + FROM vzp_stav_pojisteni t + WHERE t.rc = vzp_stav_pojisteni.rc + AND t.stav = '1' + ) + GROUP BY rc + ) y + ON y.rc = p2.rc + AND y.change_date = p2.k_datu + ) chg + ON chg.rc = cur.rc +ORDER BY cur.prijmeni, cur.jmeno; +""" + +# ========================================== +# MAIN +# ========================================== +def main(): + mysql = pymysql.connect(**MYSQL_CONFIG) + + with mysql.cursor() as cur: + cur.execute(SQL) + rows = cur.fetchall() + + mysql.close() + + print("\nREPORT – ZMĚNA STAVU POJIŠTĚNÍ\n") + + for r in rows: + jmeno = f"{r['prijmeni']} {r['jmeno']}".strip() + print( + f"pacient {jmeno}, " + f"současný (dnešní) stav pojištění je \"{r['current_stav']}\", " + f"ke změně došlo dne {r['change_date']}, " + f"kdy stav byl \"{r['change_stav']}\"" + ) + + print("\nKONEC REPORTU.") + +# ========================================== +if __name__ == "__main__": + main() diff --git a/10 Tests/2026-01-17 01.py b/10 Tests/2026-01-17 01.py new file mode 100644 index 0000000..853a270 --- /dev/null +++ b/10 Tests/2026-01-17 01.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +REPORT – ZMĚNA STAVU POJIŠTĚNÍ + +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") + +Používá pouze tabulku vzp_stav_pojisteni +""" + +import pymysql + +# ========================================== +# MYSQL CONFIG +# ========================================== +MYSQL_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "cursorclass": pymysql.cursors.DictCursor, +} + +# ========================================== +# SQL – KOREKTNÍ DEFINICE ZMĚNY (1 → X) +# ========================================== +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 + -- aktuální stav pacienta + ( + 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 + -- poslední den pojištění + ( + 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 + -- JEDEN konkrétní řádek změny (nejmenší ID) + 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(): + mysql = pymysql.connect(**MYSQL_CONFIG) + + with mysql.cursor() as cur: + cur.execute(SQL) + rows = cur.fetchall() + + mysql.close() + + print("\nREPORT – ZMĚNA STAVU POJIŠTĚNÍ\n") + + for r in rows: + 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/20260111 Jednorázové ověření b/10 Tests/20260111 Jednorázové ověření index 8c5cd2d..7ddbdd1 100644 --- a/10 Tests/20260111 Jednorázové ověření +++ b/10 Tests/20260111 Jednorázové ověření @@ -1,9 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import time -import logging -from vzpb2b_client import VZPB2BClient +from knihovny.vzpb2b_client import VZPB2BClient from datetime import date # ========================================== diff --git a/10 Tests/Doplnenijmenoaprijmeni.py b/10 Tests/Doplnenijmenoaprijmeni.py new file mode 100644 index 0000000..dd63252 --- /dev/null +++ b/10 Tests/Doplnenijmenoaprijmeni.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Doplní jméno a příjmení do vzp_stav_pojisteni +podle Medicusu tam, kde jsou NULL. +Bez volání VZP. +Bez změny jiných dat. +""" + +import sys +from pathlib import Path + +# ========================================== +# PROJECT ROOT (import fix) +# ========================================== +PROJECT_ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +import pymysql +from knihovny.medicus_db import MedicusDB + +# ========================================== +# CONFIGURATION +# ========================================== +MEDICUS_HOST = "192.168.1.4" +MEDICUS_DB_PATH = r"z:\Medicus 3\data\MEDICUS.FDB" + +MYSQL_CONFIG = { + "host": "192.168.1.76", + "port": 3307, + "user": "root", + "password": "Vlado9674+", + "database": "medevio", + "charset": "utf8mb4", + "autocommit": True +} + +# ========================================== +# INIT CONNECTIONS +# ========================================== +print("Connecting to Medicus...") +medicus = MedicusDB(MEDICUS_HOST, MEDICUS_DB_PATH) + +print("Connecting to MySQL...") +mysql = pymysql.connect( + cursorclass=pymysql.cursors.DictCursor, + **MYSQL_CONFIG +) + +# ========================================== +# FETCH REGISTERED PATIENTS +# ========================================== +print("Loading registered patients from Medicus...") +patients = medicus.get_active_registered_patients(as_dict=True) + +print(f"Loaded {len(patients)} patients") + +# ========================================== +# UPDATE MYSQL +# ========================================== +updated_rows = 0 + +with mysql.cursor() as cur: + for p in patients: + rc = p["rodcis"] + jmeno = p.get("jmeno") + prijmeni = p.get("prijmeni") + + if not rc or not jmeno or not prijmeni: + continue + + cur.execute(""" + UPDATE vzp_stav_pojisteni + SET + jmeno = COALESCE(jmeno, %s), + prijmeni = COALESCE(prijmeni, %s) + WHERE rc = %s + AND (jmeno IS NULL OR prijmeni IS NULL) + """, (jmeno, prijmeni, rc)) + + if cur.rowcount > 0: + updated_rows += cur.rowcount + +mysql.close() +medicus.close() + +print(f"\n✅ Hotovo. Aktualizováno {updated_rows} řádků ve vzp_stav_pojisteni.") diff --git a/10 Tests/test kontrola registrovaných.py b/10 Tests/test kontrola registrovaných.py index adb6182..60ce80b 100644 --- a/10 Tests/test kontrola registrovaných.py +++ b/10 Tests/test kontrola registrovaných.py @@ -4,7 +4,7 @@ import time import logging from knihovny.medicus_db import MedicusDB -from vzpb2b_client import VZPB2BClient +from knihovny.vzpb2b_client import VZPB2BClient import pymysql from datetime import date diff --git a/10 Tests/test.py b/10 Tests/test.py index 70f2d92..41b1d2b 100644 --- a/10 Tests/test.py +++ b/10 Tests/test.py @@ -1,4 +1,4 @@ -from vzpb2b_client import VZPB2BClient +from knihovny.vzpb2b_client import VZPB2BClient client = VZPB2BClient( env="simu", # or "prod" diff --git a/20 Ověření proti Medicus/10 FinalSaveInsuranceStatusScript.py b/20 Ověření proti Medicus/10 FinalSaveInsuranceStatusScript.py index f9d3aac..1107b3f 100644 --- a/20 Ověření proti Medicus/10 FinalSaveInsuranceStatusScript.py +++ b/20 Ověření proti Medicus/10 FinalSaveInsuranceStatusScript.py @@ -1,19 +1,20 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -#this script can be run several times on the same day, in such case it works incrementaly +# this script can be run several times on the same day, in such case it works incrementally import sys from pathlib import Path -# add project root (one level up) to PYTHONPATH +# ========================================== +# PROJECT ROOT (import fix) +# ========================================== PROJECT_ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(PROJECT_ROOT)) - import time import logging from knihovny.medicus_db import MedicusDB -from vzpb2b_client import VZPB2BClient +from knihovny.vzpb2b_client import VZPB2BClient import pymysql from datetime import date @@ -57,43 +58,64 @@ mysql = pymysql.connect( # ========================================== # SAVE RESULT # ========================================== -def save_insurance_status(mysql_conn, rc, k_datu, result, xml_text): +def save_insurance_status(mysql_conn, rc, prijmeni, jmeno, k_datu, result, xml_text): + """ + Uloží čistou odpověď VZP + identifikační údaje pacienta. + Pojišťovna je VÝHRADNĚ z odpovědi VZP. + """ sql = """ INSERT INTO vzp_stav_pojisteni - (rc, k_datu, stav, kod_pojistovny, nazev_pojistovny, + (rc, prijmeni, jmeno, k_datu, + stav, kod_pojistovny, nazev_pojistovny, pojisteni_kod, stav_vyrizeni, response_xml) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + VALUES (%s, %s, %s, %s, + %s, %s, %s, + %s, %s, %s) """ + with mysql_conn.cursor() as cur: cur.execute(sql, ( rc, + prijmeni, + jmeno, k_datu, result["stav"], - result["kodPojistovny"], - result["nazevPojistovny"], - result["pojisteniKod"], - result["stavVyrizeni"], + result["kodPojistovny"], # ← VZP + result["nazevPojistovny"], # ← VZP + result["pojisteniKod"], # ← VZP + result["stavVyrizeni"], # ← VZP xml_text )) + # ========================================== # CONFIGURATION # ========================================== HOST = "192.168.1.4" DB_PATH = r"z:\Medicus 3\data\MEDICUS.FDB" -PFX_PATH = r"MBcert.pfx" +PFX_PATH = PROJECT_ROOT / "certificates" / "MBcert.pfx" PFX_PASSWORD = "Vlado7309208104++" ENV = "prod" ICZ = "00000000" DIC = "00000000" +# sanity check +if not PFX_PATH.exists(): + raise FileNotFoundError(f"PFX certificate not found: {PFX_PATH}") + # ========================================== # INIT CONNECTIONS # ========================================== db = MedicusDB(HOST, DB_PATH) -vzp = VZPB2BClient(ENV, PFX_PATH, PFX_PASSWORD, icz=ICZ, dic=DIC) +vzp = VZPB2BClient( + ENV, + str(PFX_PATH), # <-- important: pass as string + PFX_PASSWORD, + icz=ICZ, + dic=DIC +) # ========================================== # FETCH REGISTERED PATIENTS @@ -108,7 +130,7 @@ today = date.today() # ========================================== patients_to_check = [] -with mysql.cursor() as cur: +with mysql.cursor(pymysql.cursors.DictCursor) as cur: for rc, prijmeni, jmeno, poj in patients: cur.execute( "SELECT MAX(k_datu) AS last_check FROM vzp_stav_pojisteni WHERE rc = %s", @@ -151,7 +173,15 @@ for idx, (rodcis, prijmeni, jmeno) in enumerate(patients_to_check, 1): continue try: - save_insurance_status(mysql, rodcis, today, result, xml) + save_insurance_status( + mysql, + rodcis, + prijmeni, + jmeno, + today, + result, + xml + ) except Exception as e: log_error(f"❌ MYSQL INSERT ERROR for RC {rodcis}: {e}") time.sleep(2) diff --git a/20 Ověření proti Medicus/10 Ověření proti medicus.py b/20 Ověření proti Medicus/10 Ověření proti medicus.py index 2e44ee6..65ee6d6 100644 --- a/20 Ověření proti Medicus/10 Ověření proti medicus.py +++ b/20 Ověření proti Medicus/10 Ověření proti medicus.py @@ -1,10 +1,20 @@ +import sys +from pathlib import Path +from datetime import datetime, date, timedelta +import os import pymysql + from knihovny.medicus_db import MedicusDB +from knihovny.vzpb2b_client import VZPB2BClient from jinja2 import Environment, FileSystemLoader from weasyprint import HTML -from pathlib import Path -from datetime import datetime -import os + +# ========================================== +# PROJECT ROOT (import + paths) +# ========================================== +script_location = Path(__file__).resolve().parent +project_root = script_location.parent +sys.path.insert(0, str(project_root)) # ========================================== # CONFIGURATION @@ -22,16 +32,23 @@ MYSQL_CONFIG = { "autocommit": True } +PFX_PATH = project_root / "certificates" / "MBcert.pfx" +PFX_PASSWORD = "Vlado7309208104++" + +ENV = "prod" +ICZ = "00000000" +DIC = "00000000" + +if not PFX_PATH.exists(): + raise FileNotFoundError(f"PFX certificate not found: {PFX_PATH}") + # ========================================== -# PATHS +# PATHS (templates, output) # ========================================== -script_location = Path(__file__).resolve().parent -project_root = script_location.parent template_dir = project_root / "Templates" output_dir = script_location / "Output" output_dir.mkdir(exist_ok=True) -# Font paths font_regular = (template_dir / "fonts" / "DejaVuSans.ttf").as_uri() font_bold = (template_dir / "fonts" / "DejaVuSans-Bold.ttf").as_uri() font_italic = (template_dir / "fonts" / "DejaVuSans-Oblique.ttf").as_uri() @@ -48,20 +65,61 @@ mysql = pymysql.connect( **MYSQL_CONFIG ) +print("Initializing VZP B2B client...") +vzp = VZPB2BClient( + ENV, + str(PFX_PATH), + PFX_PASSWORD, + icz=ICZ, + dic=DIC +) + +# ========================================== +# BINÁRNÍ HLEDÁNÍ DATA ZLOMU +# ========================================== +def find_insurance_break_date(vzp_client, rc, start_date, end_date): + try: + low = start_date + high = end_date + + stav_low = vzp_client.parse_stav_pojisteni( + vzp_client.stav_pojisteni(rc=rc, k_datu=low.isoformat()) + )["stav"] + + stav_high = vzp_client.parse_stav_pojisteni( + vzp_client.stav_pojisteni(rc=rc, k_datu=high.isoformat()) + )["stav"] + + if stav_low != "1" or stav_high == "1": + return None, None + + while (high - low).days > 1: + mid = low + timedelta(days=(high - low).days // 2) + + xml = vzp_client.stav_pojisteni(rc=rc, k_datu=mid.isoformat()) + stav_mid = vzp_client.parse_stav_pojisteni(xml)["stav"] + + if stav_mid == "1": + low = mid + else: + high = mid + + return low, high + + except Exception: + return None, None + # ========================================== # FETCH REGISTERED PATIENTS # ========================================== print("Fetching registered patients from Medicus...") patients = medicus.get_active_registered_patients(as_dict=True) - patients_by_rc = {p["rodcis"]: p for p in patients} print(f"Loaded {len(patients_by_rc)} registered patients") # ========================================== # FETCH LAST INSURANCE STATES # ========================================== -print("Fetching last insurance states from MySQL...") - sql_last_states = """ SELECT rc, stav FROM ( @@ -76,12 +134,11 @@ with mysql.cursor() as cur: cur.execute(sql_last_states) last_states = cur.fetchall() -print(f"Loaded {len(last_states)} last insurance states") - # ========================================== -# COMPARE +# COMPARE + BREAK DATE # ========================================== suspected = [] +today = date.today() for row in last_states: rc = row["rc"] @@ -89,12 +146,30 @@ for row in last_states: if rc in patients_by_rc and stav != "1": p = patients_by_rc[rc] + + with mysql.cursor() as c2: + c2.execute(""" + SELECT MAX(k_datu) AS last_insured + FROM vzp_stav_pojisteni + WHERE rc = %s AND stav = '1' + """, (rc,)) + r2 = c2.fetchone() + last_known_insured = r2["last_insured"] + + insured_to = uninsured_from = None + if last_known_insured: + insured_to, uninsured_from = find_insurance_break_date( + vzp, rc, last_known_insured, today + ) + suspected.append({ "rc": rc, "prijmeni": p["prijmeni"], "jmeno": p["jmeno"], "poj": p["poj"], - "stav": stav + "stav": stav, + "insured_to": insured_to, + "uninsured_from": uninsured_from }) print(f"Nalezeno {len(suspected)} problémových záznamů") @@ -106,13 +181,9 @@ medicus.close() mysql.close() # ========================================== -# PDF GENERATION (WEASYPRINT) +# PDF GENERATION # ========================================== -env = Environment( - loader=FileSystemLoader(str(template_dir)), - autoescape=True -) - +env = Environment(loader=FileSystemLoader(str(template_dir)), autoescape=True) template = env.get_template("vzp_console_report.html") html_content = template.render( diff --git a/MBcert.pfx b/Certificates/MBcert.pfx similarity index 100% rename from MBcert.pfx rename to Certificates/MBcert.pfx diff --git a/Templates/vzp_console_report.html b/Templates/vzp_console_report.html index 0311fda..6020369 100644 --- a/Templates/vzp_console_report.html +++ b/Templates/vzp_console_report.html @@ -61,6 +61,7 @@ table { th, td { border: 1px solid #ccc; padding: 6px 8px; + vertical-align: top; } th { @@ -79,6 +80,11 @@ tr:nth-child(even) { color: #d9534f; font-weight: bold; } + +.date-info { + font-size: 9pt; + color: #555; +} @@ -98,6 +104,7 @@ tr:nth-child(even) {