notebookvb

This commit is contained in:
Vladimir Buzalka
2026-04-30 07:09:02 +02:00
parent 2e929f1d77
commit 1b904e3da0
4 changed files with 804 additions and 0 deletions
@@ -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)}")
@@ -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}")
@@ -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}")