From 3b30c35400add54232f8b51e547cb8c097f61019 Mon Sep 17 00:00:00 2001 From: Vladimir Buzalka Date: Wed, 1 Apr 2026 06:04:40 +0200 Subject: [PATCH] notebook vb --- MedicusWithClaudeDekurz/dekurz_report.py | 289 ++++++++++++++++++ .../registrace_2025_dnes.xlsx | Bin 0 -> 20301 bytes MedicusWithClaudePoj/registrace_report.py | 117 +++++++ 3 files changed, 406 insertions(+) create mode 100644 MedicusWithClaudeDekurz/dekurz_report.py create mode 100644 MedicusWithClaudePoj/registrace_2025_dnes.xlsx create mode 100644 MedicusWithClaudePoj/registrace_report.py diff --git a/MedicusWithClaudeDekurz/dekurz_report.py b/MedicusWithClaudeDekurz/dekurz_report.py new file mode 100644 index 0000000..d641bab --- /dev/null +++ b/MedicusWithClaudeDekurz/dekurz_report.py @@ -0,0 +1,289 @@ +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ů") diff --git a/MedicusWithClaudePoj/registrace_2025_dnes.xlsx b/MedicusWithClaudePoj/registrace_2025_dnes.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..db695e6f5f4793f372e4364f710795cd1d59eb4b GIT binary patch literal 20301 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 MJV4_+n4@q11M=v}X#fBK literal 0 HcmV?d00001 diff --git a/MedicusWithClaudePoj/registrace_report.py b/MedicusWithClaudePoj/registrace_report.py new file mode 100644 index 0000000..286e2b4 --- /dev/null +++ b/MedicusWithClaudePoj/registrace_report.py @@ -0,0 +1,117 @@ +import sys, io +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +import fdb +import openpyxl +from openpyxl.styles import Font, PatternFill, Alignment +from datetime import date, timedelta +import os + +conn = fdb.connect( + dsn=r'localhost:c:\medicus 3\data\medicus.fdb', + user='SYSDBA', password='masterkey', charset='win1250' +) +cur = conn.cursor() + +zacatek = date(2025, 1, 1) +konec = date.today() +dny = [] +d = zacatek +while d <= konec: + dny.append(d) + d += timedelta(days=1) + +print(f"Počítám {len(dny)} dní ({zacatek} – {konec})...") + +vysledky = [] +for i, den in enumerate(dny): + # Počet registrovaných + cur.execute(f""" + SELECT COUNT(*) FROM KAR + WHERE vyrazen = 'N' + AND EXISTS ( + SELECT id FROM registr r + JOIN icp i ON r.idicp = i.idicp + WHERE r.idpac = kar.idpac + AND r.datum <= '{den}' + AND (r.datum_zruseni IS NULL OR r.datum_zruseni >= '{den}') + AND r.priznak IN ('V','D','A') + AND i.icp = '09305001' + AND i.odb = '001' + ) + """) + pocet = cur.fetchone()[0] + + # Zaregistrovaní tento den + cur.execute(f""" + SELECT k.RODCIS, k.PRIJMENI, k.JMENO + FROM REGISTR r JOIN KAR k ON k.IDPAC = r.IDPAC + WHERE r.datum = '{den}' + AND r.priznak IN ('V','D','A') + ORDER BY k.PRIJMENI, k.JMENO + """) + zaregistrovani = [f"{row[0]} {row[1].strip()} {row[2]}" for row in cur.fetchall()] + + # Odregistrovaní tento den + cur.execute(f""" + SELECT k.RODCIS, k.PRIJMENI, k.JMENO + FROM REGISTR r JOIN KAR k ON k.IDPAC = r.IDPAC + WHERE r.datum_zruseni = '{den}' + ORDER BY k.PRIJMENI, k.JMENO + """) + odregistrovani = [f"{row[0]} {row[1].strip()} {row[2]}" for row in cur.fetchall()] + + vysledky.append((den, pocet, zaregistrovani, odregistrovani)) + if (i + 1) % 30 == 0: + print(f" {i+1}/{len(dny)}: {den} → {pocet}") + +conn.close() + +# Excel +wb = openpyxl.Workbook() +ws = wb.active +ws.title = "Registrace" + +hlavicka_font = Font(bold=True, color="FFFFFF") +hlavicka_fill = PatternFill("solid", fgColor="2E75B6") +ws.column_dimensions['A'].width = 14 +ws.column_dimensions['B'].width = 14 +ws.column_dimensions['C'].width = 10 +ws.column_dimensions['D'].width = 45 +ws.column_dimensions['E'].width = 45 + +for col, nazev in enumerate(['Datum', 'Registrovaných', 'Změna', 'Zaregistrováno', 'Odregistrováno'], start=1): + cell = ws.cell(row=1, column=col, value=nazev) + cell.font = hlavicka_font + cell.fill = hlavicka_fill + cell.alignment = Alignment(horizontal='center') + +predchozi = None +for row_i, (den, pocet, zaregistrovani, odregistrovani) in enumerate(vysledky, start=2): + ws.cell(row=row_i, column=1, value=den).number_format = 'DD.MM.YYYY' + ws.cell(row=row_i, column=2, value=pocet).alignment = Alignment(horizontal='center') + + if predchozi is not None: + zmena = pocet - predchozi + cell = ws.cell(row=row_i, column=3, value=zmena) + cell.alignment = Alignment(horizontal='center') + if zmena > 0: + cell.font = Font(color="00AA00", bold=True) + elif zmena < 0: + cell.font = Font(color="CC0000", bold=True) + predchozi = pocet + + if zaregistrovani: + cell = ws.cell(row=row_i, column=4, value="\n".join(zaregistrovani)) + cell.alignment = Alignment(wrap_text=True, vertical='top') + cell.font = Font(color="00AA00") + + if odregistrovani: + cell = ws.cell(row=row_i, column=5, value="\n".join(odregistrovani)) + cell.alignment = Alignment(wrap_text=True, vertical='top') + cell.font = Font(color="CC0000") + +ws.freeze_panes = 'A2' + +vystup = os.path.join(os.path.dirname(__file__), 'registrace_2025_dnes.xlsx') +wb.save(vystup) +print(f"\nUloženo: {vystup}")