import os import time import fdb import openpyxl import xlwings as xw from datetime import datetime, date from openpyxl.utils import get_column_letter from openpyxl.styles import Font, PatternFill, Alignment # --- Konfigurace --- PathToSaveCSV = r"u:\Dropbox\!!!Days\Downloads Z230" timestr = time.strftime("%Y-%m-%d_%H-%M-%S_") output_path = os.path.join(PathToSaveCSV, timestr + "Pacienti.xlsx") # --- Smazání předchozích verzí --- for fname in os.listdir(PathToSaveCSV): if fname.endswith("Pacienti.xlsx"): try: os.remove(os.path.join(PathToSaveCSV, fname)) except Exception as e: print(f"Nelze smazat {fname}: {e}") # --- Připojení k DB --- con = fdb.connect( host='localhost', database=r'c:\MEDICUS 3\data\medicus.FDB', user='sysdba', password='masterkey', charset='WIN1250' ) cur = con.cursor() wb = openpyxl.Workbook() # ===================== # Pomocné funkce # ===================== # Styly pro posudky HEADER_FILL = PatternFill('solid', fgColor='2F5496') HEADER_FONT = Font(bold=True, color='FFFFFF') ZEBRA_FILL = PatternFill('solid', fgColor='DCE6F1') GREEN_FILL = PatternFill('solid', fgColor='C6EFCE') GREEN_FONT = Font(bold=True, color='276221') def style_header(ws): for cell in ws[1]: cell.fill = HEADER_FILL cell.font = HEADER_FONT cell.alignment = Alignment(horizontal='center') def autofit_ws(ws): for col in ws.columns: max_len = max((len(str(cell.value)) if cell.value is not None else 0) for cell in col) ws.column_dimensions[get_column_letter(col[0].column)].width = min(max_len + 2, 50) def sanitize(val): """Nahradí znaky neplatné pro Excel: µ → u, ostatní → _""" if not isinstance(val, str): return val result = [] for ch in val: if ch == 'µ': result.append('u') elif ord(ch) < 32 and ch not in '\t\n\r': result.append('_') elif ord(ch) in (0xFFFE, 0xFFFF) or 0xD800 <= ord(ch) <= 0xDFFF: result.append('_') else: result.append(ch) return ''.join(result) def fmt(val): return '' if val is None else sanitize(val) def parse_data(data_str): """Parsuje key=value text z HISTDOC.DATA do slovníku.""" result = {} if not data_str: return result for line in data_str.splitlines(): if '=' in line: key, _, val = line.partition('=') result[key.strip()] = val.strip() return result def parse_date(val): """Převede 'D:DD.MM.YYYY' na datetime.date.""" if val and val.startswith('D:'): try: return datetime.strptime(val[2:], '%d.%m.%Y').date() except ValueError: return val return val VYKONY_CONDITION = """ (datose >= vykony.platiod AND datose <= vykony.platido) OR (datose >= vykony.platiod AND vykony.platido IS NULL) """ VYKONY_HEADERS = ["Rodne cislo", "Jmeno", "Datum vykonu", "Kod", "Název", "Dg.", "Body"] def add_vykony_sheet(sheet_name, kody): """Přidá list s výkony filtrovanými podle seznamu kódů.""" kod_list = ", ".join(str(k) for k in kody) cur.execute(f""" SELECT dokladd.rodcis, TRIM(prijmeni) || ', ' || TRIM(jmeno), dokladd.datose, dokladd.kod, vykony.naz, dokladd.ddgn, dokladd.body FROM dokladd LEFT JOIN kar ON dokladd.rodcis = kar.rodcis JOIN vykony ON dokladd.kod = vykony.kod WHERE ({VYKONY_CONDITION}) AND dokladd.kod IN ({kod_list}) ORDER BY datose DESC, dokladd.rodcis, dokladd.kod """) rows = cur.fetchall() print(f"{sheet_name}: {len(rows)}") ws = wb.create_sheet(sheet_name) ws.append(VYKONY_HEADERS) for row in rows: ws.append(list(row)) # ===================== # List: Registrovaní # ===================== cur.execute(""" SELECT rodcis, prijmeni, jmeno, datum_registrace, registr.idpac, poj FROM registr JOIN kar ON registr.idpac = kar.idpac WHERE kar.vyrazen != 'A' AND kar.rodcis IS NOT NULL AND idicp != 0 AND datum_zruseni IS NULL """) rows = cur.fetchall() print(f"Registrovaní: {len(rows)}") ws = wb.active ws.title = 'Registrovani' ws.append(["Rodne cislo", "Prijmeni", "Jmeno", "Datum registrace", "ID pacienta", "Pojistovna"]) for row in rows: ws.append(list(row)) # ===================== # List: Očkování # ===================== cur.execute(""" SELECT rodcis, prijmeni, jmeno, ockzaz.datum, kodmz, ockzaz.poznamka, latka, nazev, expire FROM registr JOIN kar ON registr.idpac = kar.idpac JOIN ockzaz ON registr.idpac = ockzaz.idpac WHERE datum_zruseni IS NULL AND kar.vyrazen != 'A' AND kar.rodcis IS NOT NULL AND idicp != 0 ORDER BY ockzaz.datum DESC """) rows = cur.fetchall() print(f"Očkování: {len(rows)}") ws = wb.create_sheet("Očkování") ws.append(["Rodne cislo", "Prijmeni", "Jmeno", "Datum ockovani", "Kod MZ", "Sarze", "Latka", "Nazev", "Expirace"]) for row in rows: ws.append(list(row)) # ===================== # List: Recepty # ===================== cur.execute(""" SELECT kar.rodcis, TRIM(kar.prijmeni) || ' ' || SUBSTRING(kar.jmeno FROM 1 FOR 1) || '.' AS jmeno, recept.datum, TRIM(recept.lek) || ' ' || TRIM(recept.dop) AS lek, recept.expori AS Poc, CASE WHEN recept.opakovani IS NULL THEN 1 ELSE recept.opakovani END AS OP, recept.uhrada, recept.dsig, recept.NOTIFIKACE_KONTAKT AS notifikace, recept_epodani.erp, recept_epodani.vystavitel_jmeno, recept.atc, recept.CENAPOJ, recept.cenapac FROM recept LEFT JOIN RECEPT_EPODANI ON recept.id_epodani = recept_epodani.id LEFT JOIN kar ON recept.idpac = kar.idpac ORDER BY datum DESC, erp DESC """) rows = cur.fetchall() print(f"Recepty: {len(rows)}") ws = wb.create_sheet("Recepty") ws.append(["Rodné číslo", "Jméno", "Datum vystavení", "Název leku", "Poč.", "Op.", "Úhr.", "Da signa", "Notifikace", "eRECEPT", "Vystavil", "ATC", "Cena pojišťovna", "Cena pacient"]) for row in rows: ws.append([sanitize(v) if isinstance(v, str) else v for v in row]) # ===================== # List: Výkony všechny # ===================== cur.execute(f""" SELECT dokladd.rodcis, TRIM(prijmeni) || ', ' || TRIM(jmeno), dokladd.datose, dokladd.kod, dokladd.pocvyk, dokladd.ddgn, dokladd.body, vykony.naz FROM kar JOIN dokladd ON kar.rodcis = dokladd.rodcis JOIN vykony ON dokladd.kod = vykony.kod WHERE {VYKONY_CONDITION} ORDER BY dokladd.datose DESC, dokladd.rodcis """) rows = cur.fetchall() print(f"Výkony: {len(rows)}") ws = wb.create_sheet("Vykony") ws.append(["Rodne cislo", "Jmeno", "Datum vykonu", "Kod", "Pocet", "Dg.", "Body", "Nazev"]) for row in rows: ws.append(list(row)) # ===================== # Listy: Neschopenky # ===================== def pocet_dni(zacnes, konnes, pracne): dnes = date.today() if pracne == 'A': return (dnes - zacnes).days if zacnes else "NA" if pracne == 'N' and zacnes and konnes and zacnes <= konnes: return (konnes - zacnes).days return "NA" def nes_row(r): return (r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], pocet_dni(r[5], r[7], r[6]), r[8], r[9], r[10]) NES_HEADERS = ["ID pac", "Rodne cislo", "Jmeno", "Datum neschopenky", "Číslo neschopenky", "Zacatek", "Aktivní?", "Konec", "Pocet dni", "Diagnoza zacatel", "Diagnoza konec", "Aktualizovano"] cur.execute(""" SELECT nes.idpac, kar.rodcis, TRIM(prijmeni) || ', ' || TRIM(jmeno), nes.datnes, nes.ecn, nes.zacnes, nes.pracne, nes.konnes, nes.diagno, nes.kondia, nes.updated FROM nes LEFT JOIN kar ON nes.idpac = kar.idpac WHERE nes.datnes <= CURRENT_DATE ORDER BY datnes DESC """) vse = cur.fetchall() aktivni = [r for r in vse if r[6] == 'A'] print(f"Neschopenky: {len(vse)} celkem, {len(aktivni)} aktivních") ws = wb.create_sheet("Neschopenky všechny") ws.append(NES_HEADERS) for r in vse: ws.append(list(nes_row(r))) ws = wb.create_sheet("Neschopenky aktivní") ws.append(NES_HEADERS) for r in aktivni: ws.append(list(nes_row(r))) # ===================== # Výkonové listy – jednotlivé typy výkonů # ===================== add_vykony_sheet('Preventivni prohlidky', [1022, 1021]) add_vykony_sheet('INR', [1443]) add_vykony_sheet('CRP', [2230, 9111]) add_vykony_sheet('Holter', [17129]) add_vykony_sheet('Prostata', [1130, 1131, 1132, 1133, 1134]) add_vykony_sheet('TOKS', [15118, 15119, 15120, 15121]) add_vykony_sheet('COVID', [1306]) add_vykony_sheet('Streptest', [2220]) # ===================== # List: Posudky řidičák – MOTORVO (ruční) # ===================== cur.execute("SELECT IDPACI, DATUM FROM HISTDOC WHERE TYP = 'EPOSMRO'") eposmro_keys = set((r[0], r[1]) for r in cur.fetchall()) cur.execute(""" SELECT h.ID, h.DATUM, h.IDPACI, k.PRIJMENI, k.JMENO, k.RODCIS, h.DATA, h.PORCISLO, h.STAV, h.PRINTED, h.IDUZIV, h.CREATED FROM HISTDOC h JOIN KAR k ON k.IDPAC = h.IDPACI WHERE h.TYP = 'MOTORVO' ORDER BY h.ID DESC """) motorvo_rows = cur.fetchall() print(f"MOTORVO: {len(motorvo_rows)}") motorvo_headers = [ 'ID', 'DATUM', 'IDPACI', 'PRIJMENI', 'JMENO', 'RODCIS', 'PorCislo', 'DatumVyd', 'DatKonec', 'DruhProh', 'Posouzeni', 'ZpusobPodminka', 'SkupinaPodminka', 'Skupiny', 'ePosudek', 'STAV', 'PRINTED', 'IDUZIV', 'CREATED' ] ws = wb.create_sheet("Posudky řidičák") ws.append(motorvo_headers) epos_col_idx = motorvo_headers.index('ePosudek') + 1 for i, row in enumerate(motorvo_rows, start=2): (hid, datum, idpac, prijmeni, jmeno, rodcis, data_blob, porcislo, stav, printed, iduziv, created) = row data = parse_data(data_blob) if data.get('Posouzeni2') == 'T': posouzeni = 'nezpůsobilý' elif data.get('ZpusobPodminka') == 'B:1': posouzeni = 'způsobilý s podmínkou' elif data.get('Posouzeni') == 'T': posouzeni = 'způsobilý' else: posouzeni = '' ws.append([ hid, fmt(datum), idpac, fmt(prijmeni), fmt(jmeno), fmt(rodcis), fmt(porcislo or data.get('PorCislo', '')), parse_date(data.get('DatumVyd', '')), parse_date(data.get('DatKonec', '')), fmt(data.get('DruhProh', '')), posouzeni, fmt(data.get('ZpusobPodminka', '')), fmt(data.get('SkupinaPodminka', '')), fmt(data.get('ZpusobJe', '')), 'ANO' if (idpac, datum) in eposmro_keys else 'NE', fmt(stav), fmt(printed), fmt(iduziv), fmt(created), ]) if i % 2 == 0: for cell in ws[i]: cell.fill = ZEBRA_FILL cell = ws.cell(row=i, column=epos_col_idx) if cell.value == 'ANO': cell.fill = GREEN_FILL cell.font = GREEN_FONT style_header(ws) ws.freeze_panes = 'A2' autofit_ws(ws) # ===================== # List: Posudky řidičák – EPOSMRO (elektronická podání) # ===================== cur.execute(""" SELECT h.ID, h.DATUM, h.IDPACI, k.PRIJMENI, k.JMENO, k.RODCIS, h.DATA, h.STAV, h.CREATED, e.ID_PODANI, e.ODESLANO, e.STATUS FROM HISTDOC h JOIN KAR k ON k.IDPAC = h.IDPACI LEFT JOIN HISTDOC_EPOSUDEK e ON e.ID_HISTDOC = h.ID WHERE h.TYP = 'EPOSMRO' ORDER BY h.ID DESC """) epos_rows = cur.fetchall() print(f"EPOSMRO: {len(epos_rows)}") ws = wb.create_sheet("ePosudky registr") ws.append([ 'ID', 'DATUM', 'IDPACI', 'PRIJMENI', 'JMENO', 'RODCIS', 'DatumVyd', 'DatKonec', 'DruhProhlidky', 'DruhPosudku', 'Vysledek', 'StavPosudku', 'TypAkce', 'STAV', 'CREATED', 'ID_PODANI', 'ODESLANO', 'STATUS_ODESL' ]) for i, row in enumerate(epos_rows, start=2): (hid, datum, idpac, prijmeni, jmeno, rodcis, data_blob, stav, created, id_podani, odeslano, status_odesl) = row data = parse_data(data_blob) ws.append([ hid, fmt(datum), idpac, fmt(prijmeni), fmt(jmeno), fmt(rodcis), parse_date(data.get('DatumVystaveni', '')), parse_date(data.get('PlatnostDo', '')), fmt(data.get('DruhProhlidkyNazev', '')), fmt(data.get('DruhPosudkuNazev', '')), fmt(data.get('VysledekNazev', '')), fmt(data.get('StavPosudkuNazev', '')), fmt(data.get('TypAkceNazev', '')), fmt(stav), fmt(created), fmt(id_podani), fmt(odeslano), fmt(status_odesl), ]) if i % 2 == 0: for cell in ws[i]: cell.fill = ZEBRA_FILL style_header(ws) ws.freeze_panes = 'A2' autofit_ws(ws) # ===================== # Autofilter na všech listech # ===================== for ws in wb.worksheets: ws.auto_filter.ref = f"A1:{get_column_letter(ws.max_column)}{ws.max_row}" # ===================== # Uložení # ===================== con.close() wb.save(output_path) print(f"Uloženo: {output_path}") # ===================== # xlwings: autofit + centrování Recepty # ===================== with xw.App(visible=False) as app: wb_xw = xw.Book(output_path) for sheet in wb_xw.sheets: sheet.autofit() for sloupec in ["C:C", "E:E", "F:F", "G:G", "I:I", "M:M", "N:N"]: wb_xw.sheets['Recepty'].range(sloupec).api.HorizontalAlignment = 3 wb_xw.save() wb_xw.close() print("Hotovo.")