Files
medicus/MedicusWithClaudeDekurz/dekurz_report.py
T
2026-04-01 06:04:40 +02:00

290 lines
14 KiB
Python

import sys, io, re, os, glob
from datetime import date, datetime
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
import fdb
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
VYSTUPNI_ADRESAR = r'u:\Dropbox\Ordinace\Reporty'
NAZEV_REPORTU = 'Dekurzy'
DATUM_OD = '2025-01-01'
DATUM_DO = date.today().strftime('%Y-%m-%d')
conn = fdb.connect(
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
user='SYSDBA', password='masterkey', charset='win1250'
)
cur = conn.cursor()
cur.execute(f"""
SELECT d.DATUM, d.CAS, u.ZKRATKA, k.PRIJMENI, k.JMENO, k.RODCIS, k.POJ, d.DEKURS
FROM DEKURS d
JOIN KAR k ON k.IDPAC = d.IDPAC
LEFT JOIN UZIVATEL u ON u.IDUZI = d.IDUZI
WHERE d.DATUM >= '{DATUM_OD}' AND d.DATUM <= '{DATUM_DO}'
ORDER BY d.DATUM DESC, d.CAS DESC, k.PRIJMENI, k.JMENO
""")
raw_rows = cur.fetchall()
TOP_TYPY = ['Rec', 'VykA', 'Files', 'MEDLAB', 'Lab', 'Ock', 'Nes', 'Lec', 'SpecVys', 'PlaPac']
# Parse dekurzů
rows = []
for datum, cas, zkratka, prijmeni, jmeno, rodcis, poj, dekurs_blob in raw_rows:
rtf = dekurs_blob.read() if hasattr(dekurs_blob, 'read') else (dekurs_blob or '')
pocty = {}
ids_by_typ = {t: [] for t in TOP_TYPY}
ids_ostatni = []
for nazev, typ, rid in re.findall(r'"([^"]+)","([A-Za-z]+):(\d+)"', rtf):
pocty[typ] = pocty.get(typ, 0) + 1
if typ in ids_by_typ:
ids_by_typ[typ].append(int(rid))
else:
ids_ostatni.append((typ, int(rid), nazev))
top = [pocty.get(t, 0) for t in TOP_TYPY]
ostatni = sum(v for k, v in pocty.items() if k not in TOP_TYPY)
iniciala = jmeno[0] + '.' if jmeno and jmeno.strip() else ''
jmeno_cel = f"{prijmeni.strip()}, {iniciala}" if prijmeni else iniciala
rows.append((datum, cas, zkratka, jmeno_cel, rodcis, poj, top, ostatni, ids_by_typ, ids_ostatni))
# ── Načtení detailů z DB ────────────────────────────────────────────────────
def fetch_details(cur, table, pk, id_col, fields, ids):
if not ids:
return {}
result = {}
batch_size = 1000
for i in range(0, len(ids), batch_size):
batch = ids[i:i+batch_size]
ph = ','.join('?' * len(batch))
cur.execute(f"SELECT {pk}, {','.join(fields)} FROM {table} WHERE {id_col} IN ({ph})", batch)
for row in cur.fetchall():
result[row[0]] = row[1:]
return result
def get_ids(rows, typ):
return list({rid for _, _, _, _, _, _, _, _, ids_by_typ, _ in rows for rid in ids_by_typ[typ]})
rec_det = fetch_details(cur, 'RECEPT', 'ID', 'ID', ['LEK','DSIG'], get_ids(rows,'Rec'))
vyka_det = fetch_details(cur, 'DOKLADD', 'ID', 'ID', ['KOD','DDGN'], get_ids(rows,'VykA'))
files_det = fetch_details(cur, 'FILES', 'ID', 'ID', ['FILENAME','DATUM'], get_ids(rows,'Files'))
medlab_det = fetch_details(cur, 'HISTDOC', 'ID', 'ID', ['DATUM','TYP'], get_ids(rows,'MEDLAB'))
lab_det = fetch_details(cur, 'LABVH', 'IDVH', 'IDVH', ['DATUM','CISLO'], get_ids(rows,'Lab'))
ock_det = fetch_details(cur, 'OCKZAZ', 'ID', 'ID', ['DATUM','LATKA'], get_ids(rows,'Ock'))
nes_det = fetch_details(cur, 'NES', 'ID', 'ID', ['ZACNES','KONNES'], get_ids(rows,'Nes'))
lec_det = fetch_details(cur, 'LECD', 'ID', 'ID', ['KOD','DATOSE'], get_ids(rows,'Lec'))
spec_det = fetch_details(cur, 'SPECVYS', 'IDSPECVYS','IDSPECVYS',['TYP','DATUM'], get_ids(rows,'SpecVys'))
pla_det = fetch_details(cur, 'PLA', 'IDPLA', 'IDPLA', ['DATUM','CENA','DOKLAD'], get_ids(rows,'PlaPac'))
conn.close()
print(f"Načteno {len(rows)} dekurzů")
# ── Styly ──────────────────────────────────────────────────────────────────
tenka_cara = Side(style='thin', color='AAAAAA')
ohraniceni = Border(left=tenka_cara, right=tenka_cara, top=tenka_cara, bottom=tenka_cara)
hl_font = Font(bold=True, color="FFFFFF")
hl_fill = PatternFill("solid", fgColor="2E75B6")
r_fill = [PatternFill("solid", fgColor="FFFFFF"), PatternFill("solid", fgColor="DCE6F1")]
BARVY_LISTU = {
'Recepty': ('1F6B33', 'E2EFDA'),
'Výkony': ('2E4057', 'D6E4F0'),
'Soubory': ('7B3F00', 'FAE5D3'),
'MedLab': ('4A235A', 'F5EEF8'),
'Lab': ('145A32', 'D5F5E3'),
'Očkování': ('7E5109', 'FDEBD0'),
'Neschop.': ('922B21', 'FADBD8'),
'Léčiva': ('1A5276', 'D6EAF8'),
'SpecVys': ('0B5345', 'D1F2EB'),
'Platby': ('4D5656', 'EAECEE'),
'Ostatní': ('2C3E50', 'EBF5FB'),
}
def zapis_hlavicku(ws, sloupce, sirky, barva_hex):
hl_fill_l = PatternFill("solid", fgColor=barva_hex)
for col, (nazev, sirka) in enumerate(zip(sloupce, sirky), start=1):
cell = ws.cell(row=1, column=col, value=nazev)
cell.font = hl_font
cell.fill = hl_fill_l
cell.alignment = Alignment(horizontal='center')
cell.border = ohraniceni
ws.column_dimensions[cell.column_letter].width = sirka
def zapis_radek(ws, row_i, hodnoty, zarovnani, barva_hex):
fill = PatternFill("solid", fgColor="FFFFFF") if row_i % 2 == 0 \
else PatternFill("solid", fgColor=barva_hex)
for col_i, (val, align) in enumerate(zip(hodnoty, zarovnani), start=1):
cell = ws.cell(row=row_i, column=col_i, value=val)
cell.fill = fill
cell.border = ohraniceni
cell.alignment = Alignment(horizontal=align)
if col_i == 1 and isinstance(val, __import__('datetime').date):
cell.number_format = 'DD.MM.YYYY'
def hyperlink_cell(ws, row_i, col_i, cil_list, cil_radek, text, barva_hex):
fill = PatternFill("solid", fgColor="FFFFFF") if row_i % 2 == 0 \
else PatternFill("solid", fgColor=barva_hex)
cell = ws.cell(row=row_i, column=col_i)
cell.value = f'=HYPERLINK("#{cil_list}!A{cil_radek}","{text}")'
cell.font = Font(color="0000FF", underline='single')
cell.fill = fill
cell.border = ohraniceni
cell.alignment = Alignment(horizontal='center')
# ── Workbook ───────────────────────────────────────────────────────────────
wb = openpyxl.Workbook()
# Pořadí listů a jejich konfigurace: (název, typ_bookmarku, detail_dict, sloupce, šířky, pk_label)
LISTY = [
('Recepty', 'Rec', rec_det, ['Datum','Jméno','Recept','Dávkování'], [12,25,25,12], None),
('Výkony', 'VykA', vyka_det, ['Datum','Jméno','Kód výkonu','Diagnóza'], [12,25,14,10], None),
('Soubory', 'Files', files_det, ['Datum','Jméno','Soubor','Datum souboru'], [12,25,35,14], None),
('MedLab', 'MEDLAB', medlab_det, ['Datum','Jméno','Typ'], [12,25,15], None),
('Lab', 'Lab', lab_det, ['Datum','Jméno','Číslo'], [12,25,20], None),
('Očkování', 'Ock', ock_det, ['Datum','Jméno','Datum očkování','Vakcína'], [12,25,14,30], None),
('Neschop.', 'Nes', nes_det, ['Datum','Jméno','Od','Do'], [12,25,12,12], None),
('Léčiva', 'Lec', lec_det, ['Datum','Jméno','Kód','Datum výkonu'], [12,25,12,14], None),
('SpecVys', 'SpecVys', spec_det, ['Datum','Jméno','Typ vyšetření','Datum vyšetření'], [12,25,25,14], None),
('Platby', 'PlaPac', pla_det, ['Datum','Jméno','Datum platby','Částka','Doklad'], [12,25,14,12,15], None),
('Ostatní', None, None, ['Datum','Jméno','Typ','ID','Název'], [12,25,12,10,30], None),
]
# Vytvoříme listy
ws_d = wb.active
ws_d.title = "Dekurz"
ws_listy = {}
for nazev, *_ in LISTY:
ws_listy[nazev] = wb.create_sheet(nazev)
# Záhlaví listů
for nazev, typ, det, sloupce, sirky, _ in LISTY:
barva_hl, _ = BARVY_LISTU[nazev]
zapis_hlavicku(ws_listy[nazev], sloupce, sirky, barva_hl)
ws_listy[nazev].freeze_panes = 'A2'
# Záhlaví Dekurz
nazvy_d = ['Datum', 'Čas', 'Lékař', 'Jméno', 'Rodné číslo', 'Pojišťovna'] + TOP_TYPY + ['Ostatní']
sirky_d = [12, 8, 8, 25, 14, 12 ] + [8]*10 + [8]
zapis_hlavicku(ws_d, nazvy_d, sirky_d, '2E75B6')
ws_d.freeze_panes = 'A2'
ws_d.auto_filter.ref = f"A1:Q{len(rows)+1}"
# Aktuální řádek pro každý list
row_ptr = {nazev: 2 for nazev, *_ in LISTY}
# ── Plnění dat ─────────────────────────────────────────────────────────────
def get_det_hodnoty(typ, rid, datum, jmeno_cel):
"""Vrátí seznam hodnot pro řádek detailního listu."""
if typ == 'Rec':
d = rec_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0] or '', d[1] or '']
elif typ == 'VykA':
d = vyka_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0] or '', (d[1] or '').strip()]
elif typ == 'Files':
d = files_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0] or '', d[1]]
elif typ == 'MEDLAB':
d = medlab_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[1] or '']
elif typ == 'Lab':
d = lab_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[1] or '']
elif typ == 'Ock':
d = ock_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0], d[1] or '']
elif typ == 'Nes':
d = nes_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0], d[1]]
elif typ == 'Lec':
d = lec_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0] or '', d[1]]
elif typ == 'SpecVys':
d = spec_det.get(rid, ('', ''))
return [datum, jmeno_cel, d[0] or '', d[1]]
elif typ == 'PlaPac':
d = pla_det.get(rid, ('', '', ''))
return [datum, jmeno_cel, d[0], d[1], d[2] or '']
return []
ZAROVNANI = {
'Recepty': ['left','left','left','center'],
'Výkony': ['left','left','center','center'],
'Soubory': ['left','left','left','left'],
'MedLab': ['left','left','center'],
'Lab': ['left','left','center'],
'Očkování': ['left','left','left','left'],
'Neschop.': ['left','left','left','left'],
'Léčiva': ['left','left','center','left'],
'SpecVys': ['left','left','left','left'],
'Platby': ['left','left','left','right','center'],
'Ostatní': ['left','left','center','center','left'],
}
for row_i, (datum, cas, zkratka, jmeno_cel, rodcis, poj, top, ostatni, ids_by_typ, ids_ostatni) in enumerate(rows, start=2):
# Ohraničení řádku Dekurz
fill_d = r_fill[row_i % 2]
for col_i in range(1, len(nazvy_d) + 1):
ws_d.cell(row=row_i, column=col_i).fill = fill_d
ws_d.cell(row=row_i, column=col_i).border = ohraniceni
ws_d.cell(row=row_i, column=1, value=datum).number_format = 'DD.MM.YYYY'
ws_d.cell(row=row_i, column=2, value=str(cas)[:5] if cas else '').alignment = Alignment(horizontal='center')
ws_d.cell(row=row_i, column=3, value=zkratka or '').alignment = Alignment(horizontal='center')
ws_d.cell(row=row_i, column=4, value=jmeno_cel)
ws_d.cell(row=row_i, column=5, value=rodcis or '')
ws_d.cell(row=row_i, column=6, value=poj or '').alignment = Alignment(horizontal='center')
# Sloupce bookmarků
for col_off, (typ, pocet) in enumerate(zip(TOP_TYPY, top)):
col_i = 7 + col_off
if pocet == 0:
continue
# Najdi název listu pro tento typ
nazev_listu = next((n for n, t, *_ in LISTY if t == typ), None)
if nazev_listu and ids_by_typ[typ]:
_, barva_ll = BARVY_LISTU[nazev_listu]
hyperlink_cell(ws_d, row_i, col_i, nazev_listu, row_ptr[nazev_listu], pocet, barva_ll[1:] if len(barva_ll) > 6 else 'DCE6F1')
# Zapiš řádky na detailní list
ws_det = ws_listy[nazev_listu]
barva_hl, barva_r = BARVY_LISTU[nazev_listu]
for rid in ids_by_typ[typ]:
hodnoty = get_det_hodnoty(typ, rid, datum, jmeno_cel)
zarovnani_l = ZAROVNANI.get(nazev_listu, ['left']*10)
zapis_radek(ws_det, row_ptr[nazev_listu], hodnoty, zarovnani_l, barva_r)
row_ptr[nazev_listu] += 1
else:
ws_d.cell(row=row_i, column=col_i, value=pocet).alignment = Alignment(horizontal='center')
# Ostatní
if ostatni:
ws_det = ws_listy['Ostatní']
barva_hl, barva_r = BARVY_LISTU['Ostatní']
hyperlink_cell(ws_d, row_i, 17, 'Ostatní', row_ptr['Ostatní'], ostatni, barva_r)
for typ, rid, nazev in ids_ostatni:
zapis_radek(ws_det, row_ptr['Ostatní'],
[datum, jmeno_cel, typ, rid, nazev],
ZAROVNANI['Ostatní'], barva_r)
row_ptr['Ostatní'] += 1
# Autofiltr na detailních listech
for nazev, *_ in LISTY:
ws = ws_listy[nazev]
max_col = ws.max_column
max_row = ws.max_row
if max_row > 1:
ws.auto_filter.ref = f"A1:{ws.cell(row=1, column=max_col).column_letter}{max_row}"
# Smazat starý report
for stary in glob.glob(os.path.join(VYSTUPNI_ADRESAR, f'* {NAZEV_REPORTU}.xlsx')):
os.remove(stary)
print(f"Smazán: {stary}")
# Uložit nový
os.makedirs(VYSTUPNI_ADRESAR, exist_ok=True)
casova_znacka = datetime.now().strftime('%Y-%m-%d %H-%M-%S')
vystup = os.path.join(VYSTUPNI_ADRESAR, f'{casova_znacka} {NAZEV_REPORTU}.xlsx')
wb.save(vystup)
print(f"Uloženo: {vystup}")
for nazev, *_ in LISTY:
print(f" {nazev}: {row_ptr[nazev]-2} řádků")