From eda5ec3d824a8510694b53e070f9225fe6e9d183 Mon Sep 17 00:00:00 2001 From: "michaela.buzalkova" Date: Fri, 24 Oct 2025 18:56:12 +0200 Subject: [PATCH] pohoda --- 50 Dekurz pro reporter.py | 162 ++++++++++++++++++++++++++++++++++++++ 60 DXA pro reporter.py | 147 ++++++++++++++++++++++++++++++++++ pomoc.py | 144 +-------------------------------- 3 files changed, 311 insertions(+), 142 deletions(-) create mode 100644 50 Dekurz pro reporter.py create mode 100644 60 DXA pro reporter.py diff --git a/50 Dekurz pro reporter.py b/50 Dekurz pro reporter.py new file mode 100644 index 0000000..528d093 --- /dev/null +++ b/50 Dekurz pro reporter.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Export DEKURS records from Medicus Firebird DB into Excel. +- Includes only last 2 months from current date +- RTF text decoded and cleaned (Excel-safe, preserved newlines) +- Výkony of the same day concatenated into 'VYKONY DNE' +- Příjmení + Jméno merged into 'PACIENT' +- Proper column order and naming +- Bright yellow header, thin black borders, auto column widths +- Timestamped Excel filename +""" + +import re +import pandas as pd +import firebirdsql as fb +from datetime import datetime, timedelta +from pathlib import Path +from striprtf.striprtf import rtf_to_text +from openpyxl.styles import Font, Alignment, PatternFill, Border, Side +from openpyxl.utils import get_column_letter + +# ================== CONFIGURATION ================== +FDB_PATH = r"M:\Medicus\Data\Medicus.fdb" +EXPORT_DIR = Path(r"z:\Dropbox\Ordinace\Reporty") + +# calculate last 2 months dynamically +DATE_FROM = (datetime.now() - timedelta(days=2)).strftime("%Y-%m-%d") + +timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") +xlsx_path = EXPORT_DIR / f"{timestamp} Dekurz (poslední rok).xlsx" + +# ================== FIREBIRD CONNECTION ================== +conn = fb.connect( + host="192.168.1.10", + port=3050, + database=FDB_PATH, + user="SYSDBA", + password="masterkey", + charset="WIN1250", +) + +def query_df(sql, params=None): + cur = conn.cursor() + cur.execute(sql, params or ()) + rows = cur.fetchall() + cols = [d[0].strip() for d in cur.description] + return pd.DataFrame(rows, columns=cols) + +# ================== QUERY ================== +sql = f""" +SELECT + d.id, + k.prijmeni, + k.jmeno, + k.rodcis, + u.zkratka, + d.datum, + d.dekurs, + ( + SELECT LIST(dd.kod, ', ') + FROM dokladd dd + WHERE dd.rodcis = k.rodcis + AND CAST(dd.datose AS DATE) = CAST(d.datum AS DATE) + ) AS vykony_dne +FROM dekurs d +JOIN kar k ON d.idpac = k.idpac +JOIN uzivatel u ON d.iduzi = u.iduzi +WHERE d.datum >= DATE '{DATE_FROM}' +ORDER BY d.datum DESC +""" + +df = query_df(sql) +conn.close() + +# ================== DATA CLEANING ================== +def safe_rtf_to_text(x): + if isinstance(x, str) and x.strip(): + try: + return rtf_to_text(x) + except Exception: + return x + return "" + +df["DEKURS"] = df["DEKURS"].apply(safe_rtf_to_text) + +df.replace({r'(\r\n|\r|\n)': r'\r\n'}, regex=True, inplace=True) +df.replace({r'[\ud800-\udfff\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]+': ''}, + regex=True, inplace=True) +df.replace({r'(\r\n){2,}': r'\r\n', r'(\r\n)+$': ''}, + regex=True, inplace=True) + +df["PACIENT"] = df["PRIJMENI"].fillna("") + ", " + df["JMENO"].fillna("") +df.drop(columns=["PRIJMENI", "JMENO"], inplace=True) + +df.rename(columns={"ZKRATKA": "LEKAR", "VYKONY_DNE": "VYKONY DNE"}, inplace=True) +df["DATUM"] = pd.to_datetime(df["DATUM"], errors="coerce").dt.date +df.drop(columns=[c for c in df.columns if "ASCII" in c.upper()], inplace=True, errors="ignore") + +desired_order = ["DATUM", "RODCIS", "PACIENT", "LEKAR", "VYKONY DNE", "DEKURS"] +df = df[[c for c in desired_order if c in df.columns]] +# ================== CLEANUP OLD FILES ================== +for old_file in EXPORT_DIR.glob("*Dekurz (poslední rok)*.xlsx"): + try: + old_file.unlink() + print(f"🧹 Deleted old file: {old_file.name}") + except Exception as e: + print(f"⚠️ Could not delete {old_file.name}: {e}") +# ================== EXPORT TO EXCEL ================== +with pd.ExcelWriter(xlsx_path, engine="openpyxl") as writer: + df.to_excel(writer, index=False, sheet_name="Dekurz") + ws = writer.sheets["Dekurz"] + + # ----- Bright yellow header ----- + header_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") + for cell in ws[1]: + cell.font = Font(bold=True, color="000000") + cell.alignment = Alignment(horizontal="center", vertical="center") + cell.fill = header_fill + + # ----- Format Datum column ----- + for cell in ws["A"][1:]: + if hasattr(cell.value, "date"): + cell.value = cell.value.date() + cell.number_format = "DD.MM.YYYY" + + # ----- Vertical centering for key columns ----- + vertically_centered = {"DATUM", "RODCIS", "PACIENT", "LEKAR", "VYKONY DNE"} + for row in ws.iter_rows(min_row=2, max_row=ws.max_row): + for cell in row: + header = ws.cell(row=1, column=cell.column).value + if header in vertically_centered: + align = cell.alignment + cell.alignment = Alignment( + horizontal=align.horizontal or "left", + vertical="center", + wrap_text=align.wrap_text + ) + elif isinstance(cell.value, str) and "\n" in cell.value: + cell.alignment = Alignment(wrap_text=True, vertical="top") + + # ----- Column widths ----- + for col in ws.columns: + header = col[0].value + col_letter = get_column_letter(col[0].column) + if header and "DEKURS" in str(header).upper(): + ws.column_dimensions[col_letter].width = 110 + else: + max_len = max(len(str(cell.value)) if cell.value else 0 for cell in col) + ws.column_dimensions[col_letter].width = min(max_len + 2, 80) + + # ----- Thin black borders ----- + thin = Side(border_style="thin", color="000000") + border = Border(top=thin, left=thin, right=thin, bottom=thin) + for row in ws.iter_rows(min_row=1, max_row=ws.max_row, + min_col=1, max_col=ws.max_column): + for cell in row: + cell.border = border + +print(f"✅ Export hotov: {xlsx_path}") +print(f"📅 Dotaz zahrnuje data od: {DATE_FROM}") diff --git a/60 DXA pro reporter.py b/60 DXA pro reporter.py new file mode 100644 index 0000000..212244c --- /dev/null +++ b/60 DXA pro reporter.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Report: DXA requisitions (Medicus → Dropbox) +-------------------------------------------- +- Selects all histdoc records for year 2025 containing "DXA" +- Finds matching PDF files in Dropbox (by rod_cis + "dxa" in name) +- Outputs Excel report: datum, idpaci, rod_cis, prijmeni, jmeno, file +""" + +import os +import firebirdsql as fb +import pandas as pd +from pathlib import Path +from datetime import datetime + +# ================== CONFIGURATION ================== +FDB_PATH = r"M:\Medicus\Data\Medicus.fdb" +EXPORT_DIR = Path(r"z:\Dropbox\Ordinace\Reporty") +DOC_DIR = Path(r"z:\Dropbox\Ordinace\Dokumentace_zpracovaná") + + +timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") +xlsx_path = EXPORT_DIR / f"{timestamp} DXA report.xlsx" + +# ================== DATABASE QUERY ================== +conn = fb.connect( + host="192.168.1.10", + port=3050, + database=FDB_PATH, + user="SYSDBA", + password="masterkey", + charset="WIN1250", +) + +sql = """ +SELECT + h.datum, + h.idpaci, + k.rodcis, + k.prijmeni, + k.jmeno, + ( + SELECT LIST( + CAST(dd.datose AS DATE) || ':' || dd.kod, ', ' + ) + FROM dokladd dd + WHERE dd.rodcis = k.rodcis + AND EXTRACT(YEAR FROM dd.datose) = 2025 + AND dd.kod STARTING WITH '1132' + ) AS vykony_1132_2025 + +FROM histdoc h +JOIN kar k ON h.idpaci = k.idpac +WHERE EXTRACT(YEAR FROM h.datum) = 2025 + AND h.data CONTAINING 'dxa' +ORDER BY h.datum DESC +""" + + +df = pd.read_sql(sql, conn) +conn.close() + +# ================== REMOVE DUPLICATES AND SORT ================== +# Convert to datetime → drop duplicates → keep newest per patient → sort descending +df["DATUM"] = pd.to_datetime(df["DATUM"], errors="coerce") + +# Drop duplicates, keep the newest by date +df = ( + df.sort_values("DATUM", ascending=False) + .drop_duplicates(subset="RODCIS", keep="first") + .sort_values("DATUM", ascending=False) + .reset_index(drop=True) +) + +# Convert to pure date (no time portion) +df["DATUM"] = df["DATUM"].dt.date + +# ================== FIND MATCHING PDF FILES ================== +def find_dxa_file(rod_cis: str) -> str: + """Return first matching DXA file for given rod_cis or ''.""" + if not rod_cis: + return "" + rod_cis = rod_cis.strip() + + # Case-insensitive search for files like "1234567890 [DXA] something.pdf" + for file in DOC_DIR.iterdir(): + name = file.name.lower() + if ( + file.suffix.lower() == ".pdf" + and name.startswith(rod_cis.lower()) + and "[dxa]" in name + ): + return file.name + return "" + + +df["FILE"] = df["RODCIS"].apply(find_dxa_file) + + +# ================== CLEAN OLD REPORTS ================== +for f in EXPORT_DIR.glob("* DXA report.xlsx"): + try: + f.unlink() + print(f"🗑️ Deleted old report: {f.name}") + except Exception as e: + print(f"⚠️ Could not delete {f.name}: {e}") + + +# ================== EXPORT TO EXCEL ================== +with pd.ExcelWriter(xlsx_path, engine="openpyxl") as writer: + df.to_excel(writer, index=False, sheet_name="DXA") + ws = writer.sheets["DXA"] + + # Format header + from openpyxl.styles import Font, Alignment, PatternFill, Border, Side + header_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") + for cell in ws[1]: + cell.font = Font(bold=True, color="000000") + cell.alignment = Alignment(horizontal="center", vertical="center") + cell.fill = header_fill + + # Auto column width, but hardcode FILE column to 120 + from openpyxl.utils import get_column_letter + + for col in ws.columns: + col_letter = get_column_letter(col[0].column) + header = ws.cell(row=1, column=col[0].column).value + + if header == "FILE": + ws.column_dimensions[col_letter].width = 120 + else: + max_len = max(len(str(cell.value)) if cell.value else 0 for cell in col) + ws.column_dimensions[col_letter].width = min(max_len + 2, 80) + + # Borders + thin = Side(border_style="thin", color="000000") + border = Border(top=thin, left=thin, right=thin, bottom=thin) + for row in ws.iter_rows(min_row=1, max_row=ws.max_row, + min_col=1, max_col=ws.max_column): + for cell in row: + cell.border = border + +print(f"✅ Report created: {xlsx_path}") +print(f"📂 Source folder scanned: {DOC_DIR}") +print(f"🩻 {df['FILE'].astype(bool).sum()} matching DXA PDFs found") diff --git a/pomoc.py b/pomoc.py index 15786ba..24c7e44 100644 --- a/pomoc.py +++ b/pomoc.py @@ -1,142 +1,2 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -from pathlib import Path -import time -import fdb -import pandas as pd -from openpyxl import load_workbook -from openpyxl.worksheet.table import Table, TableStyleInfo -from openpyxl.styles import Font, PatternFill, Alignment -from openpyxl.utils import get_column_letter - -# ================== Výstupní cesta ================== -BASE_DIR = Path(r"..") # uprav dle potřeby -timestamp = time.strftime("%Y-%m-%d %H-%M-%S") -xlsx_name = f"Pacienti očkování {timestamp}.xlsx" -xlsx_path = BASE_DIR / xlsx_name - -# ================== Připojení k DB ================== -con = fdb.connect( - host='192.168.1.4', - database=r'z:\\MEDICUS 3\\data\\medicus.FDB', - user='sysdba', - password='masterkey', - charset='WIN1250' -) - -# ================== SQL dotaz ================== -# Pozn.: CAST(ockzaz.datum AS DATE) pro přesné párování na lecd.datose (DATE). -sql = """ -SELECT - kar.rodcis AS "Rodné číslo", - kar.prijmeni AS "Příjmení", - kar.jmeno AS "Jméno", - ockzaz.datum AS "Datum očkování", - ockzaz.kodmz AS "Kód MZ", - ockzaz.poznamka AS "Šarže", - ockzaz.latka AS "Látka", - ockzaz.nazev AS "Název", - ockzaz.expire AS "Expirace", - - /* --- VŠECHNY KÓDY Z TABULKY LECD PRO TOHO PACIENTA A TENTÝŽ DEN --- */ - ( - SELECT LIST(l.kod, ', ') - FROM lecd l - WHERE l.rodcis = kar.rodcis - AND l.datose = CAST(ockzaz.datum AS DATE) - ) AS "LECD kódy (ten den)", - /* --- VŠECHNY KÓDY Z TABULKY vykonu dokladd PRO TOHO PACIENTA A TENTÝŽ DEN --- */ - ( - SELECT LIST(d.kod, ', ') - FROM dokladd d - WHERE d.rodcis = kar.rodcis - AND d.datose = CAST(ockzaz.datum AS DATE) - ) AS "Vykony (ten den)" - -FROM registr -JOIN kar ON registr.idpac = kar.idpac -JOIN ockzaz ON registr.idpac = ockzaz.idpac -WHERE - registr.datum_zruseni IS NULL - AND kar.vyrazen <> 'A' - AND kar.rodcis IS NOT NULL - AND idicp <> 0 - and EXTRACT(YEAR FROM ockzaz.datum)=2025 -ORDER BY ockzaz.datum DESC -""" -#and nazev containing 'chřipka' -# ================== Načtení do DataFrame ================== -df = pd.read_sql(sql, con) -con.close() - -# Textové sloupce jako string -for col in ["Kód MZ", "Šarže", "Rodné číslo", "Látka", "Název", "Příjmení", "Jméno", "LECD kódy","Výkony"]: - if col in df.columns: - df[col] = df[col].astype("string") - -# Převod dat na datetime -for dcol in ["Datum očkování", "Expirace"]: - if dcol in df.columns: - df[dcol] = pd.to_datetime(df[dcol], errors="coerce") - -# ================== Uložení do Excelu ================== -with pd.ExcelWriter(xlsx_path, engine="openpyxl") as writer: - df.to_excel(writer, index=False, sheet_name="Očkování") - -# ================== Formátování ================== -wb = load_workbook(xlsx_path) - -def autosize_columns(ws): - for col_idx in range(1, ws.max_column + 1): - col_letter = get_column_letter(col_idx) - max_len = 0 - for cell in ws[col_letter]: - val = "" if cell.value is None else str(cell.value) - if len(val) > max_len: - max_len = len(val) - ws.column_dimensions[col_letter].width = min(max(12, max_len + 2), 60) - -def style_table(ws, table_name: str): - max_row = ws.max_row - max_col = ws.max_column - if max_col == 0: - return - - # Hlavička vzhled - header_fill = PatternFill("solid", fgColor="D9E1F2") - for cell in ws[1]: - cell.font = Font(bold=True) - cell.fill = header_fill - cell.alignment = Alignment(vertical="center") - - # Freeze top row - ws.freeze_panes = "A2" - - # Tabulka jen pokud jsou data - if max_row < 2: - autosize_columns(ws) - return - - ref = f"A1:{get_column_letter(max_col)}{max_row}" - tbl = Table(displayName=table_name, ref=ref) - tbl.tableStyleInfo = TableStyleInfo( - name="TableStyleMedium9", showRowStripes=True, showColumnStripes=False - ) - ws.add_table(tbl) - autosize_columns(ws) - -def format_dates(ws, columns_names): - header = [c.value for c in ws[1]] - date_cols = [header.index(name) + 1 for name in columns_names if name in header] - for col_idx in date_cols: - for row in ws.iter_rows(min_row=2, min_col=col_idx, max_col=col_idx, max_row=ws.max_row): - row[0].number_format = "DD.MM.YYYY" - -ws_main = wb["Očkování"] -style_table(ws_main, "Ockovani") -format_dates(ws_main, ["Datum očkování", "Expirace"]) - -wb.save(xlsx_path) - -print(f"Hotovo. Uloženo do: {xlsx_path.resolve()}") +import socket +print(socket.gethostname().strip()) \ No newline at end of file