# 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 | ✓ | 1–9999 |
| `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` |
