"""PDF guide generator for DAK onboarding invitations.""" from __future__ import annotations from pathlib import Path from fpdf import FPDF ASSETS_DIR = Path(__file__).resolve().parent.parent / "assets" LOGO_PATH = ASSETS_DIR / "dak_logo.png" FONT_DIR = ASSETS_DIR # Font family name used throughout the PDF _FONT = "LiberationSans" # Colours (DAK blue tones) DAK_BLUE = (0, 82, 147) DAK_DARK = (33, 37, 41) DAK_GREY = (108, 117, 125) DAK_LIGHT_BG = (240, 244, 248) class _GuidePDF(FPDF): """Custom FPDF subclass with shared header/footer styling.""" def __init__(self, **kwargs): super().__init__(**kwargs) # Register Liberation Sans (Unicode TTF) in Regular, Bold, Italic self.add_font(_FONT, "", str(FONT_DIR / "LiberationSans-Regular.ttf")) self.add_font(_FONT, "B", str(FONT_DIR / "LiberationSans-Bold.ttf")) self.add_font(_FONT, "I", str(FONT_DIR / "LiberationSans-Italic.ttf")) def header(self): if self.page_no() == 1: return # Page 1 has its own header with logo self.set_font(_FONT, "I", 8) self.set_text_color(*DAK_GREY) self.cell(0, 8, "DAK Zweitmeinungs-Portal \u2014 Anleitung", align="R") self.ln(12) def footer(self): self.set_y(-15) self.set_font(_FONT, "I", 8) self.set_text_color(*DAK_GREY) self.cell(0, 10, f"Seite {self.page_no()}/{{nb}}", align="C") # -- helpers --------------------------------------------------------------- def section_title(self, text: str) -> None: self.set_font(_FONT, "B", 14) self.set_text_color(*DAK_BLUE) self.cell(0, 10, text) self.ln(8) # blue underline self.set_draw_color(*DAK_BLUE) self.set_line_width(0.5) self.line(self.l_margin, self.get_y(), self.w - self.r_margin, self.get_y()) self.ln(6) def body_text(self, text: str) -> None: self.set_font(_FONT, "", 10) self.set_text_color(*DAK_DARK) self.multi_cell(0, 6, text) self.ln(2) def numbered_step(self, number: int, text: str) -> None: self.set_font(_FONT, "B", 10) self.set_text_color(*DAK_BLUE) self.cell(8, 6, f"{number}.") self.set_font(_FONT, "", 10) self.set_text_color(*DAK_DARK) self.multi_cell(0, 6, text) self.ln(1) def bullet_item(self, title: str, description: str) -> None: self.set_font(_FONT, "B", 10) self.set_text_color(*DAK_DARK) self.cell(4, 6, "\u2022 ") self.cell(0, 6, title) self.ln(6) if description: self.set_font(_FONT, "", 9) self.set_text_color(*DAK_GREY) self.set_x(self.l_margin + 6) self.multi_cell(0, 5, description) self.ln(2) def info_box(self, text: str) -> None: self.set_fill_color(*DAK_LIGHT_BG) self.set_draw_color(*DAK_BLUE) x = self.l_margin y = self.get_y() w = self.w - self.l_margin - self.r_margin self.set_font(_FONT, "", 9) self.set_text_color(*DAK_DARK) # calculate height self.set_xy(x + 4, y + 4) self.multi_cell(w - 8, 5, text) h = self.get_y() - y + 4 # draw box behind self.rect(x, y, w, h, style="DF") # re-render text on top self.set_xy(x + 4, y + 4) self.multi_cell(w - 8, 5, text) self.ln(4) def generate_guide_pdf(invite_url: str) -> bytes: """Generate the DAK onboarding guide as PDF bytes. Parameters ---------- invite_url: The personalised invitation/registration URL to embed in the document. Returns ------- bytes The raw PDF content (no disk I/O). """ pdf = _GuidePDF(orientation="P", unit="mm", format="A4") pdf.alias_nb_pages() pdf.set_auto_page_break(auto=True, margin=20) pdf.set_left_margin(20) pdf.set_right_margin(20) # ── Page 1: Welcome & Registration ──────────────────────────────────── pdf.add_page() # Logo if LOGO_PATH.exists(): pdf.image(str(LOGO_PATH), x=75, y=15, w=60) pdf.ln(50) else: pdf.ln(15) # Title pdf.set_font(_FONT, "B", 20) pdf.set_text_color(*DAK_BLUE) pdf.cell(0, 12, "Willkommen im", align="C") pdf.ln(12) pdf.cell(0, 12, "DAK Zweitmeinungs-Portal", align="C") pdf.ln(20) # Registration section pdf.section_title("Konto erstellen") pdf.body_text( "Um Ihr Konto zu erstellen, folgen Sie bitte diesen Schritten:" ) pdf.numbered_step(1, "Registrierungslink im Browser öffnen (siehe unten)") pdf.numbered_step(2, "Benutzername festlegen (mindestens 3 Zeichen)") pdf.numbered_step(3, "E-Mail-Adresse eingeben") pdf.numbered_step(4, "Passwort setzen (mindestens 8 Zeichen)") pdf.numbered_step(5, "\u201eRegistrieren\u201c anklicken") pdf.ln(2) # Invite URL box pdf.set_font(_FONT, "B", 10) pdf.set_text_color(*DAK_BLUE) pdf.cell(0, 6, "Ihr Registrierungslink:") pdf.ln(7) pdf.info_box(invite_url) # First login pdf.section_title("Erste Anmeldung") pdf.body_text( "Melden Sie sich mit Ihrem Benutzernamen (oder E-Mail-Adresse) " "und dem gewählten Passwort an." ) pdf.body_text( "Empfehlung: Aktivieren Sie die Zwei-Faktor-Authentifizierung " "unter Kontoverwaltung für zusätzliche Sicherheit." ) # ── Page 2: Feature overview ────────────────────────────────────────── pdf.add_page() pdf.section_title("Funktionsübersicht") pdf.body_text( "Das DAK Zweitmeinungs-Portal bietet Ihnen folgende Funktionen:" ) pdf.ln(2) features = [ ("Dashboard", "Übersicht über KPIs, Diagramme und Jahresvergleich."), ("Fälle", "Fallübersicht mit Such- und Filterfunktionen."), ("ICD-Eingabe", "ICD-Codes für Fälle erfassen und bearbeiten."), ("Berichte", "Wochenberichte als Excel-Datei herunterladen."), ( "Wochenübersicht", "Datenübersicht pro Kalenderwoche und ICD-Upload per Excel.", ), ( "Freigaben", "Personenbezogene Daten bei Bedarf anfordern (24 Stunden Gültigkeit).", ), ( "Kontoverwaltung", "Profil bearbeiten, Passwort ändern, Zwei-Faktor-Authentifizierung.", ), ] for title, desc in features: pdf.bullet_item(title, desc) # ── Page 3: Closing notes ───────────────────────────────────────────── pdf.add_page() pdf.section_title("Weitere Informationen") pdf.body_text( "Ausführliche Anleitungen zu allen Funktionen finden Sie direkt " "im Portal unter dem Menüpunkt \u201eAnleitung\u201c." ) pdf.ln(4) pdf.body_text( "Bei Fragen oder Problemen wenden Sie sich bitte an Ihren " "Ansprechpartner bei Complex Care Solutions:" ) pdf.ln(2) pdf.set_font(_FONT, "B", 10) pdf.set_text_color(*DAK_DARK) pdf.cell(0, 6, "Complex Care Solutions GmbH") pdf.ln(6) pdf.set_font(_FONT, "", 10) pdf.cell(0, 6, "E-Mail: info@complexcaresolutions.de") pdf.ln(6) pdf.cell(0, 6, "Portal: https://dak.complexcaresolutions.de") pdf.ln(16) # Final info box pdf.info_box( "Dieses Dokument wurde automatisch generiert und enthält " "Ihren persönlichen Registrierungslink. " "Bitte geben Sie diesen Link nicht an Dritte weiter." ) return pdf.output()