notebookvb
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user