Files
ordinaceprojekt/Insurance/SeznamPojistencu/04_najdi_zlomy.py
T
Vladimir Buzalka 371eed9971 notebookvb
2026-05-03 07:02:22 +02:00

234 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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}")