z230
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
# 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é:
|
||||
|
||||
```python
|
||||
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í
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
```sql
|
||||
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:
|
||||
```python
|
||||
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):
|
||||
```sql
|
||||
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ů`
|
||||
|
||||
```sql
|
||||
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`
|
||||
|
||||
```sql
|
||||
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 → Praktický lékař
|
||||
ICP: 08006272 → kód odbornosti: 272 → Alergologie
|
||||
ICP: 08075603 → kód odbornosti: 603 → Onkologie
|
||||
```
|
||||
|
||||
Funkce:
|
||||
```python
|
||||
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ý číselný kód).
|
||||
|
||||
### Slovník ODBORNOST — vybrané klíčové kódy
|
||||
|
||||
| Kód | Odbornost | Kód | Odbornost |
|
||||
|-----|-----------|-----|-----------|
|
||||
| 001 | Praktický lékař | 101 | Vnitřní lékařství |
|
||||
| 002 | Pediatr (prakt.) | 104 | Kardiologie |
|
||||
| 003 | Chirurgie | 105 | Gastroenterologie |
|
||||
| 004 | Ortopedie | 108 | Nefrologie |
|
||||
| 005 | ORL | 110 | Diabetologie |
|
||||
| 006 | Gynekologie | 121 | Endokrinologie |
|
||||
| 007 | Urologie | 156 | Hematologie |
|
||||
| 008 | Neurologie | 169 | Revmatologie |
|
||||
| 009 | Psychiatrie | 263 | Urologie |
|
||||
| 012 | Dermatovenerologie | 272 | Alergologie |
|
||||
| 018 | Pneumologie | 283 | Dětská neurochir. |
|
||||
| 021 | Radiodiagnostika | 302 | Radiodiagnostika |
|
||||
| 024 | Klin. biochemie | 324 | Klin. onkologie |
|
||||
| 060 | Dětská chirurgie | 590 | Lékárenství |
|
||||
| 074 | Neurochirurgie | 603 | Onkologie |
|
||||
| 091 | Gynekolog. onkologie | 704 | Kardiochirurgie |
|
||||
| 096 | Léčebná rehabilitace | 801 | Fyzioterapie |
|
||||
|
||||
Celý slovník obsahuje ~170 kódů (viz zdrojový kód skriptů).
|
||||
Kompletní číselník VZP/SÚKL: <https://www.sukl.cz> (sekce číselníky).
|
||||
|
||||
---
|
||||
|
||||
## 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 |
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+221
-12
@@ -16,8 +16,210 @@ import fdb
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
|
||||
# Kody odbornosti dle SUKL / VZP (posledni 3 cislice ICP)
|
||||
ODBORNOST = {
|
||||
# Základní ambulantní odbornosti
|
||||
"001": "Praktický lékař",
|
||||
"002": "Pediatr (prakt.)",
|
||||
"003": "Chirurgie",
|
||||
"004": "Ortopedie",
|
||||
"005": "ORL",
|
||||
"006": "Gynekologie",
|
||||
"007": "Urologie",
|
||||
"008": "Neurologie",
|
||||
"009": "Psychiatrie",
|
||||
"010": "Oftalmologie",
|
||||
"011": "Zubní lékařství",
|
||||
"012": "Dermatovenerologie",
|
||||
"013": "Infekční lékařství",
|
||||
"014": "Radiodiagnostika",
|
||||
"015": "Stomatochirurgie",
|
||||
"016": "Čelistní ortopedie",
|
||||
"017": "Dětská psychiatrie",
|
||||
"018": "Pneumologie",
|
||||
"019": "Anesteziologie",
|
||||
"020": "Rehabilitace",
|
||||
"021": "Radiodiagnostika",
|
||||
"022": "Radioterapie",
|
||||
"023": "Nukleární medicína",
|
||||
"024": "Klin. biochemie",
|
||||
"025": "Alergologie/imunologie",
|
||||
"026": "Hematologie",
|
||||
"027": "Soudní lékařství",
|
||||
"028": "Soudní psychiatrie",
|
||||
"029": "Lékařská genetika",
|
||||
"031": "Gastroenterologie",
|
||||
"032": "Nefrologie",
|
||||
"033": "Kardiologie",
|
||||
"034": "Endokrinologie/diab.",
|
||||
"035": "Revmatologie",
|
||||
"040": "Vnitřní lékařství",
|
||||
"041": "Geriatrie",
|
||||
"042": "Klin. farmakologie",
|
||||
"043": "Diabetologie",
|
||||
"044": "Endokrinologie",
|
||||
"045": "Hepatologie",
|
||||
"052": "Dětská neurologie",
|
||||
"060": "Dětská chirurgie",
|
||||
"065": "Plastická chirurgie",
|
||||
"066": "Cévní chirurgie",
|
||||
"067": "Kardiochirurgie",
|
||||
"072": "Foniatrie",
|
||||
"074": "Neurochirurgie",
|
||||
"077": "Maxilofaciální chir.",
|
||||
"079": "Hrudní chirurgie",
|
||||
"082": "Urologie",
|
||||
"083": "Andrologie",
|
||||
"085": "Proktologie",
|
||||
"091": "Gynekolog. onkologie",
|
||||
"092": "Reprodukční medicína",
|
||||
"096": "Léčebná rehabilitace",
|
||||
"097": "Fyzioterapie",
|
||||
# Interní a specializované odbornosti
|
||||
"101": "Vnitřní lékařství",
|
||||
"102": "Kardiologie",
|
||||
"104": "Kardiologie",
|
||||
"105": "Gastroenterologie",
|
||||
"106": "Hepatologie",
|
||||
"107": "Nefrologie",
|
||||
"108": "Nefrologie",
|
||||
"110": "Diabetologie",
|
||||
"111": "Endokrinologie",
|
||||
"114": "Pneumologie",
|
||||
"115": "Ftizeologie",
|
||||
"121": "Endokrinologie",
|
||||
"122": "Diabetologie",
|
||||
"129": "Andrologie",
|
||||
"143": "Psychiatrie",
|
||||
"144": "Psychoterapie",
|
||||
"145": "Adiktologie",
|
||||
"148": "Dětská psychiatrie",
|
||||
"155": "Oční onkologie",
|
||||
"156": "Hematologie",
|
||||
"157": "Hemostáza",
|
||||
"160": "Neurologie",
|
||||
"162": "Epileptologie",
|
||||
"163": "Dětská neurologie",
|
||||
"164": "Neurorehabilit.",
|
||||
"168": "Klin. neurofyziologie",
|
||||
"169": "Revmatologie",
|
||||
"174": "Ortoped. protetika",
|
||||
"181": "Infektologie",
|
||||
"183": "Tropická medicína",
|
||||
"185": "Mikrobiologie",
|
||||
"188": "Virologie",
|
||||
# Chirurgické a dětské odbornosti
|
||||
"200": "Stomatologie",
|
||||
"201": "Stomatochirurgie",
|
||||
"202": "Maxilofaciální chir.",
|
||||
"203": "Parodontologie",
|
||||
"204": "Ortodoncie",
|
||||
"205": "Zubní protetika",
|
||||
"206": "Dětská stomatologie",
|
||||
"220": "Pediatrie",
|
||||
"221": "Neonatologie",
|
||||
"222": "Dětská endokrinol.",
|
||||
"223": "Dětská gastroenterol.",
|
||||
"234": "Dětská hematologie",
|
||||
"239": "Dětská nefrologie",
|
||||
"243": "Dětská pneumologie",
|
||||
"245": "Dětská psychiatrie",
|
||||
"246": "Dětská revmatologie",
|
||||
"247": "Dětská kardiologie",
|
||||
"250": "Dětská neurologie",
|
||||
"251": "Dětská neurologie",
|
||||
"258": "Dětská onkologie",
|
||||
"261": "Dětská chirurgie",
|
||||
"262": "Dětská ortopedie",
|
||||
"263": "Urologie",
|
||||
"264": "Dětská stomatologie",
|
||||
"271": "Dětská klin. biochem.",
|
||||
"272": "Alergologie",
|
||||
"273": "Dětská alergologie",
|
||||
"281": "Dětská dermatologie",
|
||||
"282": "Dětská radiologie",
|
||||
"283": "Dětská neurochir.",
|
||||
"289": "Dětská kardiochir.",
|
||||
"291": "Dětská onkol. chir.",
|
||||
"294": "Dětská oftalmologie",
|
||||
"295": "Dětská gynekologie",
|
||||
# Onkologie a zobrazovací metody
|
||||
"300": "Onkologie",
|
||||
"301": "Klin. onkologie",
|
||||
"302": "Radiodiagnostika",
|
||||
"303": "Radioterapie",
|
||||
"304": "Nukleární medicína",
|
||||
"305": "Nukleární kardiologie",
|
||||
"316": "Klin. genetika",
|
||||
"319": "Soudní lékařství",
|
||||
"321": "Cytologie",
|
||||
"324": "Klin. onkologie",
|
||||
"333": "Onkologie",
|
||||
# Stomatologie (500-599)
|
||||
"501": "Zubní lékařství",
|
||||
"502": "Čelistní ortopedie",
|
||||
"503": "Stomatochirurgie",
|
||||
"508": "Parodontologie",
|
||||
"509": "Ortodoncie",
|
||||
"510": "Dětská stomatologie",
|
||||
"513": "Zubní protetika",
|
||||
"535": "Orální medicína",
|
||||
"555": "Stomatologie",
|
||||
"558": "Zubní lékařství",
|
||||
"559": "Stomatologie",
|
||||
"560": "Stomatologie",
|
||||
"562": "Stomatologie",
|
||||
"571": "Stomatologie",
|
||||
"574": "Stomatologie",
|
||||
"580": "Stomatologie",
|
||||
"581": "Stomatologie",
|
||||
"582": "Stomatologie",
|
||||
"584": "Stomatologie",
|
||||
"590": "Lékárenství",
|
||||
# Onkologie (600-699)
|
||||
"600": "Onkologie",
|
||||
"601": "Klin. onkologie",
|
||||
"603": "Onkologie",
|
||||
"606": "Radioterapie",
|
||||
"607": "Nukleární medicína",
|
||||
"615": "Onkologie",
|
||||
# Kardiochirurgie, ostatní (700+)
|
||||
"700": "Chirurgie",
|
||||
"701": "Cévní chirurgie",
|
||||
"702": "Hrudní chirurgie",
|
||||
"704": "Kardiochirurgie",
|
||||
"705": "Chirurgie",
|
||||
"706": "Plastická chirurgie",
|
||||
"719": "Dětská chirurgie",
|
||||
"721": "Ortopedie",
|
||||
"722": "Ortopedie",
|
||||
"723": "Ortopedie",
|
||||
# Fyzioterapie, rehabilitace (800+)
|
||||
"801": "Fyzioterapie",
|
||||
"802": "Ergoterapie",
|
||||
"852": "Fyzioterapie",
|
||||
"853": "Fyzioterapie",
|
||||
"858": "Fyzioterapie",
|
||||
"860": "Fyzioterapie",
|
||||
"862": "Fyzioterapie",
|
||||
"873": "Fyzioterapie",
|
||||
"880": "Rehabilitace",
|
||||
"881": "Endokrinologie",
|
||||
"885": "Rehabilitace",
|
||||
"889": "Rehabilitace",
|
||||
"890": "Rehabilitace",
|
||||
}
|
||||
|
||||
|
||||
def odbornost_z_icp(icp):
|
||||
"""Vrati nazev odbornosti z ICP kodu (posledni 3 cislice)."""
|
||||
if not icp or len(icp) < 3:
|
||||
return ""
|
||||
kod = icp[-3:]
|
||||
return ODBORNOST.get(kod, f"odb. {kod}")
|
||||
|
||||
# ── NASTAVENÍ ─────────────────────────────────────────────────────────────────
|
||||
RODNE_CISLO = "7/1234" # s lomitkem i bez lomitka
|
||||
RODNE_CISLO = "440802/018" # funguje s lomitkem i bez: 7309208104 nebo 730920/8104
|
||||
DATUM_OD = "01.01.2025" # None = vsechny predpisy
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -37,8 +239,8 @@ DB = dict(
|
||||
cursorclass = pymysql.cursors.DictCursor,
|
||||
)
|
||||
|
||||
SEP = "-" * 100
|
||||
SEP2 = "-" * 140
|
||||
SEP = "-" * 110
|
||||
SEP2 = "-" * 165
|
||||
|
||||
|
||||
def parse_datum(s, nazev):
|
||||
@@ -85,13 +287,14 @@ def tiskni_lekare(cur, pacient_id, prijmeni, jmena, datum_narozeni):
|
||||
cur.execute(
|
||||
"""
|
||||
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.pzs_nazev, pr.ulice, pr.psc, pr.mesto
|
||||
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
|
||||
""",
|
||||
(pacient_id,),
|
||||
@@ -102,11 +305,12 @@ def tiskni_lekare(cur, pacient_id, prijmeni, jmena, datum_narozeni):
|
||||
print(f" PACIENT: {prijmeni} {jmena} | nar. {datum_narozeni.strftime('%d.%m.%Y')}")
|
||||
print(SEP)
|
||||
print(f"\nPREDEPISUJICI LEKARI:")
|
||||
print(f"{'#':<4} {'Lekar':<30} {'Pracoviste a adresa':<55} {'Predpisu':>8}")
|
||||
print(f"{'#':<4} {'Lekar':<30} {'Odbornost':<25} {'Pracoviste a adresa':<50} {'Predpisu':>8}")
|
||||
print(SEP)
|
||||
for i, r in enumerate(rows, 1):
|
||||
lekar = f"{r['prijmeni']} {r['jmena']}"
|
||||
print(f"{i:<4} {lekar:<30} {r['adresa']:<55} {r['pocet_predpisu']:>8}")
|
||||
lekar = f"{r['prijmeni']} {r['jmena']}"
|
||||
odb = odbornost_z_icp(r['icp'])
|
||||
print(f"{i:<4} {lekar:<30} {odb:<25} {r['adresa']:<50} {r['pocet_predpisu']:>8}")
|
||||
if not rows:
|
||||
print(" Zadne predpisy nenalezeny.")
|
||||
|
||||
@@ -118,11 +322,13 @@ def tiskni_predpisy(cur, pacient_id, datum_od):
|
||||
cur.execute(
|
||||
f"""
|
||||
SELECT p.datum_vystaveni,
|
||||
COALESCE(v.nazev, '(nevyzvednuto)') AS vydany_lek,
|
||||
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
|
||||
@@ -138,15 +344,18 @@ def tiskni_predpisy(cur, pacient_id, datum_od):
|
||||
|
||||
od_text = f"od {datum_od.strftime('%d.%m.%Y')}" if datum_od else "vse"
|
||||
print(f"\nVSECHNY PREDPISY ({od_text}) — celkem {len(rows)}:")
|
||||
print(f"{'#':<4} {'Datum':<12} {'Vydany lek':<30} {'ATC':<8} {'Navod':<25} {'Lekar':<25} Adresa")
|
||||
print(f"{'#':<4} {'Datum':<12} {'Vydany lek':<30} {'ATC':<8} {'Navod':<20} {'Lekar':<25} {'Odbornost':<22} Adresa")
|
||||
print(SEP2)
|
||||
for i, r in enumerate(rows, 1):
|
||||
datum = r["datum_vystaveni"].strftime("%d.%m.%Y")
|
||||
lekar = f"{r['prijmeni']} {r['jmena']}"
|
||||
lek = (r["vydany_lek"] or "")[:29]
|
||||
navod = (r["navod"] or "")[:24]
|
||||
lek = (r["vydany_lek"] or "")[:28]
|
||||
if r["nevyzvednuto"]:
|
||||
lek = f"{lek} *NV"
|
||||
navod = (r["navod"] or "")[:19]
|
||||
atc = (r["atc"] or "")
|
||||
print(f"{i:<4} {datum:<12} {lek:<30} {atc:<8} {navod:<25} {lekar:<25} {r['adresa']}")
|
||||
odb = odbornost_z_icp(r["icp"])[:21]
|
||||
print(f"{i:<4} {datum:<12} {lek:<30} {atc:<8} {navod:<20} {lekar:<25} {odb:<22} {r['adresa']}")
|
||||
if not rows:
|
||||
print(" Zadne predpisy nenalezeny.")
|
||||
print()
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
"""
|
||||
Export prehledu lekoveho zaznamu pacienta do Excelu.
|
||||
|
||||
Nastaveni:
|
||||
RODNE_CISLO ... rodne cislo pacienta (s lomitkem i bez)
|
||||
DATUM_OD ... predpisy od tohoto data ve formatu DD.MM.RRRR (None = vsechny)
|
||||
VYSTUP_DIR ... slozka kam se ulozi Excel (None = stejna slozka jako skript)
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import fdb
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import (Font, PatternFill, Alignment, Border, Side,
|
||||
GradientFill)
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
# Kody odbornosti dle SUKL / VZP (posledni 3 cislice ICP)
|
||||
ODBORNOST = {
|
||||
"001": "Praktický lékař",
|
||||
"002": "Pediatr (prakt.)",
|
||||
"003": "Chirurgie",
|
||||
"004": "Ortopedie",
|
||||
"005": "ORL",
|
||||
"006": "Gynekologie",
|
||||
"007": "Urologie",
|
||||
"008": "Neurologie",
|
||||
"009": "Psychiatrie",
|
||||
"010": "Oftalmologie",
|
||||
"011": "Zubní lékařství",
|
||||
"012": "Dermatovenerologie",
|
||||
"013": "Infekční lékařství",
|
||||
"014": "Radiodiagnostika",
|
||||
"015": "Stomatochirurgie",
|
||||
"016": "Čelistní ortopedie",
|
||||
"017": "Dětská psychiatrie",
|
||||
"018": "Pneumologie",
|
||||
"019": "Anesteziologie",
|
||||
"020": "Rehabilitace",
|
||||
"021": "Radiodiagnostika",
|
||||
"022": "Radioterapie",
|
||||
"023": "Nukleární medicína",
|
||||
"024": "Klin. biochemie",
|
||||
"025": "Alergologie/imunologie",
|
||||
"026": "Hematologie",
|
||||
"027": "Soudní lékařství",
|
||||
"028": "Soudní psychiatrie",
|
||||
"029": "Lékařská genetika",
|
||||
"031": "Gastroenterologie",
|
||||
"032": "Nefrologie",
|
||||
"033": "Kardiologie",
|
||||
"034": "Endokrinologie/diab.",
|
||||
"035": "Revmatologie",
|
||||
"040": "Vnitřní lékařství",
|
||||
"041": "Geriatrie",
|
||||
"042": "Klin. farmakologie",
|
||||
"043": "Diabetologie",
|
||||
"044": "Endokrinologie",
|
||||
"045": "Hepatologie",
|
||||
"052": "Dětská neurologie",
|
||||
"060": "Dětská chirurgie",
|
||||
"065": "Plastická chirurgie",
|
||||
"066": "Cévní chirurgie",
|
||||
"067": "Kardiochirurgie",
|
||||
"072": "Foniatrie",
|
||||
"074": "Neurochirurgie",
|
||||
"077": "Maxilofaciální chir.",
|
||||
"079": "Hrudní chirurgie",
|
||||
"082": "Urologie",
|
||||
"083": "Andrologie",
|
||||
"085": "Proktologie",
|
||||
"091": "Gynekolog. onkologie",
|
||||
"092": "Reprodukční medicína",
|
||||
"096": "Léčebná rehabilitace",
|
||||
"097": "Fyzioterapie",
|
||||
"101": "Vnitřní lékařství",
|
||||
"102": "Kardiologie",
|
||||
"104": "Kardiologie",
|
||||
"105": "Gastroenterologie",
|
||||
"106": "Hepatologie",
|
||||
"107": "Nefrologie",
|
||||
"108": "Nefrologie",
|
||||
"110": "Diabetologie",
|
||||
"111": "Endokrinologie",
|
||||
"114": "Pneumologie",
|
||||
"115": "Ftizeologie",
|
||||
"121": "Endokrinologie",
|
||||
"122": "Diabetologie",
|
||||
"129": "Andrologie",
|
||||
"143": "Psychiatrie",
|
||||
"144": "Psychoterapie",
|
||||
"145": "Adiktologie",
|
||||
"148": "Dětská psychiatrie",
|
||||
"155": "Oční onkologie",
|
||||
"156": "Hematologie",
|
||||
"157": "Hemostáza",
|
||||
"160": "Neurologie",
|
||||
"162": "Epileptologie",
|
||||
"163": "Dětská neurologie",
|
||||
"164": "Neurorehabilit.",
|
||||
"168": "Klin. neurofyziologie",
|
||||
"169": "Revmatologie",
|
||||
"174": "Ortoped. protetika",
|
||||
"181": "Infektologie",
|
||||
"183": "Tropická medicína",
|
||||
"185": "Mikrobiologie",
|
||||
"188": "Virologie",
|
||||
"200": "Stomatologie",
|
||||
"201": "Stomatochirurgie",
|
||||
"202": "Maxilofaciální chir.",
|
||||
"203": "Parodontologie",
|
||||
"204": "Ortodoncie",
|
||||
"205": "Zubní protetika",
|
||||
"206": "Dětská stomatologie",
|
||||
"220": "Pediatrie",
|
||||
"221": "Neonatologie",
|
||||
"222": "Dětská endokrinol.",
|
||||
"223": "Dětská gastroenterol.",
|
||||
"234": "Dětská hematologie",
|
||||
"239": "Dětská nefrologie",
|
||||
"243": "Dětská pneumologie",
|
||||
"245": "Dětská psychiatrie",
|
||||
"246": "Dětská revmatologie",
|
||||
"247": "Dětská kardiologie",
|
||||
"250": "Dětská neurologie",
|
||||
"251": "Dětská neurologie",
|
||||
"258": "Dětská onkologie",
|
||||
"261": "Dětská chirurgie",
|
||||
"262": "Dětská ortopedie",
|
||||
"263": "Urologie",
|
||||
"264": "Dětská stomatologie",
|
||||
"271": "Dětská klin. biochem.",
|
||||
"272": "Alergologie",
|
||||
"273": "Dětská alergologie",
|
||||
"281": "Dětská dermatologie",
|
||||
"282": "Dětská radiologie",
|
||||
"283": "Dětská neurochir.",
|
||||
"289": "Dětská kardiochir.",
|
||||
"291": "Dětská onkol. chir.",
|
||||
"294": "Dětská oftalmologie",
|
||||
"295": "Dětská gynekologie",
|
||||
"300": "Onkologie",
|
||||
"301": "Klin. onkologie",
|
||||
"302": "Radiodiagnostika",
|
||||
"303": "Radioterapie",
|
||||
"304": "Nukleární medicína",
|
||||
"305": "Nukleární kardiologie",
|
||||
"316": "Klin. genetika",
|
||||
"319": "Soudní lékařství",
|
||||
"321": "Cytologie",
|
||||
"324": "Klin. onkologie",
|
||||
"333": "Onkologie",
|
||||
"501": "Zubní lékařství",
|
||||
"502": "Čelistní ortopedie",
|
||||
"503": "Stomatochirurgie",
|
||||
"508": "Parodontologie",
|
||||
"509": "Ortodoncie",
|
||||
"510": "Dětská stomatologie",
|
||||
"513": "Zubní protetika",
|
||||
"535": "Orální medicína",
|
||||
"555": "Stomatologie",
|
||||
"558": "Zubní lékařství",
|
||||
"559": "Stomatologie",
|
||||
"560": "Stomatologie",
|
||||
"562": "Stomatologie",
|
||||
"571": "Stomatologie",
|
||||
"574": "Stomatologie",
|
||||
"580": "Stomatologie",
|
||||
"581": "Stomatologie",
|
||||
"582": "Stomatologie",
|
||||
"584": "Stomatologie",
|
||||
"590": "Lékárenství",
|
||||
"600": "Onkologie",
|
||||
"601": "Klin. onkologie",
|
||||
"603": "Onkologie",
|
||||
"606": "Radioterapie",
|
||||
"607": "Nukleární medicína",
|
||||
"615": "Onkologie",
|
||||
"700": "Chirurgie",
|
||||
"701": "Cévní chirurgie",
|
||||
"702": "Hrudní chirurgie",
|
||||
"704": "Kardiochirurgie",
|
||||
"705": "Chirurgie",
|
||||
"706": "Plastická chirurgie",
|
||||
"719": "Dětská chirurgie",
|
||||
"721": "Ortopedie",
|
||||
"722": "Ortopedie",
|
||||
"723": "Ortopedie",
|
||||
"801": "Fyzioterapie",
|
||||
"802": "Ergoterapie",
|
||||
"852": "Fyzioterapie",
|
||||
"853": "Fyzioterapie",
|
||||
"858": "Fyzioterapie",
|
||||
"860": "Fyzioterapie",
|
||||
"862": "Fyzioterapie",
|
||||
"873": "Fyzioterapie",
|
||||
"880": "Rehabilitace",
|
||||
"881": "Endokrinologie",
|
||||
"885": "Rehabilitace",
|
||||
"889": "Rehabilitace",
|
||||
"890": "Rehabilitace",
|
||||
}
|
||||
|
||||
|
||||
def odbornost_z_icp(icp):
|
||||
"""Vrati nazev odbornosti z ICP kodu (posledni 3 cislice)."""
|
||||
if not icp or len(icp) < 3:
|
||||
return ""
|
||||
return ODBORNOST.get(icp[-3:], f"odb. {icp[-3:]}")
|
||||
|
||||
|
||||
# ── NASTAVENÍ ─────────────────────────────────────────────────────────────────
|
||||
RODNE_CISLO = "440802/018"
|
||||
DATUM_OD = "01.01.2025" # None = vsechny predpisy
|
||||
VYSTUP_DIR = None # None = stejny adresar jako skript
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
FB = dict(
|
||||
dsn = r"localhost:c:\medicus 3\data\medicus.fdb",
|
||||
user = "SYSDBA",
|
||||
password = "masterkey",
|
||||
charset = "win1250",
|
||||
)
|
||||
|
||||
DB = dict(
|
||||
host = "192.168.1.76",
|
||||
user = "root",
|
||||
password = "Vlado9674+",
|
||||
database = "medicus",
|
||||
charset = "utf8mb4",
|
||||
cursorclass = pymysql.cursors.DictCursor,
|
||||
)
|
||||
|
||||
# ── Barvy ─────────────────────────────────────────────────────────────────────
|
||||
C_HEADER_BG = "1F4E79" # tmave modra — hlavicka tabulky
|
||||
C_HEADER_FG = "FFFFFF" # bila — text hlavicky
|
||||
C_TITLE_BG = "2E75B6" # stredni modra — nadpis sekce
|
||||
C_TITLE_FG = "FFFFFF"
|
||||
C_INFO_BG = "DEEAF1" # svetle modra — info o pacientovi
|
||||
C_ROW_ODD = "FFFFFF" # bila
|
||||
C_ROW_EVEN = "EBF3FB" # velmi svetle modra — striped
|
||||
C_NEVYZV_BG = "FCE4D6" # lososova — nevyzvednuto
|
||||
C_BORDER = "B8CCE4"
|
||||
|
||||
|
||||
def thin_border():
|
||||
s = Side(style="thin", color=C_BORDER)
|
||||
return Border(left=s, right=s, top=s, bottom=s)
|
||||
|
||||
|
||||
def header_fill(color):
|
||||
return PatternFill("solid", fgColor=color)
|
||||
|
||||
|
||||
def parse_datum(s, nazev):
|
||||
try:
|
||||
return datetime.strptime(s, "%d.%m.%Y").date()
|
||||
except (ValueError, TypeError):
|
||||
sys.exit(f"Spatny format data '{nazev}': '{s}'")
|
||||
|
||||
|
||||
def najdi_v_firebirdu(rc):
|
||||
rc = rc.replace("/", "").replace(" ", "")
|
||||
conn = fdb.connect(**FB)
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT KAR.PRIJMENI, KAR.JMENO, KAR.DATNAR FROM KAR WHERE KAR.RODCIS = ?", (rc,))
|
||||
row = cur.fetchone()
|
||||
if not row:
|
||||
sys.exit(f"Rodne cislo '{rc}' nenalezeno v Medicusu.")
|
||||
return {"prijmeni": row[0].strip(), "jmeno": row[1].strip(), "datnar": row[2]}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def nacti_data(prijmeni, datum_narozeni, datum_od):
|
||||
conn = pymysql.connect(**DB)
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id, prijmeni, jmena, datum_narozeni FROM pacient "
|
||||
"WHERE prijmeni = %s AND datum_narozeni = %s",
|
||||
(prijmeni, datum_narozeni)
|
||||
)
|
||||
pac = cur.fetchone()
|
||||
if not pac:
|
||||
sys.exit(f"Pacient '{prijmeni}' nar. {datum_narozeni} nema zaznam v MySQL.")
|
||||
|
||||
# Lekari
|
||||
cur.execute("""
|
||||
SELECT pr.prijmeni, pr.jmena,
|
||||
pr.icp,
|
||||
pr.pzs_nazev, pr.ulice, pr.psc, pr.mesto,
|
||||
COUNT(*) AS pocet
|
||||
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 DESC
|
||||
""", (pac["id"],))
|
||||
lekari = cur.fetchall()
|
||||
|
||||
# Predpisy
|
||||
podminka = "AND p.datum_vystaveni >= %s" if datum_od else ""
|
||||
params = (pac["id"], datum_od) if datum_od else (pac["id"],)
|
||||
cur.execute(f"""
|
||||
SELECT p.datum_vystaveni,
|
||||
COALESCE(v.nazev, p.nazev) AS vydany_lek,
|
||||
v.nazev IS NULL AS nevyzvednuto,
|
||||
p.atc, p.navod,
|
||||
pr.prijmeni AS lek_prijmeni, pr.jmena AS lek_jmena,
|
||||
pr.icp,
|
||||
pr.pzs_nazev, pr.ulice, pr.psc, pr.mesto
|
||||
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 {podminka}
|
||||
ORDER BY p.datum_vystaveni DESC
|
||||
""", params)
|
||||
predpisy = cur.fetchall()
|
||||
|
||||
return pac, lekari, predpisy
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def nastav_sirky(ws, sirky):
|
||||
for col, width in sirky.items():
|
||||
ws.column_dimensions[col].width = width
|
||||
|
||||
|
||||
def autofit(ws, min_width=5, max_width=60, padding=2):
|
||||
"""Autofit sloupcu a radku podle obsahu."""
|
||||
col_widths = {}
|
||||
for row in ws.iter_rows():
|
||||
for cell in row:
|
||||
if cell.value is None:
|
||||
continue
|
||||
# Preskoc mergnuté bunky — jejich sirka se pocita ze zakladni bunky
|
||||
if isinstance(cell, type(cell)) and hasattr(cell, 'column'):
|
||||
text = str(cell.value)
|
||||
# Tučný text je trochu širší
|
||||
factor = 1.15 if (cell.font and cell.font.bold) else 1.0
|
||||
width = len(text) * factor + padding
|
||||
col = get_column_letter(cell.column)
|
||||
col_widths[col] = max(col_widths.get(col, min_width), width)
|
||||
|
||||
for col, width in col_widths.items():
|
||||
ws.column_dimensions[col].width = min(max(width, min_width), max_width)
|
||||
|
||||
# Autofit výšky řádků (wrap_text obsah)
|
||||
for row in ws.iter_rows():
|
||||
max_lines = 1
|
||||
for cell in row:
|
||||
if cell.value and cell.alignment and cell.alignment.wrap_text:
|
||||
col_w = ws.column_dimensions[get_column_letter(cell.column)].width or 10
|
||||
lines = max(1, int(len(str(cell.value)) / max(col_w, 1)) + 1)
|
||||
max_lines = max(max_lines, lines)
|
||||
row_num = row[0].row
|
||||
if max_lines > 1:
|
||||
ws.row_dimensions[row_num].height = max(ws.row_dimensions[row_num].height or 15,
|
||||
max_lines * 14)
|
||||
|
||||
|
||||
def zapis_nadpis_sekce(ws, row, text, n_cols):
|
||||
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=n_cols)
|
||||
cell = ws.cell(row=row, column=1, value=text)
|
||||
cell.font = Font(name="Arial", bold=True, size=11, color=C_TITLE_FG)
|
||||
cell.fill = header_fill(C_TITLE_BG)
|
||||
cell.alignment = Alignment(horizontal="left", vertical="center", indent=1)
|
||||
ws.row_dimensions[row].height = 20
|
||||
return row + 1
|
||||
|
||||
|
||||
def zapis_hlavicku(ws, row, hlavicka, n_cols=None):
|
||||
for col, text in enumerate(hlavicka, 1):
|
||||
cell = ws.cell(row=row, column=col, value=text)
|
||||
cell.font = Font(name="Arial", bold=True, size=10, color=C_HEADER_FG)
|
||||
cell.fill = header_fill(C_HEADER_BG)
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
cell.border = thin_border()
|
||||
ws.row_dimensions[row].height = 28
|
||||
return row + 1
|
||||
|
||||
|
||||
def zapis_radek(ws, row, hodnoty, highlight=False):
|
||||
bg = C_NEVYZV_BG if highlight else (C_ROW_EVEN if row % 2 == 0 else C_ROW_ODD)
|
||||
fill = header_fill(bg)
|
||||
for col, val in enumerate(hodnoty, 1):
|
||||
cell = ws.cell(row=row, column=col, value=val)
|
||||
cell.font = Font(name="Arial", size=10)
|
||||
cell.fill = fill
|
||||
cell.alignment = Alignment(vertical="center", wrap_text=True)
|
||||
cell.border = thin_border()
|
||||
ws.row_dimensions[row].height = 18
|
||||
return row + 1
|
||||
|
||||
|
||||
def vytvor_excel(pac, lekari, predpisy, datum_od, fb_pac):
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Lekovy zaznam"
|
||||
|
||||
# ── Záhlaví — info o pacientovi ──────────────────────────────────────────
|
||||
n_cols = 8
|
||||
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=n_cols)
|
||||
title_cell = ws.cell(row=1, column=1,
|
||||
value=f"LÉKOVÝ ZÁZNAM — {pac['prijmeni'].upper()} {fb_pac['jmeno'].upper()}")
|
||||
title_cell.font = Font(name="Arial", bold=True, size=14, color=C_HEADER_FG)
|
||||
title_cell.fill = header_fill(C_HEADER_BG)
|
||||
title_cell.alignment = Alignment(horizontal="left", vertical="center", indent=1)
|
||||
ws.row_dimensions[1].height = 32
|
||||
|
||||
info = [
|
||||
("Datum narození:", pac["datum_narozeni"].strftime("%d.%m.%Y")),
|
||||
("Datum tisku:", datetime.today().strftime("%d.%m.%Y")),
|
||||
("Předpisy od:", datum_od.strftime("%d.%m.%Y") if datum_od else "vše"),
|
||||
]
|
||||
for i, (label, val) in enumerate(info, 2):
|
||||
ws.merge_cells(start_row=i, start_column=1, end_row=i, end_column=2)
|
||||
ws.merge_cells(start_row=i, start_column=3, end_row=i, end_column=n_cols)
|
||||
lbl = ws.cell(row=i, column=1, value=label)
|
||||
lbl.font = Font(name="Arial", bold=True, size=10)
|
||||
lbl.fill = header_fill(C_INFO_BG)
|
||||
lbl.alignment = Alignment(vertical="center", indent=1)
|
||||
val_cell = ws.cell(row=i, column=3, value=val)
|
||||
val_cell.font = Font(name="Arial", size=10)
|
||||
val_cell.fill = header_fill(C_INFO_BG)
|
||||
val_cell.alignment = Alignment(vertical="center")
|
||||
ws.row_dimensions[i].height = 16
|
||||
|
||||
row = len(info) + 3 # prázdný řádek
|
||||
|
||||
# ── Tabulka lékařů ───────────────────────────────────────────────────────
|
||||
row = zapis_nadpis_sekce(ws, row, "PŘEDEPISUJÍCÍ LÉKAŘI", n_cols)
|
||||
row = zapis_hlavicku(ws, row, ["#", "Lékař", "Odbornost", "Pracoviště", "Ulice", "PSČ", "Město", "Předpisů"])
|
||||
for i, r in enumerate(lekari, 1):
|
||||
adresa_ulice = r.get("ulice") or ""
|
||||
row = zapis_radek(ws, row, [
|
||||
i,
|
||||
f"{r['prijmeni']} {r['jmena']}",
|
||||
odbornost_z_icp(r.get("icp")),
|
||||
r.get("pzs_nazev") or "",
|
||||
adresa_ulice,
|
||||
r.get("psc") or "",
|
||||
r.get("mesto") or "",
|
||||
r["pocet"],
|
||||
])
|
||||
|
||||
row += 1 # prázdný řádek
|
||||
|
||||
# ── Tabulka předpisů ─────────────────────────────────────────────────────
|
||||
od_text = datum_od.strftime("%d.%m.%Y") if datum_od else "vše"
|
||||
row = zapis_nadpis_sekce(ws, row, f"VŠECHNY PŘEDPISY (od {od_text}) — celkem {len(predpisy)}", n_cols)
|
||||
row = zapis_hlavicku(ws, row, ["#", "Datum", "Vydaný lék", "ATC", "Návod", "Lékař", "Odbornost", "Pracoviště a adresa"])
|
||||
for i, r in enumerate(predpisy, 1):
|
||||
nevyzv = bool(r["nevyzvednuto"])
|
||||
adresa = (f"{r.get('pzs_nazev') or ''}, {r.get('ulice') or ''}, "
|
||||
f"{r.get('psc') or ''} {r.get('mesto') or ''}").strip(", ")
|
||||
row = zapis_radek(ws, row, [
|
||||
i,
|
||||
r["datum_vystaveni"].strftime("%d.%m.%Y") if r["datum_vystaveni"] else "",
|
||||
r["vydany_lek"],
|
||||
r.get("atc") or "",
|
||||
r.get("navod") or "",
|
||||
f"{r['lek_prijmeni']} {r['lek_jmena']}",
|
||||
odbornost_z_icp(r.get("icp")),
|
||||
adresa,
|
||||
], highlight=nevyzv)
|
||||
|
||||
# ── Autofit sloupců a řádků ───────────────────────────────────────────────
|
||||
autofit(ws, min_width=5, max_width=60)
|
||||
|
||||
# Zmraz záhlaví
|
||||
ws.freeze_panes = "A2"
|
||||
|
||||
return wb
|
||||
|
||||
|
||||
def main():
|
||||
datum_od = parse_datum(DATUM_OD, "DATUM_OD") if DATUM_OD else None
|
||||
|
||||
fb_pac = najdi_v_firebirdu(RODNE_CISLO)
|
||||
prijmeni = fb_pac["prijmeni"]
|
||||
datum_narozeni = fb_pac["datnar"]
|
||||
|
||||
print(f"Nacitam data: {prijmeni} {fb_pac['jmeno']} nar. {datum_narozeni} ...")
|
||||
pac, lekari, predpisy = nacti_data(prijmeni, datum_narozeni, datum_od)
|
||||
print(f" {len(lekari)} lekaru, {len(predpisy)} predpisu")
|
||||
|
||||
wb = vytvor_excel(pac, lekari, predpisy, datum_od, fb_pac)
|
||||
|
||||
vyst = Path(VYSTUP_DIR) if VYSTUP_DIR else Path(__file__).parent
|
||||
zaklad = vyst / f"LZ_{prijmeni}_{fb_pac['jmeno']}_{datum_narozeni}.xlsx"
|
||||
if not zaklad.exists():
|
||||
soubor = zaklad
|
||||
else:
|
||||
i = 2
|
||||
while True:
|
||||
soubor = vyst / f"LZ_{prijmeni}_{fb_pac['jmeno']}_{datum_narozeni}_v{i}.xlsx"
|
||||
if not soubor.exists():
|
||||
break
|
||||
i += 1
|
||||
wb.save(soubor)
|
||||
print(f"Ulozeno: {soubor}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -10,18 +10,67 @@ z eRecept SÚKL API a jejich uložení do relační databáze MySQL.
|
||||
| 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ě |
|
||||
| `06UlozitDoMySQL.py` | DDL schématu, parsování XML, import do MySQL — používá se jako **knihovna**, ne spouštět přímo! |
|
||||
| `07StahnoutVsechny.py` | **Hlavní skript** — načte pacienty z Medicusu, stáhne lékové záznamy, uloží XML i DB záznamy |
|
||||
| `reimport_z_xml.py` | Reimport XML ze zálohy bez volání API — viz sekce níže |
|
||||
|
||||
```
|
||||
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)
|
||||
recept/
|
||||
├── setup.ps1 ← vytvoří .venv, nainstaluje závislosti, Playwright chromium
|
||||
├── requirements.txt ← seznam Python závislostí
|
||||
├── .venv/ ← virtuální prostředí (Python 3.x)
|
||||
│
|
||||
├── LékovýZáznamWithClaude/
|
||||
│ ├── 05UlozitOdpoved.py
|
||||
│ ├── 06UlozitDoMySQL.py
|
||||
│ ├── 07StahnoutVsechny.py
|
||||
│ ├── reimport_z_xml.py
|
||||
│ ├── LEKOVY_ZAZNAM_DB.md ← tento soubor
|
||||
│ ├── 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)
|
||||
│
|
||||
└── Dotazy/
|
||||
├── prehled_pacienta.py ← konzolový přehled pacienta
|
||||
├── prehled_pacienta_excel.py ← export přehledu pacienta do Excelu
|
||||
└── DOTAZY.md ← dokumentace dotazovacích skriptů
|
||||
```
|
||||
|
||||
> **⚠️ NIKDY nespouštět `06UlozitDoMySQL.py` přímo** — zavolá `vytvor_schema()`,
|
||||
> která provede `DROP TABLE` a smaže celou databázi.
|
||||
> Pro import dat vždy použít `07StahnoutVsechny.py` nebo `reimport_z_xml.py`.
|
||||
|
||||
---
|
||||
|
||||
## Nastavení prostředí (jednorázově)
|
||||
|
||||
```powershell
|
||||
# PowerShell — spustit jednou po naklonování projektu
|
||||
cd U:\recept
|
||||
.\setup.ps1
|
||||
```
|
||||
|
||||
`setup.ps1` provede:
|
||||
1. Vytvoří `.venv` s Python interpretem z `C:\Python\python.exe`
|
||||
2. Nainstaluje všechny závislosti z `requirements.txt`
|
||||
3. Nainstaluje Playwright Chromium (pro případné automatizace)
|
||||
|
||||
Po nastavení aktivace:
|
||||
```powershell
|
||||
.venv\Scripts\Activate.ps1
|
||||
```
|
||||
|
||||
### requirements.txt
|
||||
|
||||
```
|
||||
requests
|
||||
requests-pkcs12
|
||||
pymysql
|
||||
fdb
|
||||
zeep
|
||||
mysql-connector-python
|
||||
playwright
|
||||
openpyxl
|
||||
```
|
||||
|
||||
---
|
||||
@@ -29,9 +78,6 @@ LékovýZáznamWithClaude/
|
||||
## 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
|
||||
|
||||
@@ -40,6 +86,9 @@ python 07StahnoutVsechny.py --prijmeni Buzalka,Buzalková,Kusinová
|
||||
|
||||
# Dávkování po částech
|
||||
python 07StahnoutVsechny.py --offset 100 --limit 50
|
||||
|
||||
# Reimport ze zálohy XML (bez volání API) — viz níže
|
||||
python reimport_z_xml.py
|
||||
```
|
||||
|
||||
---
|
||||
@@ -275,7 +324,7 @@ Lékaři, kteří pacientovi předepisovali (ze všech ordinací).
|
||||
| `prijmeni` | VARCHAR(35) | |
|
||||
| `jmena` | VARCHAR(24) | |
|
||||
| `icz` | CHAR(8) | IČZ zdravotnického zařízení |
|
||||
| `icp` | CHAR(8) | IČP pracoviště |
|
||||
| `icp` | CHAR(8) | IČP pracoviště — **poslední 3 číslice = kód odbornosti** (001 = prakt. lékař, 272 = alergologie…) |
|
||||
| `pzs_nazev` | VARCHAR(200) | název zdravotnického zařízení |
|
||||
| `ulice` | VARCHAR(150) | |
|
||||
| `mesto` | VARCHAR(100) | |
|
||||
@@ -374,10 +423,96 @@ SELECT prijmeni, jmena, datum_narozeni, poznamka
|
||||
FROM pacient
|
||||
WHERE poznamka IS NOT NULL
|
||||
ORDER BY prijmeni;
|
||||
|
||||
-- lékaři dle odbornosti — kolik předpisů pochází od které speciality
|
||||
SELECT RIGHT(pr.icp, 3) AS odb_kod, COUNT(*) AS pocet_predpisu
|
||||
FROM predpis p
|
||||
JOIN predepisujici pr ON pr.lekar_kod = p.kod_predepisujiciho
|
||||
WHERE pr.icp IS NOT NULL
|
||||
GROUP BY RIGHT(pr.icp, 3)
|
||||
ORDER BY pocet_predpisu DESC;
|
||||
|
||||
-- lékový záznam pacienta dle rodného čísla (přes Firebird → MySQL)
|
||||
-- krok 1: z Medicusu zjistit příjmení a datum narozeni pro RC 7309208104
|
||||
-- krok 2:
|
||||
SELECT pac.prijmeni, pac.jmena, pac.datum_narozeni,
|
||||
p.datum_vystaveni,
|
||||
COALESCE(v.nazev, p.nazev) AS vydany_lek,
|
||||
v.nazev IS NULL AS nevyzvednuto,
|
||||
p.atc, p.navod,
|
||||
pr.prijmeni AS lekar, RIGHT(pr.icp, 3) AS odb_kod
|
||||
FROM pacient pac
|
||||
JOIN zprava z ON z.pacient_id = pac.id
|
||||
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 pac.prijmeni = 'Buzalka' AND pac.datum_narozeni = '1973-09-20'
|
||||
ORDER BY p.datum_vystaveni DESC;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reimport ze zálohy XML (`reimport_z_xml.py`)
|
||||
|
||||
Slouží k opětovnému naplnění MySQL databáze z lokálních XML souborů **bez volání eRecept API**.
|
||||
Použití: obnova po neúmyslném smazání databáze, migrace na nový server, re-parsování při změně schématu.
|
||||
|
||||
### Jak funguje
|
||||
|
||||
1. Načte všechny registrované pacienty z Firebirdu (ICP `09305001`, odbornost `001`)
|
||||
2. Pro každý XML soubor v archivu:
|
||||
- Naparsuje XML (volá `parsuj_xml()` z `06UlozitDoMySQL.py`)
|
||||
- Dohledá pacienta v Firebirdu dle příjmení + data narození z XML
|
||||
- Pokud je registrovaný → `upsert` pacienta do MySQL (INSERT ON DUPLICATE KEY UPDATE)
|
||||
- Zavolá `uloz()` — INSERT IGNORE, takže duplicity se ignorují
|
||||
3. Výpis průběhu: `[ 1/1177] Buzalka_Vladimir_1973-09-20.xml OK 12p 18v`
|
||||
|
||||
### Spuštění
|
||||
|
||||
```bash
|
||||
# Výchozí adresář: xml_archive/2026-04-11
|
||||
python reimport_z_xml.py
|
||||
|
||||
# Konkrétní podadresář
|
||||
python reimport_z_xml.py xml_archive/2026-04-11
|
||||
|
||||
# Celý archiv rekurzivně (všechna data)
|
||||
python reimport_z_xml.py xml_archive
|
||||
```
|
||||
|
||||
### Konfigurace v souboru
|
||||
|
||||
```python
|
||||
XML_ADRESAR = Path(__file__).parent / "xml_archive" / "2026-04-11" # výchozí adresář
|
||||
ICP = "09305001" # IČP ordinace pro filtr registrovaných pacientů
|
||||
ODB = "001" # odbornost (001 = praktický lékař)
|
||||
```
|
||||
|
||||
### Poznámky
|
||||
|
||||
- Pacienti, kteří nejsou v Firebirdu registrováni pod daným ICP/ODB, se přeskočí
|
||||
(pokud ale existují v MySQL z předchozího importu, data se aktualizují)
|
||||
- Firebird slouží jako autoritativní zdroj identit — `idpac` z KAR se propíše do MySQL `pacient.idpac`
|
||||
- `INSERT IGNORE` zajistí idempotentnost — opakované spuštění nepřidá duplikáty
|
||||
|
||||
---
|
||||
|
||||
## Dotazovací skripty (`Dotazy/`)
|
||||
|
||||
Viz samostatnou dokumentaci: [`Dotazy/DOTAZY.md`](../Dotazy/DOTAZY.md)
|
||||
|
||||
Stručný přehled:
|
||||
|
||||
| Skript | Co dělá |
|
||||
|--------|---------|
|
||||
| `prehled_pacienta.py` | Konzolový výpis lékového záznamu pacienta (lékaři + předpisy) |
|
||||
| `prehled_pacienta_excel.py` | Totéž, ale exportuje do formátovaného souboru Excel (.xlsx) |
|
||||
|
||||
Pacient se identifikuje **rodným číslem** (nastavení `RODNE_CISLO` v záhlaví skriptu).
|
||||
Oba skripty zobrazují **vydaný lék** (ne předepsaný), **odbornost lékaře** a příznak `*NV` pro nevyzvednuto.
|
||||
|
||||
---
|
||||
|
||||
## Závislosti (Python)
|
||||
|
||||
```
|
||||
@@ -385,10 +520,15 @@ requests
|
||||
requests-pkcs12
|
||||
pymysql
|
||||
fdb
|
||||
zeep
|
||||
mysql-connector-python
|
||||
playwright
|
||||
openpyxl
|
||||
```
|
||||
|
||||
```bash
|
||||
pip install requests requests-pkcs12 pymysql fdb
|
||||
# Instalace (nebo použít setup.ps1)
|
||||
pip install requests requests-pkcs12 pymysql fdb openpyxl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
Reimport vsech XML souboru z xml_archive do MySQL — bez volani API.
|
||||
|
||||
Pouziti:
|
||||
python reimport_z_xml.py # vsechna XML z 2026-04-11
|
||||
python reimport_z_xml.py xml_archive/2026-04-11 # konkretni adresar
|
||||
python reimport_z_xml.py xml_archive # vsechny podadresare rekurzivne
|
||||
"""
|
||||
|
||||
import sys
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
from datetime import date
|
||||
import fdb
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
|
||||
# Windows konzole
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(errors="replace")
|
||||
|
||||
# ── Konfigurace ───────────────────────────────────────────────────────────────
|
||||
XML_ADRESAR = Path(__file__).parent / "xml_archive" / "2026-04-11"
|
||||
|
||||
FB = dict(
|
||||
dsn = r"localhost:c:\medicus 3\data\medicus.fdb",
|
||||
user = "SYSDBA",
|
||||
password = "masterkey",
|
||||
charset = "win1250",
|
||||
)
|
||||
|
||||
DB = dict(
|
||||
host = "192.168.1.76",
|
||||
user = "root",
|
||||
password = "Vlado9674+",
|
||||
database = "medicus",
|
||||
charset = "utf8mb4",
|
||||
cursorclass = pymysql.cursors.DictCursor,
|
||||
)
|
||||
|
||||
ICP = "09305001"
|
||||
ODB = "001"
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
# Nacteni parsovaci logiky z 06UlozitDoMySQL.py
|
||||
_spec = importlib.util.spec_from_file_location(
|
||||
"m06", Path(__file__).parent / "06UlozitDoMySQL.py"
|
||||
)
|
||||
_m06 = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_m06)
|
||||
|
||||
parsuj_xml = _m06.parsuj_xml
|
||||
uloz = _m06.uloz
|
||||
inicializuj_schema = _m06.inicializuj_schema
|
||||
|
||||
|
||||
def nacti_pacienty_z_fb():
|
||||
"""Vrati slovnik {(prijmeni_upper, datnar): idpac} ze vsech pacientu v Medicusu."""
|
||||
conn = fdb.connect(**FB)
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
dnes = date.today().isoformat()
|
||||
cur.execute("""
|
||||
SELECT KAR.IDPAC, KAR.PRIJMENI, KAR.JMENO, KAR.DATNAR
|
||||
FROM KAR
|
||||
WHERE KAR.vyrazen = 'N'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM registr r
|
||||
JOIN icp i ON r.idicp = i.idicp
|
||||
WHERE r.idpac = kar.idpac
|
||||
AND r.datum <= ?
|
||||
AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= ?)
|
||||
AND r.priznak IN ('V','D','A')
|
||||
AND i.icp = ?
|
||||
AND i.odb = ?
|
||||
)
|
||||
""", (dnes, dnes, ICP, ODB))
|
||||
result = {}
|
||||
for row in cur.fetchall():
|
||||
idpac, prijmeni, jmeno, datnar = row
|
||||
klic = (prijmeni.strip().upper(), datnar)
|
||||
result[klic] = {"idpac": idpac, "prijmeni": prijmeni.strip(), "jmeno": jmeno.strip(), "datnar": datnar}
|
||||
print(f"Firebird: nacteno {len(result)} registrovanych pacientu")
|
||||
return result
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def upsert_pacient(cur, pac):
|
||||
cur.execute("""
|
||||
INSERT INTO pacient (idpac, prijmeni, jmena, datum_narozeni)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
prijmeni = VALUES(prijmeni),
|
||||
jmena = VALUES(jmena)
|
||||
""", (pac["idpac"], pac["prijmeni"], pac["jmeno"], pac["datnar"]))
|
||||
cur.execute("SELECT id FROM pacient WHERE idpac = %s", (pac["idpac"],))
|
||||
return cur.fetchone()["id"]
|
||||
|
||||
|
||||
def main():
|
||||
# Adresar z argumentu nebo default
|
||||
adresar = Path(sys.argv[1]) if len(sys.argv) > 1 else XML_ADRESAR
|
||||
if not adresar.is_dir():
|
||||
sys.exit(f"Adresar neexistuje: {adresar}")
|
||||
|
||||
# Najdi vsechna XML rekurzivne
|
||||
xml_soubory = sorted(adresar.rglob("*.xml"))
|
||||
if not xml_soubory:
|
||||
sys.exit(f"Zadne XML soubory nalezeny v: {adresar}")
|
||||
print(f"Nalezeno {len(xml_soubory)} XML souboru v: {adresar}")
|
||||
|
||||
# Nacti pacienty z Firebirdu
|
||||
fb_pacienti = nacti_pacienty_z_fb()
|
||||
|
||||
# Pripoj se k MySQL a inicializuj schema
|
||||
conn = pymysql.connect(**DB)
|
||||
try:
|
||||
inicializuj_schema(conn)
|
||||
|
||||
ok = chyba = preskoceno = 0
|
||||
p_celkem = v_celkem = 0
|
||||
|
||||
for i, xml_path in enumerate(xml_soubory, 1):
|
||||
rel = xml_path.relative_to(Path(__file__).parent)
|
||||
try:
|
||||
zprava, predpisy, vydeji, predepisujici, vydavajici = parsuj_xml(xml_path)
|
||||
except Exception as e:
|
||||
print(f"[{i:4}/{len(xml_soubory)}] {xml_path.name:<45} CHYBA parsovani: {e}")
|
||||
chyba += 1
|
||||
continue
|
||||
|
||||
# Zjisti prijmeni a datum narozeni z XML odpovedi
|
||||
pac_prijmeni = (zprava.get("pacient_prijmeni") or "").upper()
|
||||
pac_datnar = zprava.get("pacient_datum_narozeni") # string YYYY-MM-DD nebo None
|
||||
|
||||
# Prevod na date objekt pro porovnani s Firebirdem
|
||||
if pac_datnar and isinstance(pac_datnar, str):
|
||||
try:
|
||||
from datetime import datetime
|
||||
pac_datnar_d = datetime.strptime(pac_datnar[:10], "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
pac_datnar_d = None
|
||||
elif hasattr(pac_datnar, "year"):
|
||||
pac_datnar_d = pac_datnar
|
||||
else:
|
||||
pac_datnar_d = None
|
||||
|
||||
klic = (pac_prijmeni, pac_datnar_d)
|
||||
fb_pac = fb_pacienti.get(klic)
|
||||
|
||||
if not fb_pac:
|
||||
# Pacient neni registrovan — uloz bez idpac (bude ignorovan pri hromadnem behu)
|
||||
# Zkus najit v MySQL podle jmena a data
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT id FROM pacient WHERE prijmeni = %s AND datum_narozeni = %s",
|
||||
(zprava.get("pacient_prijmeni"), pac_datnar)
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
pacient_id = row["id"]
|
||||
else:
|
||||
preskoceno += 1
|
||||
print(f"[{i:4}/{len(xml_soubory)}] {xml_path.name:<45} PRESKOCENO (neni v registru)")
|
||||
continue
|
||||
else:
|
||||
with conn.cursor() as cur:
|
||||
pacient_id = upsert_pacient(cur, fb_pac)
|
||||
conn.commit()
|
||||
|
||||
try:
|
||||
stats = uloz(conn, zprava, predpisy, vydeji, predepisujici, vydavajici,
|
||||
pacient_id=pacient_id, xml_soubor=str(rel))
|
||||
conn.commit()
|
||||
p_celkem += stats["predpisy_novych"]
|
||||
v_celkem += stats["vydeji_novych"]
|
||||
print(f"[{i:4}/{len(xml_soubory)}] {xml_path.name:<45} OK "
|
||||
f"{stats['predpisy_novych']:3}p {stats['vydeji_novych']:3}v")
|
||||
ok += 1
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
print(f"[{i:4}/{len(xml_soubory)}] {xml_path.name:<45} CHYBA ukladani: {e}")
|
||||
chyba += 1
|
||||
|
||||
print()
|
||||
print(f"Hotovo: {ok} OK, {chyba} chyb, {preskoceno} preskoceno")
|
||||
print(f"Celkem vlozeno: {p_celkem} predpisu, {v_celkem} vydejuu")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,38 @@
|
||||
import fdb, sys
|
||||
|
||||
conn = fdb.connect(
|
||||
dsn=r"localhost:c:\medicus 3\data\medicus.fdb",
|
||||
user="SYSDBA", password="masterkey", charset="win1250"
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Try CSSZ_ODB
|
||||
try:
|
||||
cur.execute("SELECT FIRST 30 * FROM CSSZ_ODB")
|
||||
cols = [d[0] for d in cur.description]
|
||||
print("CSSZ_ODB cols:", cols)
|
||||
for r in cur.fetchall():
|
||||
print(r)
|
||||
except Exception as e:
|
||||
print("CSSZ_ODB:", e)
|
||||
|
||||
# Try ODB table
|
||||
try:
|
||||
cur.execute("SELECT FIRST 30 * FROM ODB")
|
||||
cols = [d[0] for d in cur.description]
|
||||
print("ODB cols:", cols)
|
||||
for r in cur.fetchall():
|
||||
print(r)
|
||||
except Exception as e:
|
||||
print("ODB:", e)
|
||||
|
||||
# List all tables with ODB in name
|
||||
try:
|
||||
cur.execute("SELECT RDB$RELATION_NAME FROM RDB$RELATIONS WHERE RDB$RELATION_NAME LIKE '%ODB%' AND RDB$SYSTEM_FLAG = 0")
|
||||
for r in cur.fetchall():
|
||||
print("Table:", r[0].strip())
|
||||
except Exception as e:
|
||||
print("table list:", e)
|
||||
|
||||
conn.close()
|
||||
print("DONE")
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
CSSZ_ODB cols: ['ID', 'KOD', 'NAZEV', 'PLATNOST_OD', 'PLATNOST_DO', 'POZNAMKA']
|
||||
(104, 'L01', 'alergologie a klinickß imunologie', datetime.date(2019, 9, 1), None, None)
|
||||
(105, 'L02', 'anesteziologie a intenzivnÝ medicÝna', datetime.date(2019, 9, 1), None, None)
|
||||
(106, 'L04', 'cÚvnÝ chirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(107, 'L05', 'dermatovenerologie', datetime.date(2019, 9, 1), None, None)
|
||||
(108, 'L07', 'dýtskß chirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(109, 'L08', 'dýtskÚ lÚka°stvÝ', datetime.date(2019, 9, 1), None, None)
|
||||
(110, 'L09', 'endokrinologie a diabetologie', datetime.date(2019, 9, 1), None, None)
|
||||
(111, 'L10', 'gastroenterologie', datetime.date(2019, 9, 1), None, None)
|
||||
(112, 'L11', 'geriatrie', datetime.date(2019, 9, 1), None, None)
|
||||
(113, 'L12', 'gynekologie a porodnictvÝ', datetime.date(2019, 9, 1), None, None)
|
||||
(114, 'L13', 'hematologie a transf˙znÝ lÚka°stvÝ', datetime.date(2019, 9, 1), None, None)
|
||||
(115, 'L14', 'hygiena a epidemiologie', datetime.date(2019, 9, 1), None, None)
|
||||
(116, 'L15', 'chirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(117, 'L16', 'infekŔnÝ lÚka°stvÝ', datetime.date(2019, 9, 1), None, None)
|
||||
(118, 'L17', 'kardiochirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(119, 'L18', 'kardiologie', datetime.date(2019, 9, 1), None, None)
|
||||
(120, 'L19', 'klinickß biochemie', datetime.date(2019, 9, 1), None, None)
|
||||
(121, 'L20', 'klinickß onkologie', datetime.date(2019, 9, 1), None, None)
|
||||
(122, 'L21', 'lÚka°skß genetika', datetime.date(2019, 9, 1), None, None)
|
||||
(123, 'L22', 'lÚka°skß mikrobiologie', datetime.date(2019, 9, 1), None, None)
|
||||
(124, 'L23', 'nefrologie', datetime.date(2019, 9, 1), None, None)
|
||||
(125, 'L24', 'neurochirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(126, 'L25', 'neurologie', datetime.date(2019, 9, 1), None, None)
|
||||
(127, 'L26', 'nukleßrnÝ medicÝna', datetime.date(2019, 9, 1), None, None)
|
||||
(128, 'L27', 'oftalmologie', datetime.date(2019, 9, 1), None, None)
|
||||
(129, 'L28', 'ortopedie a traumatologie pohybovÚho ˙strojÝ', datetime.date(2019, 9, 1), None, None)
|
||||
(130, 'L29', 'otorinolaryngologie a chirurgie hlavy a krku', datetime.date(2019, 9, 1), None, None)
|
||||
(131, 'L30', 'patologie', datetime.date(2019, 9, 1), None, None)
|
||||
(132, 'L31', 'plastickß chirurgie', datetime.date(2019, 9, 1), None, None)
|
||||
(133, 'L32', 'pneumologie a ftizeologie', datetime.date(2019, 9, 1), None, None)
|
||||
ODB: ('Error while preparing SQL statement:\n- SQLCODE: -204\n- Dynamic SQL Error\n- SQL error code = -204\n- Table unknown\n- ODB\n- At line 1, column 24', -204, 335544569)
|
||||
Table: HODBOD
|
||||
Table: ODBORN
|
||||
Table: ODBORN_MASK
|
||||
Table: ODBORN_SKUP
|
||||
Table: CSSZ_ODB
|
||||
DONE
|
||||
Reference in New Issue
Block a user