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
+208
View File
@@ -0,0 +1,208 @@
"""
003_aktivni_PN_seznam.py
========================
Vypíše seznam pacientů s aktivní pracovní neschopností (PN) k dnešnímu datu.
SQL logika převzata přímo z Medicusu (report "Přehled práce neschopných pacientů"):
- ZACNES <= dnes
- (KONNES >= dnes) OR (KONNES IS NULL)
- PRACNE = 'A'
- STORNO <> 'T'
Spouštění: tlačítkem z Medicusu nebo přímo pythonu.
"""
import sys, os, traceback
_LOG = r"C:\Users\vlado\PycharmProjects\Medicus\PNWithClaude\003_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
# ── Připojení ─────────────────────────────────────────────────────────────────
DSN = r'localhost:c:\medicus 3\data\medicus.fdb'
USER = 'SYSDBA'
PASSWORD = 'masterkey'
CHARSET = 'win1250'
# ── SQL ───────────────────────────────────────────────────────────────────────
SQL = """
SELECT
kar.PRIJMENI,
kar.JMENO,
kar.RODCIS,
nes.ZACNES,
nes.KONNES,
nes.DIAGNO,
COALESCE(nes.ECN, nes.CISNES) AS CISNES
FROM NES nes
JOIN KAR kar ON kar.IDPAC = nes.IDPAC
WHERE
nes.ZACNES <= ?
AND (nes.KONNES >= ? OR nes.KONNES IS NULL)
AND nes.PRACNE = 'A'
AND nes.STORNO <> 'T'
ORDER BY kar.PRIJMENI, kar.JMENO
"""
def nacti_data():
dnes = datetime.date.today()
conn = fdb.connect(dsn=DSN, user=USER, password=PASSWORD, charset=CHARSET)
try:
cur = conn.cursor()
cur.execute(SQL, (dnes, dnes))
rows = cur.fetchall()
return dnes, rows
finally:
conn.close()
def delka_PN(zacnes, konnes, dnes):
"""Počet dní PN (od začátku do dnes nebo do konce)."""
do = konnes if konnes else dnes
return (do - zacnes).days + 1
def zobraz_okno(dnes, rows):
root = tk.Tk()
root.title(f"Aktivní pracovní neschopnost {dnes.strftime('%d.%m.%Y')}")
w, h = 900, 550
root.update_idletasks()
sw = root.winfo_screenwidth()
sh = root.winfo_screenheight()
x = (sw - w) // 2
y = (sh - h) // 2
root.geometry(f"{w}x{h}+{x}+{y}")
# ── Záhlaví ───────────────────────────────────────────────────────────────
hlavicka = tk.Frame(root, bg="#2c5f8a", pady=8)
hlavicka.pack(fill=tk.X)
tk.Label(
hlavicka,
text=f"Pracovní neschopnost aktivní k {dnes.strftime('%d.%m.%Y')} ({len(rows)} pacientů)",
font=("Segoe UI", 13, "bold"),
fg="white", bg="#2c5f8a"
).pack()
# ── Tabulka ───────────────────────────────────────────────────────────────
cols = ("Příjmení", "Jméno", "Rodné číslo", "Začátek", "Konec", "Dní", "Diagnóza", "Č. neschopenky")
frame = tk.Frame(root)
frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
scroll_y = tk.Scrollbar(frame, orient=tk.VERTICAL)
scroll_y.pack(side=tk.RIGHT, fill=tk.Y)
scroll_x = tk.Scrollbar(frame, orient=tk.HORIZONTAL)
scroll_x.pack(side=tk.BOTTOM, fill=tk.X)
tree = ttk.Treeview(
frame,
columns=cols,
show="headings",
yscrollcommand=scroll_y.set,
xscrollcommand=scroll_x.set
)
scroll_y.config(command=tree.yview)
scroll_x.config(command=tree.xview)
# Šířky sloupců
widths = [120, 100, 110, 85, 85, 50, 80, 150]
for col, w in zip(cols, widths):
tree.column(col, width=w, anchor=tk.W)
# ── Řazení kliknutím na záhlaví ───────────────────────────────────────────
_sort_state = {} # col -> ascending True/False
def sort_by(col):
ascending = not _sort_state.get(col, False)
_sort_state[col] = ascending
# Index sloupce pro čtení hodnoty
col_idx = cols.index(col)
data = [(tree.set(k, col), k) for k in tree.get_children("")]
# Číselné řazení pro "Dní"
if col == "Dní":
data.sort(key=lambda t: int(t[0]) if t[0].isdigit() else 0, reverse=not ascending)
else:
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í
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))
# Obarvi znovu střídavě
for idx, (_, k) in enumerate(data):
current_tags = list(tree.item(k, "tags"))
dlouha = "dlouha" in current_tags
if dlouha:
tree.item(k, tags=("dlouha",))
else:
tree.item(k, tags=("even" if idx % 2 == 0 else "odd",))
for col in cols:
tree.heading(col, text=col, command=lambda c=col: sort_by(c))
# Střídavé barvy řádků
tree.tag_configure("even", background="#f0f4f8")
tree.tag_configure("odd", background="white")
tree.tag_configure("dlouha", foreground="#c0392b") # červeně > 90 dní
for i, row in enumerate(rows):
prijmeni, jmeno, rodcis, zacnes, konnes, diagno, cisnes = row
dni = delka_PN(zacnes, konnes, dnes)
konec_str = konnes.strftime("%d.%m.%Y") if konnes else "trvá"
diagno_str = (diagno or "").strip()
cisnes_str = (cisnes or "").strip()
tag = "dlouha" if dni > 90 else ("even" if i % 2 == 0 else "odd")
tree.insert("", tk.END, values=(
prijmeni,
jmeno,
rodcis,
zacnes.strftime("%d.%m.%Y"),
konec_str,
dni,
diagno_str,
cisnes_str,
), tags=(tag,))
tree.pack(fill=tk.BOTH, expand=True)
# ── Výchozí řazení: Dní vzestupně ─────────────────────────────────────────
sort_by("Dní")
# ── Legenda + zavřít ──────────────────────────────────────────────────────
spodek = tk.Frame(root, pady=6)
spodek.pack(fill=tk.X)
tk.Label(
spodek,
text="Červeně = PN delší než 90 dní",
font=("Segoe UI", 9),
fg="#c0392b"
).pack(side=tk.LEFT, padx=15)
tk.Button(
spodek,
text="Zavřít",
command=root.destroy,
width=12,
font=("Segoe UI", 10)
).pack(side=tk.RIGHT, padx=15)
root.mainloop()
# ── Hlavní tok ────────────────────────────────────────────────────────────────
if __name__ == "__main__":
try:
dnes, rows = nacti_data()
zobraz_okno(dnes, rows)
except Exception:
with open(_LOG, "w", encoding="utf-8") as f:
traceback.print_exc(file=f)
raise