z230
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
"""
|
||||
Report posledních receptů — export do Excelu.
|
||||
|
||||
Soubor se ukládá do adresáře report/ s verzováním:
|
||||
report_predpisy_RRRR-MM-DD.xlsx
|
||||
report_predpisy_RRRR-MM-DD_v2.xlsx (pokud základní existuje)
|
||||
atd.
|
||||
|
||||
Spuštění:
|
||||
python report/report_predpisy_excel.py
|
||||
"""
|
||||
|
||||
from datetime import date, datetime
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
import pymysql
|
||||
import pymysql.cursors
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(errors="replace")
|
||||
|
||||
# ── Konfigurace ───────────────────────────────────────────────────────────────
|
||||
DB = dict(
|
||||
host = "192.168.1.76",
|
||||
user = "root",
|
||||
password = "Vlado9674+",
|
||||
database = "medicus",
|
||||
charset = "utf8mb4",
|
||||
cursorclass = pymysql.cursors.DictCursor,
|
||||
)
|
||||
|
||||
LIMIT = 100
|
||||
VYSTUP_DIR = Path(__file__).parent # adresář report/
|
||||
|
||||
# ── Barvy (stejné jako prehled_pacienta_excel.py) ────────────────────────────
|
||||
C_HEADER_BG = "1F4E79"
|
||||
C_HEADER_FG = "FFFFFF"
|
||||
C_TITLE_BG = "2E75B6"
|
||||
C_TITLE_FG = "FFFFFF"
|
||||
C_INFO_BG = "DEEAF1"
|
||||
C_ROW_ODD = "FFFFFF"
|
||||
C_ROW_EVEN = "EBF3FB"
|
||||
C_NEVYZV_BG = "FCE4D6"
|
||||
C_BORDER = "B8CCE4"
|
||||
|
||||
# ── Pomocné funkce stylování ─────────────────────────────────────────────────
|
||||
def thin_border():
|
||||
s = Side(style="thin", color=C_BORDER)
|
||||
return Border(left=s, right=s, top=s, bottom=s)
|
||||
|
||||
def fill(color):
|
||||
return PatternFill("solid", fgColor=color)
|
||||
|
||||
def zapis_nadpis_sekce(ws, row, text, n_cols):
|
||||
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=n_cols)
|
||||
cell = ws.cell(row=row, column=1, value=text)
|
||||
cell.font = Font(name="Arial", bold=True, size=11, color=C_TITLE_FG)
|
||||
cell.fill = fill(C_TITLE_BG)
|
||||
cell.alignment = Alignment(horizontal="left", vertical="center", indent=1)
|
||||
ws.row_dimensions[row].height = 22
|
||||
return row + 1
|
||||
|
||||
def zapis_hlavicku(ws, row, hlavicka):
|
||||
for col, text in enumerate(hlavicka, 1):
|
||||
cell = ws.cell(row=row, column=col, value=text)
|
||||
cell.font = Font(name="Arial", bold=True, size=10, color=C_HEADER_FG)
|
||||
cell.fill = fill(C_HEADER_BG)
|
||||
cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
||||
cell.border = thin_border()
|
||||
ws.row_dimensions[row].height = 28
|
||||
return row + 1
|
||||
|
||||
def zapis_radek(ws, row, hodnoty, highlight=False):
|
||||
bg = C_NEVYZV_BG if highlight else (C_ROW_EVEN if row % 2 == 0 else C_ROW_ODD)
|
||||
f = fill(bg)
|
||||
for col, val in enumerate(hodnoty, 1):
|
||||
cell = ws.cell(row=row, column=col, value=val)
|
||||
cell.font = Font(name="Arial", size=10)
|
||||
cell.fill = f
|
||||
cell.alignment = Alignment(vertical="center", wrap_text=True)
|
||||
cell.border = thin_border()
|
||||
ws.row_dimensions[row].height = 18
|
||||
return row + 1
|
||||
|
||||
def autofit(ws, min_w=5, max_w=60, pad=2):
|
||||
col_widths = {}
|
||||
for row in ws.iter_rows():
|
||||
for cell in row:
|
||||
if cell.value is None:
|
||||
continue
|
||||
factor = 1.15 if (cell.font and cell.font.bold) else 1.0
|
||||
w = len(str(cell.value)) * factor + pad
|
||||
c = get_column_letter(cell.column)
|
||||
col_widths[c] = max(col_widths.get(c, min_w), w)
|
||||
for c, w in col_widths.items():
|
||||
ws.column_dimensions[c].width = min(max(w, min_w), max_w)
|
||||
|
||||
# ── Data ──────────────────────────────────────────────────────────────────────
|
||||
def nacti_odbornosti(conn):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT vp.icp, o.nazev
|
||||
FROM vzp_pracoviste vp
|
||||
JOIN odbornost o ON o.kod = vp.odbornost
|
||||
WHERE CURDATE() BETWEEN vp.platnost_od AND vp.platnost_do
|
||||
ORDER BY vp.platnost_od DESC
|
||||
""")
|
||||
result = {}
|
||||
for row in cur.fetchall():
|
||||
result.setdefault(row["icp"], row["nazev"])
|
||||
return result
|
||||
|
||||
def odbornost_z_icp(icp, slovnik):
|
||||
if not icp or len(icp) < 3:
|
||||
return ""
|
||||
return slovnik.get(icp, f"odb. {icp[-3:]}")
|
||||
|
||||
def nacti_predpisy(conn, limit):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT
|
||||
p.datum_vystaveni,
|
||||
z.pacient_prijmeni,
|
||||
z.pacient_jmena,
|
||||
z.pacient_datum_narozeni,
|
||||
COALESCE(v.nazev, p.nazev) AS vydany_lek,
|
||||
v.nazev IS NULL AS nevyzvednuto,
|
||||
p.atc,
|
||||
p.navod,
|
||||
pr.prijmeni AS lekar_prijmeni,
|
||||
pr.jmena AS lekar_jmena,
|
||||
pr.icp,
|
||||
pr.pzs_nazev,
|
||||
pr.ulice,
|
||||
pr.psc,
|
||||
pr.mesto,
|
||||
rd.stav,
|
||||
rd.druh_pojisteni
|
||||
FROM predpis p
|
||||
JOIN zprava z ON z.id = p.zprava_id
|
||||
JOIN predepisujici pr ON pr.lekar_kod = p.kod_predepisujiciho
|
||||
LEFT JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis
|
||||
LEFT JOIN recept_plp rp ON rp.id_lp = p.id_lp_predpis
|
||||
LEFT JOIN recept_doklad rd ON rd.id_dokladu = rp.id_dokladu
|
||||
ORDER BY p.datum_vystaveni DESC, p.id DESC
|
||||
LIMIT %s
|
||||
""", (limit,))
|
||||
return cur.fetchall()
|
||||
|
||||
# ── Verzování souboru ─────────────────────────────────────────────────────────
|
||||
def versioned_path(dir_: Path, dnes: str) -> Path:
|
||||
zaklad = dir_ / f"report_predpisy_{dnes}.xlsx"
|
||||
if not zaklad.exists():
|
||||
return zaklad
|
||||
v = 2
|
||||
while True:
|
||||
p = dir_ / f"report_predpisy_{dnes}_v{v}.xlsx"
|
||||
if not p.exists():
|
||||
return p
|
||||
v += 1
|
||||
|
||||
# ── Sestavení sešitu ──────────────────────────────────────────────────────────
|
||||
def vytvor_excel(rows, odbornosti, dnes):
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = "Predpisy"
|
||||
|
||||
N = 8 # počet sloupců
|
||||
|
||||
# ── Záhlaví ───────────────────────────────────────────────────────────────
|
||||
ws.merge_cells(start_row=1, start_column=1, end_row=1, end_column=N)
|
||||
title = ws.cell(row=1, column=1,
|
||||
value=f"PŘEHLED RECEPTŮ — posledních {LIMIT}")
|
||||
title.font = Font(name="Arial", bold=True, size=14, color=C_HEADER_FG)
|
||||
title.fill = fill(C_HEADER_BG)
|
||||
title.alignment = Alignment(horizontal="left", vertical="center", indent=1)
|
||||
ws.row_dimensions[1].height = 32
|
||||
|
||||
info_radky = [
|
||||
("Datum tisku:", datetime.today().strftime("%d.%m.%Y")),
|
||||
("Počet záznamů:", str(len(rows))),
|
||||
("Nejstarší datum:", str(rows[-1]["datum_vystaveni"]) if rows else ""),
|
||||
("Nejnovější datum:", str(rows[0]["datum_vystaveni"]) if rows else ""),
|
||||
]
|
||||
for i, (label, val) in enumerate(info_radky, 2):
|
||||
ws.merge_cells(start_row=i, start_column=1, end_row=i, end_column=2)
|
||||
ws.merge_cells(start_row=i, start_column=3, end_row=i, end_column=N)
|
||||
lbl = ws.cell(row=i, column=1, value=label)
|
||||
lbl.font = Font(name="Arial", bold=True, size=10)
|
||||
lbl.fill = fill(C_INFO_BG)
|
||||
lbl.alignment = Alignment(vertical="center", indent=1)
|
||||
v = ws.cell(row=i, column=3, value=val)
|
||||
v.font = Font(name="Arial", size=10)
|
||||
v.fill = fill(C_INFO_BG)
|
||||
v.alignment = Alignment(vertical="center")
|
||||
ws.row_dimensions[i].height = 16
|
||||
|
||||
row = len(info_radky) + 3 # prázdný řádek
|
||||
|
||||
# ── Tabulka ───────────────────────────────────────────────────────────────
|
||||
row = zapis_nadpis_sekce(ws, row, f"RECEPTY (celkem {len(rows)})", N)
|
||||
row = zapis_hlavicku(ws, row, [
|
||||
"#", "Datum", "Pacient", "Vydaný lék / ATC", "Návod",
|
||||
"Lékař", "Odbornost", "Pracoviště a adresa",
|
||||
])
|
||||
|
||||
for i, r in enumerate(rows, 1):
|
||||
nevyzv = bool(r["nevyzvednuto"])
|
||||
pacient = f"{r['pacient_prijmeni']} {r['pacient_jmena']}".strip()
|
||||
lekar = f"{r['lekar_prijmeni']} {r['lekar_jmena']}".strip()
|
||||
odb = odbornost_z_icp(r["icp"], odbornosti)
|
||||
adresa = ", ".join(filter(None, [
|
||||
r.get("pzs_nazev") or "",
|
||||
r.get("ulice") or "",
|
||||
f"{r.get('psc') or ''} {r.get('mesto') or ''}".strip(),
|
||||
]))
|
||||
lek_atc = r["vydany_lek"] or ""
|
||||
if r.get("atc"):
|
||||
lek_atc += f"\n{r['atc']}"
|
||||
stav_text = ""
|
||||
if nevyzv:
|
||||
stav_text = " *NV"
|
||||
elif r.get("stav") == "ZRUSENY":
|
||||
stav_text = " *ZR"
|
||||
|
||||
datum_str = (r["datum_vystaveni"].strftime("%d.%m.%Y")
|
||||
if r["datum_vystaveni"] else "")
|
||||
|
||||
row = zapis_radek(ws, row, [
|
||||
i,
|
||||
datum_str,
|
||||
pacient,
|
||||
lek_atc + stav_text,
|
||||
r.get("navod") or "",
|
||||
lekar,
|
||||
odb,
|
||||
adresa,
|
||||
], highlight=nevyzv)
|
||||
|
||||
# ── Dokončení ─────────────────────────────────────────────────────────────
|
||||
autofit(ws)
|
||||
ws.freeze_panes = "A7" # zmraz záhlaví + info řádky
|
||||
|
||||
return wb
|
||||
|
||||
|
||||
def main():
|
||||
conn = pymysql.connect(**DB)
|
||||
print("Nacitam odbornosti...")
|
||||
odbornosti = nacti_odbornosti(conn)
|
||||
print(f"Nacitam poslednich {LIMIT} receptu...")
|
||||
rows = nacti_predpisy(conn, LIMIT)
|
||||
conn.close()
|
||||
print(f" Nacteno: {len(rows)} zaznamu")
|
||||
|
||||
dnes = date.today().isoformat()
|
||||
wb = vytvor_excel(rows, odbornosti, dnes)
|
||||
soubor = versioned_path(VYSTUP_DIR, dnes)
|
||||
wb.save(soubor)
|
||||
print(f"Ulozeno: {soubor}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user