Files
ordinaceprojekt/Vykony/report_vykony.py
T
2026-06-01 12:17:41 +02:00

202 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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()