""" fill_poukaz_dp.py ----------------- Vyplní formulář "Poukaz na vyšetření / ošetření DP" (VZP-06dp/2024). Vstup: PDF šablona (plochý formulář) Výstup: vyplněný PDF Použití: python fill_poukaz_dp.py Závislosti: pip install pypdf reportlab """ from pathlib import Path from io import BytesIO from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from pypdf import PdfReader, PdfWriter # --------------------------------------------------------------------------- # NASTAVENÍ — sem doplňte cestu k šabloně a výstupnímu souboru # --------------------------------------------------------------------------- SABLONA = Path(__file__).parent / "Poukaz DP zdroj.pdf" VYSTUP = Path(__file__).parent / "Poukaz DP vyplneny.pdf" # --------------------------------------------------------------------------- # DATA FORMULÁŘE — doplňte hodnoty # --------------------------------------------------------------------------- DATA = { # ── Hlavička ───────────────────────────────────────────────────────────── "kod_pojistovny": "111", # 3 číslice (VZP = 111) "icp": "12345678", # IČP pracoviště "odbornost": "001", # odbornost "datum": "22.05.2026", # datum vystavení "poradove_cislo": "42", # pořadové č. poukazu / nepřerušené DP "platnost_do": "22.08.2026", # platnost poukazu # ── Pacient ────────────────────────────────────────────────────────────── "pacient": "Novák Jan", "cislo_pojistence": "7001015432", "zkladni_dg": "I10", # základní diagnóza (MKN kód) "variabilni_symbol":"123456", "ost_dg": "E11", # ostatní diagnózy "kod_nahrady": "", # ── Adresa a kontakty ──────────────────────────────────────────────────── "adresa_pacienta": "Dlouhá 12, 110 00 Praha 1, tel: 777 123 456", "dalsi_prislusnici":"ne", # "ano" nebo "ne" "kontaktni_osoba": "Nováková Marie (manželka), tel: 777 654 321", # ── Klinické údaje ──────────────────────────────────────────────────────── "pecovatelska_sluzba": "ne", # "ano" nebo "ne" "mobilita": "b) omezená: chodí s holí", "smyslove_omezeni": "zraková vada – brýle", "sebeobsluha": "b) omezená: potřebuje pomoc při hygieně", "medikace": "Metformin 1000 mg 1-0-1, Amlodipine 5 mg 1-0-0, inzulín Lantus 20j večer", "dalsi_informace": "alergie: penicilin; inkontinence moči; byt 2. patro bez výtahu", "cil_dp": "Edukace v aplikaci inzulínu, kontrola glykémie, péče o DM nohu", # ── Požadované výkony (max 5 řádků) ───────────────────────────────────── # Každý výkon: {"kod": "06101", "popis": "...", "popis2": "..."} "pozadovano": [ {"kod": "06101", "popis": "Komplexní ošetřovatelská péče – 1× denně, 5× týdně", "popis2": ""}, {"kod": "06129", "popis": "Aplikace inzulínu – 1× denně, 7× týdně", "popis2": ""}, {"kod": "06111", "popis": "Odběr biologického materiálu – 1× týdně", "popis2": ""}, {"kod": "", "popis": "", "popis2": ""}, {"kod": "", "popis": "", "popis2": ""}, ], } # --------------------------------------------------------------------------- # Pomocná funkce — zkrátí text pokud je moc dlouhý # --------------------------------------------------------------------------- def _fit(text: str, max_chars: int) -> str: return text[:max_chars] + "…" if len(text) > max_chars else text # --------------------------------------------------------------------------- # Vytvoří overlay stránku s textem # --------------------------------------------------------------------------- def _vytvor_overlay(data: dict) -> BytesIO: buf = BytesIO() W, H = A4 # 595.3 × 841.9 pt c = canvas.Canvas(buf, pagesize=A4) c.setFont("Helvetica", 8) def txt(x, y, text, size=8, bold=False): if not text: return font = "Helvetica-Bold" if bold else "Helvetica" c.setFont(font, size) c.drawString(x, y, str(text)) # ── Hlavička ───────────────────────────────────────────────────────────── txt(58, 795, data["kod_pojistovny"], size=8) # Kód pojišťovny txt(200, 800, data["icp"], size=8) # IČP txt(200, 782, data["odbornost"], size=8) # Odbornost txt(310, 800, data["datum"], size=8) # Datum txt(425, 800, data["poradove_cislo"], size=8) # Pořadové č. txt(470, 765, data["platnost_do"], size=8) # Platnost do # ── Pacient ────────────────────────────────────────────────────────────── txt(195, 726, _fit(data["pacient"], 40), size=8) # Pacient txt(115, 709, data["cislo_pojistence"], size=8) # Č. pojištěnce txt(390, 709, data["zkladni_dg"], size=8) # Základní dg. txt(115, 692, data["variabilni_symbol"], size=8) # Variabilní symbol txt(390, 692, data["ost_dg"], size=8) # Ost. dg. txt(390, 675, data["kod_nahrady"], size=8) # Kód náhrady # ── Adresa pacienta ─────────────────────────────────────────────────────── txt(335, 634, _fit(data["adresa_pacienta"], 70), size=8) # Další příslušníci — podtrhne zvolenou možnost if data["dalsi_prislusnici"].lower() == "ano": txt(310, 600, "ano", size=8, bold=True) else: txt(323, 600, "ne", size=8, bold=True) # ── Kontaktní osoba ─────────────────────────────────────────────────────── txt(420, 573, _fit(data["kontaktni_osoba"], 50), size=8) # Pacient v péči pečovatelské služby if data["pecovatelska_sluzba"].lower() == "ano": txt(310, 536, "ano", size=8, bold=True) else: txt(323, 536, "ne", size=8, bold=True) # ── Mobilita ────────────────────────────────────────────────────────────── mob = data["mobilita"] if mob.lower().startswith("a"): txt(230, 521, "✓ plná", size=8, bold=True) else: # odstraní "b) omezená:" prefix pokud tam je detail = mob.replace("b) omezená:", "").replace("b)omezená:", "").strip() txt(280, 504, _fit(detail, 60), size=8) # ── Smyslové omezení ────────────────────────────────────────────────────── txt(280, 479, _fit(data["smyslove_omezeni"], 65), size=8) # ── Sebeobsluha ─────────────────────────────────────────────────────────── sebo = data["sebeobsluha"] if sebo.lower().startswith("a"): txt(390, 452, "✓ plná", size=8, bold=True) else: detail = sebo.replace("b) omezená:", "").replace("b)omezená:", "").strip() txt(280, 437, _fit(detail, 60), size=8) # ── Medikace (max 3 řádky) ──────────────────────────────────────────────── med_lines = _rozlom(data["medikace"], 80) for i, line in enumerate(med_lines[:3]): txt(58, 413 - i * 14, line, size=8) # ── Další informace (max 3 řádky) ───────────────────────────────────────── info_lines = _rozlom(data["dalsi_informace"], 80) for i, line in enumerate(info_lines[:3]): txt(58, 370 - i * 14, line, size=8) # ── Cíl DP (max 2 řádky) ────────────────────────────────────────────────── cil_lines = _rozlom(data["cil_dp"], 80) for i, line in enumerate(cil_lines[:2]): txt(58, 321 - i * 14, line, size=8) # ── Požadované výkony ───────────────────────────────────────────────────── # Souřadnice každého řádku (y pro kód, y pro 1. text, y pro 2. text) radky = [ (240, 263, 243), # řádek 1 (197, 210, 192), # řádek 2 (152, 163, 145), # řádek 3 (110, 120, 102), # řádek 4 ( 68, 78, 60), # řádek 5 ] for i, vykon in enumerate(data["pozadovano"][:5]): y_kod, y_txt1, y_txt2 = radky[i] txt(58, y_kod, vykon.get("kod", ""), size=8) txt(285, y_txt1, _fit(vykon.get("popis", ""), 75), size=8) txt(285, y_txt2, _fit(vykon.get("popis2", ""), 75), size=8) c.save() buf.seek(0) return buf def _rozlom(text: str, sirka: int) -> list[str]: """Rozlomí dlouhý text na řádky po max `sirka` znacích (na hranici slova).""" words = text.split() lines, current = [], "" for w in words: if len(current) + len(w) + 1 <= sirka: current = (current + " " + w).strip() else: if current: lines.append(current) current = w if current: lines.append(current) return lines # --------------------------------------------------------------------------- # Hlavní funkce # --------------------------------------------------------------------------- def vyplnit_poukaz(data: dict, sablona: Path, vystup: Path): if not sablona.exists(): raise FileNotFoundError(f"Šablona nenalezena: {sablona}") overlay_buf = _vytvor_overlay(data) reader = PdfReader(sablona) writer = PdfWriter() overlay = PdfReader(overlay_buf) # Stránka 1 — překryjeme overlayem page1 = reader.pages[0] page1.merge_page(overlay.pages[0]) writer.add_page(page1) # Stránka 2 — beze změny if len(reader.pages) > 1: writer.add_page(reader.pages[1]) vystup.parent.mkdir(parents=True, exist_ok=True) with open(vystup, "wb") as f: writer.write(f) print(f"✓ Hotovo: {vystup}") # --------------------------------------------------------------------------- if __name__ == "__main__": vyplnit_poukaz(DATA, SABLONA, VYSTUP)