# Lékový záznam eRecept → MySQL Pipeline pro hromadné stažení lékových záznamů všech registrovaných pacientů z eRecept SÚKL API a jejich uložení do relační databáze MySQL. --- ## Soubory | Soubor | Co dělá | |--------|---------| | `05UlozitOdpoved.py` | Stáhne XML pro **jednoho** pacienta (ruční test/ladění) | | `06UlozitDoMySQL.py` | DDL schématu, parsování XML, import do MySQL — používá se jako knihovna i samostatně | | `07StahnoutVsechny.py` | **Hlavní skript** — načte pacienty z Medicusu, stáhne lékové záznamy, uloží XML i DB záznamy | ``` LékovýZáznamWithClaude/ ├── 05UlozitOdpoved.py ├── 06UlozitDoMySQL.py ├── 07StahnoutVsechny.py ├── LEKOVY_ZAZNAM_DB.md ├── Logs/ ← log každého běhu (UTF-8, YYYY-MM-DD_HH-MM-SS.log) ├── Tests/ ← starší vývojové skripty └── xml_archive/ ← archiv XML odpovědí (YYYY-MM-DD/Prijmeni_Jmena_datnar.xml) ``` --- ## Typické spuštění ```bash # Čistý start — jednou (DROP + CREATE schéma, importuje odpoved_lekovy_zaznam.xml) python 06UlozitDoMySQL.py # Hromadné stažení všech registrovaných pacientů python 07StahnoutVsechny.py # Pouze vybraná příjmení (testování / rodina) python 07StahnoutVsechny.py --prijmeni Buzalka,Buzalková,Kusinová # Dávkování po částech python 07StahnoutVsechny.py --offset 100 --limit 50 ``` --- ## Autentizace (eRecept SÚKL, ostrý provoz) | Parametr | Hodnota | |----------|---------| | Endpoint | `https://lekar-soap.erecept.sukl.cz/cuer/Lekar2` | | 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 | `NacistLekovyZaznam` | | XML namespace | `http://www.sukl.cz/erp/201912` | | Verze zprávy | `202501A` | Certifikát = identifikace **ordinace**, UUID+heslo = identifikace **lékaře jako osoby**. --- ## Zdroj pacientů — Medicus (Firebird) Pacienti se načítají přímo z `medicus.fdb` jako registrovaní pacienti ordinace: ``` DSN: localhost:c:\medicus 3\data\medicus.fdb User: SYSDBA / masterkey Charset: win1250 IČP: 09305001 (odbornost 001 — praktický lékař) ``` Podmínky registrace: `vyrazen = 'N'`, `registr.priznak IN ('V','D','A')`, registrace platná k dnešnímu datu. --- ## Logika přírůstkového stahování ``` první stažení pacienta → PocetMesicu = 60 (maximum, 5 let) opakované stažení → ceil(dny od posledního stažení / 30) + 1 ``` - Překryv 1 měsíce zajistí, že nepřijdeme o nic na hranici období. - `INSERT IGNORE` na `id_lp_predpis` / `id_lp_vydej` zabrání duplikátům. - Pauza mezi voláními API: **náhodně 10–20 sekund**. --- ## Ošetření chyb API Pacienti, kteří nejsou v eReceptu ztotožněni (nikdy nebyli v lékárně s e-receptem), vrátí SOAP Fault `Z002`. Skript: 1. Zachytí chybu (HTTP 500 nebo SOAP Fault v těle odpovědi) 2. Uloží text chyby do `pacient.poznamka` 3. Pokračuje dalším pacientem Při příštím úspěšném stažení se `poznamka` automaticky vymaže. ```sql -- přehled pacientů s chybou SELECT prijmeni, jmena, datum_narozeni, poznamka FROM pacient WHERE poznamka IS NOT NULL; ``` --- ## XML archiv Každá odpověď API se uloží jako soubor: ``` xml_archive/YYYY-MM-DD/Prijmeni_Jmena_YYYY-MM-DD.xml ``` Cesta je zároveň uložena v `zprava.xml_soubor`. Účel: možnost re-parsování při budoucích změnách schématu bez nutnosti znovu volat API. --- ## Výstup do konzole a logů Konzole zobrazuje jen jeden řádek na pacienta: ``` [ 1/1621] Abohamda Horia OK 168p 252v 247 KB [ 5/1621] Alakbarov Farid CHYBA Z002 - Lekovy zaznam ne... ``` Kompletní detaily (počty nových záznamů, ID zprávy, doba čekání) jsou v: ``` Logs/YYYY-MM-DD_HH-MM-SS.log ``` --- ## Databázové schéma — `medicus` (MySQL) Všechny délky a datové typy jsou přesně dle XSD, **ne odhady**. Lék je denormalizován přímo do řádku předpisu/výdeje. ### Relační diagram ``` pacient (1) └── zprava (N) -- každé volání API = 1 zpráva ├── predpis (N) │ └── predpis_slozka (N) -- složky IPLP z předpisu └── vydej (N) └── vydej_slozka (N) -- složky IPLP z výdeje vydej.id_lp_predpis → predpis.id_lp_predpis (párování výdeje s předpisem) predpis.kod_predepisujiciho → predepisujici.lekar_kod vydej.kod_vydavajiciho → vydavajici.lekarnik_kod ``` ### Tabulka `pacient` Zrcadlo registrovaných pacientů z Medicusu. Aktualizuje se při každém běhu `07`. | Sloupec | Typ | Poznámka | |---------|-----|----------| | `id` | INT PK | | | `idpac` | INT UNIQUE | IDPAC z tabulky KAR v Medicusu | | `prijmeni` | VARCHAR(35) | | | `jmena` | VARCHAR(24) | | | `datum_narozeni` | DATE | | | `aktivni` | TINYINT(1) | 0 = přeskočit při hromadném běhu | | `poznamka` | VARCHAR(500) | poslední chyba API; NULL = OK | ### Tabulka `zprava` Jeden řádek = jedno volání API (jeden pacient, jeden čas). | Sloupec | Typ | Poznámka | |---------|-----|----------| | `id_zpravy` | CHAR(36) UNIQUE | UUID z eReceptu | | `pacient_id` | INT FK → pacient | | | `verze` | VARCHAR(20) | verze zprávy (202501A) | | `odeslano` | DATETIME | čas odeslání dotazu | | `aplikace` | VARCHAR(512) | SW SÚKL serveru | | `id_podani` | CHAR(36) | UUID podání | | `prijato` | DATETIME | čas přijetí odpovědi | | `pacient_prijmeni` | VARCHAR(35) | z XML odpovědi | | `pacient_jmena` | VARCHAR(24) | z XML odpovědi | | `pacient_datum_narozeni` | DATE | z XML odpovědi | | `xml_soubor` | VARCHAR(255) | relativní cesta k archivu | | `stazeno` | DATETIME | automaticky při INSERT | ### Tabulka `predpis` Dle `lz_nacteni_predepsany_lp_erp_type`. | Sloupec | Typ | NOT NULL | Poznámka | |---------|-----|----------|----------| | `id_lp_predpis` | CHAR(36) UNIQUE | ✓ | UUID z eReceptu | | `zprava_id` | INT FK | ✓ | | | `kod_predepisujiciho` | VARCHAR(36) | ✓ | UUID lékaře | | `datum_vystaveni` | DATE | ✓ | | | `mnozstvi` | SMALLINT | ✓ | 1–9999 | | `navod` | VARCHAR(80) | ✓ | | | `opakovani` | INT | | | | `modry_pruh` | TINYINT(1) | | návykové látky | | `typ_leku` | ENUM | | HVLPReg / HVLPNereg / IPLP / INN | | `lek_kod` | CHAR(7) | | kód SÚKL (jen HVLP) | | `atc` | VARCHAR(7) | | ATC kód | | `nazev` | VARCHAR(200) | | | | `forma` | VARCHAR(27) | | | | `sila` | VARCHAR(24) | | | | `cesta_podani` | VARCHAR(15) | | POR, INH, … | | `baleni` | VARCHAR(22) | | string, např. "100 ks" | | `postup_pripravy` | VARCHAR(4000) | | receptura IPLP | ### Tabulka `predpis_slozka` Složky IPLP předpisů (lékař typicky nevyplňuje, kvalitní data spíše u výdeje). | Sloupec | Typ | NOT NULL | |---------|-----|----------| | `predpis_id` | INT FK | ✓ | | `mnozstvi` | DECIMAL(15,6) | ✓ | | `jednotka` | ENUM('g','ks') | ✓ | | `nazev` | VARCHAR(200) | ✓ | | `surovina` | CHAR(7) | | | `hvlp_reg` | CHAR(7) | | ### Tabulka `vydej` Dle `lz_nacteni_vydany_lp_erp_type`. | Sloupec | Typ | NOT NULL | Poznámka | |---------|-----|----------|----------| | `id_lp_vydej` | CHAR(36) UNIQUE | ✓ | UUID výdeje | | `zprava_id` | INT FK | ✓ | | | `id_lp_predpis` | CHAR(36) FK | | NULL = výdej bez e-předpisu | | `kod_vydavajiciho` | VARCHAR(36) | ✓ | UUID lékárníka | | `datum_vydeje` | DATE | ✓ | | | `mnozstvi` | DECIMAL(6,2) | ✓ | desetinné — např. 0.5 balení | | `navod` | VARCHAR(80) | ✓ | | | `exspirace` | DATE | | exspirace šarže | | `sarze` | VARCHAR(50) | ✓ | | | `seriove_cislo` | VARCHAR(20) | | léky s el. sledováním | | `pozn` | VARCHAR(1000) | | poznámka lékárníka | | `typ_leku` | ENUM | | HVLPReg / HVLPNereg / IPLP | | `lek_kod` | CHAR(7) | | kód SÚKL nebo KodVZP (IPLP) | | `atc` | VARCHAR(7) | | jen HVLP | | `nazev` | VARCHAR(146) | | | | `forma` | VARCHAR(27) | | | | `sila` | VARCHAR(24) | | | | `cesta_podani` | VARCHAR(15) | | | | `postup_pripravy` | VARCHAR(4000) | | receptura IPLP | ### Tabulka `vydej_slozka` Jako `predpis_slozka`, navíc `hrazeno_zp`. Data lékáren — kvalita závisí na lékárně. | Sloupec | Typ | NOT NULL | Poznámka | |---------|-----|----------|----------| | `vydej_id` | INT FK | ✓ | | | `mnozstvi` | DECIMAL(15,6) | ✓ | | | `jednotka` | ENUM('g','ks') | ✓ | | | `nazev` | VARCHAR(200) | ✓ | | | `hrazeno_zp` | DECIMAL(9,2) | | částka hrazená ZP | | `surovina` | CHAR(7) | | | | `hvlp_reg` | CHAR(7) | | | ### Tabulka `predepisujici` Lékaři, kteří pacientovi předepisovali (ze všech ordinací). | Sloupec | Typ | Poznámka | |---------|-----|----------| | `lekar_kod` | CHAR(36) UNIQUE | UUID lékaře = predpis.kod_predepisujiciho | | `prijmeni` | VARCHAR(35) | | | `jmena` | VARCHAR(24) | | | `icz` | CHAR(8) | IČZ zdravotnického zařízení | | `icp` | CHAR(8) | IČP pracoviště | | `pzs_nazev` | VARCHAR(200) | název zdravotnického zařízení | | `ulice` | VARCHAR(150) | | | `mesto` | VARCHAR(100) | | | `psc` | CHAR(5) | | | `telefon` | VARCHAR(20) | | ### Tabulka `vydavajici` Lékárníci / lékárny, kde byl výdej. | Sloupec | Typ | Poznámka | |---------|-----|----------| | `lekarnik_kod` | CHAR(36) UNIQUE | UUID lékárníka = vydej.kod_vydavajiciho | | `prijmeni` | VARCHAR(35) | | | `jmena` | VARCHAR(24) | | | `pzs_nazev` | VARCHAR(200) | název lékárny | | `ulice` | VARCHAR(150) | | | `mesto` | VARCHAR(100) | | | `psc` | CHAR(5) | | | `telefon` | VARCHAR(20) | | --- ## Typy léků v Predpis i Vydej Každý předpis / výdej obsahuje právě **jeden** z těchto elementů: | Typ | Popis | Klíčová pole | |-----|-------|-------------| | `HVLPReg` | Registrovaný hromadně vyráběný LP | Kod (SÚKL), ATC, Nazev, Forma, Sila, Baleni | | `HVLPNereg` | Neregistrovaný HVLP | stejná struktura jako HVLPReg | | `IPLP` | Individuálně připravovaný LP (magistraliter) | Nazev, PostupPripravy, Slozka[] | | `INN` | Předpis účinnou látkou (genericky) | Nazev, Forma, Sila, Baleni | #### IPLP — dvojí uložení receptury - **Předpis**: lékař zadal recepturu jako volný text v `PostupPripravy`. Složky typicky nevyplněny. - **Výdej**: lékárna zaznamenala strukturované složky (`Slozka` s množstvím, jednotkou, názvem suroviny). Kvalita dat závisí na lékárně. --- ## Užitečné analytické dotazy ```sql -- nejčastěji předepisované ATC skupiny za posledních 12 měsíců SELECT atc, nazev, COUNT(*) AS pocet, MAX(datum_vystaveni) AS naposledy FROM predpis WHERE datum_vystaveni >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) GROUP BY atc, nazev ORDER BY pocet DESC; -- co bylo předepsáno ale nevyzvednuto (non-compliance) SELECT pac.prijmeni, pac.jmena, p.datum_vystaveni, p.nazev, p.atc, p.navod FROM predpis p JOIN zprava z ON z.id = p.zprava_id JOIN pacient pac ON pac.id = z.pacient_id LEFT JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis WHERE v.id_lp_vydej IS NULL ORDER BY p.datum_vystaveni DESC; -- lékový záznam konkrétního pacienta (předpisy + výdeje) SELECT p.datum_vystaveni, p.typ_leku, p.nazev, p.atc, p.navod, v.datum_vydeje, v.mnozstvi AS vydano FROM pacient pac JOIN zprava z ON z.pacient_id = pac.id JOIN predpis p ON p.zprava_id = z.id LEFT JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis WHERE pac.prijmeni = 'Buzalka' AND pac.jmena = 'Vladimír' ORDER BY p.datum_vystaveni DESC; -- IPLP magistraliter — kompletní receptury s frekvencí (napříč pacienty) SELECT p.nazev, p.postup_pripravy, COUNT(*) AS pocet_predpisu FROM predpis p WHERE p.typ_leku = 'IPLP' GROUP BY p.nazev, p.postup_pripravy ORDER BY pocet_predpisu DESC; -- nejčastěji používané suroviny v magistrech SELECT nazev, jednotka, COUNT(*) AS pocet FROM vydej_slozka GROUP BY nazev, jednotka ORDER BY pocet DESC; -- generická záměna: co předepsal lékař vs. co lékárna vydala SELECT pac.prijmeni, pac.jmena, p.datum_vystaveni, p.nazev AS predepsano, p.atc, v.nazev AS vydano, v.datum_vydeje FROM pacient pac JOIN zprava z ON z.pacient_id = pac.id JOIN predpis p ON p.zprava_id = z.id JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis WHERE p.nazev <> v.nazev; -- pacienti s chybou API (neztotožněni) SELECT prijmeni, jmena, datum_narozeni, poznamka FROM pacient WHERE poznamka IS NOT NULL ORDER BY prijmeni; ``` --- ## Závislosti (Python) ``` requests requests-pkcs12 pymysql fdb ``` ```bash pip install requests requests-pkcs12 pymysql fdb ``` --- ## XSD zdroje Schéma verze `202501A`, soubory v `Dokumentace/2025-04-24/WSDL_XSD/NEPRIORITNI_WEBOVE_SLUZBY/`: | Soubor | Obsah | |--------|-------| | `Cuer2Schema.xsd` | `NacistLekovyZaznamOdpoved`, `lz_nacteni_predepsany_lp_erp_type`, `lz_nacteni_vydany_lp_erp_type`, `slozka_iplp_*` | | `CuerSchema.xsd` | `hvlp_type`, `zprava_odpoved_type`, `zprava_type`, `jmeno_osoby_type`, `jednotka` |