Files
recept/LékovýZáznamWithClaude/LEKOVY_ZAZNAM_DB.md
T
2026-04-06 21:37:25 +02:00

294 lines
10 KiB
Markdown
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.
# Lékový záznam eRecept → MySQL
Popis pipeline pro stažení lékového záznamu pacienta z eRecept SÚKL API
a jeho uložení do relační databáze MySQL.
---
## Soubory
| Soubor | Co dělá |
|--------|---------|
| `05UlozitOdpoved.py` | Zavolá SOAP API, stáhne XML odpověď, uloží ji do `odpoved_lekovy_zaznam.xml` |
| `06UlozitDoMySQL.py` | Naparsuje uložené XML, vytvoří/přepíše tabulky v MySQL, vloží data |
### Typické spuštění
```bash
# 1. stáhnout čerstvá data z eReceptu
python 05UlozitOdpoved.py
# 2. uložit do databáze
python 06UlozitDoMySQL.py
# nebo předat jiný XML soubor
python 06UlozitDoMySQL.py C:\cesta\k\jine_odpovedi.xml
```
---
## 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**.
---
## Dotaz — parametry `NacistLekovyZaznamLekarDotaz`
```xml
<Doklad>
<Pristupujici>
<Uzivatel>UUID lékaře</Uzivatel>
<Pracoviste>IČP ordinace</Pracoviste>
</Pristupujici>
<PocetZnakuATC>7</PocetZnakuATC> <!-- 5 nebo 7 -->
<PocetMesicu>60</PocetMesicu> <!-- max 99 -->
<Pacient>
<Totoznost>
<Jmeno><Prijmeni>...</Prijmeni><Jmena>...</Jmena></Jmeno>
<DatumNarozeni>YYYY-MM-DD</DatumNarozeni>
</Totoznost>
</Pacient>
</Doklad>
```
---
## Struktura XML odpovědi
Dle XSD `Cuer2Schema.xsd` + `CuerSchema.xsd`, verze `202501A`:
```
NacistLekovyZaznamOdpoved
├── Doklad
│ ├── Pacient jméno + datum narození
│ ├── PredepisujiciSeznam lékaři, kteří předepisovali
│ ├── VydavajiciSeznam lékárny, kde byl výdej
│ ├── PredpisSeznam
│ │ └── Predpis × N co bylo předepsáno
│ ├── VydejSeznam
│ │ └── Vydej × N co bylo vydáno v lékárně
│ └── DuplicitaSeznam duplicitní výdeje
└── Zprava metadata odpovědi (ID, čas, verze SW)
```
### 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 (receptura), Slozka[] |
| `INN` | Předpis účinnou látkou (genericky) | Nazev, Forma, Sila, Baleni |
#### IPLP — dvojí uložení receptury
- **Předpis** (`PredpisSeznam/Predpis/IPLP`): lékař zadal recepturu jako **volný text** v `PostupPripravy`. Prvky `Slozka` zde lékař typicky nevyplňuje.
- **Výdej** (`VydejSeznam/Vydej/IPLP`): lékárna při přípravě zaznamenala **strukturované složky** (`Slozka` s množstvím, jednotkou a názvem suroviny). Kvalita dat závisí na lékárně — některé rozkládají do detailu (suroviny, obal, taxa laborum), jiné evidují jen 1 ks jako celek.
---
## Databázové schéma — `medicus` (MySQL 9.6)
Všechny délky a datové typy jsou přesně dle XSD, **ne odhady**.
Varianta B — lék je denormalizován přímo do řádku předpisu/výdeje.
### Relační diagram
```
zprava (1)
├── 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)
```
### Tabulka `zprava`
Jeden řádek = jedna odpověď API (jedno volání `NacistLekovyZaznam`).
| Sloupec | Typ | Zdroj v XSD |
|---------|-----|-------------|
| `id_zpravy` | CHAR(36) UNIQUE | `zprava_type.ID_Zpravy` |
| `verze` | VARCHAR(20) | `zprava_type.Verze` |
| `odeslano` | DATETIME | `zprava_type.Odeslano` |
| `aplikace` | VARCHAR(512) | `zprava_odpoved_type.Aplikace` |
| `id_podani` | CHAR(36) | `zprava_odpoved_type.ID_Podani` |
| `prijato` | DATETIME | `zprava_odpoved_type.Prijato` |
| `pacient_prijmeni` | VARCHAR(35) | `jmeno_osoby_type.Prijmeni` |
| `pacient_jmena` | VARCHAR(24) | `jmeno_osoby_type.Jmena` |
| `pacient_datum_narozeni` | DATE | `lz_totoznost_type.DatumNarozeni` |
| `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) | ✓ | UUID z eReceptu, globálně unikátní |
| `kod_predepisujiciho` | VARCHAR(36) | ✓ | UUID lékaře |
| `datum_vystaveni` | DATE | ✓ | |
| `mnozstvi` | SMALLINT | ✓ | 19999 |
| `navod` | VARCHAR(80) | ✓ | max 80 znaků dle XSD |
| `opakovani` | INT | | opakování předpisu |
| `modry_pruh` | TINYINT(1) | | modrý pruh (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 (jen HVLP) |
| `nazev` | VARCHAR(200) | | max 200 — INN má nejdelší název |
| `forma` | VARCHAR(27) | | léková forma |
| `sila` | VARCHAR(24) | | síla přípravku |
| `cesta_podani` | VARCHAR(15) | | POR, INH, … |
| `baleni` | VARCHAR(22) | | **string**, ne číslo (může být "100 ks") |
| `postup_pripravy` | VARCHAR(4000) | | receptura IPLP jako volný text |
### Tabulka `predpis_slozka`
Dle `slozka_iplp_predpis_type`. Řádky vznikají jen pro IPLP předpisy se strukturovanými složkami.
| Sloupec | Typ | NOT NULL | Poznámka |
|---------|-----|----------|----------|
| `predpis_id` | INT | ✓ | FK → predpis.id |
| `mnozstvi` | DECIMAL(15,6) | ✓ | přesné množství suroviny |
| `jednotka` | ENUM('g','ks') | ✓ | |
| `nazev` | VARCHAR(200) | ✓ | název suroviny / přípravku |
| `surovina` | CHAR(7) | | kód suroviny v registru SÚKL |
| `hvlp_reg` | CHAR(7) | | pokud je složka registrovaný HVLP |
### Tabulka `vydej`
Dle `lz_nacteni_vydany_lp_erp_type`.
| Sloupec | Typ | NOT NULL | Poznámka |
|---------|-----|----------|----------|
| `id_lp_vydej` | CHAR(36) | ✓ | UUID výdeje |
| `id_lp_predpis` | CHAR(36) | | FK → predpis.id_lp_predpis, 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) | ✓ | číslo šarže |
| `seriove_cislo` | VARCHAR(20) | | sériové číslo (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 (HVLP) 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`
Dle `slozka_iplp_type`. Jako `predpis_slozka`, ale navíc obsahuje `hrazeno_zp`.
| Sloupec | Typ | NOT NULL | Poznámka |
|---------|-----|----------|----------|
| `vydej_id` | INT | ✓ | FK → vydej.id |
| `mnozstvi` | DECIMAL(15,6) | ✓ | |
| `jednotka` | ENUM('g','ks') | ✓ | |
| `nazev` | VARCHAR(200) | ✓ | název suroviny |
| `hrazeno_zp` | DECIMAL(9,2) | | částka hrazená zdravotní pojišťovnou |
| `surovina` | CHAR(7) | | |
| `hvlp_reg` | CHAR(7) | | |
---
## Klíčové vlastnosti importu (`06UlozitDoMySQL.py`)
- **Idempotentní**: `INSERT IGNORE` na `id_lp_predpis` / `id_lp_vydej` — opakované spuštění se stejným XML nepřidá duplikáty.
- **Víc pacientů**: `zprava.id_zpravy` je UNIQUE — každé volání API (jiný pacient, jiný čas) vytvoří nový řádek v `zprava`. Předpisy a výdeje napříč pacienty se agregují v `predpis` / `vydej`.
- **Schema se přepisuje**: `vytvor_schema()` vždy DROPne a znovu vytvoří všech 5 tabulek. Při produkčním použití (více pacientů) tuto část odstraňte a spouštějte DDL jen jednou při inicializaci.
---
## 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
SELECT p.datum_vystaveni, p.nazev, p.atc, p.navod
FROM predpis p
LEFT JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis
WHERE v.id_lp_vydej IS NULL;
-- IPLP magistraliter — kompletní receptury s frekvencí
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;
-- IPLP výdeje — strukturované složky (kde je lékárna zadala)
SELECT v.datum_vydeje, v.nazev AS pripravek,
GROUP_CONCAT(s.mnozstvi, ' ', s.jednotka, ' ', s.nazev
ORDER BY s.id SEPARATOR ' + ') AS slozeni
FROM vydej v
JOIN vydej_slozka s ON s.vydej_id = v.id
WHERE v.typ_leku = 'IPLP'
GROUP BY v.id
ORDER BY v.datum_vydeje DESC;
-- nejčastěji používané suroviny v magistrech (ze strany výdeje)
SELECT nazev, jednotka, COUNT(*) AS pocet
FROM vydej_slozka
GROUP BY nazev, jednotka
ORDER BY pocet DESC;
-- párování: co předepsal lékař vs. co lékárna vydala (generická záměna)
SELECT p.datum_vystaveni,
p.nazev AS predepsano, p.atc,
v.nazev AS vydano, v.datum_vydeje
FROM predpis p
JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis
WHERE p.nazev <> v.nazev;
```
---
## Závislosti (Python)
```
requests
requests-pkcs12
pymysql
```
```bash
pip install requests requests-pkcs12 pymysql
```
---
## XSD zdroje
Schéma verze `202501A`, soubory v `Dokumentace/2025-04-24/WSDL_XSD/NEPRIORITNI_WEBOVE_SLUZBY/`:
| Soubor | Obsah |
|--------|-------|
| `Cuer2Schema.xsd` | Typy specifické pro lékový záznam: `NacistLekovyZaznamOdpoved`, `lz_nacteni_predepsany_lp_erp_type`, `lz_nacteni_vydany_lp_erp_type`, `slozka_iplp_*` |
| `CuerSchema.xsd` | Společné typy: `hvlp_type`, `zprava_odpoved_type`, `zprava_type`, `jmeno_osoby_type`, `jednotka` |