notebookvb

This commit is contained in:
Vladimir Buzalka
2026-05-03 07:02:22 +02:00
parent d013e43d34
commit 371eed9971
9 changed files with 1260 additions and 0 deletions
@@ -0,0 +1,233 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys as _sys
_sys.stdout.reconfigure(encoding="utf-8", errors="replace")
_sys.stderr.reconfigure(encoding="utf-8", errors="replace")
"""
04_najdi_zlomy.py
=================
Pro pacienty z seznam_pojistencu_davky, kteří NEMAJÍ záznam v vzp_registrace_lekari
(skript kdojelekar je nezachytil), najde bod zlomu registrace u naší ambulance.
Algoritmus:
1. Dotaz VZP dnes — je pacient stále registrován u nás (ICP=09305001)?
ANO → datum_ukonceni z odpovědi = výsledek
2. NE → hledáme rokem dozadu (od dnes, 1 rok, 2 roky …)
dokud nenajdeme rok kdy BYL registrován → tím ohraničíme interval [lo, hi]
3. V intervalu [lo, hi] binární hledání na den přesně
(nebo pokud datum_ukonceni z VZP odpovědi není 3000, použijeme ho přímo)
Výsledek se uloží do tabulky seznam_pojistencu_zlomy a vytiskne na konzoli.
"""
import time
import sys
from datetime import date, timedelta
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "Knihovny"))
from mysql_db import connect_mysql
from vzpb2b_client import VZPB2BClient
# ── Konfigurace ───────────────────────────────────────────────────────────────
PFX_PATH = str(Path(__file__).resolve().parents[1] / "Certificates" / "picka.pfx")
PFX_PASSWORD = "Vlado7309208104+"
ICZ = "09305000"
NASA_ICP = "09305001"
API_PAUSE = 2 # sekundy mezi VZP dotazy
CREATE_SQL = """
CREATE TABLE IF NOT EXISTS seznam_pojistencu_zlomy (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
cip VARCHAR(12) NOT NULL,
prijmeni VARCHAR(30) NOT NULL,
jmeno VARCHAR(24) NOT NULL,
posledni_davka DATE NOT NULL COMMENT 'Poslední měsíc kdy byl v dávce',
zlom_datum DATE NULL COMMENT 'Poslední den registrace u nás (NULL=stále aktivní)',
zlom_zdroj VARCHAR(60) NULL COMMENT 'Jak byl zlom určen',
stav VARCHAR(20) NOT NULL COMMENT 'aktivní / ukončen / nenalezen',
dotazeno_dne DATE NOT NULL,
UNIQUE KEY uq_cip (cip)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Zlomy registrace pro pacienty bez záznamu v kdojelekar';
"""
# ── VZP klient ────────────────────────────────────────────────────────────────
vzp = VZPB2BClient("prod", PFX_PATH, PFX_PASSWORD, icz=ICZ)
def je_registrovan(rc: str, k_datu: date) -> tuple[bool, date | None]:
"""
Vrátí (registrován_u_nas: bool, datum_ukonceni: date|None).
datum_ukonceni = None pokud nelze parsovat nebo pacient není u nás.
"""
xml = vzp.registrace_lekare(rc, k_datu.isoformat(), odbornosti=["001"])
time.sleep(API_PAUSE)
try:
zaznamy = vzp.parse_registrace_lekare(xml)
except Exception as e:
print(f" [CHYBA parsování] {e}")
return False, None
for z in zaznamy:
if z.get("ma_lekare") and z.get("ICP") == NASA_ICP and z.get("kod_odbornosti") == "001":
du_str = z.get("datum_ukonceni")
try:
du = date.fromisoformat(du_str) if du_str else None
except ValueError:
du = None
return True, du
return False, None
def najdi_zlom(rc: str, posledni_davka: date) -> tuple[date | None, str, str]:
"""
Vrátí (zlom_datum, zlom_zdroj, stav).
zlom_datum = poslední den kdy byl registrován (None = stále aktivní).
"""
today = date.today()
# ── Krok 1: dotaz dnes ────────────────────────────────────────────────────
print(f" [dnes {today}]", end=" ", flush=True)
reg, du = je_registrovan(rc, today)
if reg:
if du and du.year < 3000:
print(f"registrován, ukončení {du}")
return du, "VZP datum_ukonceni (dnes)", "ukončen"
else:
print("registrován, bez data ukončení → stále aktivní")
return None, "VZP dnes aktivní", "aktivní"
print("NENÍ registrován")
# ── Krok 2: hledání po rocích dozadu ─────────────────────────────────────
# lo = víme, že tam BYL (posledni_davka)
# hi = víme, že tam NENÍ (today)
lo: date = posledni_davka
hi: date = today
probe = today.replace(year=today.year - 1)
while probe >= posledni_davka:
print(f" [rok {probe}]", end=" ", flush=True)
reg_p, du_p = je_registrovan(rc, probe)
if reg_p:
lo = probe
print(f"registrován")
if du_p and du_p.year < 3000:
print(f" → datum_ukonceni z VZP: {du_p}")
return du_p, f"VZP datum_ukonceni (dotaz {probe})", "ukončen"
break
else:
hi = probe
print("není")
try:
probe = probe.replace(year=probe.year - 1)
except ValueError:
break
else:
# Ani v posledni_davka není registrován — neobvyklé
print(f" ! Ani k datu {posledni_davka} není registrován — zkouším přímo")
reg_lo, du_lo = je_registrovan(rc, posledni_davka)
if not reg_lo:
return None, "nenalezen ani k datu poslední dávky", "nenalezen"
lo = posledni_davka
if du_lo and du_lo.year < 3000:
return du_lo, f"VZP datum_ukonceni ({posledni_davka})", "ukončen"
# ── Krok 3: binární hledání v intervalu [lo, hi] ─────────────────────────
print(f" Binární hledání: {lo}{hi}")
iterace = 0
while (hi - lo).days > 1:
iterace += 1
mid = lo + timedelta(days=(hi - lo).days // 2)
print(f" [{iterace}. iterace: {mid}]", end=" ", flush=True)
reg_m, du_m = je_registrovan(rc, mid)
if reg_m:
lo = mid
print("registrován")
if du_m and du_m.year < 3000:
print(f" → datum_ukonceni z VZP: {du_m}")
return du_m, f"VZP datum_ukonceni (binární {mid})", "ukončen"
else:
hi = mid
print("není")
print(f" → Zlom: poslední den registrace = {lo}")
return lo, f"binární hledání ({iterace} kroků)", "ukončen"
# ── Načtení pacientů ──────────────────────────────────────────────────────────
conn = connect_mysql()
cur = conn.cursor()
cur.execute(CREATE_SQL)
# Unikátní CIP v seznamu (VZP, pojišťovna 111)
cur.execute("SELECT DISTINCT cip FROM seznam_pojistencu_davky WHERE pojistovna='111'")
vsechny_cip = {r[0] for r in cur.fetchall()}
# CIP které jsou v registrace_lekari (u nás, odb 001)
cur.execute("""
SELECT DISTINCT rc FROM vzp_registrace_lekari
WHERE kod_odbornosti='001' AND ICP='09305001' AND ma_lekare=1
""")
zname_cip = {r[0] for r in cur.fetchall()}
# Nespárované
nesparovane_cip = vsechny_cip - zname_cip
# Doplním jméno a posledni_davka
cur.execute("""
SELECT cip, MIN(prijmeni), MIN(jmeno),
MAX(DATE(CONCAT(davka_rok, '-', LPAD(davka_mesic,2,'0'), '-01')))
FROM seznam_pojistencu_davky
WHERE pojistovna='111'
GROUP BY cip
""")
info = {r[0]: (r[1], r[2], r[3]) for r in cur.fetchall()}
pacienti = [
(cip, *info[cip])
for cip in sorted(nesparovane_cip)
if cip in info
]
print(f"Pacientů ke zpracování: {len(pacienti)}\n")
print("=" * 70)
# ── Hlavní smyčka ─────────────────────────────────────────────────────────────
vysledky = []
for cip, prijmeni, jmeno, posledni_davka in pacienti:
print(f"\n{prijmeni} {jmeno} (CIP: {cip}, poslední dávka: {posledni_davka})")
try:
zlom, zdroj, stav = najdi_zlom(cip, posledni_davka)
except Exception as e:
print(f" CHYBA: {e}")
zlom, zdroj, stav = None, f"chyba: {e}", "chyba"
vysledky.append((cip, prijmeni, jmeno, posledni_davka, zlom, zdroj, stav))
cur.execute("""
INSERT INTO seznam_pojistencu_zlomy
(cip, prijmeni, jmeno, posledni_davka, zlom_datum, zlom_zdroj, stav, dotazeno_dne)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE
posledni_davka=VALUES(posledni_davka),
zlom_datum=VALUES(zlom_datum),
zlom_zdroj=VALUES(zlom_zdroj),
stav=VALUES(stav),
dotazeno_dne=VALUES(dotazeno_dne)
""", (cip, prijmeni, jmeno, posledni_davka, zlom, zdroj, stav, date.today()))
cur.close()
conn.close()
# ── Výsledky ──────────────────────────────────────────────────────────────────
print("\n" + "=" * 70)
print(f"\n{'Příjmení':<25} {'Jméno':<20} {'CIP':<12} {'Poslední dávka':<15} {'Zlom':<12} Stav")
print("-" * 95)
for cip, pri, jme, pd, zd, zdroj, stav in vysledky:
zlom_str = str(zd) if zd else ""
print(f"{pri:<25} {jme:<20} {cip:<12} {str(pd):<15} {zlom_str:<12} {stav}")