""" Website Taal-, Consistentie- & LLM-analyse — PDF Rapport Generator ==================================================================== Gebruik dit script als startpunt. Vervang de inhoud van BEVINDINGEN met de werkelijk gevonden actiepunten. De stijlfuncties en layoutlogica hoef je niet aan te passen. Gebruik: pip install reportlab --break-system-packages -q python rapport_generator.py """ from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import ParagraphStyle from reportlab.lib.units import cm from reportlab.lib import colors from reportlab.platypus import ( SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable, KeepTogether, PageBreak ) from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT from datetime import date import os # ── Configuratie — pas dit aan per opdracht ─────────────────────────────────── MERKNAAM = "HelloPrint" # Merknaam van de geanalyseerde site DOMEINNAAM = "helloprint.com/nl-nl" # Domein / pad DOELGROEP = "B2B klein zakelijk (tot 10 medewerkers)" LOCALE = "nl-NL" OUTPUT = f"/mnt/user-data/outputs/{MERKNAAM}_Taal_Consistentie_LLM_{date.today().strftime('%Y%m%d')}.pdf" # ───────────────────────────────────────────────────────────────────────────── doc = SimpleDocTemplate( OUTPUT, pagesize=A4, rightMargin=2.2*cm, leftMargin=2.2*cm, topMargin=2.5*cm, bottomMargin=2.5*cm, ) # ── Kleurenpalet ────────────────────────────────────────────────────────────── GROEN = colors.HexColor("#1a6b2f") GROEN_L = colors.HexColor("#eaf5ec") BLAUW = colors.HexColor("#1a3a6b") BLAUW_L = colors.HexColor("#e8eef5") PAARS = colors.HexColor("#5b2d8e") PAARS_L = colors.HexColor("#f3eefb") TEAL = colors.HexColor("#0e6b5e") TEAL_L = colors.HexColor("#e6f4f2") ROOD = colors.HexColor("#b03030") ROOD_L = colors.HexColor("#fdf0ee") ORANJE = colors.HexColor("#c05c00") ORANJE_L = colors.HexColor("#fef5e7") DONKER = colors.HexColor("#1a1a1a") GRIJS = colors.HexColor("#f4f4f4") GRIJS2 = colors.HexColor("#e8e8e8") LICHT = colors.HexColor("#777777") WIT = colors.white # ── Stijlen ─────────────────────────────────────────────────────────────────── def ps(name, **kw): return ParagraphStyle(name, **kw) BODY = ps("B", fontSize=9.5, leading=14, fontName="Helvetica", textColor=DONKER, spaceAfter=4) SMALL = ps("S", fontSize=8.5, leading=12, fontName="Helvetica", textColor=LICHT) H1 = ps("H1", fontSize=13, leading=17, fontName="Helvetica-Bold", textColor=BLAUW, spaceBefore=16, spaceAfter=5) H2 = ps("H2", fontSize=10.5,leading=14, fontName="Helvetica-Bold", textColor=DONKER, spaceBefore=8, spaceAfter=3) # ── Thema- en prioriteitsconfiguratie ───────────────────────────────────────── THEMA = { "TAAL": (BLAUW, BLAUW_L, "T"), "CONSISTENTIE": (GROEN, GROEN_L, "C"), "LLM": (PAARS, PAARS_L, "L"), "NAV": (TEAL, TEAL_L, "N"), } PRIO = { "HOOG": ROOD, "MIDDEL": ORANJE, "LAAG": GROEN, } _tellers = {k: 0 for k in THEMA} def omslagrij(tekst, stijlkw, achtergrond): """Eén gekleurde rij voor het omslagblok.""" t = Table([[Paragraph(tekst, ps(f"omslag_{hash(tekst)}", **stijlkw))]], colWidths=[17*cm]) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,-1), achtergrond), ("TOPPADDING", (0,0), (-1,-1), 6), ("BOTTOMPADDING", (0,0), (-1,-1), 6), ("LEFTPADDING", (0,0), (-1,-1), 20), ("RIGHTPADDING", (0,0), (-1,-1), 20), ])) return t def actiepunt(thema, prio, titel, probleem, vindplaats, actie): """ Geeft een opgemaakte actiepunt-blok terug als ReportLab Flowable. Parameters ---------- thema : "TAAL" | "CONSISTENTIE" | "LLM" | "NAV" prio : "HOOG" | "MIDDEL" | "LAAG" titel : Korte probleemtitel (string) probleem : Uitleg van het probleem (mag HTML-tags bevatten) vindplaats : Exacte locatie op de pagina actie : Concreet advies (mag HTML-tags bevatten) """ _tellers[thema] += 1 nr = _tellers[thema] tk, tl, afk = THEMA[thema] pk = PRIO[prio] uid = f"{thema}{nr}" def badge(tekst, breedte, achtergrond): t = Table([[Paragraph(f"{tekst}", ps(f"bdg_{uid}_{tekst}", fontSize=8, fontName="Helvetica-Bold", textColor=WIT, leading=10, alignment=TA_CENTER))]], colWidths=[breedte]) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,-1), achtergrond), ("TOPPADDING", (0,0), (-1,-1), 4), ("BOTTOMPADDING", (0,0), (-1,-1), 4), ("LEFTPADDING", (0,0), (-1,-1), 2), ("RIGHTPADDING", (0,0), (-1,-1), 2), ("ROUNDEDCORNERS", [3]), ])) return t labels = Table([[badge(f"{afk}{nr}", 0.9*cm, tk), Spacer(0.25*cm, 1), badge(prio, 1.4*cm, pk)]], colWidths=[1*cm, 0.35*cm, 1.5*cm]) labels.setStyle(TableStyle([ ("TOPPADDING", (0,0), (-1,-1), 0), ("BOTTOMPADDING", (0,0), (-1,-1), 0), ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0), ])) inhoud = [ Paragraph(f"{titel}", ps(f"ti_{uid}", fontSize=10.5, fontName="Helvetica-Bold", textColor=DONKER, leading=14, spaceAfter=3)), Paragraph(f"Probleem: {probleem}", ps(f"pr_{uid}", fontSize=9, fontName="Helvetica", textColor=DONKER, leading=13, spaceAfter=2)), Paragraph(f"Vindplaats: {vindplaats}", ps(f"vp_{uid}", fontSize=8.5, fontName="Helvetica", textColor=LICHT, leading=12, spaceAfter=3)), Paragraph(f"\u25B8 Actie: {actie}", ps(f"ac_{uid}", fontSize=9, fontName="Helvetica-Bold", textColor=tk, leading=13)), ] blok = Table([[labels, inhoud]], colWidths=[3.1*cm, 13.9*cm]) blok.setStyle(TableStyle([ ("VALIGN", (0,0), (-1,-1), "TOP"), ("BACKGROUND", (0,0), (-1,-1), tl), ("TOPPADDING", (0,0), (-1,-1), 10), ("BOTTOMPADDING", (0,0), (-1,-1), 10), ("LEFTPADDING", (0,0), (-1,-1), 10), ("RIGHTPADDING", (0,0), (-1,-1), 10), ("ROUNDEDCORNERS", [5]), ])) return KeepTogether([blok, Spacer(1, 7)]) def nav_tabel(items, kolombreedtes=(3.2*cm, 1.3*cm, 12.5*cm)): """ Geeft een opgemaakte navigatiebeoordelingstabel terug. items = lijst van tuples: (nav_naam, symbool, kleur, toelichting) symbool: "✓" | "△" | "✗" kleur: GROEN | ORANJE | ROOD """ data = [[ Paragraph("Menu-item", ps("nv_h1", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12)), Paragraph("Oordeel", ps("nv_h2", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), Paragraph("Toelichting", ps("nv_h3", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12)), ]] for naam, symbool, kleur, toelichting in items: uid = naam.replace(" ", "_") data.append([ Paragraph(f"{naam}", ps(f"nv_naam_{uid}", fontSize=9, fontName="Helvetica-Bold", textColor=DONKER, leading=12)), Paragraph(symbool, ps(f"nv_sym_{uid}", fontSize=11, fontName="Helvetica-Bold", textColor=kleur, leading=14, alignment=TA_CENTER)), Paragraph(toelichting, ps(f"nv_toe_{uid}", fontSize=8.5, fontName="Helvetica", textColor=DONKER, leading=12)), ]) t = Table(data, colWidths=list(kolombreedtes)) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,0), BLAUW), ("ROWBACKGROUNDS", (0,1), (-1,-1), [WIT, GRIJS]), ("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#dddddd")), ("TOPPADDING", (0,0), (-1,-1), 7), ("BOTTOMPADDING", (0,0), (-1,-1), 7), ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8), ("VALIGN", (0,0), (-1,-1), "TOP"), ])) return t def mapping_tabel(rijen, header_kleur=TEAL): """ Vergelijkingstabel nav-item vs. pagina-naam vs. status. rijen = lijst van tuples: (nav_naam, pagina_naam, status_tekst, status_kleur) """ data = [[ Paragraph("Nav-item", ps("mp_h1", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12)), Paragraph("Naam op overzichtspagina", ps("mp_h2", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12)), Paragraph("Status", ps("mp_h3", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), ]] for nav, pagina, status, kleur in rijen: uid = nav.replace(" ", "_") data.append([ Paragraph(nav, ps(f"mp_n_{uid}", fontSize=9, fontName="Helvetica-Bold", textColor=DONKER, leading=12)), Paragraph(pagina, ps(f"mp_p_{uid}", fontSize=9, fontName="Helvetica", textColor=DONKER, leading=12)), Paragraph(status, ps(f"mp_s_{uid}", fontSize=8.5, fontName="Helvetica-Bold", textColor=kleur, leading=12, alignment=TA_CENTER)), ]) t = Table(data, colWidths=[3.5*cm, 7.5*cm, 6*cm]) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,0), header_kleur), ("ROWBACKGROUNDS", (0,1), (-1,-1), [WIT, GRIJS]), ("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#dddddd")), ("TOPPADDING", (0,0), (-1,-1), 7), ("BOTTOMPADDING", (0,0), (-1,-1), 7), ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8), ("VALIGN", (0,0), (-1,-1), "MIDDLE"), ])) return t def prioriteitsmatrix(acties): """ Slottabel met alle actiepunten, thema, impact en doorlooptijd. acties = lijst van tuples: (nr, omschrijving, thema_label, impact_kleur, doorlooptijd) """ data = [[ Paragraph("#", ps("pm_h0", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), Paragraph("Actie", ps("pm_h1", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12)), Paragraph("Thema", ps("pm_h2", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), Paragraph("Impact", ps("pm_h3", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), Paragraph("Doorlooptijd", ps("pm_h4", fontSize=9, textColor=WIT, fontName="Helvetica-Bold", leading=12, alignment=TA_CENTER)), ]] for nr, omschr, thema_lbl, impact_kleur, tijd in acties: data.append([ Paragraph(str(nr), ps(f"pm_nr_{nr}", fontSize=9, fontName="Helvetica-Bold", textColor=DONKER, leading=12, alignment=TA_CENTER)), Paragraph(omschr, ps(f"pm_om_{nr}", fontSize=9, fontName="Helvetica", textColor=DONKER, leading=13)), Paragraph(thema_lbl, ps(f"pm_th_{nr}", fontSize=8.5, fontName="Helvetica", textColor=LICHT, leading=12, alignment=TA_CENTER)), Paragraph("■", ps(f"pm_ic_{nr}", fontSize=12, fontName="Helvetica-Bold", textColor=impact_kleur, leading=14, alignment=TA_CENTER)), Paragraph(tijd, ps(f"pm_ti_{nr}", fontSize=8.5, fontName="Helvetica", textColor=LICHT, leading=12, alignment=TA_CENTER)), ]) t = Table(data, colWidths=[0.8*cm, 9.2*cm, 2.4*cm, 1.8*cm, 2.8*cm]) t.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,0), BLAUW), ("ROWBACKGROUNDS", (0,1), (-1,-1), [WIT, GRIJS]), ("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#e0e0e0")), ("TOPPADDING", (0,0), (-1,-1), 6), ("BOTTOMPADDING", (0,0), (-1,-1), 6), ("LEFTPADDING", (0,0), (-1,-1), 7), ("RIGHTPADDING", (0,0), (-1,-1), 7), ("VALIGN", (0,0), (-1,-1), "MIDDLE"), ])) return t # ══════════════════════════════════════════════════════════════════════════════ # RAPPORT — pas de inhoud hieronder aan per opdracht # ══════════════════════════════════════════════════════════════════════════════ story = [] # ── Omslag ──────────────────────────────────────────────────────────────────── story.append(omslagrij( "Analyse rapport", dict(fontSize=9.5, fontName="Helvetica", textColor=colors.HexColor("#a8c0e0"), leading=14), BLAUW)) story.append(omslagrij( "Taal • Consistentie • LLM-vindbaarheid", dict(fontSize=24, fontName="Helvetica-Bold", textColor=WIT, leading=30), BLAUW)) story.append(omslagrij( f"{DOMEINNAAM}  |  Doelgroep: {DOELGROEP}  |  Locale: {LOCALE}", dict(fontSize=10, fontName="Helvetica", textColor=colors.HexColor("#9ab8d8"), leading=14), BLAUW)) story.append(Spacer(1, 4)) meta = Table([[ Paragraph(f"Datum: {date.today().strftime('%d %B %Y')}", SMALL), Paragraph(f"Pagina: {DOMEINNAAM}", SMALL), Paragraph("Opgesteld door: Claude (Anthropic)", SMALL), ]], colWidths=[5.5*cm, 6.5*cm, 5*cm]) meta.setStyle(TableStyle([ ("BACKGROUND", (0,0), (-1,-1), GRIJS), ("TOPPADDING", (0,0), (-1,-1), 7), ("BOTTOMPADDING", (0,0), (-1,-1), 7), ("LEFTPADDING", (0,0), (-1,-1), 8), ("RIGHTPADDING", (0,0), (-1,-1), 8), ])) story.append(meta) story.append(Spacer(1, 14)) story.append(Paragraph( f"Dit rapport analyseert {DOMEINNAAM} op taal, consistentie en LLM-vindbaarheid. " f"De primaire doelgroep is {DOELGROEP.lower()}. " "Elk knelpunt heeft een concreet actiepunt met vindplaats en aanbeveling.", BODY)) story.append(Spacer(1, 10)) # ── SECTIE 1 — TAAL ─────────────────────────────────────────────────────────── story.append(Paragraph("1   Taal — zuiverheid en geschiktheid voor de doelgroep", H1)) story.append(HRFlowable(width="100%", thickness=1.5, color=BLAUW, spaceAfter=8)) story.append(Paragraph( "Vul hier een korte inleiding in over de algemene taalstijl van de geanalyseerde pagina.", BODY)) story.append(Spacer(1, 8)) # Voeg hier actiepunten toe met de actiepunt()-functie: story.append(actiepunt( "TAAL", "HOOG", "Titel van het taalprobleem", "Beschrijving van het probleem. Waarom is dit een probleem voor de doelgroep?", "Exacte locatie op de pagina (bijv. 'Hoofdnavigatie — item 3', '<title>-tag', 'H1-heading')", "Concreet advies. Geef bij voorkeur een voorbeeldtekst: 'Vervang X door Y.'")) # ── SECTIE 2 — MENUNAMEN ────────────────────────────────────────────────────── story.append(Paragraph("2   Menunamen — helderheid voor de bezoeker", H1)) story.append(HRFlowable(width="100%", thickness=1.5, color=BLAUW, spaceAfter=8)) story.append(Paragraph( "Beoordeling van elk navigatie-item vanuit het perspectief van de doelgroep.", BODY)) story.append(Spacer(1, 8)) # Voorbeeld nav_tabel() — pas items aan op basis van de geanalyseerde navigatie: story.append(nav_tabel([ ("Menu-item 1", "✓", GROEN, "Helder en herkenbaar."), ("Menu-item 2", "△", ORANJE, "Dubbelzinnig of te vaag. Toelichting."), ("Menu-item 3", "✗", ROOD, "Problematisch. Reden + aanbeveling."), ])) story.append(Spacer(1, 10)) # ── SECTIE 3 — CONSISTENTIE ─────────────────────────────────────────────────── story.append(Paragraph("3   Consistentie — toon, aanspreekvorm en categorienamen", H1)) story.append(HRFlowable(width="100%", thickness=1.5, color=BLAUW, spaceAfter=8)) story.append(Paragraph("Inleiding consistentie.", BODY)) story.append(Spacer(1, 8)) story.append(actiepunt( "CONSISTENTIE", "HOOG", "Consistentieprobleem", "Beschrijving.", "Vindplaats.", "Actie.")) # ── SECTIE 4 — LLM-VINDBAARHEID ─────────────────────────────────────────────── story.append(Paragraph("4   LLM-vindbaarheid", H1)) story.append(HRFlowable(width="100%", thickness=1.5, color=BLAUW, spaceAfter=8)) story.append(Paragraph("Inleiding LLM.", BODY)) story.append(Spacer(1, 8)) story.append(actiepunt( "LLM", "HOOG", "LLM-probleem", "Beschrijving.", "Vindplaats.", "Actie.")) # ── SECTIE 5 — NAVIGATIESTRUCTUUR (optioneel) ───────────────────────────────── # Voeg deze sectie toe als je ook een overzichts-/categoriepagina hebt geanalyseerd. # # story.append(PageBreak()) # story.append(Paragraph("5   Navigatiestructuur — nav vs. overzichtspagina", H1)) # story.append(HRFlowable(width="100%", thickness=1.5, color=TEAL, spaceAfter=8)) # story.append(Paragraph("5.1 Naamvergelijking", H2)) # story.append(mapping_tabel([ # ("Nav-item 1", "Paginanaam 1", "✓ Consistent", GROEN), # ("Nav-item 2", "Andere naam op pagina", "✗ Mismatch", ROOD), # ("Nav-item 3", "Subcategorie onder 'Overig'", "△ Inconsistent", ORANJE), # ])) # ── SECTIE 6 — PRIORITEITSOVERZICHT ────────────────────────────────────────── story.append(PageBreak()) story.append(Paragraph("Prioriteitsoverzicht — wat als eerste aanpakken", H1)) story.append(HRFlowable(width="100%", thickness=1.5, color=BLAUW, spaceAfter=10)) # Vul aan met alle actiepunten uit het rapport: story.append(prioriteitsmatrix([ ("1", "Omschrijving actie 1", "TAAL", ROOD, "1 dag"), ("2", "Omschrijving actie 2", "CONS", ROOD, "1 dag"), ("3", "Omschrijving actie 3", "LLM", ROOD, "1–2 wkn"), ("4", "Omschrijving actie 4", "NAV", ORANJE, "1 week"), ("5", "Omschrijving actie 5", "LLM", GROEN, "Doorlopend"), ])) story.append(Spacer(1, 8)) # Legenda legenda_impact = Table([[ Paragraph("■ Hoog — direct zichtbaar, groot effect", ps("leg1", fontSize=8.5, fontName="Helvetica", textColor=ROOD, leading=12)), Paragraph("■ Middel — merkbaar effect, planbaar", ps("leg2", fontSize=8.5, fontName="Helvetica", textColor=ORANJE, leading=12)), Paragraph("■ Laag — finetuning / continu", ps("leg3", fontSize=8.5, fontName="Helvetica", textColor=GROEN, leading=12)), ]], colWidths=[6*cm, 6*cm, 5*cm]) legenda_impact.setStyle(TableStyle([ ("TOPPADDING", (0,0), (-1,-1), 0), ("BOTTOMPADDING", (0,0), (-1,-1), 0), ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0), ])) story.append(legenda_impact) story.append(Spacer(1, 10)) legenda_thema = Table([[ Paragraph("T = Taal", ps("lt1", fontSize=8.5, fontName="Helvetica", textColor=BLAUW, leading=12)), Paragraph("C = Consistentie", ps("lt2", fontSize=8.5, fontName="Helvetica", textColor=GROEN, leading=12)), Paragraph("L = LLM", ps("lt3", fontSize=8.5, fontName="Helvetica", textColor=PAARS, leading=12)), Paragraph("N = Navigatie", ps("lt4", fontSize=8.5, fontName="Helvetica", textColor=TEAL, leading=12)), ]], colWidths=[4*cm, 4*cm, 4*cm, 5*cm]) legenda_thema.setStyle(TableStyle([ ("TOPPADDING", (0,0), (-1,-1), 0), ("BOTTOMPADDING", (0,0), (-1,-1), 0), ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0), ])) story.append(legenda_thema) story.append(Spacer(1, 20)) # ── Footer ──────────────────────────────────────────────────────────────────── story.append(HRFlowable(width="100%", thickness=0.8, color=GRIJS2, spaceAfter=8)) footer_t = Table([[ Paragraph(f"{MERKNAAM} — Taal, Consistentie & LLM-vindbaarheid — Vertrouwelijk", SMALL), Paragraph(f"Opgesteld {date.today().strftime('%d-%m-%Y')} | Claude (Anthropic)", ps("footer_r", fontSize=8, textColor=LICHT, fontName="Helvetica", leading=11, alignment=TA_RIGHT)), ]], colWidths=[9*cm, 8*cm]) footer_t.setStyle(TableStyle([ ("TOPPADDING", (0,0), (-1,-1), 0), ("BOTTOMPADDING", (0,0), (-1,-1), 0), ("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0), ("VALIGN", (0,0), (-1,-1), "MIDDLE"), ])) story.append(footer_t) # ── Bouw PDF ────────────────────────────────────────────────────────────────── doc.build(story) print(f"PDF klaar: {OUTPUT}")