209 lines
7.5 KiB
Python
209 lines
7.5 KiB
Python
"""
|
||
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
|