211 lines
8.2 KiB
Python
211 lines
8.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
report_tracking.py
|
|
==================
|
|
Excel report nad MongoDB `ordinace.registrovani_tracking`.
|
|
|
|
Pro každého pacienta zobrazí:
|
|
jméno, datum narození, rodné číslo, pojišťovnu, stav a důvod (kdo + od kdy).
|
|
|
|
Identifikační údaje (jméno, datum narození, pojišťovna) se berou AUTORITATIVNĚ
|
|
z Medicus Firebird tabulky `kar` (přes Knihovny.medicus_db.get_medicus_connection).
|
|
Stav a důvod (kategorie, flag, flag_duvod, praktik kdo/od kdy) z Mongo trackingu.
|
|
|
|
Výstup: report_registrovani_<vychozi_datum>.xlsx v tomto adresáři.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from datetime import date
|
|
from collections import defaultdict
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
import pymongo
|
|
from Knihovny.medicus_db import get_medicus_connection
|
|
from openpyxl import Workbook
|
|
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
|
from openpyxl.utils import get_column_letter
|
|
|
|
# ── Konfigurace ────────────────────────────────────────────────────────────────
|
|
MONGO_URI = "mongodb://192.168.1.76:27017"
|
|
MONGO_DB = "ordinace"
|
|
MONGO_COLL = "registrovani_tracking"
|
|
|
|
POJ_NAZVY = {
|
|
"111": "VZP", "201": "VoZP", "205": "ČPZP", "207": "OZP",
|
|
"209": "ZPŠ", "211": "ZPMV ČR", "213": "RBP",
|
|
}
|
|
|
|
# Barvy podle kategorie
|
|
BLUE_HEADER = "1F497D"
|
|
WHITE = "FFFFFF"
|
|
BARVA_KAT = {
|
|
"OK_BUZALKOVA": "EBF1DE", # zelená
|
|
"JINY_PRAKTIK": "FCE4D6", # červená
|
|
"BEZ_PRAKTIKA_VZP": "FFF2CC", # žlutá
|
|
"BEZ_ZAZNAMU_VZP": "DCE6F1", # modrá
|
|
}
|
|
STAV_TEXT = {
|
|
"OK_BUZALKOVA": "V souboru",
|
|
"JINY_PRAKTIK": "NEBYL v souboru",
|
|
"BEZ_PRAKTIKA_VZP": "NEBYL v souboru",
|
|
"BEZ_ZAZNAMU_VZP": "NEBYL v souboru",
|
|
}
|
|
|
|
|
|
def chunked(seq, n):
|
|
for i in range(0, len(seq), n):
|
|
yield seq[i:i + n]
|
|
|
|
|
|
def nacti_kar(conn, rcs):
|
|
"""Vrátí {rc: {prijmeni, jmeno, datnar, poj}} z Medicus kar."""
|
|
out = {}
|
|
cur = conn.cursor()
|
|
for batch in chunked(rcs, 500): # Firebird IN má limit 1500 prvků
|
|
ph = ",".join("?" for _ in batch)
|
|
cur.execute(
|
|
f"SELECT TRIM(rodcis), TRIM(prijmeni), TRIM(jmeno), datnar, TRIM(poj) "
|
|
f"FROM kar WHERE rodcis IN ({ph})", batch)
|
|
for rc, prij, jm, datnar, poj in cur.fetchall():
|
|
out[(rc or "").strip()] = {
|
|
"prijmeni": prij, "jmeno": jm,
|
|
"datnar": datnar, "poj": (poj or "").strip(),
|
|
}
|
|
return out
|
|
|
|
|
|
def main():
|
|
client = pymongo.MongoClient(MONGO_URI, serverSelectionTimeoutMS=3000)
|
|
coll = client[MONGO_DB][MONGO_COLL]
|
|
|
|
docs = list(coll.find({}))
|
|
vychozi = docs[0].get("vychozi_datum", "snimek") if docs else "snimek"
|
|
rcs = [d["_id"] for d in docs]
|
|
|
|
print(f"Pacientů v trackingu: {len(rcs)}")
|
|
print("Načítám kar z Medicusu ...")
|
|
conn = get_medicus_connection()
|
|
kar = nacti_kar(conn, rcs)
|
|
conn.close()
|
|
print(f"Dohledáno v kar: {len(kar)}")
|
|
|
|
# ── Sestavení řádků ────────────────────────────────────────────────────────
|
|
rows = []
|
|
for d in docs:
|
|
rc = d["_id"]
|
|
a = d.get("aktualni", {})
|
|
k = kar.get(rc, {})
|
|
|
|
prijmeni = k.get("prijmeni") or d.get("prijmeni") or ""
|
|
jmeno = k.get("jmeno") or d.get("jmeno") or ""
|
|
datnar = k.get("datnar")
|
|
poj_kod = k.get("poj") or (d.get("pojistovna") or {}).get("kod") or ""
|
|
kat = a.get("kategorie", "")
|
|
|
|
rows.append({
|
|
"prijmeni": prijmeni,
|
|
"jmeno": jmeno,
|
|
"datnar": datnar.strftime("%d.%m.%Y") if datnar else "",
|
|
"rc": rc,
|
|
"poj": f"{poj_kod} {POJ_NAZVY.get(poj_kod, '')}".strip(),
|
|
"stav": STAV_TEXT.get(kat, kat),
|
|
"kategorie": a.get("kategorie_popis", ""),
|
|
"duvod": a.get("flag_duvod", ""),
|
|
"kat_kod": kat,
|
|
})
|
|
|
|
# Řazení: nejdřív flagnutí (mimo soubor), pak podle příjmení
|
|
rows.sort(key=lambda r: (r["kat_kod"] == "OK_BUZALKOVA", r["prijmeni"], r["jmeno"]))
|
|
|
|
# ── Excel ──────────────────────────────────────────────────────────────────
|
|
wb = Workbook()
|
|
|
|
# List 1: Přehled
|
|
ws_p = wb.active
|
|
ws_p.title = "Přehled"
|
|
ws_p.column_dimensions["A"].width = 34
|
|
ws_p.column_dimensions["B"].width = 14
|
|
ws_p.merge_cells("A1:B1")
|
|
t = ws_p["A1"]
|
|
t.value = f"Registrovaní pacienti k {vychozi} — ověření praktika u VZP"
|
|
t.font = Font(name="Arial", bold=True, size=13, color=WHITE)
|
|
t.fill = PatternFill("solid", fgColor=BLUE_HEADER)
|
|
t.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
|
ws_p.row_dimensions[1].height = 34
|
|
ws_p["A2"] = f"Vygenerováno: {date.today().strftime('%d.%m.%Y')}"
|
|
ws_p["A2"].font = Font(name="Arial", italic=True, size=9, color="595959")
|
|
|
|
counts = defaultdict(int)
|
|
for r in rows:
|
|
counts[r["kat_kod"]] += 1
|
|
|
|
ws_p.cell(row=4, column=1, value="Kategorie / stav").font = Font(bold=True)
|
|
ws_p.cell(row=4, column=2, value="Počet").font = Font(bold=True)
|
|
poradi = ["OK_BUZALKOVA", "JINY_PRAKTIK", "BEZ_PRAKTIKA_VZP", "BEZ_ZAZNAMU_VZP"]
|
|
KAT_POPIS = {
|
|
"OK_BUZALKOVA": "V souboru (praktik Buzalková)",
|
|
"JINY_PRAKTIK": "Mimo soubor — jiný praktik",
|
|
"BEZ_PRAKTIKA_VZP": "Mimo soubor — bez praktika u VZP",
|
|
"BEZ_ZAZNAMU_VZP": "Mimo soubor — bez záznamu u VZP",
|
|
}
|
|
for i, kat in enumerate(poradi):
|
|
r = 5 + i
|
|
c1 = ws_p.cell(row=r, column=1, value=KAT_POPIS[kat])
|
|
c2 = ws_p.cell(row=r, column=2, value=counts[kat])
|
|
fill = PatternFill("solid", fgColor=BARVA_KAT[kat])
|
|
c1.fill = fill; c2.fill = fill
|
|
c1.font = Font(name="Arial", size=10)
|
|
ws_p.cell(row=9, column=1, value="CELKEM").font = Font(bold=True)
|
|
ws_p.cell(row=9, column=2, value=len(rows)).font = Font(bold=True)
|
|
mimo = len(rows) - counts["OK_BUZALKOVA"]
|
|
ws_p.cell(row=10, column=1, value="z toho NEBYL v zakoupeném souboru").font = Font(bold=True, color="C00000")
|
|
ws_p.cell(row=10, column=2, value=mimo).font = Font(bold=True, color="C00000")
|
|
|
|
# List 2: Pacienti
|
|
ws = wb.create_sheet("Pacienti")
|
|
COLS = [
|
|
("Příjmení", 20), ("Jméno", 14), ("Datum narození", 14),
|
|
("Rodné číslo", 14), ("Pojišťovna", 14), ("Stav", 16),
|
|
("Kategorie", 30), ("Důvod (kdo / od kdy)", 52),
|
|
]
|
|
for ci, (h, w) in enumerate(COLS, 1):
|
|
c = ws.cell(row=1, column=ci, value=h)
|
|
c.font = Font(name="Arial", bold=True, color=WHITE, size=10)
|
|
c.fill = PatternFill("solid", fgColor=BLUE_HEADER)
|
|
c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
|
ws.column_dimensions[get_column_letter(ci)].width = w
|
|
ws.row_dimensions[1].height = 30
|
|
ws.freeze_panes = "A2"
|
|
|
|
thin = Side(style="thin", color="D9D9D9")
|
|
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
|
|
|
for ri, r in enumerate(rows, 2):
|
|
bg = BARVA_KAT.get(r["kat_kod"], "FFFFFF")
|
|
data = [r["prijmeni"], r["jmeno"], r["datnar"], r["rc"], r["poj"],
|
|
r["stav"], r["kategorie"], r["duvod"]]
|
|
for ci, val in enumerate(data, 1):
|
|
c = ws.cell(row=ri, column=ci, value=val)
|
|
c.font = Font(name="Arial", size=9)
|
|
c.fill = PatternFill("solid", fgColor=bg)
|
|
c.border = border
|
|
c.alignment = Alignment(vertical="center", wrap_text=(ci == 8))
|
|
if ci == 6 and r["kat_kod"] != "OK_BUZALKOVA":
|
|
c.font = Font(name="Arial", size=9, bold=True, color="C00000")
|
|
|
|
ws.auto_filter.ref = f"A1:{get_column_letter(len(COLS))}{len(rows) + 1}"
|
|
|
|
out = Path(__file__).resolve().parent / f"report_registrovani_{vychozi}.xlsx"
|
|
wb.save(out)
|
|
print(f"\nUloženo: {out}")
|
|
print(f"Řádků: {len(rows)} | v souboru: {counts['OK_BUZALKOVA']} | mimo: {mimo}")
|
|
client.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|