596 lines
27 KiB
Python
596 lines
27 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
poukaz_dp_gen.py
|
||
Generátor Poukazu na vyšetření/ošetření DP (VZP-06dp/2024).
|
||
Formulář je kreslen od nuly pomocí reportlab – žádné překrývání PDF šablony.
|
||
|
||
Závislosti:
|
||
pip install reportlab
|
||
"""
|
||
|
||
from pathlib import Path
|
||
from reportlab.pdfgen import canvas
|
||
from reportlab.lib.pagesizes import A4
|
||
from reportlab.pdfbase import pdfmetrics
|
||
from reportlab.pdfbase.ttfonts import TTFont
|
||
|
||
# ── Registrace fontu s českou podporou ────────────────────────────────────────
|
||
def _reg_font():
|
||
candidates = [
|
||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
|
||
"C:/Windows/Fonts/arial.ttf",
|
||
"C:/Windows/Fonts/arialbd.ttf",
|
||
]
|
||
reg, reg_bold = False, False
|
||
for p in candidates:
|
||
if Path(p).exists():
|
||
if not reg and "Bold" not in p and "bd" not in p:
|
||
pdfmetrics.registerFont(TTFont("Czech", p))
|
||
reg = True
|
||
elif not reg_bold and ("Bold" in p or "bd" in p):
|
||
pdfmetrics.registerFont(TTFont("Czech-Bold", p))
|
||
reg_bold = True
|
||
if not reg:
|
||
# fallback – Helvetica (funguje pro základní znaky)
|
||
return "Helvetica", "Helvetica-Bold"
|
||
if not reg_bold:
|
||
return "Czech", "Czech"
|
||
return "Czech", "Czech-Bold"
|
||
|
||
FONT, FONT_BOLD = _reg_font()
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# DATA FORMULÁŘE – sem doplňte hodnoty z Medicusu
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
DATA = {
|
||
# ── Hlavička ──────────────────────────────────────────────────────────────
|
||
"kod_pojistovny": "111", # 3 číslice, VZP = 111
|
||
"icp": "12345678", # IČP pracoviště (max 8 číslic)
|
||
"odbornost": "001", # odbornost (3 číslice)
|
||
"datum": "22.05.2026", # datum vystavení
|
||
"poradove_cislo": "42", # pořadové číslo / nepřerušené DP
|
||
"platnost_do": "22.08.2026", # platnost poukazu
|
||
|
||
# ── Pacient ───────────────────────────────────────────────────────────────
|
||
"pacient": "Novák Jan",
|
||
"cislo_pojistence": "7001015432", # rodné číslo / č. pojištěnce
|
||
"zkladni_dg": "I10", # MKN kód základní diagnózy
|
||
"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" / "ne"
|
||
"kontaktni_osoba": "Nováková Marie (manželka), tel: 777 654 321",
|
||
|
||
# ── Klinické údaje ────────────────────────────────────────────────────────
|
||
"pecovatelska_sluzba": "ne", # "ano" / "ne"
|
||
|
||
"mobilita": "b", # "a" = plná / "b" = omezená
|
||
"mobilita_detail": "chodí s holí",
|
||
|
||
"smyslove_omezeni": "zraková vada – brýle",
|
||
|
||
"sebeobsluha": "b", # "a" = plná / "b" = omezená
|
||
"sebeobsluha_detail": "potřebuje pomoc při hygieně",
|
||
|
||
# Každá položka = jeden řádek (max 3)
|
||
"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) ─────────────────────────────────────────────
|
||
# kod … kód výkonu (max 5 číslic)
|
||
# popis … text na prvním řádku
|
||
# popis2 … pokračování na druhém řádku (nepovinné)
|
||
"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": ""},
|
||
],
|
||
}
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# GEOMETRIE (souřadnice od SHORA, konverze na RL přes yt())
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
PAGE_W, PAGE_H = A4 # 595 × 842 pt
|
||
|
||
# Vnější zaoblený rámeček
|
||
FX1, FX2 = 28, 567 # levý / pravý okraj
|
||
FB, FT = 52, 818 # spodní / horní okraj (RL souřadnice od zdola)
|
||
|
||
IL = FX1 + 7 # inner left (odsazení textu od okraje)
|
||
IR = FX2 - 7 # inner right
|
||
|
||
|
||
def yt(from_top: float) -> float:
|
||
"""Převede y-od-shora (pt) na reportlab y-od-zdola."""
|
||
return PAGE_H - from_top
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# POMOCNÉ FUNKCE
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
def txt(c, x, y_rl, text, size=7.5, bold=False, align="left"):
|
||
if not text:
|
||
return
|
||
c.setFont(FONT_BOLD if bold else FONT, size)
|
||
if align == "right":
|
||
c.drawRightString(x, y_rl, str(text))
|
||
elif align == "center":
|
||
c.drawCentredString(x, y_rl, str(text))
|
||
else:
|
||
c.drawString(x, y_rl, str(text))
|
||
|
||
|
||
def hline(c, x1, y_rl, x2=None, w=0.5):
|
||
if x2 is None:
|
||
x2 = IR
|
||
c.setLineWidth(w)
|
||
c.setDash([])
|
||
c.line(x1, y_rl, x2, y_rl)
|
||
|
||
|
||
def vline(c, x, y1_rl, y2_rl, w=0.5):
|
||
c.setLineWidth(w)
|
||
c.setDash([])
|
||
c.line(x, y1_rl, x, y2_rl)
|
||
|
||
|
||
def dotline(c, x1, y_rl, x2=None):
|
||
if x2 is None:
|
||
x2 = IR
|
||
c.setLineWidth(0.35)
|
||
c.setDash([1.5, 2.5])
|
||
c.line(x1, y_rl, x2, y_rl)
|
||
c.setDash([])
|
||
|
||
|
||
def chkbox(c, x, y_rl, checked: bool, size=7):
|
||
"""Zaškrtávací políčko."""
|
||
c.setLineWidth(0.6)
|
||
c.setDash([])
|
||
c.rect(x, y_rl - 1, size, size, stroke=1, fill=0)
|
||
if checked:
|
||
c.setFont(FONT_BOLD, size - 0.5)
|
||
c.drawString(x + 1, y_rl, "✓")
|
||
|
||
|
||
def digit_box(c, x, y_rl, width, height, digits, value_text=""):
|
||
"""Prostý rámeček pro zadávání hodnot (bez vnitřních oddělovačů)."""
|
||
c.setLineWidth(0.7)
|
||
c.setDash([])
|
||
c.rect(x, y_rl, width, height, stroke=1, fill=0)
|
||
if value_text:
|
||
c.setFont(FONT, height * 0.65)
|
||
c.drawString(x + 3, y_rl + height * 0.2, value_text[:digits])
|
||
|
||
|
||
def wrap(text: str, max_chars: int) -> list[str]:
|
||
"""Rozlomí text na řádky po max_chars znacích (na hranici slova)."""
|
||
if not text:
|
||
return [""]
|
||
words = text.split()
|
||
lines, cur = [], ""
|
||
for w in words:
|
||
if cur and len(cur) + 1 + len(w) > max_chars:
|
||
lines.append(cur)
|
||
cur = w
|
||
else:
|
||
cur = (cur + " " + w).strip()
|
||
if cur:
|
||
lines.append(cur)
|
||
return lines or [""]
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SEKCE 1: HLAVIČKA
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
#
|
||
# ┌─────────────────┬──────────┬──────────────────────┬──────────────────────┐
|
||
# │ Kód pojišťovny │ požaduje │ IČP [________] Datum [________] │ Pořadové číslo…
|
||
# │ [___] │ díl A │ Odbornost [___] │ nepřerušené DP:
|
||
# └─────────────────┴──────────┴──────────────────────┴──────────────────────┘
|
||
# Platnost do:
|
||
|
||
HDR_TOP = 25 # horní okraj formuláře (od shora)
|
||
HDR_MID = 50 # vodorovná čára mezi řádky hlavičky
|
||
HDR_BOT = 73 # dolní okraj hlavičky
|
||
|
||
# Svislé dělicí čáry v hlavičce
|
||
HX1 = 130 # po Kód pojišťovny
|
||
HX2 = 198 # po požaduje díl A
|
||
HX3 = 295 # mezi IČP a Datum
|
||
HX4 = 388 # konec IČP+Datum sekce (začátek Pořadové číslo)
|
||
|
||
|
||
def draw_hlavicka(c, d):
|
||
# Vnější rámeček hlavičky
|
||
hline(c, FX1, yt(HDR_BOT), FX2, w=1.2)
|
||
hline(c, FX1, yt(HDR_MID), HX4, w=0.5)
|
||
|
||
# Svislé čáry
|
||
for x in (HX1, HX2, HX4):
|
||
vline(c, x, yt(HDR_TOP), yt(HDR_BOT), w=0.8)
|
||
vline(c, HX3, yt(HDR_TOP), yt(HDR_MID), w=0.5)
|
||
|
||
# Kód pojišťovny
|
||
txt(c, IL, yt(HDR_TOP + 7), "Kód pojišťovny", size=6.5)
|
||
digit_box(c, IL, yt(HDR_MID - 5), 40, 12, 3, d.get("kod_pojistovny", ""))
|
||
|
||
# požaduje díl A
|
||
cx = (HX1 + HX2) / 2
|
||
txt(c, cx, yt(HDR_TOP + 12), "požaduje", size=7, bold=True, align="center")
|
||
txt(c, cx, yt(HDR_TOP + 21), "díl A", size=7, bold=True, align="center")
|
||
|
||
# IČP
|
||
txt(c, HX2 + 3, yt(HDR_TOP + 7), "IČP", size=6.5)
|
||
digit_box(c, HX2 + 22, yt(HDR_MID - 5), HX3 - HX2 - 27, 12, 8, d.get("icp", ""))
|
||
|
||
# Datum
|
||
txt(c, HX3 + 3, yt(HDR_TOP + 7), "Datum", size=6.5)
|
||
digit_box(c, HX3 + 28, yt(HDR_MID - 5), HX4 - HX3 - 32, 12, 10, d.get("datum", ""))
|
||
|
||
# Odbornost
|
||
txt(c, HX2 + 3, yt(HDR_MID + 7), "Odbornost", size=6.5)
|
||
digit_box(c, HX2 + 48, yt(HDR_BOT - 5), HX3 - HX2 - 52, 12, 3, d.get("odbornost", ""))
|
||
|
||
# Pořadové číslo (pravý sloupec)
|
||
txt(c, HX4 + 4, yt(HDR_TOP + 21), "Pořadové číslo poukazu", size=6.5)
|
||
txt(c, HX4 + 4, yt(HDR_TOP + 30), "nepřerušené DP:", size=6.5)
|
||
txt(c, HX4 + 80, yt(HDR_TOP + 36), d.get("poradove_cislo", ""), size=8)
|
||
|
||
# Platnost do
|
||
txt(c, HX4 + 4, yt(HDR_MID + 19), "Platnost do:", size=6.5)
|
||
hline(c, HX4 + 50, yt(HDR_MID + 13), FX2 - 4, w=0.4)
|
||
txt(c, HX4 + 54, yt(HDR_MID + 25), d.get("platnost_do", ""), size=8)
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SEKCE 2: NADPIS
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
TTL_TOP = HDR_BOT # 73
|
||
TTL_BOT = TTL_TOP + 22 # 95
|
||
|
||
|
||
def draw_nadpis(c):
|
||
hline(c, FX1, yt(TTL_BOT), FX2, w=0.8)
|
||
# "POUKAZ NA VYŠETŘENÍ / OŠETŘENÍ"
|
||
c.setFont(FONT_BOLD, 12)
|
||
c.drawCentredString(270, yt(TTL_TOP + 15), "POUKAZ NA VYŠETŘENÍ / OŠETŘENÍ")
|
||
# "DP" – velké
|
||
c.setFont(FONT_BOLD, 22)
|
||
c.drawString(390, yt(TTL_TOP + 18), "DP")
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SEKCE 3: BLOK PACIENTA
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
PAC_TOP = TTL_BOT # 95
|
||
PAC_ROW = 19 # výška každého řádku
|
||
PAC_BOT = PAC_TOP + 4 * PAC_ROW # 171
|
||
|
||
# Svislé dělicí čáry v bloku pacienta
|
||
PX1 = 340 # Č. pojištěnce / Základní diagnóza
|
||
PX2 = 340 # (sdílí s PX1 pro 2. a 3. řádek)
|
||
PX3 = 410 # Ost. dg. / Kód náhrady
|
||
|
||
# Šířka bloku pacienta (bez pravého sloupce s razítkem)
|
||
PAC_RIGHT = 540 # pravý okraj tabulky pacienta
|
||
|
||
|
||
def draw_pacient(c, d):
|
||
# Vnější pravý okraj tabulky pacienta
|
||
vline(c, PAC_RIGHT, yt(PAC_TOP), yt(PAC_BOT), w=1.0)
|
||
|
||
# Řádek 1: Pacient
|
||
y1 = yt(PAC_TOP + PAC_ROW)
|
||
hline(c, FX1, y1, PAC_RIGHT, w=0.6)
|
||
txt(c, IL, y1 + 5, "Pacient", size=7)
|
||
txt(c, IL + 50, y1 + 5, d.get("pacient", ""), size=8.5)
|
||
|
||
# Řádek 2: Č. pojištěnce | Základní diagnóza
|
||
y2 = yt(PAC_TOP + 2 * PAC_ROW)
|
||
hline(c, FX1, y2, PAC_RIGHT, w=0.6)
|
||
vline(c, PX1, yt(PAC_TOP + PAC_ROW), y2, w=0.5)
|
||
txt(c, IL, y2 + 5, "Č. pojištěnce", size=7)
|
||
digit_box(c, IL + 68, y2 + 3, 130, 13, 10, d.get("cislo_pojistence", ""))
|
||
txt(c, PX1 + 4, y2 + 5, "Základní diagnóza", size=7)
|
||
digit_box(c, PX1 + 86, y2 + 3, 65, 13, 5, d.get("zkladni_dg", ""))
|
||
|
||
# Řádek 3: Variabilní symbol | Ost. dg. | Kód náhrady
|
||
y3 = yt(PAC_TOP + 3 * PAC_ROW)
|
||
hline(c, FX1, y3, PAC_RIGHT, w=0.6)
|
||
vline(c, PX2, yt(PAC_TOP + 2 * PAC_ROW), y3, w=0.5)
|
||
vline(c, PX3, yt(PAC_TOP + 2 * PAC_ROW), y3, w=0.5)
|
||
txt(c, IL, y3 + 5, "Variabilní symbol", size=7)
|
||
digit_box(c, IL + 83, y3 + 3, 100, 13, 8, d.get("variabilni_symbol", ""))
|
||
txt(c, PX2 + 4, y3 + 5, "Ost. dg.", size=7)
|
||
digit_box(c, PX2 + 38, y3 + 3, 60, 13, 5, d.get("ost_dg", ""))
|
||
txt(c, PX3 + 4, y3 + 5, "Kód náhrady", size=7)
|
||
digit_box(c, PX3 + 54, y3 + 3, 50, 13, 4, d.get("kod_nahrady", ""))
|
||
|
||
# Řádek 4: Ad zařízení domácí péče + razítko
|
||
y4 = yt(PAC_BOT)
|
||
hline(c, FX1, y4, PAC_RIGHT, w=0.6)
|
||
txt(c, IL, y4 + 5, "Ad zařízení domácí péče:", size=7, bold=True)
|
||
# razítko a podpis (pravý sloupec, malé kurzívní)
|
||
c.setFont(FONT, 6)
|
||
c.drawRightString(FX2 - 4, y4 + 4, "razítko a podpis požadujícího")
|
||
hline(c, PAC_RIGHT + 5, y4 + 2, FX2 - 4, w=0.4)
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SEKCE 4: TEXTOVÁ POLE
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
def draw_textova_pole(c, d) -> float:
|
||
"""Vrací cur_top (od shora) po posledním poli."""
|
||
top = PAC_BOT + 6 # 177 od shora
|
||
LBL = 7.5 # velikost fontu popisků
|
||
VAL = 8.0 # velikost fontu hodnot
|
||
GAP = 6 # mezera mezi sekcemi
|
||
|
||
def label_and_value(label_text, value_text, label_width, max_chars=70):
|
||
nonlocal top
|
||
top += GAP
|
||
y_rl = yt(top + 9)
|
||
txt(c, IL, y_rl, label_text, size=LBL)
|
||
txt(c, IL + label_width, y_rl, value_text[:max_chars] if value_text else "", size=VAL)
|
||
dotline(c, IL + label_width, y_rl - 2)
|
||
top += 16
|
||
|
||
def extra_dotline(value_text="", indent=0):
|
||
nonlocal top
|
||
top += 13
|
||
y_rl = yt(top)
|
||
dotline(c, IL + indent, y_rl)
|
||
if value_text:
|
||
txt(c, IL + indent, y_rl + 3, value_text[:80], size=VAL)
|
||
|
||
def checkbox_row(label_text, field_name, label_width=None):
|
||
nonlocal top
|
||
top += GAP
|
||
y_rl = yt(top + 9)
|
||
txt(c, IL, y_rl, label_text, size=LBL)
|
||
lw = label_width or (len(label_text) * 4.2)
|
||
cx = IL + lw + 5
|
||
val = d.get(field_name, "ne").lower()
|
||
chkbox(c, cx, y_rl, checked=(val == "ano"), size=7)
|
||
txt(c, cx + 9, y_rl, "ano", size=LBL)
|
||
chkbox(c, cx + 34, y_rl, checked=(val == "ne"), size=7)
|
||
txt(c, cx + 43, y_rl, "ne", size=LBL)
|
||
top += 16
|
||
|
||
# ── Adresa pacienta ───────────────────────────────────────────────────────
|
||
adresa_lines = wrap(d.get("adresa_pacienta", ""), 75)
|
||
label_and_value("Adresa pacienta (místo poskytování DP) a telefon:", adresa_lines[0], 260)
|
||
extra_dotline(adresa_lines[1] if len(adresa_lines) > 1 else "")
|
||
|
||
# ── Příslušníci domácnosti ────────────────────────────────────────────────
|
||
checkbox_row("Další příslušníci domácnosti na této adrese:", "dalsi_prislusnici", label_width=242)
|
||
|
||
# ── Kontaktní osoba ───────────────────────────────────────────────────────
|
||
kont_lines = wrap(d.get("kontaktni_osoba", ""), 55)
|
||
label_and_value(
|
||
"Kontaktní osoba pro DP (jméno, vztah k pacientovi, adresa a telefon – je-li rozdílná od adresy pacienta):",
|
||
kont_lines[0], 432, max_chars=55
|
||
)
|
||
extra_dotline(kont_lines[1] if len(kont_lines) > 1 else "")
|
||
|
||
# ── Pečovatelská služba ───────────────────────────────────────────────────
|
||
checkbox_row("Pacient v péči pečovatelské služby:", "pecovatelska_sluzba", label_width=186)
|
||
|
||
# ── Mobilita ──────────────────────────────────────────────────────────────
|
||
top += GAP
|
||
y_mob = yt(top + 9)
|
||
txt(c, IL, y_mob, "Mobilita pacienta:", size=LBL)
|
||
mob = d.get("mobilita", "a").lower()
|
||
chkbox(c, IL + 98, y_mob, checked=(mob == "a"), size=7)
|
||
txt(c, IL + 107, y_mob, "a) plná", size=LBL)
|
||
top += 16
|
||
|
||
top += 2
|
||
y_mob2 = yt(top + 9)
|
||
chkbox(c, IL + 98, y_mob2, checked=(mob == "b"), size=7)
|
||
txt(c, IL + 107, y_mob2, "b) omezená:", size=LBL)
|
||
mob_detail = d.get("mobilita_detail", "") if mob == "b" else ""
|
||
txt(c, IL + 167, y_mob2, mob_detail[:60], size=VAL)
|
||
dotline(c, IL + 163, y_mob2 - 2)
|
||
top += 16
|
||
|
||
# ── Smyslové omezení ──────────────────────────────────────────────────────
|
||
label_and_value("Smyslové omezení:", d.get("smyslove_omezeni", ""), 95)
|
||
|
||
# ── Sebeobsluha ───────────────────────────────────────────────────────────
|
||
top += GAP
|
||
y_sebe = yt(top + 9)
|
||
txt(c, IL, y_sebe, "Schopnost základní sebeobsluhy, včetně dodržování léčebného režimu:", size=LBL)
|
||
sebe = d.get("sebeobsluha", "a").lower()
|
||
chkbox(c, IL + 374, y_sebe, checked=(sebe == "a"), size=7)
|
||
txt(c, IL + 383, y_sebe, "a) plná", size=LBL)
|
||
top += 16
|
||
|
||
top += 2
|
||
y_sebe2 = yt(top + 9)
|
||
chkbox(c, IL + 98, y_sebe2, checked=(sebe == "b"), size=7)
|
||
txt(c, IL + 107, y_sebe2, "b) omezená:", size=LBL)
|
||
sebe_detail = d.get("sebeobsluha_detail", "") if sebe == "b" else ""
|
||
txt(c, IL + 167, y_sebe2, sebe_detail[:60], size=VAL)
|
||
dotline(c, IL + 163, y_sebe2 - 2)
|
||
top += 16
|
||
|
||
# ── Medikace ──────────────────────────────────────────────────────────────
|
||
med = d.get("medikace", [])
|
||
if isinstance(med, str):
|
||
med = [med]
|
||
med_lines = []
|
||
for item in med:
|
||
med_lines.extend(wrap(item, 80))
|
||
|
||
top += GAP
|
||
y_med = yt(top + 9)
|
||
txt(c, IL, y_med, "Významné údaje o současné medikaci, včetně aplikace inzulínu a diety:", size=LBL)
|
||
txt(c, IL + 357, y_med, med_lines[0][:35] if med_lines else "", size=VAL)
|
||
dotline(c, IL + 353, y_med - 2)
|
||
top += 16
|
||
extra_dotline(med_lines[1] if len(med_lines) > 1 else "")
|
||
extra_dotline(med_lines[2] if len(med_lines) > 2 else "")
|
||
top += 3
|
||
|
||
# ── Další informace ───────────────────────────────────────────────────────
|
||
info = d.get("dalsi_informace", [])
|
||
if isinstance(info, str):
|
||
info = [info]
|
||
info_lines = []
|
||
for item in info:
|
||
info_lines.extend(wrap(item, 80))
|
||
|
||
top += GAP
|
||
y_info = yt(top + 9)
|
||
txt(c, IL, y_info, "Další informace (alergie, kontinence, údaje o bydlišti atd.):", size=LBL)
|
||
txt(c, IL + 315, y_info, info_lines[0][:40] if info_lines else "", size=VAL)
|
||
dotline(c, IL + 311, y_info - 2)
|
||
top += 16
|
||
extra_dotline(info_lines[1] if len(info_lines) > 1 else "")
|
||
extra_dotline(info_lines[2] if len(info_lines) > 2 else "")
|
||
top += 3
|
||
|
||
# ── Cíl DP ────────────────────────────────────────────────────────────────
|
||
cil = d.get("cil_dp", [])
|
||
if isinstance(cil, str):
|
||
cil = [cil]
|
||
cil_lines = []
|
||
for item in cil:
|
||
cil_lines.extend(wrap(item, 80))
|
||
|
||
top += GAP
|
||
y_cil = yt(top + 9)
|
||
txt(c, IL, y_cil, "Cíl předepsané DP, kterého má být dosaženo:", size=LBL)
|
||
txt(c, IL + 236, y_cil, cil_lines[0][:50] if cil_lines else "", size=VAL)
|
||
dotline(c, IL + 232, y_cil - 2)
|
||
top += 16
|
||
extra_dotline(cil_lines[1] if len(cil_lines) > 1 else "")
|
||
top += 5
|
||
|
||
return top
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SEKCE 5: POŽADOVÁNO
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
def draw_pozadovano(c, d, start_top: float):
|
||
top = start_top + 5
|
||
|
||
# Nadpis
|
||
top += 8
|
||
y_hdr = yt(top + 9)
|
||
txt(c, IL, y_hdr, "Požadováno:", size=8, bold=True)
|
||
txt(c, IL + 68, y_hdr,
|
||
"(Pro úhradu požadované péče zdravotní pojišťovnou je nezbytná jednoznačná specifikace požadavku,",
|
||
size=6.5)
|
||
top += 12
|
||
txt(c, IL + 68, yt(top + 5),
|
||
"včetně počtu v jednom dni a frekvence v týdnu)", size=6.5)
|
||
top += 10
|
||
|
||
# Výkony (5 řádků)
|
||
KOD_X = IL # x-start kódového rámečku
|
||
KOD_W = 108 # šířka kódového rámečku
|
||
KOD_H = 22 # výška kódového rámečku
|
||
TXT_X = KOD_X + KOD_W + 8 # x-start textové části
|
||
|
||
ROW_H = 42 # výška jednoho výkonu (rámeček + 2 řádky textu)
|
||
|
||
for vykon in (d.get("pozadovano", []) + [{}, {}, {}, {}, {}])[:5]:
|
||
y_top_row = yt(top)
|
||
|
||
# Kódový rámeček
|
||
c.setLineWidth(0.9)
|
||
c.setDash([])
|
||
c.rect(KOD_X, y_top_row - KOD_H - 4, KOD_W, KOD_H, stroke=1, fill=0)
|
||
# Hodnota kódu
|
||
kod_val = vykon.get("kod", "")
|
||
if kod_val:
|
||
txt(c, KOD_X + 5, y_top_row - KOD_H + 2, kod_val, size=9, bold=True)
|
||
|
||
# Textový řádek 1
|
||
txt(c, TXT_X, y_top_row - 10, vykon.get("popis", "")[:75], size=8)
|
||
dotline(c, TXT_X, y_top_row - 13, IR)
|
||
|
||
# Textový řádek 2 (pokračování)
|
||
txt(c, TXT_X, y_top_row - 25, vykon.get("popis2", "")[:75], size=8)
|
||
dotline(c, TXT_X, y_top_row - 28, IR)
|
||
|
||
top += ROW_H
|
||
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# HLAVNÍ FUNKCE
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
def generuj_poukaz(data: dict, vystup: Path):
|
||
c = canvas.Canvas(str(vystup), pagesize=A4)
|
||
|
||
# Vnější zaoblený rámeček
|
||
c.setLineWidth(1.8)
|
||
c.roundRect(FX1, FB, FX2 - FX1, FT - FB, 6, stroke=1, fill=0)
|
||
|
||
draw_hlavicka(c, data)
|
||
draw_nadpis(c)
|
||
draw_pacient(c, data)
|
||
cur_top = draw_textova_pole(c, data)
|
||
draw_pozadovano(c, data, cur_top)
|
||
|
||
# Zápatí
|
||
txt(c, FX1 + 2, FB - 12, "VZP-06dp/2024", size=6)
|
||
|
||
c.save()
|
||
print(f"✓ Uloženo: {vystup}")
|
||
|
||
|
||
# ── Spuštění ──────────────────────────────────────────────────────────────────
|
||
if __name__ == "__main__":
|
||
folder = Path(__file__).parent
|
||
# Najde nejvyšší existující číslo verze a přidá 1
|
||
existing = [f for f in folder.glob("Poukaz DP vyplneny_v*.pdf")]
|
||
nums = []
|
||
for f in existing:
|
||
try:
|
||
nums.append(int(f.stem.rsplit("_v", 1)[1]))
|
||
except (ValueError, IndexError):
|
||
pass
|
||
next_v = max(nums, default=2) + 1
|
||
out = folder / f"Poukaz DP vyplneny_v{next_v}.pdf"
|
||
generuj_poukaz(DATA, out)
|
||
dá 1
|
||
existing = [f for f in folder.glob("Poukaz DP vyplneny_v*.pdf")]
|
||
nums = []
|
||
for f in existing:
|
||
try:
|
||
nums.append(int(f.stem.rsplit("_v", 1)[1]))
|
||
except (ValueError, IndexError):
|
||
pass
|
||
next_v = max(nums, default=2) + 1
|
||
out = folder / f"Poukaz DP vyplneny_v{next_v}.pdf"
|
||
generuj_poukaz(DATA, out)
|