# 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*
