notebookvb

This commit is contained in:
Vladimir Buzalka
2026-05-31 07:51:29 +02:00
parent ac37a6e6db
commit a9ef60212d
17 changed files with 3590 additions and 0 deletions
+235
View File
@@ -0,0 +1,235 @@
"""
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)