Files
ordinaceprojekt/Recepty/Dotazy/DOTAZY.md
T
Vladimir Buzalka adb84523cd Přidán podprojekt Recepty (eRecept SÚKL)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 07:06:17 +02:00

8.1 KiB

Dotazy — přehled lékového záznamu pacienta

Skripty pro zobrazení a export lékového záznamu konkrétního pacienta z MySQL databáze medicus.
Pacient se identifikuje rodným číslem — to se vyhledá v lokální Firebird databázi Medicusu, odkud se získá příjmení a datum narození, a teprve těmito dvěma hodnotami se najde pacient v MySQL.


Soubory

Soubor Co dělá
prehled_pacienta.py Konzolový výpis — lékaři + předpisy pacienta
prehled_pacienta_excel.py Export do formátovaného souboru Excel (.xlsx)

Nastavení (obě skripty)

Na začátku každého souboru jsou tři proměnné:

RODNE_CISLO = "440802/018"    # rodné číslo — funguje s lomítkem i bez: "4408020183"
DATUM_OD    = "01.01.2025"    # předpisy od tohoto data; None = všechny předpisy
VYSTUP_DIR  = None            # pouze excel: složka výstupu; None = stejná jako skript

Spuštění

# Konzolový výpis
.venv\Scripts\python.exe Dotazy\prehled_pacienta.py

# Export do Excelu
.venv\Scripts\python.exe Dotazy\prehled_pacienta_excel.py

Zdroje dat

1. Firebird — Medicus (medicus.fdb)

Slouží výhradně k identifikaci pacienta podle rodného čísla.

DSN:      localhost:c:\medicus 3\data\medicus.fdb
User:     SYSDBA / masterkey
Charset:  win1250
Tabulka:  KAR

Dotaz:

SELECT KAR.PRIJMENI, KAR.JMENO, KAR.DATNAR
FROM KAR WHERE KAR.RODCIS = ?

Rodné číslo se normalizuje před dotazem — odstraní se lomítko a mezery:

rc = rc.replace("/", "").replace(" ", "").strip()

2. MySQL — databáze medicus

Obsahuje lékové záznamy stažené z eReceptu SÚKL.

Host:     192.168.1.76
User:     root
DB:       medicus

Pacient se vyhledá podle příjmení a data narození (získaných z Firebirdu):

SELECT id, prijmeni, jmena, datum_narozeni
FROM pacient
WHERE prijmeni = %s AND datum_narozeni = %s

Co se zobrazuje

Část 1 — Předepisující lékaři

Všichni lékaři, kteří pacientovi za celou dobu předepsali alespoň jeden lék,
seřazeni sestupně podle počtu předpisů.

Sloupce: # | Lékař | Odbornost | Pracoviště a adresa | Předpisů

SELECT pr.prijmeni, pr.jmena,
       pr.icp,
       CONCAT(pr.pzs_nazev, ', ', pr.ulice, ', ', pr.psc, ' ', pr.mesto) AS adresa,
       COUNT(*) AS pocet_predpisu
FROM zprava z
JOIN predpis p         ON p.zprava_id     = z.id
JOIN predepisujici pr  ON pr.lekar_kod    = p.kod_predepisujiciho
WHERE z.pacient_id = %s
GROUP BY pr.lekar_kod, pr.prijmeni, pr.jmena, pr.icp,
         pr.pzs_nazev, pr.ulice, pr.psc, pr.mesto
ORDER BY pocet_predpisu DESC

Část 2 — Všechny předpisy

Předpisy od DATUM_OD, seřazené sestupně dle data vystavení.

Zobrazuje se vydaný lék (z tabulky vydej), nikoli předepsaný název.
Pokud lék nebyl vyzvednut, zobrazí se předepsaný název s příznakem *NV.

Sloupce: # | Datum | Vydaný lék | ATC | Návod | Lékař | Odbornost | Adresa

SELECT p.datum_vystaveni,
       COALESCE(v.nazev, p.nazev) AS vydany_lek,
       v.nazev IS NULL            AS nevyzvednuto,
       p.atc,
       p.navod,
       pr.prijmeni,
       pr.jmena,
       pr.icp,
       CONCAT(pr.pzs_nazev, ', ', pr.ulice, ', ', pr.psc, ' ', pr.mesto) AS adresa
FROM zprava z
JOIN predpis p         ON p.zprava_id     = z.id
JOIN predepisujici pr  ON pr.lekar_kod    = p.kod_predepisujiciho
LEFT JOIN vydej v      ON v.id_lp_predpis = p.id_lp_predpis
WHERE z.pacient_id = %s
  AND p.datum_vystaveni >= %s     -- pouze pokud DATUM_OD není None
ORDER BY p.datum_vystaveni DESC

Klíčový princip COALESCE(v.nazev, p.nazev):

  • v.nazev — název léku, který lékárna skutečně vydala (může být jiná značka než předepsaná)
  • p.nazev — název léku, který lékař předepsal (zobrazí se jen pokud výdej neexistuje → *NV)

