""" Export zdravotních výkonů do XLSX — jeden list na odbornost. Každý výkon se rozvine do tolika řádků, kolik má nositelů. Pokud nositelé chybí, výkon dostane jeden prázdný řádek. Požadavky: pip install pymongo openpyxl """ from pathlib import Path from datetime import datetime, timezone from pymongo import MongoClient import openpyxl from openpyxl.styles import Font, PatternFill, Alignment, Border, Side from openpyxl.utils import get_column_letter # ── Nastavení ───────────────────────────────────────────────────────────────── ODBORNOSTI = ["001", "002"] # [] = všechny odbornosti VYSTUP = Path(__file__).parent / "vykony_report.xlsx" # ────────────────────────────────────────────────────────────────────────────── MONGO_URI = "mongodb://192.168.1.76:27017/" MONGO_DB = "zdravotni_vykony" SLOUPCE = [ ("cislo_vykonu", "Číslo výkonu", 10), ("nazev", "Název", 45), ("kategorie", "Kategorie", 22), ("typ_formulare", "Typ formuláře", 14), ("doba_trvani", "Doba trvání", 10), ("omezeni_mistem", "Omezení místem", 22), ("omezeni_frekvenci", "Omezení frekvencí", 16), ("nepocitat_rezii", "Nepočítat režii", 12), ("body_prime", "Body přímé", 10), ("body_osobni", "Body osobní", 10), ("body_rezijni", "Body režijní", 10), ("body_celkem", "Body celkem", 10), ("postup", "Postup výkonu", 50), ("nositel_kategorie", "Nositel – kategorie", 14), ("nositel_funkce", "Nositel – funkce", 30), ("nositel_cas", "Nositel – čas", 10), ("nositel_body", "Nositel – body", 12), ] BARVA_HLAVICKA = "1F4E79" BARVA_VYKON_A = "DEEAF1" # sudý výkon BARVA_VYKON_B = "FFFFFF" # lichý výkon def _bool_text(v) -> str: if v is True: return "ano" if v is False: return "ne" return "" def _postup(d: dict) -> str: casti = [] if d.get("cim_zacina"): casti.append(d["cim_zacina"].strip()) if d.get("obsah_rozsah"): casti.append(d["obsah_rozsah"].strip()) if d.get("cim_konci"): casti.append(d["cim_konci"].strip()) return "\n\n".join(casti) def _nositele(d: dict) -> list[dict]: """Vrátí nositelé bez řádků Celkem a bez prázdných řádků.""" result = [] for n in d.get("nositele", []): # přeskoč řádky kde kdekoliv je "Celkem:" hodnoty = [str(v) for v in n.values()] if any("celkem" in v.lower() for v in hodnoty): continue # přeskoč zcela prázdné řádky if not any(v.strip() for v in hodnoty if v): continue result.append(n) return result def _vykon_radky(v: dict, d: dict) -> list[dict]: """Rozlož výkon do řádků podle nositelů. Min. 1 řádek.""" base = { "cislo_vykonu": v.get("cislo_vykonu", ""), "nazev": d.get("nazev") or v.get("nazev_vykonu", ""), "kategorie": d.get("kategorie") or v.get("kategorie", ""), "typ_formulare": d.get("typ_formulare", ""), "doba_trvani": d.get("doba_trvani") if d.get("doba_trvani") is not None else v.get("doba_trvani", ""), "omezeni_mistem": d.get("omezeni_mistem") or v.get("omezeni_mistem", ""), "omezeni_frekvenci": d.get("omezeni_frekvenci") or v.get("omezeni_frekvenci", ""), "nepocitat_rezii": _bool_text(d.get("nepocitat_rezii")), "body_prime": d.get("body_prime") if d.get("body_prime") is not None else v.get("prime_naklady", ""), "body_osobni": d.get("body_osobni") if d.get("body_osobni") is not None else v.get("osobni", ""), "body_rezijni": d.get("body_rezijni") if d.get("body_rezijni") is not None else v.get("body_rezijni", ""), "body_celkem": d.get("body_celkem") if d.get("body_celkem") is not None else v.get("body_celkem", ""), "postup": _postup(d), } nositele = _nositele(d) if not nositele: return [{**base, "nositel_kategorie": "", "nositel_funkce": "", "nositel_cas": "", "nositel_body": ""}] radky = [] for n in nositele: radky.append({ **base, "nositel_kategorie": n.get("Kategorie", ""), "nositel_funkce": n.get("Funkce", ""), "nositel_cas": n.get("Cas", ""), "nositel_body": n.get("Bodyaktualni", ""), }) return radky def nastav_list(ws): """Záhlaví + šířky sloupců.""" hl_font = Font(bold=True, color="FFFFFF", size=10) hl_fill = PatternFill("solid", fgColor=BARVA_HLAVICKA) hl_align = Alignment(horizontal="center", vertical="center", wrap_text=True) thin = Side(style="thin", color="AAAAAA") border = Border(left=thin, right=thin, bottom=thin) for col_idx, (_, label, sirka) in enumerate(SLOUPCE, 1): cell = ws.cell(row=1, column=col_idx, value=label) cell.font = hl_font cell.fill = hl_fill cell.alignment = hl_align cell.border = border ws.column_dimensions[get_column_letter(col_idx)].width = sirka ws.row_dimensions[1].height = 30 ws.freeze_panes = "A2" def zapis_radek(ws, row_idx: int, radek: dict, barva: str): fill = PatternFill("solid", fgColor=barva) thin = Side(style="thin", color="DDDDDD") border = Border(left=thin, right=thin, bottom=thin) align_def = Alignment(vertical="top", wrap_text=False) align_wrap= Alignment(vertical="top", wrap_text=True) for col_idx, (klic, _, _) in enumerate(SLOUPCE, 1): hodnota = radek.get(klic, "") cell = ws.cell(row=row_idx, column=col_idx, value=hodnota) cell.fill = fill cell.border = border cell.alignment = align_wrap if klic in ("nazev", "postup", "nositel_funkce") else align_def def main(): print("Připojuji k MongoDB...") client = MongoClient(MONGO_URI) col_vykony = client[MONGO_DB]["vykony"] col_detaily = client[MONGO_DB]["detaily"] # Zjisti odbornosti if ODBORNOSTI: odbornosti = ODBORNOSTI else: odbornosti = sorted(col_vykony.distinct("odbornost", {"_aktivni": True})) print(f"Odbornosti: {odbornosti}") wb = openpyxl.Workbook() wb.remove(wb.active) # odstraň defaultní prázdný list for odbornost in odbornosti: print(f" Zpracovávám odbornost {odbornost}...") vykony = list(col_vykony.find( {"odbornost": odbornost, "_aktivni": True}, {"_id": 0}, ).sort("cislo_vykonu", 1)) cisla = [v["cislo_vykonu"] for v in vykony] detaily_map = { d["cislo_vykonu"]: d for d in col_detaily.find({"cislo_vykonu": {"$in": cisla}}, {"_id": 0}) } ws = wb.create_sheet(title=f"Odbornost {odbornost}") nastav_list(ws) row_idx = 2 for vykon_idx, v in enumerate(vykony): d = detaily_map.get(v["cislo_vykonu"], {}) radky = _vykon_radky(v, d) barva = BARVA_VYKON_A if vykon_idx % 2 == 0 else BARVA_VYKON_B for radek in radky: zapis_radek(ws, row_idx, radek, barva) row_idx += 1 print(f" → {row_idx - 2} řádků, {len(vykony)} výkonů") client.close() wb.save(VYSTUP) print(f"\nUloženo: {VYSTUP}") if __name__ == "__main__": main()