from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as RLImage, Table, TableStyle, PageBreak
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
import csv, json, hashlib, shutil, math, textwrap

ROOT = Path('/home/agent/family/Schule/Zeugnisse')
OUT = ROOT / 'Archiv_2022-2025_Emilia_Valerie'
DOCS = OUT / 'Dokumente'
CHARTS = OUT / 'Auswertung'
DOCS.mkdir(parents=True, exist_ok=True)
CHARTS.mkdir(parents=True, exist_ok=True)
CACHE = Path('/home/agent/.hermes/cache/images')

A4PX = (1240, 1754)  # A4 portrait at ~150dpi

# Manual extraction from the supplied certificate photos. Early qualitative assessments are mapped for charting:
# sehr gut=6, gut=5, genügend=4, ungenügend=3. Numeric Zeugnisnoten remain unchanged.
subjects = ['Deutsch','Mathematik','Englisch','Natur, Mensch, Gesellschaft','Bewegung und Sport','Bildnerisches Gestalten','Musik','Textiles und Technisches Gestalten']
short = {
    'Deutsch':'Deutsch', 'Mathematik':'Mathe', 'Englisch':'Englisch',
    'Natur, Mensch, Gesellschaft':'NMG', 'Bewegung und Sport':'Sport',
    'Bildnerisches Gestalten':'BG', 'Musik':'Musik', 'Textiles und Technisches Gestalten':'TTG'
}
qmap = {'sehr gut':6.0, 'gut':5.0, 'genügend':4.0, 'ungenügend':3.0}

def grades(**kw):
    return {s: kw.get(s) for s in subjects}

