notebook vb

This commit is contained in:
2026-03-25 17:32:12 +01:00
parent 5674de1247
commit 82719baba8
7 changed files with 1086 additions and 0 deletions
+291
View File
@@ -0,0 +1,291 @@
# PNWithClaude Dokumentace projektu
> **Účel:** Python skripty spouštěné tlačítkem z Medicusu, které rozšiřují možnosti
> vestavěných reportů. Vznikly díky SQL loggeru/debuggeru v Medicusu, který odhalil
> strukturu databáze.
---
## Prostředí
| Položka | Hodnota |
|---|---|
| Python | 3.12.9 (64-bit) |
| Python exe | `C:\Users\vlado\PycharmProjects\Medicus\.venv\Scripts\python.exe` |
| Pythonw exe | `C:\Users\vlado\PycharmProjects\Medicus\.venv\Scripts\pythonw.exe` |
| Projekt | `C:\Users\vlado\PycharmProjects\Medicus\` |
| Skripty | `C:\Users\vlado\PycharmProjects\Medicus\PNWithClaude\` |
| Databáze | Firebird `localhost:c:\medicus 3\data\medicus.fdb` |
### Klíčové Python balíčky
- `fdb` Firebird databázový driver
- `tkinter` GUI (součást Pythonu, není třeba instalovat)
---
## Připojení k databázi
```python
import fdb
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA',
password='masterkey',
charset='win1250'
)
```
> ⚠️ Charset `win1250` je kritický bez něj jsou česká písmena rozbitá.
---
## Konvence pojmenování skriptů
```
NNN_popisne_jmeno.py
```
- `NNN` = třímístné číslo (001, 002, 003, ...)
- Příklad: `003_aktivni_PN_seznam.py`
---
## Jak nastavit tlačítko v Medicusu
Medicus → **Konfigurace → Externí programy** (nebo Nástroje → Nastavení)
### Pole v dialogu „Externí program"
| Pole | Hodnota |
|---|---|
| **Příkazový řádek** | `"C:\Users\vlado\PycharmProjects\Medicus\.venv\Scripts\pythonw.exe" "C:\Users\vlado\PycharmProjects\Medicus\PNWithClaude\NNN_skript.py"` |
| **Program** | `C:\Users\vlado\PycharmProjects\Medicus\.venv\Scripts\pythonw.exe` |
| **Spustit v** | *(nechat prázdné)* |
| **Popis** | Název tlačítka v Medicusu |
| **Pacient** | ☐ nezaškrtávat (pokud skript nepotřebuje aktuálního pacienta) |
> ⚠️ **`pythonw.exe`** místo `python.exe` = žádné černé konzolové okno!
>
> ⚠️ **„Spustit v" nechat prázdné** Medicus hlásí chybu pokud složka
> „neexistuje" (i když existuje), ale hlavně to nepotřebujeme,
> protože používáme absolutní cesty všude.
### Jak přidat tlačítko na lištu
Po uložení externího programu → pravým tlačítkem na lištu → přizpůsobit →
najít nový program → přetáhnout na lištu.
### Předávání dat z Medicusu skriptu
Medicus umí předat proměnné přes příkazový řádek, např.:
```
"pythonw.exe" "skript.py" "%RODCISN%" "%JMENO%" "%PRIJMENI%"
```
Dostupné proměnné (ukázka ze stávající konfigurace laboratoře):
- `%JMENO%` jméno pacienta
- `%PRIJMENI%` příjmení
- `%RODCISN%` rodné číslo (bez lomítka)
- `%POJ%` pojišťovna
- `%DGN%` diagnóza
V Pythonu pak čteš: `sys.argv[1]`, `sys.argv[2]`, atd.
---
## Jak zjistit SQL dotazy Medicusu SQL Logger
1. Medicus → **Nástroje → SQL Monitor** (nebo podobně v menu)
2. Zapnout logging
3. Provést akci v Medicusu (otevřít záložku, spustit report)
4. Zkopírovat SQL z logu
Log se ukládá do: `C:\Medicus 3\Debug\Monitor_DDMMYY_HHMMSS.log`
> 💡 **Zlatý důl!** SQL logger odhalí přesnou strukturu tabulek a podmínky
> filtrování, které Medicus interně používá.
---
## Struktura databáze klíčové tabulky
### Pacienti
| Tabulka | Popis |
|---|---|
| `KAR` | Kartotéka pacientů `IDPAC`, `PRIJMENI`, `JMENO`, `RODCIS` |
### Pracovní neschopnost (PN)
| Tabulka | Popis |
|---|---|
| `NES` | **Hlavní tabulka neschopenek** |
| `HPN` | Elektronická podání na ČSSZ (eNeschopenka) komunikační vrstva, 5693 záznamů |
| `HPN_NOTIFIKACE` | Notifikace z ČSSZ |
| `HPN_NOTIFIKACE_DETAIL` | Detail notifikací, stavy podání (`STAV_PODANI`) |
| `HPN_PODANI` | Podání na ČSSZ |
| `HOSPNES` | Hospitalizační neschopenky |
### Klíčové sloupce tabulky `NES`
| Sloupec | Popis |
|---|---|
| `IDPAC` | ID pacienta (JOIN s KAR) |
| `ZACNES` | Začátek PN (DATE) |
| `KONNES` | Konec PN (DATE) NULL = stále trvá |
| `DIAGNO` | Diagnóza (MKN-10 kód) |
| `CISNES` | Číslo neschopenky (starý formát) |
| `ECN` | eČíslo neschopenky (nový elektronický formát) |
| `PRACNE` | Pracovní neschopnost = `'A'` |
| `STORNO` | Storno záznamu = `'T'` (True) |
| `STATDPNKOD` | Stav DPN kód |
| `VERZE_DPN` | Verze DPN |
---
## SQL aktivní PN k dnešnímu datu
```sql
SELECT
kar.PRIJMENI,
kar.JMENO,
kar.RODCIS,
nes.ZACNES,
nes.KONNES,
nes.DIAGNO,
COALESCE(nes.ECN, nes.CISNES) AS CISNES -- preferuj eČíslo, fallback na staré
FROM NES nes
JOIN KAR kar ON kar.IDPAC = nes.IDPAC
WHERE
nes.ZACNES <= CAST('TODAY' AS DATE)
AND (nes.KONNES >= CAST('TODAY' AS DATE) OR nes.KONNES IS NULL)
AND nes.PRACNE = 'A'
AND nes.STORNO <> 'T'
ORDER BY kar.PRIJMENI, kar.JMENO
```
> 💡 `COALESCE(nes.ECN, nes.CISNES)` starší neschopenky mají jen `CISNES`,
> novější elektronické mají `ECN`. Takhle dostaneme vždy správné číslo.
---
## Šablona nového skriptu
```python
"""
NNN_nazev_skriptu.py
====================
Popis co skript dělá.
"""
import sys, os, traceback
_LOG = r"C:\Users\vlado\PycharmProjects\Medicus\PNWithClaude\NNN_error.log"
def _log_exception(exc_type, exc_value, exc_tb):
with open(_LOG, "w", encoding="utf-8") as f:
traceback.print_exception(exc_type, exc_value, exc_tb, file=f)
sys.excepthook = _log_exception
import fdb
import datetime
import tkinter as tk
from tkinter import ttk
DSN = r'localhost:c:\medicus 3\data\medicus.fdb'
USER = 'SYSDBA'
PASSWORD = 'masterkey'
CHARSET = 'win1250'
SQL = """
SELECT ...
FROM ...
WHERE ...
"""
def nacti_data():
conn = fdb.connect(dsn=DSN, user=USER, password=PASSWORD, charset=CHARSET)
try:
cur = conn.cursor()
cur.execute(SQL)
return cur.fetchall()
finally:
conn.close()
def zobraz_okno(rows):
root = tk.Tk()
root.title("Název okna")
# Centrování na střed monitoru
w, h = 900, 550
root.update_idletasks()
sw, sh = root.winfo_screenwidth(), root.winfo_screenheight()
root.geometry(f"{w}x{h}+{(sw-w)//2}+{(sh-h)//2}")
# ... GUI kód ...
root.mainloop()
if __name__ == "__main__":
try:
rows = nacti_data()
zobraz_okno(rows)
except Exception:
with open(_LOG, "w", encoding="utf-8") as f:
traceback.print_exc(file=f)
raise
```
---
## Řazení v ttk.Treeview vzorový kód
```python
_sort_state = {} # uchovává směr řazení pro každý sloupec
def sort_by(col):
ascending = not _sort_state.get(col, False)
_sort_state[col] = ascending
data = [(tree.set(k, col), k) for k in tree.get_children("")]
if col == "Dní": # číselné sloupce
data.sort(key=lambda t: int(t[0]) if t[0].isdigit() else 0, reverse=not ascending)
else: # textové sloupce
data.sort(key=lambda t: t[0].lower(), reverse=not ascending)
for idx, (_, k) in enumerate(data):
tree.move(k, "", idx)
# Šipka v záhlaví aktivního sloupce
for c in cols:
arrow = ("" if ascending else "") if c == col else ""
tree.heading(c, text=c + arrow, command=lambda c=c: sort_by(c))
# Připoj řazení na záhlaví
for col in cols:
tree.heading(col, text=col, command=lambda c=col: sort_by(c))
# Výchozí řazení při spuštění
sort_by("Dní")
```
---
## Ladění problémů
### Skript nefunguje z Medicusu, ale z PyCharmu ano
1. Přepnout dočasně na `python.exe` (ne `pythonw.exe`) uvidíš konzoli
2. Zkontrolovat `NNN_error.log` v adresáři skriptů
3. Nejčastější příčiny:
- `__file__` je prázdný → používej **absolutní cesty** pro log soubory
- Chybí `fbclient.dll` v PATH → testuj test skriptem
- Pole „Spustit v" v Medicusu → **nechat prázdné**
### Test skript pro ověření prostředí
Viz `test_spusteni.py` testuje Python, fdb import, tkinter a DB spojení.
Výstup: `Python: OK`, `fdb: OK`, `tkinter: OK`, `DB spojeni: OK`
### Černé konzolové okno
Použít `pythonw.exe` místo `python.exe` v konfiguraci Medicusu.
---
## Seznam skriptů
| Číslo | Soubor | Popis |
|---|---|---|
| 001 | `001_pruzkum_PN_tabulek.py` | Průzkum struktury DB tabulky a sloupce s PN |
| 003 | `003_aktivni_PN_seznam.py` | **Aktivní PN k dnešnímu datu** tlačítko na liště |
> Číslo 002 přeskočeno (bylo pracovní/testovací stadium).
---
*Projekt vznikl: březen 2026 | Medicus 3 Komfort | Firebird DB | Python 3.12*