#!/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}")