Odbornost lékaře

Odbornost se odvozuje z posledních 3 číslic pole predepisujici.icp (IČP pracoviště).

ICP:  09305001  →  kód odbornosti: 001  →  všeobecné praktické lékařství
ICP:  08006272  →  kód odbornosti: 272  →  alergologie
ICP:  08075603  →  kód odbornosti: 603  →  onkologie

Funkce:

def odbornost_z_icp(icp):
    if not icp or len(icp) < 3:
        return ""
    return ODBORNOST.get(icp[-3:], f"odb. {icp[-3:]}")

Pro neznámé kódy se zobrazí odb. XXX (XXX = třímístný kód).

Zdroj dat — tabulky vzp_pracoviste + odbornost (MySQL)

Slovník ODBORNOST se načítá dynamicky při startu skriptu z MySQL:

def _nacti_odbornosti():
    conn = pymysql.connect(**DB)
    try:
        with conn.cursor(pymysql.cursors.Cursor) as cur:
            cur.execute("""
                SELECT vp.icp, o.nazev
                FROM vzp_pracoviste vp
                JOIN odbornost o ON o.kod = vp.odbornost
                WHERE CURDATE() BETWEEN vp.platnost_od AND vp.platnost_do
                ORDER BY vp.platnost_od DESC
            """)
            result = {}
            for icp, nazev in cur.fetchall():
                result.setdefault(icp, nazev)
            return result
    finally:
        conn.close()

ODBORNOST = _nacti_odbornosti()
  • vzp_pracoviste — oficiální číselník VZP (stahován týdně z VZP Point přes import_vzp_pracoviste.py), obsahuje ~52 000 záznamů s přímou vazbou ICP → kód odbornosti
  • odbornost — číselník názvů odborností importovaný z Firebird tabulky odborn (360 aktuálně platných kódů)
  • Vyhledávání probíhá podle plného 8-znakového ICP — spolehlivé i pro pracoviště, která změnila odbornost
  • Slovník obsahuje ~43 000 aktuálně platných ICP kódů

Excel export (prehled_pacienta_excel.py)

Soubor se ukládá do stejné složky jako skript (nebo do VYSTUP_DIR).

Pojmenování souborů

LZ_{Prijmeni}_{Jmeno}_{datum_narozeni}.xlsx         ← základní
LZ_{Prijmeni}_{Jmeno}_{datum_narozeni}_v2.xlsx       ← pokud základní existuje
LZ_{Prijmeni}_{Jmeno}_{datum_narozeni}_v3.xlsx       ← atd.

Versioning zabrání přepsání dříve exportovaných souborů.

Vzhled a formátování

Prvek Barva Popis
Záhlaví (jméno pacienta) #1F4E79 tmavě modrá tučné, 14pt
Záhlaví tabulky #1F4E79 tmavě modrá bílý text, 10pt
Nadpis sekce #2E75B6 střední modrá bílý text, 11pt
Info o pacientovi #DEEAF1 světle modrá datum narozeni, datum tisku, předpisy od
Sudé řádky #EBF3FB velmi světle modrá střídání řádků
Liché řádky #FFFFFF bílá
Nevyzvednuto #FCE4D6 lososová zvýraznění celého řádku
Ohraničení #B8CCE4 světle modrá tenká linka
  • Font: Arial ve všech buňkách
  • Automatická šířka sloupců a výška řádků (autofit)
  • Zmrazení prvního řádku (freeze_panes = "A2")
  • 8 sloupců: # | Lékař/Datum | Odbornost/Vydaný lék | Pracoviště/ATC | … | Předpisů/Pracoviště a adresa

Tabulka lékařů (8 sloupců)

# | Lékař | Odbornost | Pracoviště | Ulice | PSČ | Město | Předpisů

Tabulka předpisů (8 sloupců)

# | Datum | Vydaný lék | ATC | Návod | Lékař | Odbornost | Pracoviště a adresa


Závislosti

pymysql    ← MySQL klient
fdb        ← Firebird klient
openpyxl   ← Excel export (pouze prehled_pacienta_excel.py)

Všechny jsou součástí requirements.txt a nainstalují se přes setup.ps1.


Typické chybové situace

Chyba Příčina Řešení
Rodne cislo nenalezeno v Medicusu RC není v tabulce KAR Zkontrolovat číslo, ověřit v Medicusu
Pacient nema zaznam v MySQL Lékový záznam nebyl stažen Spustit 07StahnoutVsechny.py nebo reimport_z_xml.py
PermissionError při ukládání xlsx Soubor je otevřen v Excelu Zavřít Excel a spustit znovu — verzování uloží jako _v2
Odbornost zobrazena jako odb. XXX Kód není ve slovníku Informativní stav — kód je platný, jen není pojmenován