# Images grouped into real documents. One Valerie 2023/24 Zwischenbericht page 1 was sent twice; both are archived,
# but the PDF uses one page 1 and one page 2.
documents = [
    # Valerie older docs
    dict(kind='Valerie', type='Zwischenbericht', school_year='2023/2024', class_level='1', date='2024-02-01', teacher='Ida Kaysel',
         pages=['img_74e9f09a7ffa.jpg','img_3de1ab7cfdb9.jpg'], extra_pages=['img_c2a7d3c9f4ff.jpg'],
         notes=grades(Deutsch=5.0, Mathematik=5.0, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.0,'Bildnerisches Gestalten':6.0,'Musik':6.0,'Textiles und Technisches Gestalten':6.0}),
         source='qualitativ: sehr gut/gut/genügend/ungenügend'),
    dict(kind='Valerie', type='Lernbericht/Jahreszeugnis', school_year='2023/2024', class_level='1', date='2024-07-04', teacher='Ida Kaysel',
         pages=['img_3cf3e9437f4e.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=5.0, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.0,'Bildnerisches Gestalten':6.0,'Musik':6.0,'Textiles und Technisches Gestalten':6.0}),
         source='qualitativ: sehr gut/gut/genügend/ungenügend'),
    dict(kind='Valerie', type='Zwischenbericht', school_year='2024/2025', class_level='2', date='2025-01-30', teacher='Ida Kaysel',
         pages=['img_1b34dc26815d.jpg','img_f35eb54e5ab6.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=5.0, **{'Natur, Mensch, Gesellschaft':5.5,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':5.5,'Musik':5.5,'Textiles und Technisches Gestalten':5.5}), source='Note'),
    dict(kind='Valerie', type='Jahreszeugnis', school_year='2024/2025', class_level='2', date='2025-07-03', teacher='Ida Kaysel',
         pages=['img_61525cca9a9e.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=5.0, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':6.0,'Musik':5.0,'Textiles und Technisches Gestalten':5.5}), source='Note'),
    # Emilia older docs
    dict(kind='Emilia', type='Zwischenbericht', school_year='2022/2023', class_level='1', date='2023-02-03', teacher='Anna Zaugg',
         pages=['img_b2c42eac9543.jpg','img_dbfa6eb2475a.jpg'], extra_pages=[],
         notes=grades(Deutsch=6.0, Mathematik=5.0, **{'Natur, Mensch, Gesellschaft':6.0,'Bewegung und Sport':6.0,'Bildnerisches Gestalten':5.0,'Musik':6.0,'Textiles und Technisches Gestalten':5.0}),
         source='qualitativ: sehr gut/gut/genügend/ungenügend'),
    dict(kind='Emilia', type='Lernbericht/Jahreszeugnis', school_year='2022/2023', class_level='1', date='2023-07-06', teacher='Anna Zaugg',
         pages=['img_e951440e7de8.jpg'], extra_pages=[],
         notes=grades(Deutsch=6.0, Mathematik=4.0, **{'Natur, Mensch, Gesellschaft':6.0,'Bewegung und Sport':6.0,'Bildnerisches Gestalten':5.0,'Musik':6.0,'Textiles und Technisches Gestalten':6.0}),
         source='qualitativ: sehr gut/gut/genügend/ungenügend'),
    dict(kind='Emilia', type='Zwischenbericht', school_year='2023/2024', class_level='2', date='2024-02-01', teacher='Anna Zaugg',
         pages=['img_95c4fa8ed7ca.jpg','img_135f505862d8.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.5, Mathematik=4.5, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':5.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.0}), source='Note'),
    dict(kind='Emilia', type='Jahreszeugnis', school_year='2023/2024', class_level='2', date='2024-07-04', teacher='Anna Zaugg',
         pages=['img_09fb52ab72b3.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=4.5, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':5.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.5}), source='Note'),
    dict(kind='Emilia', type='Zwischenbericht', school_year='2024/2025', class_level='3', date='2025-01-30', teacher='Anna Weiss',
         pages=['img_9715f0a408ad.jpg','img_298884130ff9.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=4.0, Englisch=6.0, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.0,'Bildnerisches Gestalten':5.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.0}), source='Note'),
    dict(kind='Emilia', type='Jahreszeugnis', school_year='2024/2025', class_level='3', date='2025-07-03', teacher='Anna Weiss',
         pages=['img_19afc8655521.jpg'], extra_pages=[],
         notes=grades(Deutsch=5.0, Mathematik=4.0, Englisch=5.5, **{'Natur, Mensch, Gesellschaft':5.5,'Bewegung und Sport':4.5,'Bildnerisches Gestalten':5.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.0}), source='Note'),
]

# Existing already-archived current data, included for full trend charts.
current = [
    dict(kind='Valerie', type='Zwischenbericht', school_year='2025/2026', class_level='3', date='2026-01-29', teacher='Ida Kaysel',
         notes=grades(Deutsch=5.0, Mathematik=4.5, Englisch=5.5, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':5.5,'Musik':5.5,'Textiles und Technisches Gestalten':5.0}), source='Note', existing_path='2025-2026/Zwischenberichte_2026-01-29/Zwischenbericht_Valerie_Gasser_2025-2026_2026-01-29.pdf'),
    dict(kind='Valerie', type='Jahreszeugnis', school_year='2025/2026', class_level='3', date='2026-07-02', teacher='Ida Kaysel',
         notes=grades(Deutsch=5.0, Mathematik=4.0, Englisch=5.5, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':5.5,'Bildnerisches Gestalten':5.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.0}), source='Note', existing_path='2025-2026/Zeugnis_Valerie_Gasser_2025-2026.pdf'),
    dict(kind='Emilia', type='Zwischenbericht', school_year='2025/2026', class_level='4', date='2026-01-29', teacher='Jone Johnsan Arulthas',
         notes=grades(Deutsch=5.5, Mathematik=5.0, Englisch=5.0, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':4.5,'Bildnerisches Gestalten':5.5,'Musik':5.5,'Textiles und Technisches Gestalten':5.5}), source='Note', existing_path='2025-2026/Zwischenberichte_2026-01-29/Zwischenbericht_Emilia_Gasser_2025-2026_2026-01-29.pdf'),
    dict(kind='Emilia', type='Jahreszeugnis', school_year='2025/2026', class_level='4', date='2026-07-02', teacher='Jone Johnsan Arulthas',
         notes=grades(Deutsch=5.5, Mathematik=5.0, Englisch=5.5, **{'Natur, Mensch, Gesellschaft':5.0,'Bewegung und Sport':4.5,'Bildnerisches Gestalten':6.0,'Musik':5.5,'Textiles und Technisches Gestalten':5.5}), source='Note', existing_path='2025-2026/Zeugnis_Emilia_Gasser_2025-2026.pdf'),
]

def a4_pdf_from_images(image_paths, pdf_path):
    pages=[]
    for p in image_paths:
        im = Image.open(p).convert('RGB')
        im.thumbnail((A4PX[0]-90, A4PX[1]-90), Image.LANCZOS)
        canvas = Image.new('RGB', A4PX, 'white')
        canvas.paste(im, ((A4PX[0]-im.width)//2, (A4PX[1]-im.height)//2))
        pages.append(canvas)
    pages[0].save(pdf_path, 'PDF', save_all=True, append_images=pages[1:], resolution=150.0)

archive_rows=[]
trend=[]
for d in documents:
    folder = DOCS / d['kind'] / d['school_year'].replace('/','-') / (d['date'] + '_' + d['type'].replace('/','-').replace(' ','_'))
    folder.mkdir(parents=True, exist_ok=True)
    copied=[]
    for i, name in enumerate(d['pages'],1):
        src=CACHE/name
        dst=folder / f"{d['kind']}_{d['type'].replace('/','-')}_{d['school_year'].replace('/','-')}_{d['date']}_Seite_{i}.jpg"
        shutil.copy2(src,dst)
        copied.append(dst)
    for j, name in enumerate(d.get('extra_pages',[]),1):
        src=CACHE/name
        dst=folder / f"{d['kind']}_{d['type'].replace('/','-')}_{d['school_year'].replace('/','-')}_{d['date']}_Zusatzscan_{j}.jpg"
        shutil.copy2(src,dst)
    pdf=folder / f"{d['kind']}_{d['type'].replace('/','-')}_{d['school_year'].replace('/','-')}_{d['date']}.pdf"
    a4_pdf_from_images(copied, pdf)
    d['pdf_path']=str(pdf.relative_to(ROOT))
    d['folder']=str(folder.relative_to(ROOT))
    d['sha256_pdf']=hashlib.sha256(pdf.read_bytes()).hexdigest()
    archive_rows.append({k:d.get(k,'') for k in ['kind','type','school_year','class_level','date','teacher','source','pdf_path','folder','sha256_pdf']})
    for s,v in d['notes'].items():
        if v is not None:
            trend.append({'kind':d['kind'],'date':d['date'],'school_year':d['school_year'],'type':d['type'],'class_level':d['class_level'],'subject':s,'value':v,'source':d['source']})

for d in current:
    for s,v in d['notes'].items():
        if v is not None:
            trend.append({'kind':d['kind'],'date':d['date'],'school_year':d['school_year'],'type':d['type'],'class_level':d['class_level'],'subject':s,'value':v,'source':d['source']})

# Write machine-readable datasets
with (OUT/'document_index.csv').open('w', newline='', encoding='utf-8') as f:
    w=csv.DictWriter(f, fieldnames=['kind','type','school_year','class_level','date','teacher','source','pdf_path','folder','sha256_pdf'])
    w.writeheader(); w.writerows(archive_rows)
with (OUT/'leistungsentwicklung_daten.csv').open('w', newline='', encoding='utf-8') as f:
    w=csv.DictWriter(f, fieldnames=['kind','date','school_year','type','class_level','subject','value','source'])
    w.writeheader(); w.writerows(sorted(trend, key=lambda r:(r['kind'],r['date'],r['subject'])))
(OUT/'leistungsentwicklung_daten.json').write_text(json.dumps({'mapping_qualitativ':qmap,'records':trend}, ensure_ascii=False, indent=2), encoding='utf-8')

# Chart helpers
font_reg = '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'
font_bold = '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf'
F = ImageFont.truetype(font_reg, 25)
Fsmall = ImageFont.truetype(font_reg, 19)
Ftiny = ImageFont.truetype(font_reg, 16)
FB = ImageFont.truetype(font_bold, 34)
FH = ImageFont.truetype(font_bold, 25)
colors_subject = {
    'Deutsch': '#2563EB', 'Mathematik': '#DC2626', 'Englisch': '#7C3AED',
    'Natur, Mensch, Gesellschaft': '#059669', 'Bewegung und Sport': '#EA580C',
    'Bildnerisches Gestalten': '#DB2777', 'Musik': '#0D9488', 'Textiles und Technisches Gestalten': '#6B7280'
}

def draw_round_rect(draw, xy, radius, fill, outline=None, width=1):
    draw.rounded_rectangle(xy, radius=radius, fill=fill, outline=outline, width=width)

def chart_panel(draw, data, xlabels, subj_list, box, title):
    x0,y0,x1,y1=box
    draw_round_rect(draw, box, 20, '#FFFFFF', '#D5DFEA', 2)
    draw.text((x0+24,y0+14), title, font=FH, fill='#0F172A')
    plot=(x0+72,y0+72,x1-34,y1-74)
    px0,py0,px1,py1=plot
    # y grid from 3.5 to 6.0
    ymin,ymax=3.5,6.0
    for val in [4,4.5,5,5.5,6]:
        yy=py1-(val-ymin)/(ymax-ymin)*(py1-py0)
        draw.line((px0,yy,px1,yy), fill='#E5E7EB', width=1)
        draw.text((x0+18,yy-10), str(val).replace('.',','), font=Ftiny, fill='#64748B')
    # x grid labels
    n=len(xlabels)
    xs=[px0 + i*(px1-px0)/(n-1) for i in range(n)] if n>1 else [(px0+px1)/2]
    for x,lbl in zip(xs,xlabels):
        draw.line((x,py0,x,py1), fill='#F1F5F9', width=1)
        draw.text((x-36, py1+12), lbl, font=Ftiny, fill='#475569')
    draw.line((px0,py1,px1,py1), fill='#94A3B8', width=2)
    draw.line((px0,py0,px0,py1), fill='#94A3B8', width=2)
    # Light deterministic offsets keep equal/similar curves visible instead of hiding them.
    # The numeric grade is still represented by the horizontal grid; the offset is visual only.
    offsets = [-10, -3, 4, 11]
    x_offsets = [-5, -2, 2, 5]
    for si, s in enumerate(subj_list):
        pts=[]
        yoff = offsets[si % len(offsets)]
        xoff = x_offsets[si % len(x_offsets)]
        for i, lbl in enumerate(xlabels):
            v=data.get(lbl,{}).get(s)
            if v is None: continue
            yy=py1-(v-ymin)/(ymax-ymin)*(py1-py0) + yoff
            pts.append((xs[i]+xoff,yy,v))
        if len(pts)>=2:
            # white underlay improves separation when several coloured lines run close together
            draw.line([(p[0],p[1]) for p in pts], fill='white', width=8)
            draw.line([(p[0],p[1]) for p in pts], fill=colors_subject[s], width=4)
        for x,y,v in pts:
            draw.ellipse((x-7,y-7,x+7,y+7), fill='white', outline=colors_subject[s], width=3)
            draw.ellipse((x-4,y-4,x+4,y+4), fill=colors_subject[s], outline=colors_subject[s], width=1)
    draw.text((x1-650, y0+20), 'Hinweis: Linien sind bei gleichen Noten minimal versetzt, damit alle Farben sichtbar bleiben.', font=Ftiny, fill='#64748B')
    # legend
    lx=x0+26; ly=y1-48
    for s in subj_list:
        draw.line((lx,ly+8,lx+30,ly+8), fill=colors_subject[s], width=5)
        draw.text((lx+38, ly), short[s], font=Ftiny, fill='#334155')
        lx += 125
        if lx > x1-140:
            lx=x0+26; ly+=22

def make_dashboard(kind):
    recs=[r for r in trend if r['kind']==kind]
    # unique dates in order
    dates=sorted({r['date'] for r in recs})
    label_by_date={}
    for d in dates:
        rr=[r for r in recs if r['date']==d][0]
        suffix='ZB' if 'Zwischen' in rr['type'] else 'JZ'
        label_by_date[d]=rr['school_year'].split('/')[0][-2:]+'/'+rr['school_year'].split('/')[1][-2:]+' '+suffix
    xlabels=[label_by_date[d] for d in dates]
    data={label_by_date[d]:{} for d in dates}
    source_by_label={label_by_date[d]:None for d in dates}
    for r in recs:
        data[label_by_date[r['date']]][r['subject']]=r['value']
        source_by_label[label_by_date[r['date']]]=r['source']

    W,H=2400,1650
    im=Image.new('RGB',(W,H),'#F6F8FB')
    draw=ImageDraw.Draw(im)
    # header
    draw_round_rect(draw,(40,35,W-40,155),24,'#0F172A')
    draw.text((80,60),f'Leistungsentwicklung {kind}',font=FB,fill='white')
    draw.text((80,105),'Skala: Noten 1–6; qualitative Lernberichte wurden für die Kurve umgerechnet: sehr gut=6, gut=5, genügend=4.',font=Fsmall,fill='#CBD5E1')
    # highlights
    bys={s:[] for s in subjects}
    for lbl in xlabels:
        for s,v in data[lbl].items(): bys[s].append((lbl,v))
    if kind=='Valerie':
        insight='Kernaussage: Insgesamt stabile gute Leistungen; Mathematik fällt zuletzt von 5/4.5 auf 4 und ist der klare Übungsfokus.'
    else:
        insight='Kernaussage: Deutsch/NMG/Musik bleiben stark; Mathematik erholt sich 2025/26 deutlich von 4 auf 5.'
    draw_round_rect(draw,(40,180,W-40,250),18,'#E0F2FE','#BAE6FD',2)
    draw.text((70,200),insight,font=FH,fill='#075985')

    chart_panel(draw, data, xlabels, ['Deutsch','Mathematik','Englisch','Natur, Mensch, Gesellschaft'], (40,280,W-40,820), 'Kernfächer')
    chart_panel(draw, data, xlabels, ['Bewegung und Sport','Bildnerisches Gestalten','Musik','Textiles und Technisches Gestalten'], (40,850,W-40,1390), 'Weitere Fächer')
    # table strip
    y=1420
    draw.text((60,y), 'Kurz-Tabelle der Zeugnis-/Berichtwerte', font=FH, fill='#0F172A')
    y+=42
    cols=['Zeitpunkt']+[short[s] for s in subjects]
    colw=[250,190,190,190,190,190,190,190,190]
    x=60
    for c,w in zip(cols,colw):
        draw.rectangle((x,y,x+w,y+38), fill='#E2E8F0', outline='#CBD5E1')
        draw.text((x+8,y+9),c,font=Ftiny,fill='#0F172A')
        x+=w
    y+=38
    for lbl in xlabels[-6:]:
        x=60
        row=[lbl]+[data[lbl].get(s) for s in subjects]
        for c,w in zip(row,colw):
            draw.rectangle((x,y,x+w,y+34), fill='white', outline='#E2E8F0')
            txt='' if c is None else str(c).replace('.',',')
            draw.text((x+8,y+8),txt,font=Ftiny,fill='#334155')
            x+=w
        y+=34
    out=CHARTS/f'Leistungsentwicklung_{kind}.png'
    im.save(out, quality=95)
    return out

pngs=[make_dashboard('Emilia'), make_dashboard('Valerie')]

# PDF report
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='TitleDE', parent=styles['Title'], fontName='Helvetica-Bold', fontSize=22, leading=26, spaceAfter=12))
styles.add(ParagraphStyle(name='BodyDE', parent=styles['BodyText'], fontSize=10.5, leading=14, spaceAfter=6))
styles.add(ParagraphStyle(name='HDE', parent=styles['Heading2'], fontSize=14, leading=18, spaceBefore=8, spaceAfter=6))
report=CHARTS/'Leistungsentwicklung_Emilia_Valerie_Auswertung.pdf'
doc=SimpleDocTemplate(str(report), pagesize=landscape(A4), rightMargin=1.0*cm, leftMargin=1.0*cm, topMargin=1.0*cm, bottomMargin=1.0*cm)
story=[Paragraph('Leistungsentwicklung Emilia & Valerie', styles['TitleDE']),
       Paragraph('Grafische Auswertung der abgelegten Zwischenberichte, Lernberichte und Jahreszeugnisse. Frühe qualitative Bewertungen wurden für die Darstellung umgerechnet: sehr gut=6, gut=5, genügend=4, ungenügend=3. Diese Punkte sind als Näherung zu lesen; spätere Werte sind echte Noten.', styles['BodyDE'])]
for p in pngs:
    story.append(PageBreak())
    story.append(RLImage(str(p), width=27*cm, height=18.56*cm))
story.append(PageBreak())
story.append(Paragraph('Dokumentierte neue Archivdokumente', styles['HDE']))
rows=[['Kind','Datum','Dokument','Schuljahr','Klasse','PDF']]
for r in archive_rows:
    rows.append([r['kind'],r['date'],r['type'],r['school_year'],r['class_level'],r['pdf_path']])
t=Table(rows, colWidths=[2.0*cm,2.4*cm,4.2*cm,2.5*cm,1.4*cm,14.5*cm])
t.setStyle(TableStyle([('FONT',(0,0),(-1,-1),'Helvetica',7),('BACKGROUND',(0,0),(-1,0),colors.HexColor('#E2E8F0')),('GRID',(0,0),(-1,-1),0.25,colors.HexColor('#CBD5E1')),('VALIGN',(0,0),(-1,-1),'TOP')]))
story.append(t)
doc.build(story)

# README
readme = OUT/'README.md'
readme.write_text('''# Archiv Zeugnisse/Zwischenberichte 2022–2025 – Emilia & Valerie

Ablage der älteren Zeugnisse und Zwischenberichte aus den Fotos.

## Struktur
- `Dokumente/`: je Kind/Schuljahr/Dokument mit Original-JPGs und A4-PDF.
- `Auswertung/`: grafische Leistungsentwicklung als PNG und PDF.
- `document_index.csv`: maschinenlesbarer Index der neu abgelegten Dokumente.
- `leistungsentwicklung_daten.csv/json`: Datengrundlage für die Diagramme inkl. bestehender 2025/2026-Daten.

## Wichtige Hinweise zur Auswertung
- Frühe Lernberichte der 1. Klasse verwenden teils qualitative Kategorien statt Noten.
- Für die Diagramme wurde gemappt: **sehr gut=6**, **gut=5**, **genügend=4**, **ungenügend=3**.
- Diese umgerechneten Werte sind für Trend-Lesbarkeit gedacht, nicht als offizielle Noten.

## Grafiken
- `Auswertung/Leistungsentwicklung_Emilia.png`
- `Auswertung/Leistungsentwicklung_Valerie.png`
- `Auswertung/Leistungsentwicklung_Emilia_Valerie_Auswertung.pdf`

## Kurzfazit
- **Valerie:** starke, stabile Nebenfächer; Mathematik sinkt zuletzt auf 4.0 und bleibt der klare Fokus.
- **Emilia:** Mathematik war 2022/23–2024/25 schwächer, erholt sich 2025/26 auf 5.0; Deutsch/NMG/Musik insgesamt stark.
''', encoding='utf-8')

# Update root index if exists separately later by caller; print results
print('OUT', OUT)
print('new_documents', len(archive_rows))
print('trend_rows', len(trend))
print('report', report, report.stat().st_size)
for p in pngs: print('png', p, p.stat().st_size)
