From 1b904e3da03d4589b383a7fc4e4c4194b4278f32 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Thu, 30 Apr 2026 07:09:02 +0200 Subject: [PATCH] notebookvb --- .../KdoJeLékař/export_neregistrovani_vzp.py | 362 ++++++++++++++++++ .../neregistrovani_vzp_20250101.xlsx | Bin 0 -> 19581 bytes .../StavPojisteni/batch_stav0_20250101.py | 265 +++++++++++++ .../zkontroluj_rc_jednorazove.py | 177 +++++++++ 4 files changed, 804 insertions(+) create mode 100644 Insurance/KdoJeLékař/export_neregistrovani_vzp.py create mode 100644 Insurance/KdoJeLékař/neregistrovani_vzp_20250101.xlsx create mode 100644 Insurance/StavPojisteni/batch_stav0_20250101.py create mode 100644 Insurance/StavPojisteni/zkontroluj_rc_jednorazove.py diff --git a/Insurance/KdoJeLékař/export_neregistrovani_vzp.py b/Insurance/KdoJeLékař/export_neregistrovani_vzp.py new file mode 100644 index 0000000..a8ccc07 --- /dev/null +++ b/Insurance/KdoJeLékař/export_neregistrovani_vzp.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Exportuje 151 pacientů registrovaných v Medicusu k 1.1.2025, +u nichž VZP k tomuto datu nevykazuje registraci v odbornosti 001 u IČP 09305001. +Výstup: Excel s komentářem a aktuálním stavem v Medicusu. +""" + +import sys +from pathlib import Path +from datetime import date +from collections import defaultdict + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent)) + +from Knihovny.mysql_db import connect_mysql +import fdb, socket +from openpyxl import Workbook +from openpyxl.styles import (Font, PatternFill, Alignment, Border, Side, + GradientFill) +from openpyxl.utils import get_column_letter + +# ── Konfigurace ──────────────────────────────────────────────────────────────── +K_DATU_HIST = "2025-01-01" +TODAY = date.today() +OUT_FILE = Path(__file__).resolve().parent / f"neregistrovani_vzp_20250101.xlsx" + +POJ_NAZVY = { + "111": "VZP", + "201": "ČPZP", + "205": "ČPZP (ex-OZP)", + "207": "OZP", + "209": "ZPŠ", + "211": "ZPMV", + "213": "RBP", +} + +# ── Barvy ────────────────────────────────────────────────────────────────────── +BLUE_HEADER = "1F497D" +WHITE = "FFFFFF" +LIGHT_BLUE = "DCE6F1" +LIGHT_GREEN = "EBF1DE" +LIGHT_YELLOW = "FFFFC0" +LIGHT_RED = "FCE4D6" +LIGHT_GREY = "F2F2F2" +ORANGE = "F4B942" + +# ── Data z MySQL ─────────────────────────────────────────────────────────────── +mysql = connect_mysql() +cur = mysql.cursor() + +cur.execute(""" + SELECT rc FROM vzp_registrace_raw WHERE k_datu = %s + AND rc NOT IN ( + SELECT rc FROM vzp_registrace_lekari + WHERE k_datu = %s AND kod_odbornosti = '001' + AND ICP = '09305001' AND ma_lekare = 1 + ) +""", (K_DATU_HIST, K_DATU_HIST)) +problematicke_rcs = [row[0] for row in cur.fetchall()] + +ph = ",".join(["%s"] * len(problematicke_rcs)) +cur.execute(f""" + SELECT rc, prijmeni, jmeno, kod_odbornosti, ma_lekare, ICP, + nazev_lekare, nazev_zzz, poj_kod, poj_zkratka + FROM vzp_registrace_lekari + WHERE k_datu = %s AND rc IN ({ph}) AND kod_odbornosti = '001' +""", (K_DATU_HIST, *problematicke_rcs)) + +vzp = {} +for rc, prijmeni, jmeno, odb, ma, icp, nazev_lek, nazev_zzz, poj_kod, poj_zkr in cur.fetchall(): + vzp[rc] = {"prijmeni": prijmeni or "", "jmeno": jmeno or "", + "ma_lekare": bool(ma), "ICP": icp or "", + "nazev_lekare": nazev_lek or "", "nazev_zzz": nazev_zzz or "", + "poj_kod": poj_kod or "", "poj_zkratka": poj_zkr or ""} + +mysql.close() + +# ── Data z Medicusu ──────────────────────────────────────────────────────────── +computer_name = socket.gethostname().upper() +dsn_map = { + "LEKAR": r"localhost:M:\medicus\data\medicus.fdb", + "SESTRA": r"192.168.1.10:m:\medicus\data\medicus.fdb", + "LENOVO": r"192.168.1.10:m:\medicus\data\medicus.fdb", +} +dsn = dsn_map.get(computer_name, r"localhost:c:\medicus 3\data\medicus.fdb") +fb_conn = fdb.connect(dsn=dsn, user="SYSDBA", password="masterkey", charset="win1250") +fb_cur = fb_conn.cursor() + +# Aktuálně aktivní pacienti +fb_cur.execute(""" + SELECT kar.rodcis FROM kar + WHERE kar.vyrazen = 'N' AND kar.rodcis IS NOT NULL AND kar.rodcis <> '' + AND EXISTS ( + SELECT 1 FROM registr r JOIN icp i ON r.idicp = i.idicp + WHERE r.idpac = kar.idpac + AND r.datum <= CURRENT_DATE + AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= CURRENT_DATE) + AND r.priznak IN ('V','D','A') + AND i.icp = '09305001' AND i.odb = '001' + ) +""") +aktualne_aktivni = {(row[0] or "").strip() for row in fb_cur.fetchall()} + +# Detail všech 151 pacientů +ph_fb = ",".join(["?" for _ in problematicke_rcs]) +fb_cur.execute(f""" + SELECT kar.rodcis, kar.prijmeni, kar.jmeno, kar.poj, kar.vyrazen, + r.datum, r.datum_zruseni, r.priznak + FROM kar + LEFT JOIN registr r ON r.idpac = kar.idpac + LEFT JOIN icp i ON r.idicp = i.idicp AND i.icp = '09305001' AND i.odb = '001' + WHERE kar.rodcis IN ({ph_fb}) + ORDER BY kar.rodcis, r.datum DESC +""", problematicke_rcs) + +medicus = {} +for row in fb_cur.fetchall(): + rc = (row[0] or "").strip() + if rc not in medicus: + medicus[rc] = { + "prijmeni": (row[1] or "").strip(), + "jmeno": (row[2] or "").strip(), + "poj": str(row[3] or "").strip(), + "vyrazen": (row[4] or "").strip(), + "reg_datum": row[5], + "reg_datum_zruseni":row[6], + "reg_priznak": (row[7] or "").strip(), + } + +fb_conn.close() + +# ── Kategorizace ─────────────────────────────────────────────────────────────── +def kategorie(rc, vzp_row, med_row, aktivni_set): + poj = med_row.get("poj", "") if med_row else "" + if not vzp_row: + if poj != "111": + return "JINÁ POJIŠŤOVNA", "VZP neregistruje — pojištěnec jiné pojišťovny.", LIGHT_BLUE + return "BEZ VZP ZÁZNAMU", "VZP nevrátila žádný záznam přesto, že jde o pojištěnce VZP. Pravděpodobně chybí registrace u VZP.", LIGHT_RED + + if vzp_row["ma_lekare"]: + return "REGISTROVÁN JINDE", f"VZP eviduje registraci u jiného lékaře: {vzp_row['nazev_zzz']} (ICP {vzp_row['ICP']}).", LIGHT_RED + + return "BEZ LÉKAŘE U VZP", "VZP eviduje pojištěnce, ale bez registrujícího lékaře v odbornosti 001.", LIGHT_YELLOW + + +# ── Sestavení řádků ──────────────────────────────────────────────────────────── +rows = [] +for rc in problematicke_rcs: + vzp_row = vzp.get(rc) + med_row = medicus.get(rc) + aktivni = rc in aktualne_aktivni + + prijmeni = (med_row or vzp_row or {}).get("prijmeni", "") + jmeno = (med_row or vzp_row or {}).get("jmeno", "") + poj_kod = med_row.get("poj", "") if med_row else (vzp_row or {}).get("poj_kod", "") + poj_nazev = POJ_NAZVY.get(poj_kod, poj_kod) + + med_stav = "Aktivní" if aktivni else ("Odregistrován" if med_row else "Nenalezen v Medicusu") + reg_datum = med_row.get("reg_datum") if med_row else None + reg_datum_zrus = med_row.get("reg_datum_zruseni") if med_row else None + + kat, komentar, barva = kategorie(rc, vzp_row, med_row, aktualne_aktivni) + + vzp_icp = vzp_row["ICP"] if vzp_row and vzp_row["ma_lekare"] else "" + vzp_lek = vzp_row["nazev_zzz"] if vzp_row and vzp_row["ma_lekare"] else "" + vzp_zzz = vzp_row["nazev_lekare"] if vzp_row and vzp_row["ma_lekare"] else "" + + rows.append({ + "prijmeni": prijmeni, + "jmeno": jmeno, + "rc": rc, + "poj_kod": poj_kod, + "poj_nazev": poj_nazev, + "med_stav": med_stav, + "reg_datum": reg_datum.strftime("%d.%m.%Y") if reg_datum else "", + "reg_zruseni": reg_datum_zrus.strftime("%d.%m.%Y") if reg_datum_zrus else "", + "kategorie": kat, + "komentar": komentar, + "vzp_icp": vzp_icp, + "vzp_lek": vzp_lek, + "vzp_zzz": vzp_zzz, + "barva": barva, + }) + +rows.sort(key=lambda r: (r["kategorie"], r["prijmeni"], r["jmeno"])) + +# ── Excel ────────────────────────────────────────────────────────────────────── +wb = Workbook() + +# ──────────────────────────────────────────────────────────────────────────────── +# SHEET 1: Přehled +# ──────────────────────────────────────────────────────────────────────────────── +ws_info = wb.active +ws_info.title = "Přehled" + +def hdr_cell(ws, row, col, value): + c = ws.cell(row=row, column=col, value=value) + c.font = Font(name="Arial", bold=True, color=WHITE, size=11) + c.fill = PatternFill("solid", fgColor=BLUE_HEADER) + c.alignment = Alignment(horizontal="center", vertical="center") + return c + +def val_cell(ws, row, col, value, bold=False, bg=None): + c = ws.cell(row=row, column=col, value=value) + c.font = Font(name="Arial", bold=bold, size=10) + c.alignment = Alignment(wrap_text=True, vertical="top") + if bg: + c.fill = PatternFill("solid", fgColor=bg) + return c + +ws_info.column_dimensions["A"].width = 36 +ws_info.column_dimensions["B"].width = 18 +ws_info.column_dimensions["C"].width = 60 + +# Titulek +ws_info.merge_cells("A1:C1") +t = ws_info["A1"] +t.value = f"Pacienti registrovaní v Medicusu k 1. 1. 2025, ale dle VZP bez registrace u IČP 09305001" +t.font = Font(name="Arial", bold=True, size=13, color=WHITE) +t.fill = PatternFill("solid", fgColor=BLUE_HEADER) +t.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) +ws_info.row_dimensions[1].height = 36 + +ws_info.merge_cells("A2:C2") +ws_info["A2"].value = f"Vygenerováno: {TODAY.strftime('%d. %m. %Y')} | Stav v Medicusu k dnešnímu dni" +ws_info["A2"].font = Font(name="Arial", italic=True, size=9, color="595959") +ws_info["A2"].alignment = Alignment(horizontal="center") + +# Souhrn počtů +counts = defaultdict(int) +counts_aktivni = defaultdict(int) +for r in rows: + counts[r["kategorie"]] += 1 + if r["med_stav"] == "Aktivní": + counts_aktivni[r["kategorie"]] += 1 + +hdr_cell(ws_info, 4, 1, "Kategorie") +hdr_cell(ws_info, 4, 2, "Počet pacientů") +hdr_cell(ws_info, 4, 3, "Komentář") + +KAT_BARVY = { + "REGISTROVÁN JINDE": LIGHT_RED, + "BEZ LÉKAŘE U VZP": LIGHT_YELLOW, + "JINÁ POJIŠŤOVNA": LIGHT_BLUE, + "BEZ VZP ZÁZNAMU": LIGHT_RED, +} +KAT_POPIS = { + "REGISTROVÁN JINDE": "VZP k 1.1.2025 eviduje registraci u jiného praktického lékaře. Pacient se pravděpodobně přeregistroval jinam, aniž by byl v Medicusu odregistrován.", + "BEZ LÉKAŘE U VZP": "VZP eviduje pojištěnce, ale v odbornosti 001 mu neeviduje žádného lékaře. Může jít o opožděné zpracování přihlášky nebo technickou chybu.", + "JINÁ POJIŠŤOVNA": "Pacient není pojištěncem VZP — VZP o něm data nemá, proto nebylo vráceno nic. To je očekávané chování.", + "BEZ VZP ZÁZNAMU": "VZP pojištěnec (111), ale VZP nevrátila žádný záznam. Může jít o nesprávné RC, neaktivní pojistný vztah nebo chybu v komunikaci.", +} + +for i, kat in enumerate(["REGISTROVÁN JINDE", "BEZ LÉKAŘE U VZP", "JINÁ POJIŠŤOVNA", "BEZ VZP ZÁZNAMU"]): + r = 5 + i + bg = KAT_BARVY[kat] + val_cell(ws_info, r, 1, kat, bold=True, bg=bg) + val_cell(ws_info, r, 2, f"{counts[kat]} ({counts_aktivni[kat]} stále aktivní)", bg=bg) + val_cell(ws_info, r, 3, KAT_POPIS[kat], bg=bg) + ws_info.row_dimensions[r].height = 42 + +ws_info.row_dimensions[4].height = 20 + +# Celkem +val_cell(ws_info, 10, 1, "CELKEM", bold=True) +val_cell(ws_info, 10, 2, f"{len(rows)} ({sum(counts_aktivni.values())} aktivní)", bold=True) + +# Metodika +ws_info.merge_cells("A12:C12") +ws_info["A12"].value = "Metodika" +ws_info["A12"].font = Font(name="Arial", bold=True, size=11, color=BLUE_HEADER) + +metodika_text = ( + "Skript kdojelekar_20250101.py dotázal VZP B2B na registrujícího lékaře (odbornost 001) " + "pro každého pacienta registrovaného k 1. 1. 2025 v Medicusu u IČP 09305001. " + "Pacienti v tomto souboru jsou ti, u nichž VZP k danému datu nevrátila záznam s ICP=09305001 a ma_lekare=1. " + "Stav v Medicusu je aktuální k dnešnímu dni (" + TODAY.strftime("%d. %m. %Y") + ")." +) +ws_info.merge_cells("A13:C13") +c = ws_info["A13"] +c.value = metodika_text +c.font = Font(name="Arial", size=9, color="595959") +c.alignment = Alignment(wrap_text=True, vertical="top") +ws_info.row_dimensions[13].height = 56 + +# Doporučení +ws_info.merge_cells("A15:C15") +ws_info["A15"].value = "Doporučení" +ws_info["A15"].font = Font(name="Arial", bold=True, size=11, color=BLUE_HEADER) + +ws_info.merge_cells("A16:C16") +c = ws_info["A16"] +c.value = ( + "1. REGISTROVÁN JINDE — ověřit s pacientem při návštěvě, zda se přeregistroval; pokud ano, odregistrovat v Medicusu.\n" + "2. BEZ LÉKAŘE U VZP — zkontrolovat, zda přihláška registrace byla správně odeslána a VZP ji eviduje.\n" + "3. JINÁ POJIŠŤOVNA — tyto pacienty prověřit u příslušné pojišťovny (ČPZP, OZP…) analogickým dotazem.\n" + "4. BEZ VZP ZÁZNAMU — ověřit správnost RC a aktivitu pojistného vztahu přímo u VZP." +) +c.font = Font(name="Arial", size=10) +c.alignment = Alignment(wrap_text=True, vertical="top") +ws_info.row_dimensions[16].height = 80 + +# ──────────────────────────────────────────────────────────────────────────────── +# SHEET 2: Data +# ──────────────────────────────────────────────────────────────────────────────── +ws = wb.create_sheet("Pacienti") + +COLS = [ + ("Příjmení", 20), + ("Jméno", 14), + ("Rodné číslo", 14), + ("Pojišťovna", 12), + ("Stav v Medicusu\ndnes", 16), + ("Datum registrace\nv Medicusu", 16), + ("Datum zrušení\nv Medicusu", 16), + ("Kategorie VZP problému", 22), + ("Komentář", 52), + ("VZP ICP jiného lékaře", 18), + ("VZP — jméno lékaře", 28), + ("VZP — název ZZZ", 36), +] + +for col_idx, (header, width) in enumerate(COLS, 1): + c = ws.cell(row=1, column=col_idx, value=header) + c.font = Font(name="Arial", bold=True, color=WHITE, size=10) + c.fill = PatternFill("solid", fgColor=BLUE_HEADER) + c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) + ws.column_dimensions[get_column_letter(col_idx)].width = width + +ws.row_dimensions[1].height = 32 +ws.freeze_panes = "A2" + +thin = Side(style="thin", color="BFBFBF") +border = Border(left=thin, right=thin, top=thin, bottom=thin) + +for row_idx, r in enumerate(rows, 2): + bg = r["barva"] + data = [ + r["prijmeni"], r["jmeno"], r["rc"], f"{r['poj_kod']} {r['poj_nazev']}", + r["med_stav"], r["reg_datum"], r["reg_zruseni"], + r["kategorie"], r["komentar"], + r["vzp_icp"], r["vzp_lek"], r["vzp_zzz"], + ] + for col_idx, value in enumerate(data, 1): + c = ws.cell(row=row_idx, column=col_idx, value=value) + c.font = Font(name="Arial", size=9) + c.fill = PatternFill("solid", fgColor=bg) + c.border = border + c.alignment = Alignment(vertical="top", wrap_text=(col_idx in (9, 11, 12))) + if r["med_stav"] == "Aktivní" and col_idx == 5: + c.font = Font(name="Arial", size=9, bold=True, color="375623") + elif r["med_stav"] != "Aktivní" and col_idx == 5: + c.font = Font(name="Arial", size=9, color="843C0C") + ws.row_dimensions[row_idx].height = 32 + +# AutoFilter +ws.auto_filter.ref = f"A1:{get_column_letter(len(COLS))}1" + +wb.save(OUT_FILE) +print(f"Uloženo: {OUT_FILE}") +print(f"Celkem řádků: {len(rows)}") diff --git a/Insurance/KdoJeLékař/neregistrovani_vzp_20250101.xlsx b/Insurance/KdoJeLékař/neregistrovani_vzp_20250101.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..590a60dbfa256633ef4fc17ee19f178796517445 GIT binary patch literal 19581 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�VGB&8d;h6vToc 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 ZizR9WJHRjw0RX^^^EN}Z-ka<`{RIp~i)#P? literal 0 HcmV?d00001 diff --git a/Insurance/StavPojisteni/batch_stav0_20250101.py b/Insurance/StavPojisteni/batch_stav0_20250101.py new file mode 100644 index 0000000..d9f7607 --- /dev/null +++ b/Insurance/StavPojisteni/batch_stav0_20250101.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +batch_stav0_20250101.py +======================== +Zpracuje 50 pacientů, kteří k 1. 1. 2025 vrátili stavVyrizeniPozadavku=0 +na dotaz registrace lékaře (= VZP pojištěnce nenašla). + +Pro každého: + 1. Dotáže VZP stavPojisteni k 2025-01-01 → uloží do vzp_stav_pojisteni. + 2. Pokud stav != '1' a != '4', binárně hledá zlom pojištění + a uloží do vzp_sledovani_pojisteni. + +Resumovatelný — pacienty, kteří už mají záznam v vzp_stav_pojisteni +k 2025-01-01, přeskočí. +""" + +import sys +import time +from pathlib import Path +from datetime import date, timedelta + +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from Knihovny.mysql_db import connect_mysql +from Knihovny.vzpb2b_client import VZPB2BClient + +# ── KONFIGURACE ─────────────────────────────────────────────────────────────── + +K_DATU = date(2025, 1, 1) +API_DELAY = 1.5 + +PFX_PATH = Path(__file__).resolve().parent.parent / "Certificates" / "picka.pfx" +PFX_PASSWORD = "Vlado7309208104+" +ICZ = "00000000" +DIC = "00000000" +ENV = "prod" + +# ── PACIENTI ───────────────────────────────────────────────────────────────── + +PACIENTI = [ + ("415513130", "Rohlíková", "Marie"), + ("420622031", "Hamerník", "Josef"), + ("430127082", "Anderle", "Václav"), + ("435625017", "Kafková", "Marie"), + ("436005111", "Plzáková", "Ivana"), + ("445624103", "Kloučková", "Vlasta"), + ("446116017", "Strnadová", "Dagmar"), + ("456016085", "Kubcová", "Anna"), + ("485627038", "Poustková", "Jiřina"), + ("506109148", "Holubcová", "Svatava"), + ("6008040247", "Šulc", "Jiří"), + ("6054574130", "Přibová", "Darina"), + ("6102694070", "Elouchefoun", "Aziz"), + ("6654251978", "Svozilová", "Ivana"), + ("6808292018", "Moudrý", "Jiří"), + ("6853222079", "Milatová", "Martina"), + ("6909154934", "Novák", "Petr"), + ("7056764319", "Michlíková", "Anna"), + ("7157734210", "Moudry Molloy", "Joanne"), + ("7309300449", "Vojáček", "Aleš"), + ("7410540709", "Torres blanco", "Jose maria"), + ("7459303599", "Noháčová", "Kateřina"), + ("7651090106", "Matějková", "Jana"), + ("7803744597", "Barrell", "Peter"), + ("7855080420", "Vondřičková", "Zuzana"), + ("7908030427", "Smetana", "Libor"), + ("7957312099", "Nimeřická", "Michaela"), + ("7961794126", "Tomyshynets", "Halyna"), + ("8005291404", "Hanzl", "František"), + ("8156013041", "Maršíková", "Simona"), + ("8157544241", "Jarošová", "Zuzana"), + ("8203120299", "Otčenášek", "Vojtěch"), + ("8253215223", "Slavíková", "Kateřina"), + ("8301070558", "Kříž", "Michal"), + ("8352210438", "Bartáková", "Jana"), + ("8412175123", "Mičulka", "Jan"), + ("8454664262", "Feoktistová", "Irina"), + ("8462150147", "Říhová", "Markéta"), + ("8503120417", "Šindelář", "Tomáš"), + ("8552170517", "Slabá", "Gabriela"), + ("8558150227", "Horáková", "Lucie"), + ("8652034380", "Kopová", "Jana"), + ("8754280403", "Jindrová", "Helena"), + ("8755075153", "Babjáková", "Jana"), + ("8910584023", "Pham Van", "Duy"), + ("8953010330", "Špatná", "Markéta"), + ("8956039037", "Slavíková", "Zuzana"), + ("9002025956", "Banáš", "Martin"), + ("9010262448", "Bečica", "Marek"), + ("9811040305", "Sidej", "Natan"), +] + +# ── INIT ────────────────────────────────────────────────────────────────────── + +vzp = VZPB2BClient(ENV, str(PFX_PATH), PFX_PASSWORD, icz=ICZ, dic=DIC) +mysql = connect_mysql() +call_count = 0 +today = date.today() + +# ── HELPERS ─────────────────────────────────────────────────────────────────── + +def check_stav(rc: str, check_date: date) -> str: + global call_count + if call_count > 0: + time.sleep(API_DELAY) + call_count += 1 + print(f" [{call_count}] {check_date.isoformat()} ...", end=" ", flush=True) + xml = vzp.stav_pojisteni(rc=rc, k_datu=check_date.isoformat()) + stav = vzp.parse_stav_pojisteni(xml)["stav"] + print(f"stav = {stav!r}") + return stav + +def uloz_stav(rc, prijmeni, jmeno, k_datu, stav): + xml = vzp.stav_pojisteni(rc=rc, k_datu=k_datu.isoformat()) + with mysql.cursor() as cur: + cur.execute(""" + INSERT INTO vzp_stav_pojisteni (rc, prijmeni, jmeno, k_datu, stav, response_xml) + VALUES (%s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE stav=VALUES(stav), response_xml=VALUES(response_xml) + """, (rc, prijmeni, jmeno, k_datu, stav, xml)) + +def najdi_zlom(rc: str, last_ok_mysql) -> tuple: + if last_ok_mysql: + low = last_ok_mysql + high = today + print(f" MySQL last stav='1': {low} → [{low} … {high}]") + else: + print(" MySQL: žádný stav='1' — hledám zpětně po rocích ...") + prev_probe = today + low = high = None + for n in range(1, 21): + y = today.year - n + try: + probe = date(y, today.month, today.day) + except ValueError: + probe = date(y, today.month, today.day - 1) + stav = check_stav(rc, probe) + if stav == "1": + low = probe + high = prev_probe + print(f" Nalezeno stav='1' k {low} → [{low} … {high}]") + break + prev_probe = probe + if low is None: + print(" NELZE: stav='1' nenalezen ani 20 let zpět.") + return None, None + + stav_low = check_stav(rc, low) + stav_high = check_stav(rc, high) + + if stav_low != "1": + print(f" NELZE: dolní mez {low} má stav='{stav_low}'.") + return None, None + if stav_high == "1": + print(f" INFO: horní mez {high} má stav='1' — aktuálně pojištěn.") + return None, None + + print(f" Binární hledání ({(high - low).days} dní) ...") + while (high - low).days > 1: + mid = low + timedelta(days=(high - low).days // 2) + stav = check_stav(rc, mid) + if stav == "1": + low = mid + else: + high = mid + + return low, high # insured_to, uninsured_from + +# ── RESUME: načti již hotové ────────────────────────────────────────────────── + +with mysql.cursor() as cur: + cur.execute("SELECT rc FROM vzp_stav_pojisteni WHERE k_datu = %s", (K_DATU,)) + hotove = {row[0] for row in cur.fetchall()} + +zbyvaji = [(rc, p, j) for rc, p, j in PACIENTI if rc not in hotove] +print(f"Celkem pacientů: {len(PACIENTI)}, již hotovo: {len(hotove)}, zbývá: {len(zbyvaji)}") +print(f"API prodleva: {API_DELAY}s | K_DATU: {K_DATU}\n") +print("=" * 60) + +# ── HLAVNÍ SMYČKA ───────────────────────────────────────────────────────────── + +vysledky = [] + +for i, (rc, prijmeni, jmeno) in enumerate(zbyvaji, 1): + print(f"\n[{i}/{len(zbyvaji)}] {prijmeni} {jmeno} (RC: {rc})") + + # Krok 1: stav k K_DATU + if call_count > 0: + time.sleep(API_DELAY) + call_count += 1 + print(f" [{call_count}] {K_DATU.isoformat()} ...", end=" ", flush=True) + xml_hist = vzp.stav_pojisteni(rc=rc, k_datu=K_DATU.isoformat()) + stav_hist = vzp.parse_stav_pojisteni(xml_hist)["stav"] + print(f"stav = {stav_hist!r}") + + with mysql.cursor() as cur: + cur.execute(""" + INSERT INTO vzp_stav_pojisteni (rc, prijmeni, jmeno, k_datu, stav, response_xml) + VALUES (%s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE stav=VALUES(stav), response_xml=VALUES(response_xml) + """, (rc, prijmeni, jmeno, K_DATU, stav_hist, xml_hist)) + + # Krok 2: zlom (jen pokud stav != '1' a != '4') + insured_to = uninsured_from = None + + if stav_hist in ("1", "4"): + print(f" → pojištěn (stav={stav_hist!r}), zlom se nehledá") + else: + # Zkontroluj watchlist + with mysql.cursor() as cur: + cur.execute("SELECT insured_to, uninsured_from FROM vzp_sledovani_pojisteni WHERE rc = %s", (rc,)) + existuje = cur.fetchone() + + if existuje: + insured_to, uninsured_from = existuje + print(f" → již ve watchlistu: insured_to={insured_to}, uninsured_from={uninsured_from}") + else: + with mysql.cursor() as cur: + cur.execute( + "SELECT MAX(k_datu) FROM vzp_stav_pojisteni WHERE rc = %s AND stav = '1'", + (rc,) + ) + r = cur.fetchone() + last_ok = r[0] if r and r[0] else None + + insured_to, uninsured_from = najdi_zlom(rc, last_ok) + + with mysql.cursor() as cur: + cur.execute(""" + INSERT INTO vzp_sledovani_pojisteni + (rc, prijmeni, jmeno, insured_to, uninsured_from, + aktualni_stav, prvni_detekce, posledni_kontrola) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + insured_to=VALUES(insured_to), + uninsured_from=VALUES(uninsured_from), + aktualni_stav=VALUES(aktualni_stav), + posledni_kontrola=VALUES(posledni_kontrola) + """, (rc, prijmeni, jmeno, insured_to, uninsured_from, + stav_hist, today, today)) + + vysledky.append({ + "rc": rc, "prijmeni": prijmeni, "jmeno": jmeno, + "stav_20250101": stav_hist, + "insured_to": insured_to, + "uninsured_from": uninsured_from, + }) + +# ── SOUHRN ──────────────────────────────────────────────────────────────────── + +mysql.close() + +print(f"\n{'=' * 60}") +print(f" SOUHRN — {len(vysledky)} zpracovaných pacientů") +print(f"{'=' * 60}") +print(f" {'Příjmení':<20} {'Jméno':<14} {'Stav':>5} {'Pojištěn do':<13} {'Nepoj. od'}") +print(f" {'-'*58}") +for v in vysledky: + ins = str(v["insured_to"]) if v["insured_to"] else "-" + uni = str(v["uninsured_from"]) if v["uninsured_from"] else "-" + print(f" {v['prijmeni']:<20} {v['jmeno']:<14} {v['stav_20250101']:>5} {ins:<13} {uni}") + +print(f"\nCelkem VZP dotazů: {call_count}") diff --git a/Insurance/StavPojisteni/zkontroluj_rc_jednorazove.py b/Insurance/StavPojisteni/zkontroluj_rc_jednorazove.py new file mode 100644 index 0000000..93fa5ef --- /dev/null +++ b/Insurance/StavPojisteni/zkontroluj_rc_jednorazove.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +zkontroluj_rc_jednorazove.py +============================= +Jednorázový skript pro libovolné RC: + 1. Dotáže VZP na stav pojištění k zadanému K_DATU. + 2. Uloží do vzp_stav_pojisteni. + 3. Pokud stav != '1' a != '4', spustí binární hledání zlomu + a uloží do vzp_sledovani_pojisteni. +""" + +import sys +import time +from pathlib import Path +from datetime import date, timedelta + +PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent +sys.path.insert(0, str(PROJECT_ROOT)) + +from Knihovny.mysql_db import connect_mysql +from Knihovny.vzpb2b_client import VZPB2BClient + +# ── KONFIGURACE ─────────────────────────────────────────────────────────────── + +RC = "430127082" +PRIJMENI = "Anderle" +JMENO = "Václav" +K_DATU = date(2025, 1, 1) # datum pro první dotaz + +PFX_PATH = Path(__file__).resolve().parent.parent / "Certificates" / "picka.pfx" +PFX_PASSWORD = "Vlado7309208104+" +ICZ = "00000000" +DIC = "00000000" +ENV = "prod" + +API_DELAY = 2 + +# ── INIT ────────────────────────────────────────────────────────────────────── + +vzp = VZPB2BClient(ENV, str(PFX_PATH), PFX_PASSWORD, icz=ICZ, dic=DIC) +mysql = connect_mysql() +call_count = 0 +today = date.today() + +def check_stav(rc: str, check_date: date) -> str: + global call_count + if call_count > 0: + time.sleep(API_DELAY) + call_count += 1 + print(f" [{call_count}] VZP dotaz k {check_date.isoformat()} ...", end=" ", flush=True) + xml = vzp.stav_pojisteni(rc=rc, k_datu=check_date.isoformat()) + stav = vzp.parse_stav_pojisteni(xml)["stav"] + print(f"stav = {stav!r}") + return stav + +def uloz_stav(rc, prijmeni, jmeno, k_datu, stav, xml): + with mysql.cursor() as cur: + cur.execute(""" + INSERT INTO vzp_stav_pojisteni + (rc, prijmeni, jmeno, k_datu, stav, response_xml) + VALUES (%s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE stav=VALUES(stav), response_xml=VALUES(response_xml) + """, (rc, prijmeni, jmeno, k_datu, stav, xml)) + +def najdi_zlom(rc, last_ok_mysql): + if last_ok_mysql: + low = last_ok_mysql + high = today + print(f" MySQL last stav='1': {low} -> [{low} ... {high}]") + else: + print(" MySQL: žádný stav='1' — hledám zpětně po rocích ...") + prev_probe = today + low = high = None + for n in range(1, 21): + y = today.year - n + try: + probe = date(y, today.month, today.day) + except ValueError: + probe = date(y, today.month, today.day - 1) + stav = check_stav(rc, probe) + if stav == "1": + low = probe + high = prev_probe + print(f" Nalezeno stav='1' k {low} -> [{low} ... {high}]") + break + prev_probe = probe + if low is None: + print(" NELZE: stav='1' nenalezen ani 20 let zpět.") + return None, None + + stav_low = check_stav(rc, low) + stav_high = check_stav(rc, high) + + if stav_low != "1": + print(f" NELZE: dolní mez {low} má stav='{stav_low}'.") + return None, None + if stav_high == "1": + print(f" INFO: horní mez {high} má stav='1' — pacient je aktuálně pojištěn, žádný zlom.") + return None, None + + print(f" Binární hledání v rozsahu {(high - low).days} dní ...") + while (high - low).days > 1: + mid = low + timedelta(days=(high - low).days // 2) + stav = check_stav(rc, mid) + if stav == "1": + low = mid + else: + high = mid + + return low, high # insured_to, uninsured_from + +# ── KROK 1: Dotaz k zadanému datu ───────────────────────────────────────────── + +print(f"\n{PRIJMENI} {JMENO} (RC: {RC})") +print(f"── Krok 1: stav pojištění k {K_DATU} ─────────────────────────────────") + +xml_hist = vzp.stav_pojisteni(rc=RC, k_datu=K_DATU.isoformat()) +stav_hist = vzp.parse_stav_pojisteni(xml_hist)["stav"] +call_count += 1 +print(f" stav k {K_DATU}: {stav_hist!r}") + +uloz_stav(RC, PRIJMENI, JMENO, K_DATU, stav_hist, xml_hist) +print(f" Uloženo do vzp_stav_pojisteni (k_datu={K_DATU})") + +# ── KROK 2: Pokud není pojištěn, najdi zlom ─────────────────────────────────── + +if stav_hist in ("1", "4"): + print(f"\nStav '{stav_hist}' = pojištěn (nebo cizinec), zlom se nehledá.") +else: + print(f"\nStav '{stav_hist}' = nepojištěn/nenalezen") + print(f"── Krok 2: hledám zlom pojištění ─────────────────────────────────────") + + # Zkontroluj, zda je pacient již ve watchlistu + with mysql.cursor() as cur: + cur.execute("SELECT insured_to, uninsured_from FROM vzp_sledovani_pojisteni WHERE rc = %s", (RC,)) + existuje = cur.fetchone() + + if existuje: + print(f" Pacient již ve watchlistu: insured_to={existuje[0]}, uninsured_from={existuje[1]}") + print(f" Přeskakuji hledání zlomu.") + else: + with mysql.cursor() as cur: + cur.execute( + "SELECT MAX(k_datu) FROM vzp_stav_pojisteni WHERE rc = %s AND stav = '1'", + (RC,) + ) + r = cur.fetchone() + last_ok = r[0] if r and r[0] else None + + insured_to, uninsured_from = najdi_zlom(RC, last_ok) + + with mysql.cursor() as cur: + cur.execute(""" + INSERT INTO vzp_sledovani_pojisteni + (rc, prijmeni, jmeno, insured_to, uninsured_from, + aktualni_stav, prvni_detekce, posledni_kontrola) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + ON DUPLICATE KEY UPDATE + insured_to=VALUES(insured_to), + uninsured_from=VALUES(uninsured_from), + aktualni_stav=VALUES(aktualni_stav), + posledni_kontrola=VALUES(posledni_kontrola) + """, (RC, PRIJMENI, JMENO, insured_to, uninsured_from, + stav_hist, today, today)) + + print(f"\n{'=' * 50}") + print(f" VÝSLEDEK — {PRIJMENI} {JMENO} (RC: {RC})") + print(f"{'=' * 50}") + print(f" Stav k {K_DATU} : {stav_hist!r}") + print(f" Poslední den pojištěn : {insured_to or 'nezjištěno'}") + print(f" První den bez pojištění : {uninsured_from or 'nezjištěno'}") + print(f" Celkem VZP dotazů : {call_count}") + print(f"{'=' * 50}\n") + +mysql.close() +print(f"Hotovo. Celkem VZP dotazů: {call_count}")