234 lines
9.4 KiB
Python
234 lines
9.4 KiB
Python
#!/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}")
|