diff --git a/NačteníPředpisuWithClaude/08StahnoutPredpisy.py b/NačteníPředpisuWithClaude/08StahnoutPredpisy.py new file mode 100644 index 0000000..a277de9 --- /dev/null +++ b/NačteníPředpisuWithClaude/08StahnoutPredpisy.py @@ -0,0 +1,189 @@ +""" +Stazeni detailu receptu (NacistPredpis) pro poslednich N receptu z Medicusu. + +Spusteni: + python 08StahnoutPredpisy.py # 10 receptu od 2025-01-01 + python 08StahnoutPredpisy.py --limit 50 # 50 receptu + python 08StahnoutPredpisy.py --od 2026-01-01 + +Pauza mezi volanimi: 5 sekund. +XML odpovedi se ukladaji do xml_archive/YYYY-MM-DD/ERP_kod.xml +""" + +import sys +import time +import uuid +from datetime import datetime, timezone, date +from pathlib import Path + +import fdb +from requests import Session +from requests_pkcs12 import Pkcs12Adapter + +if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(errors="replace") + +# ── Konfigurace eRecept ────────────────────────────────────────────────────── +PFX_FILE = r"C:\Users\vlado\PycharmProjects\Recepty\AMBSUKL214235369G_31DEC2024.pfx" +PFX_PASS = "Vlado7309208104++" +API_USER = "e08c89c6-2b1a-4eba-8ed9-4e3e63618379" +API_PASS = "Buzalka@Vladimir2025" +UZIVATEL = "E08C89C6-2B1A-4EBA-8ED9-4E3E63618379" +PRACOVISTE = "00214235367" +ENDPOINT = "https://lekar-soap.erecept.sukl.cz/cuer/Lekar" +NAMESPACE = "http://www.sukl.cz/erp/201704" + +PAUZA = 5 # sekund + +# ── Konfigurace Firebird ───────────────────────────────────────────────────── +FB_DSN = r'localhost:c:\medicus 3\data\medicus.fdb' +FB_USER = 'SYSDBA' +FB_PASS = 'masterkey' +FB_CHARSET = 'win1250' + +# ── Adresare ───────────────────────────────────────────────────────────────── +XML_DIR = Path(__file__).parent / "xml_archive" + + +def nacti_erp_kody(fb_conn, datum_od, limit, prijmeni=None): + """Nacte unikatni ID_Dokladu (erp kody) z Firebirdu — recept_epodani.erp.""" + sql = """ + SELECT FIRST ? r.datum, r.lek, r.dop, r.idpac, + TRIM(kar.prijmeni) AS prijmeni, TRIM(kar.jmeno) AS jmeno, + ep.erp + FROM recept r + JOIN recept_epodani ep ON r.id_epodani = ep.id + JOIN kar ON r.idpac = kar.idpac + WHERE r.datum >= ? AND ep.erp IS NOT NULL + """ + params = [limit, datum_od] + if prijmeni: + placeholders = ",".join(["?"] * len(prijmeni)) + sql += f" AND UPPER(TRIM(kar.prijmeni)) IN ({placeholders})" + params.extend(p.upper() for p in prijmeni) + sql += " ORDER BY r.datum DESC" + cur = fb_conn.cursor() + cur.execute(sql, params) + rows = cur.fetchall() + cur.close() + # deduplikace dle erp kodu — zachovat prvni vyskyt (nejnovejsi datum) + seen = set() + unique = [] + for row in rows: + erp = row[6] + if erp not in seen: + seen.add(erp) + unique.append(row) + return unique + + +def volej_nacist_predpis(sess, erp_kod): + """Zavola NacistPredpis a vrati (status_code, response_text).""" + id_zpravy = str(uuid.uuid4()) + odeslano = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S+00:00") + + soap_body = ( + '' + '' + '' + f'' + f'' + f'' + f'{UZIVATEL}' + f'{PRACOVISTE}' + f'' + f'' + f'{erp_kod}' + f'' + f'' + f'' + f'{id_zpravy}' + f'202501A' + f'{odeslano}' + f'MEDICUS_____' + f'' + f'' + '' + '' + ) + + headers = { + "Content-Type": 'text/xml; charset="UTF-8"', + "SOAPAction": '"NacistPredpis"', + "User-Agent": "Medicus", + } + + resp = sess.post(ENDPOINT, data=soap_body.encode("utf-8"), headers=headers, timeout=15) + return resp.status_code, resp.text + + +## ── Parametry (uprav zde) ──────────────────────────────────────────────────── +LIMIT = 100 # max pocet receptu ke stazeni +DATUM_OD = "2025-01-01" # recepty od tohoto data +PRIJMENI = ["Buzalka"] # filtr prijmeni (list), nebo None = vsichni +## ───────────────────────────────────────────────────────────────────────────── + + +def main(): + datum_od = DATUM_OD + dnes = date.today().isoformat() + out_dir = XML_DIR / dnes + out_dir.mkdir(parents=True, exist_ok=True) + + # Firebird + print(f"Pripojuji Firebird...") + fb = fdb.connect(dsn=FB_DSN, user=FB_USER, password=FB_PASS, charset=FB_CHARSET) + rows = nacti_erp_kody(fb, datum_od, LIMIT, PRIJMENI) + fb.close() + print(f"Nacteno {len(rows)} receptu z Medicusu (od {datum_od})\n") + + if not rows: + print("Zadne recepty k stazeni.") + return + + # SOAP session + sess = Session() + sess.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_FILE, pkcs12_password=PFX_PASS)) + sess.auth = (API_USER, API_PASS) + + ok = 0 + chyby = 0 + + for i, row in enumerate(rows, 1): + datum_rec, lek, dop, idpac, prijmeni, jmeno, erp_kod = row + lek_str = f"{lek} {dop}".strip() if dop else str(lek).strip() + label = f"{prijmeni} {jmeno}".strip() + + print(f"[{i:4d}/{len(rows)}] {label:30s} {erp_kod} ", end="", flush=True) + + try: + status, text = volej_nacist_predpis(sess, erp_kod) + + if status == 200 and "" not in text: + xml_file = out_dir / f"{erp_kod}.xml" + xml_file.write_text(text, encoding="utf-8") + size_kb = len(text.encode("utf-8")) / 1024 + print(f"OK {size_kb:6.1f} KB {lek_str[:40]}") + ok += 1 + else: + # SOAP Fault nebo HTTP chyba + chyba_short = text[:120].replace("\n", " ") + print(f"CHYBA HTTP {status} {chyba_short}") + # ulozit i chybovou odpoved + xml_file = out_dir / f"{erp_kod}_CHYBA.xml" + xml_file.write_text(text, encoding="utf-8") + chyby += 1 + + except Exception as e: + print(f"EXCEPTION {e}") + chyby += 1 + + if i < len(rows): + time.sleep(PAUZA) + + print(f"\nHotovo: {ok} OK, {chyby} chyb") + print(f"XML: {out_dir}") + + +if __name__ == "__main__": + main() diff --git a/NačteníPředpisuWithClaude/NacistPredpis_DOKUMENTACE.md b/NačteníPředpisuWithClaude/NacistPredpis_DOKUMENTACE.md index 41f0e9b..a8dd612 100644 --- a/NačteníPředpisuWithClaude/NacistPredpis_DOKUMENTACE.md +++ b/NačteníPředpisuWithClaude/NacistPredpis_DOKUMENTACE.md @@ -1,333 +1,202 @@ # NacistPredpis — Funkční SOAP klient pro IS eRecept SÚKL -## Status -**OVĚŘENO FUNKČNÍ** — 6. dubna 2026 -Odpověď serveru: `HTTP 200`, velikost: ~3.7 KB -Schéma ověřeno proti: `schema1.xsd` verze `202501A` (dokumentace SÚKL 2025-04-24) -Debug ověřen z: `MedicusDebug/eR_SuklMesage_bf002578-*.mime` +Pipeline pro stažení **detailu jednotlivých receptů** z eRecept SÚKL API. +Doplňuje existující hromadný lékový záznam (`NacistLekovyZaznam`) o údaje, +které hromadný dotaz nevrací. --- -## Co tato operace dělá +## Soubory -`NacistPredpis` je SOAP operace IS eRecept (SÚKL), která vrátí **kompletní detail jednoho konkrétního receptu** podle jeho ID. +| Soubor | Co dělá | +|--------|---------| +| `NacistPredpis_FUNKCNI.py` | Stáhne detail **jednoho** receptu dle hardcoded ID_Dokladu (ruční test) | +| `08StahnoutPredpisy.py` | **Hlavní skript** — načte ERP kódy z Medicusu, stáhne detaily, uloží XML | -Vrací vše: pacienta (jméno, adresa, rodné číslo, ZP), lékaře, ordinaci, předepsaný lék (ATC, dávkování, úhrada), stav receptu, výdej z lékárny. - -Typické použití: lékař si zobrazí detail receptu, který sám vystavil, nebo ověří zda byl vydán. +``` +NačteníPředpisuWithClaude/ +├── NacistPredpis_FUNKCNI.py ← test jednoho receptu +├── 08StahnoutPredpisy.py ← hromadné stahování +├── NacistPredpis_DOKUMENTACE.md ← tento soubor +├── xml_archive/ ← archiv XML odpovědí (YYYY-MM-DD/ERP_KOD.xml) +├── MedicusDebug/ ← zachycené SOAP požadavky z Medicusu +└── Tests/ ← starší vývojové soubory +``` --- -## Klíčové informace +## Co NacistPredpis vrací navíc oproti NacistLekovyZaznam -### Endpoint (produkce) -``` -https://lekar-soap.erecept.sukl.cz/cuer/Lekar -``` -> Tato operace je na **starším** endpointu `/cuer/Lekar` — **bez** číslice 2. -> Operace `NacistLekovyZaznam` je naopak na `/cuer/Lekar2`. +Hromadný lékový záznam (`NacistLekovyZaznam`, endpoint `/cuer/Lekar2`) +vrací seznam předpisů a výdejů za pacienta, ale **bez detailů o receptu jako celku**. -### Endpoint (testovací prostředí) -``` -https://lekar-soap.test-erecept.sukl.cz/cuer/Lekar -``` +`NacistPredpis` (endpoint `/cuer/Lekar`, namespace `201704`) vrací detail +jednoho konkrétního receptu, včetně: -### SOAPAction -``` -"NacistPredpis" -``` +### Údaje o receptu (dokladu) -### XML namespace -``` -http://www.sukl.cz/erp/201704 -``` -> Starší namespace z roku 2017 — tato operace existuje od počátku IS eRecept. -> Na rozdíl od `NacistLekovyZaznam` (namespace `201912`) zde zůstává `201704`. +| Pole | Popis | +|------|-------| +| `ID_Dokladu` | Alfanumerický kód receptu (např. `PPIBVF93285E`) | +| `Stav` | Stav receptu: PREDEPSANY, CASTECNE_VYDANY, PLNE_VYDANY, ZRUSENY… | +| `PlatnostDo` | Datum konce platnosti receptu | +| `VypisDo` | Prodloužení platnosti výpisem | +| `Akutni` | Příznak akutní péče | +| `Rodina` | „Pro potřebu rodiny" / ad usum proprium | +| `Opakovani` | Počet výdejů u opakovacích receptů | +| `DruhPojisteni` | VEREJNE / OSTATNI | +| `ModryPruh` | Omamné/psychotropní látky | +| `Pozn` | Poznámka na receptu (max 1000 znaků) | +| `ZapocitatelnyDoplatekZbyvaDoLimitu` | Zbývá do limitu doplatků pacienta | +| `Zmena` / `Zalozeni` | Datetime poslední změny / vytvoření | + +### Údaje o úhradě léku (per PLP) + +| Pole | Popis | +|------|-------| +| `Uhrada` | ZAKLADNI / ZVYSENA / NEHRAZENY | +| `Prekroceni` | Překročení limitu | + +### Údaje o pacientovi + +| Pole | Popis | +|------|-------| +| `CP` | Číslo pojištěnce (rodné číslo) | +| `ZP` | Zdravotní pojišťovna (kód + název) | +| `Adresa` | Kompletní adresa pacienta | +| `Pohlavi` | M / Z | +| `Telefon` | Telefonní číslo | +| `Notifikace` | SMS / Email | + +### Údaje o předepisujícím + +| Pole | Popis | +|------|-------| +| `Odbornost` | Kód + název (např. 001 — všeobecné praktické lékařství) | +| `Email` | Email lékaře | + +### Zkrácený výdej + +Odpověď obsahuje i sekci `Vydej[]` se zkrácenou informací o výdejích — název lékárny, +jméno lékárníka (často „skryto"), datum vydeje, vydané léky. --- -## Porovnání s NacistLekovyZaznam +## Porovnání operací | | NacistPredpis | NacistLekovyZaznam | |---|---|---| | **Namespace** | `201704` | `201912` | | **Endpoint** | `/cuer/Lekar` | `/cuer/Lekar2` | | **SOAPAction** | `NacistPredpis` | `NacistLekovyZaznam` | -| **Identifikace** | ID receptu (alfanumerický kód) | jméno + datum narození pacienta | +| **Identifikace** | ID_Dokladu (alfanumerický kód receptu) | jméno + datum narození pacienta | | **Výsledek** | detail jednoho receptu | celý lékový záznam pacienta (roky) | -| **Velikost odpovědi** | ~3.7 KB | ~227 KB | +| **Velikost odpovědi** | ~3.5–4.5 KB | ~227 KB | --- -## Autentizace (dvojitá — stejná jako u všech operací) +## Autentizace (stejná jako u všech operací eReceptu) -### 1. Klientský certifikát (TLS mutual auth) -- Soubor: `AMBSUKL214235369G_31DEC2024.pfx` -- Formát: PKCS#12 (`.pfx`) -- Knihovna: `requests-pkcs12` → `Pkcs12Adapter` - -### 2. HTTP Basic Auth -- Uživatel: UUID lékaře -- Heslo: osobní heslo lékaře do portálu eRecept +| Parametr | Hodnota | +|----------|---------| +| Endpoint | `https://lekar-soap.erecept.sukl.cz/cuer/Lekar` | +| mTLS certifikát | `AMBSUKL214235369G_31DEC2024.pfx` (platnost do 31. 12. 2026) | +| HTTP Basic user | UUID lékaře `e08c89c6-2b1a-4eba-8ed9-4e3e63618379` | +| SOAP operace | `NacistPredpis` | +| XML namespace | `http://www.sukl.cz/erp/201704` | +| Verze zprávy | `202501A` | --- -## Struktura SOAP dotazu +## Zdroj ID_Dokladu — Medicus (Firebird) -```xml - - - - - - - - E08C89C6-2B1A-4EBA-8ED9-4E3E63618379 - 00214235367 - - - PPIBVF93285E - - - - - - - bf002578-4e0d-48f9-86b4-ea3e21f280ec - 202501A - 2026-04-06T07:24:04+00:00 - MEDICUS_____ - - - - - -``` - -### Parametry dotazu (ověřeno proti schema1.xsd verze 202501A) - -| Element | Povinný | Omezení | Poznámka | -|---|---|---|---| -| `Uzivatel` | ✅ ano | UUID formát | UUID lékaře přidělené SÚKL | -| `Pracoviste` | ✅ ano | — | IČP pracoviště lékaře | -| `ID_Dokladu` | ❌ volitelný* | 12–25 znaků | alfanumerický kód receptu | -| `ID_Podani` | ❌ volitelný* | UUID formát (36 znaků) | alternativní identifikátor podání | -| `ID_Zpravy` | ✅ ano | UUID formát | nové UUID pro každý dotaz | -| `Verze` | ✅ ano | — | aktuálně `202501A` | -| `Odeslano` | ✅ ano | ISO 8601 | čas odeslání s časovou zónou | -| `SW_Klienta` | ✅ ano | přesně 12 znaků | doplnit mezerami | - -> *Uvést právě jedno z: `ID_Dokladu` nebo `ID_Podani` — viz chybový kód L001. - -### Formát ID receptu (`ID_Dokladu`) - -Alfanumerický kód délky 12–25 znaků, např. `PPIBVF93285E`. -- Nesmí obsahovat písmena O (zaměnitelné s nulou), I (zaměnitelné s jedničkou), Y, Z -- Nesmí obsahovat malá písmena ani diakritiku -- Lékař jej vidí na průvodce receptu / v Medicusu - ---- - -## Struktura odpovědi (ověřeno proti schema1.xsd verze 202501A) +Alfanumerický kód receptu (ID_Dokladu) **není** v hromadném lékovém záznamu. +Nachází se v tabulce **`RECEPT_EPODANI`** v Medicusu: ``` -NacteniPredpisuOdpoved -├── Doklad -│ ├── ID_Dokladu alfanumerický kód receptu -│ ├── DatumVystaveni datum -│ ├── PlatnostDo datum -│ ├── VypisDo datum — volitelný (prodloužená platnost výpisem) -│ ├── Akutni boolean — volitelný -│ ├── Rodina boolean — volitelný -│ ├── Preshranicni boolean — volitelný -│ ├── Opakovani int — volitelný (opakovací předpis) -│ │ -│ ├── Pacient -│ │ ├── Totoznost -│ │ │ ├── Jmeno (Prijmeni, Jmena) -│ │ │ ├── DatumNarozeni -│ │ │ ├── Adresa (ulice, číslo, obec, PSC, okres...) -│ │ │ └── ROB způsob ztotožnění: ECD / JPDN / JPDNA -│ │ ├── CP číslo pojištěnce (rodné číslo) -│ │ ├── ZP Kod + Nazev zdravotní pojišťovny -│ │ ├── Telefon volitelný -│ │ ├── Notifikace SMS / Email — volitelný -│ │ └── Pohlavi M / Z — volitelný -│ │ -│ ├── Predepisujici -│ │ ├── Lekar Kod (UUID) + Jmeno -│ │ ├── ICZ 8 číslic -│ │ ├── ICP 8 číslic -│ │ ├── PZS Kod + Nazev + IC + Telefon + Adresa -│ │ ├── Telefon -│ │ ├── Email volitelný -│ │ └── Odbornost Kod + Nazev -│ │ -│ ├── Doporucujici volitelný — doporučující lékař -│ │ ├── Jmeno -│ │ ├── PZS -│ │ └── Odbornost -│ │ -│ ├── Revize volitelný — schválení revizním lékařem ZP -│ │ -│ ├── PLP[] předepsané léčivé přípravky (1 nebo více) -│ │ ├── Mnozstvi int, 1–9999 -│ │ ├── Navod dávkování, max 80 znaků (např. "1-1-0") -│ │ ├── Uhrada ZAKLADNI / ZVYSENA / NEHRAZENY -│ │ ├── HVLPReg volitelný — registrovaný lék -│ │ │ ├── Kod SÚKL kód (7 číslic, např. "0087076") -│ │ │ ├── ATC ATC kód (např. "R05CB15") -│ │ │ ├── Nazev např. "ERDOMED" -│ │ │ ├── Forma léková forma (např. "CPS DUR") -│ │ │ ├── Sila síla (např. "300MG") -│ │ │ ├── CestaPodani např. "POR" -│ │ │ └── Baleni počet kusů v balení -│ │ ├── HVLPNereg volitelný — neregistrovaný HVLP -│ │ ├── IPLP volitelný — individuálně připravovaný LP -│ │ ├── INN volitelný — generický název -│ │ ├── Prekroceni boolean — překročení limitu -│ │ ├── ID_LP_Zdroj int — ID zdroje LP -│ │ └── ID_LP UUID položky předpisu -│ │ -│ ├── Pozn poznámka, max 1000 znaků — volitelný -│ ├── Stav stav receptu (viz níže) -│ │ -│ ├── Vydej[] volitelný — výdeje z lékáren (zkrácená forma) -│ │ ├── ID_Dokladu ID výdeje -│ │ ├── ID_Dokladu_ERP ID receptu (zpětný odkaz) -│ │ ├── DatumVydeje datum -│ │ ├── NazevPZS název lékárny -│ │ ├── JmenoVydavajiciho jméno lékárníka (může být "skryto") -│ │ ├── VLP[] vydané léčivé přípravky -│ │ │ ├── Mnozstvi -│ │ │ ├── Nazev plný název léku (např. "ERDOMED 300MG CPS DUR 20") -│ │ │ ├── Kod SÚKL kód -│ │ │ ├── Navod -│ │ │ └── IdLpErp UUID zpětný odkaz na PLP -│ │ ├── Zmena datetime -│ │ └── Zalozeni datetime -│ │ -│ ├── Zmena datetime — poslední změna receptu -│ ├── Zalozeni datetime — vytvoření receptu -│ ├── DruhPojisteni VEREJNE / OSTATNI — volitelný -│ ├── ModryPruh boolean — volitelný (omamné látky) -│ ├── Papirovy boolean — volitelný -│ └── ZapocitatelnyDoplatekZbyvaDoLimitu decimal — zbývající limit doplatků pacienta -│ -└── Zprava - ├── ID_Zpravy nové UUID od serveru - ├── Verze - ├── Odeslano - ├── ID_Podani echo ID podání - └── Prijato datetime — čas přijetí na serveru +RECEPT.id_epodani → RECEPT_EPODANI.id +RECEPT_EPODANI.erp = ID_Dokladu (např. "PPIBVF93285E") ``` -### Popis jednotlivých polí odpovědi (zdroj: `documentationCuer.html` SÚKL 2025-04-24) +### SQL dotaz -| Pole | Typ | Povinné | Popis | -|---|---|---|---| -| `ID_Dokladu` | string 12–25 | ✅ | Identifikátor dokladu | -| `DatumVystaveni` | date | ✅ | Datum vystavení eReceptu | -| `PlatnostDo` | date | ✅ | Datum konce platnosti eReceptu určené při předepsání | -| `VypisDo` | date | ❌ | Datum prodloužení platnosti eReceptu (lékárna může platnost prodloužit výpisem) | -| `Akutni` | boolean | ❌ | Symbol Akutní péče nebo Neodkladná péče | -| `Rodina` | boolean | ❌ | Symbol „Pro potřebu rodiny" nebo „Ad usum proprium" (lékař předepisuje sobě/rodině) | -| `Preshranicni` | boolean | ❌ | Přeshraniční předpis *(nepoužívá se)* | -| `Opakovani` | long | ❌ | Celkový počet výdejů u opakovacích receptů | -| `Pacient` | nacteni_pacient_type | ✅ | Údaje o pacientovi | -| `Predepisujici` | nacteni_predepisujici_type | ✅ | Údaje o předepisujícím lékaři | -| `Doporucujici` | nacteni_doporucujici_type | ❌ | Údaje o doporučujícím lékaři | -| `Revize` | nacteni_revize_type | ❌ | Schválení revizním lékařem ZP *(nepoužívá se)* | -| `PLP` | nacteni_predepsany_lp_erp_type[] | ❌ | Předepsané položky (léky) | -| `Pozn` | string 1–1000 | ❌ | Poznámka na eReceptu | -| `UpozornitLekare` | enum | ❌ | Akceptace zpětného předání poznámky z lékárny — viz tabulka níže | -| `Stav` | enum | ✅ | Stav eReceptu — viz tabulka níže | -| `Vydej` | nacteni_zkraceny_vydej_erp_doklad_odpoved_type[] | ❌ | Seznam výdejů uskutečněných dosud na tento eRecept | -| `Zmena` | dateTime | ✅ | Datum a čas poslední změny eReceptu | -| `Zalozeni` | dateTime | ✅ | Datum a čas založení eReceptu | -| `DruhPojisteni` | enum | ❌ | Druh pojištění pacienta — viz tabulka níže | -| `ModryPruh` | boolean | ❌ | Předpis na LP s obsahem vysoce návykové látky (omamné/psychotropní) | -| `Papirovy` | boolean | ❌ | Digitalizovaný papírový předpis *(nepoužívá se)* | -| `ZapocitatelnyDoplatekZbyvaDoLimitu` | decimal 0–999999.99 | ❌ | Částka zbývající do max. ročního limitu započitatelných doplatků pacienta (po překročení limitu ZP hradí doplatky za pacienta) | +```sql +SELECT DISTINCT ep.erp, r.datum, r.lek, r.dop, + TRIM(kar.prijmeni) AS prijmeni, TRIM(kar.jmeno) AS jmeno +FROM recept r +JOIN recept_epodani ep ON r.id_epodani = ep.id +JOIN kar ON r.idpac = kar.idpac +WHERE r.datum >= '2025-01-01' AND ep.erp IS NOT NULL +ORDER BY r.datum DESC +``` + +### Statistika (duben 2026) + +- **13 571** receptů s ERP kódem od 1. 1. 2025 +- **13 578** receptů celkem (7 bez ERP kódu — papírové/neodeslané) --- -### Hodnoty stavu receptu (`Stav`) — enum `stav_elektronickeho_receptu` +## 08StahnoutPredpisy.py — hlavní skript -| Hodnota | Popis | -|---|---| -| `KE_SCHVALENI` | *(Nepoužívá se)* Ke schválení zdravotní pojišťovnou | -| `ZAMITNUTY` | *(Nepoužívá se)* Zamítnutý zdravotní pojišťovnou | -| `PREDEPSANY` | Předepsaný — výchozí stav po založení, lze provést výdej ✅ | -| `PRIPRAVOVANY` | Připravovaný — lékárna pracuje na výdeji (připravuje IPLP nebo objednala HVLP u distributora); výdej může provést jen tato lékárna; používá se i pro blokaci při technickém výpadku | -| `CASTECNE_VYDANY` | Částečně vydaný — výdej proběhl, ale může následovat další (prodloužená platnost nebo opakovací recept s dalšími opakováními) | -| `PLNE_VYDANY` | Plně vydaný — výdej dokončen, žádný další výdej nenásleduje ✅ | -| `NEDOKONCENY_VYDEJ` | Nedokončený výdej — lékárna nevydala léčivo, ale zaznamenala doplňující informaci (důvod nevydání); lze použít pouze u prvního takového výdeje | -| `UZAVRENY` | *(Nepoužívá se)* Uzavřený | - -### Hodnoty druhu pojištění (`DruhPojisteni`) — enum `druh_pojisteni` - -| Hodnota | Popis | -|---|---| -| `NEZADANO` | Nezadáno | -| `VEREJNE` | Veřejné zdravotní pojištění (běžný případ v ČR) | -| `SMLUVNI_PRIPOJISTENI` | Smluvní připojištění | -| `CESTOVNI_PRIPOJISTENI` | Cestovní zdravotní připojištění | -| `POJISTENI_EU` | Pojištění EU (pacient z jiného státu EU) | - -### Hodnoty upozornění lékaře (`UpozornitLekare`) — enum `upozornit_lekare` - -Lékárník může k výdeji přidat poznámku pro předepisujícího lékaře. Toto pole říká, zda lékař tuto funkci akceptoval a jak urgentně chce být informován. - -| Hodnota | Popis | -|---|---| -| `BEZODKLADNE` | Upozornit lékaře bezodkladně (akceptuje BEZODKLADNE i PRISTI_NAVSTEVA) | -| `PRISTI_NAVSTEVA` | Upozornit lékaře při příští návštěvě pacienta (akceptuje pouze PRISTI_NAVSTEVA) | -| *(neuvedeno)* | Lékař neakceptoval zpětné předávání poznámek z lékárny | - -### Hodnoty způsobu ztotožnění (`ROB`) - -| Hodnota | Popis | -|---|---| -| `ECD` | elektronicky čitelný doklad (občanský průkaz/pas) | -| `JPDN` | jednoznačný dle jména, příjmení, data a adresy | -| `JPDNA` | jednoznačný dle jména, příjmení a data (bez adresy) | - ---- - -## HTTP hlavičky +### Parametry (editovat přímo v souboru) ```python -headers = { - "Content-Type": 'text/xml; charset="UTF-8"', - "SOAPAction": '"NacistPredpis"', # uvozovky jsou součástí hodnoty! - "User-Agent": "Medicus" -} +LIMIT = 100 # max počet receptů ke stažení +DATUM_OD = "2025-01-01" # recepty od tohoto data +PRIJMENI = ["Buzalka"] # filtr příjmení (list), nebo None = všichni ``` +### Co dělá + +1. Připojí se k Firebirdu, načte unikátní ERP kódy (deduplikované — jeden recept může mít více léků) +2. Pro každý ERP kód zavolá `NacistPredpis` přes SOAP API +3. Uloží XML odpověď do `xml_archive/YYYY-MM-DD/{ERP_KOD}.xml` +4. Chybové odpovědi uloží jako `{ERP_KOD}_CHYBA.xml` +5. Pauza 5 sekund mezi voláními + +### Výstup v konzoli + +``` +[ 1/55] Buzalka Vladimír PPM5HM49EBF9 OK 3.6 KB EZETIMIB/ATORVASTATIN STADA 10MG/20MG TB +[ 25/55] Buzalka Vladimír POMOTIAJ77PI CHYBA HTTP 500 D003 - Předpis zrušen... +``` + +### Ošetření chyb + +| Kód | Popis | Chování skriptu | +|-----|-------|-----------------| +| **D003** | Předpis byl zrušen lékařem | Uloží `_CHYBA.xml`, pokračuje dál | +| HTTP 500 | SOAP Fault (obecný) | Uloží `_CHYBA.xml`, pokračuje dál | +| Timeout / Exception | Síťová chyba | Vypíše EXCEPTION, pokračuje dál | + --- -## Závislosti (Python) +## Ověřeno (14. 4. 2026) -``` -requests -requests-pkcs12 -``` +Testovací běh na receptech pacienta Buzalka od 1. 1. 2025: + +- **55** unikátních ERP kódů nalezeno v Medicusu +- **51** úspěšně staženo (OK) +- **4** chyby D003 (zrušené recepty) +- XML uloženy do `xml_archive/2026-04-14/` --- -## Soubor skriptu +## Další kroky (plán) -`NacistPredpis_FUNKCNI.py` — funkční implementace, ověřeno 6. 4. 2026. +1. **MySQL tabulka `recept`** — uložit detail receptu (stav, platnost, úhrada, pojišťovna…) +2. **Parsování XML** — extrakce dat z odpovědí do MySQL +3. **Inkrementální stahování** — procházet jen nové recepty od posledního běhu +4. **Stažení všech 13 571 receptů** od 1. 1. 2025 (odhad: ~19 hodin při 5s pauze) --- -## Zdroje dokumentace SÚKL +## XSD zdroje -| Soubor | Datum | Obsah | -|---|---|---| -| `MedicusDebug/eR_PostData.xml` | 2026-04-05 | reálný dotaz z Medicusu | -| `MedicusDebug/eR_SuklMesage_*.mime` | 2026-04-05 | HTTP hlavičky + tělo dotazu | -| `MedicusDebug/eR_SuklMesage_*_Resp.xml` | 2026-04-05 | reálná odpověď serveru | -| `CUERLekar.wsdl` | 2025-04-24 | definice operace NacistPredpis | -| `schema1.xsd` | 2025-04-24 | kompletní XSD schéma dotazu i odpovědi | +Schéma verze `202501A`, soubory v `Dokumentace/2025-04-24/WSDL_XSD/`: + +| Soubor | Obsah | +|--------|-------| +| `PRIORITNI_WEBOVE_SLUZBY/schema1.xsd` | `NacteniPredpisuDotaz`, `identifikace_dokladu_type`, `nacteni_predpisu_erp_odpoved_type` | +| `NEPRIORITNI_WEBOVE_SLUZBY/CuerSchema.xsd` | `hvlp_type`, `zprava_type`, `jmeno_osoby_type` |