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