File: //workspace/montessori_redesign.py
#!/usr/bin/env python3
"""Montessori-Redesign für FOS Deutsch Arbeitsblätter"""
from docx import Document
from docx.shared import Pt, RGBColor, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import os
import re
COLORS = {
'dunkel_grau': RGBColor(70, 70, 70),
'mittel_grau': RGBColor(100, 100, 100),
}
def set_cell_shading(cell, color_hex):
shading_elm = OxmlElement('w:shd')
shading_elm.set(qn('w:fill'), color_hex)
cell._tc.get_or_add_tcPr().append(shading_elm)
def set_cell_border(cell, top=None, bottom=None, left=None, right=None):
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
tcBorders = OxmlElement('w:tcBorders')
for edge, data in [('top', top), ('bottom', bottom), ('left', left), ('right', right)]:
if data:
tag = OxmlElement(f'w:{edge}')
tag.set(qn('w:val'), data.get('val', 'single'))
tag.set(qn('w:sz'), str(data.get('sz', 4)))
tag.set(qn('w:color'), data.get('color', '808080'))
tcBorders.append(tag)
tcPr.append(tcBorders)
def create_header(doc, title, themenbereich=None, kompetenz=None):
"""Erstellt eleganten Header-Bereich"""
table = doc.add_table(rows=1, cols=1)
table.alignment = WD_TABLE_ALIGNMENT.CENTER
cell = table.cell(0, 0)
set_cell_shading(cell, 'FAF7F0')
# Titel
p = cell.paragraphs[0]
run = p.add_run(title)
run.bold = True
run.font.size = Pt(16)
run.font.color.rgb = COLORS['dunkel_grau']
p.paragraph_format.space_after = Pt(8)
# Trennlinie
p_line = cell.add_paragraph()
pPr = p_line._p.get_or_add_pPr()
pBdr = OxmlElement('w:pBdr')
bottom = OxmlElement('w:bottom')
bottom.set(qn('w:val'), 'single')
bottom.set(qn('w:sz'), '6')
bottom.set(qn('w:color'), 'C4A491')
pBdr.append(bottom)
pPr.append(pBdr)
# Info
if themenbereich or kompetenz:
p_info = cell.add_paragraph()
p_info.paragraph_format.space_before = Pt(6)
if themenbereich:
run = p_info.add_run(f"Themenbereich: {themenbereich}\n")
run.italic = True
run.font.size = Pt(9)
run.font.color.rgb = COLORS['mittel_grau']
if kompetenz:
run = p_info.add_run(f"Kompetenz: {kompetenz}")
run.italic = True
run.font.size = Pt(9)
run.font.color.rgb = COLORS['mittel_grau']
# Rahmen
set_cell_border(cell,
top={'sz': 16, 'color': 'C4A491'},
bottom={'sz': 8, 'color': 'C3D2C3'},
left={'sz': 4, 'color': 'A0A0A0'},
right={'sz': 4, 'color': 'A0A0A0'})
doc.add_paragraph()
return table
def create_box(doc, title, content_lines, box_type='aufgabe'):
"""Erstellt gestaltete Box"""
table = doc.add_table(rows=1, cols=1)
table.alignment = WD_TABLE_ALIGNMENT.CENTER
cell = table.cell(0, 0)
# Farbe je nach Typ
color_map = {
'aufgabe': 'F2EBDC', # Sand
'material': 'C3D2C3', # Salbei
'reflexion': 'B0BCB8', # Blau
}
set_cell_shading(cell, color_map.get(box_type, 'F2EBDC'))
if title:
p = cell.paragraphs[0]
run = p.add_run(title)
run.bold = True
run.font.size = Pt(11)
run.font.color.rgb = COLORS['dunkel_grau']
p.paragraph_format.space_after = Pt(8)
for line in content_lines:
p = cell.add_paragraph()
p.paragraph_format.space_before = Pt(2)
p.paragraph_format.space_after = Pt(2)
run = p.add_run(line)
run.font.size = Pt(10)
run.font.color.rgb = COLORS['dunkel_grau']
# Rahmen
set_cell_border(cell,
top={'sz': 12, 'color': 'A0A0A0'},
bottom={'sz': 12, 'color': 'A0A0A0'},
left={'sz': 12, 'color': 'A0A0A0'},
right={'sz': 12, 'color': 'A0A0A0'})
doc.add_paragraph()
return table
def create_image_placeholder(doc, caption):
"""Erstellt Bild-Platzhalter"""
table = doc.add_table(rows=2, cols=1)
table.alignment = WD_TABLE_ALIGNMENT.CENTER
# Bildbereich
img_cell = table.cell(0, 0)
set_cell_shading(img_cell, 'F2EBDC')
p = img_cell.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(f"[Bild: {caption}]")
run.font.size = Pt(11)
run.font.color.rgb = COLORS['mittel_grau']
p.paragraph_format.space_before = Pt(20)
p.paragraph_format.space_after = Pt(20)
# Beschriftung
cap_cell = table.cell(1, 0)
set_cell_shading(cap_cell, 'FAF7F0')
p = cap_cell.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run(caption)
run.italic = True
run.font.size = Pt(9)
run.font.color.rgb = COLORS['mittel_grau']
for cell in [img_cell, cap_cell]:
set_cell_border(cell,
top={'sz': 8, 'color': 'C3D2C3'},
bottom={'sz': 8, 'color': 'C3D2C3'},
left={'sz': 8, 'color': 'C3D2C3'},
right={'sz': 8, 'color': 'C3D2C3'})
doc.add_paragraph()
return table
def redesign_ab(input_path, output_path, illustration=None):
"""Redesign eines Arbeitsblatts"""
print(f"Verarbeite: {os.path.basename(input_path)}")
doc_orig = Document(input_path)
doc = Document()
# Seite einrichten
for section in doc.sections:
section.page_width = Cm(21)
section.page_height = Cm(29.7)
section.left_margin = Cm(2.5)
section.right_margin = Cm(2.5)
section.top_margin = Cm(2)
section.bottom_margin = Cm(2)
# Inhalt extrahieren
title = None
themenbereich = None
kompetenz = None
aufgaben = []
current_aufgabe = None
lehrerhinweise = []
for para in doc_orig.paragraphs:
text = para.text.strip()
if not text:
continue
style = para.style.name if para.style else 'Normal'
if style == 'Heading 1' and not title:
title = text
continue
if text.startswith('Themenbereich:'):
themenbereich = text.replace('Themenbereich:', '').strip()
continue
if text.startswith('Kompetenz:'):
kompetenz = text.replace('Kompetenz:', '').strip()
continue
if 'Illustration' in text or 'Abbildung:' in text:
if not illustration:
# Extrahiere Illustrationsnamen
match = re.search(r'(VK|JG\d{2})[_\s](.+?)\s*(?:Illustration|$)', text, re.I)
if match:
illustration = match.group(0).replace('Abbildung: ', '').strip()
continue
if text.startswith('===') or text.startswith('---'):
continue
if text == 'Lehrerhinweise:':
current_aufgabe = 'lehrer'
continue
if current_aufgabe == 'lehrer':
lehrerhinweise.append(text)
continue
if style == 'Heading 2':
if current_aufgabe and current_aufgabe != 'lehrer':
aufgaben.append(current_aufgabe)
current_aufgabe = {'titel': text, 'inhalt': []}
continue
if current_aufgabe and isinstance(current_aufgabe, dict):
if not text.startswith('___'):
current_aufgabe['inhalt'].append(text)
if current_aufgabe and isinstance(current_aufgabe, dict):
aufgaben.append(current_aufgabe)
# NEUES DOKUMENT AUFBAUEN
if title:
create_header(doc, title, themenbereich, kompetenz)
# Bild OBEN
if illustration:
create_image_placeholder(doc, illustration)
# Aufgaben
for i, aufgabe in enumerate(aufgaben):
if isinstance(aufgabe, dict):
box_type = 'material' if i == 0 else 'aufgabe'
create_box(doc, aufgabe['titel'], aufgabe['inhalt'], box_type)
# Reflexion
p = doc.add_paragraph()
run = p.add_run("Reflexion")
run.bold = True
run.font.size = Pt(12)
run.font.color.rgb = COLORS['dunkel_grau']
create_box(doc, None, [
"Was hast du bei dieser Einheit gelernt?",
"Welche Fragen sind noch offen geblieben?"
], 'reflexion')
# Lehrerhinweise
if lehrerhinweise:
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(12)
p = doc.add_paragraph()
run = p.add_run("Hinweise für Lehrkräfte")
run.bold = True
run.font.size = Pt(9)
run.font.color.rgb = COLORS['mittel_grau']
for hinweis in lehrerhinweise[:3]:
p = doc.add_paragraph()
run = p.add_run(f"• {hinweis[:150]}")
run.font.size = Pt(8)
run.font.color.rgb = COLORS['mittel_grau']
doc.save(output_path)
print(f" ✓ Gespeichert: {os.path.basename(output_path)}")
return True
def find_illustration(filename, prefix):
"""Findet passende Illustration"""
mapping = {
'VK': {
'KI-gestützt': 'VK_KI-Schreiben_Illustration',
'Digitale': 'VK_Digitale-Texte_Illustration',
'Feedback': 'VK_Funktion1_Einstieg',
},
'JG11': {
'Social': 'JG11_Social-Media_Illustration',
'KI-gestütztes': 'JG11_Funktion1_Einstieg',
'Podcast': 'JG11_Podcast-Medien_Illustration',
},
'JG12': {
'Desinformation': 'JG12_Medienmanipulation_Analyse',
'Framing': 'JG12_Framing_Perspektiven',
'KI_gestütztes': 'JG12_Funktion1_Einstieg',
},
'JG13': {
'Data': 'JG13_Data_Bias_Algorithmen',
'Wissenschaft': 'JG13_Funktion1_Einstieg',
'Mensch': 'JG13_Mensch_vs_KI_Kreativitaet',
}
}
for keyword, illus in mapping.get(prefix, {}).items():
if keyword.lower() in filename.lower():
return illus
return None
def main():
base = "/mnt/onedrive_write/FOS_Deutsch_Lehrplanvergleich_2026"
jahrgaenge = [
('02_Vorklasse/Arbeitsblaetter', 'VK'),
('03_Jahrgang_11/Arbeitsblätter', 'JG11'),
('04_Jahrgang_12/Arbeitsblätter', 'JG12'),
('05_Jahrgang_13/Arbeitsblätter', 'JG13'),
]
print("=" * 60)
print("REDESIGN: FOS Deutsch → Montessori-Stil")
print("=" * 60)
done = []
errors = []
for ordner, prefix in jahrgaenge:
path = os.path.join(base, ordner)
if not os.path.exists(path):
continue
for filename in os.listdir(path):
if not filename.endswith('.docx'):
continue
if '_Montessori' in filename or '_Redesign' in filename:
continue
input_file = os.path.join(path, filename)
# Prüfen ob lesbar
try:
with open(input_file, 'rb') as f:
f.read(100)
except:
print(f" ⚠ Nicht lesbar: {filename}")
errors.append(filename)
continue
base_name = filename.replace('.docx', '')
output_file = os.path.join(path, f"{base_name}_Montessori.docx")
illus = find_illustration(filename, prefix)
try:
redesign_ab(input_file, output_file, illus)
done.append({'original': filename, 'neu': f"{base_name}_Montessori.docx", 'illustration': illus})
except Exception as e:
print(f" ✗ Fehler bei {filename}: {e}")
errors.append(filename)
print("\n" + "=" * 60)
print(f"Redesigniert: {len(done)} Arbeitsblätter")
print(f"Fehler: {len(errors)}")
return done, errors
if __name__ == '__main__':
done, errors = main()