Files
medicus/PNWithClaude/README.md
T
2026-03-25 17:32:12 +01:00

8.4 KiB
Raw Blame History

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

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

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

"""
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

_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