"""Migration 2026-05-25: Projekt-Tabelle um Eval-Achsen + Cover-Foto. Differenzierte Selbstbewertung (Etappe 6, nach Reality-Check mit Herb): - Gesamt-Bewertung war schon da (selbsteinschaetzung). - NEU: Planung-Qualitaet + Ausfuehrungs-Qualitaet als zweite Achsen (gut/ok/schwierig), damit SuS reflektiert was wirklich besser ginge. - NEU: was_klappte_nicht — Freitext, damit der Lehrer beim Review-Misserfolg Kontext hat (statt Selbst-Geschoenrede). - NEU: cover_blob + cover_mime — Crop-Tool aus admin_software_bild.html portiert, gleiches Pattern wie software_seite.bild_blob. Idempotent. Legt zusaetzlich 2 Mock-Projekte fuer Profil 100 (Test-Biene) an, falls keine eigenen Projekte existieren. Damit ist die Cockpit-/Katalog-Logik testbar ohne von Hand Daten anzulegen. """ import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from database import get_db NEUE_SPALTEN = [ ("selbsteinschaetzung_planung", "TEXT"), ("selbsteinschaetzung_ausfuehrung", "TEXT"), ("was_klappte_nicht", "TEXT"), ("cover_blob", "BLOB"), ("cover_mime", "TEXT"), ] MOCK_PROJEKTE = [ { "titel": "Schluesselanhaenger mit meinem Namen", "beschreibung_md": ( "Mein erstes 3D-Modell ueberhaupt! Mit MakerWorld-Customizer einen " "Schluesselanhaenger mit *Test* eingegeben. PLA gelb auf P1S #1, etwa " "25 Minuten Druckzeit." ), "status_lebenszyklus": "abgeschlossen", "abgeschlossen_am": "2026-05-15 14:30:00", "selbsteinschaetzung": "zufrieden", "selbsteinschaetzung_planung": "ok", "selbsteinschaetzung_ausfuehrung": "gut", "was_gelernt": "Slicen geht erstaunlich schnell — und wenn Bett warm genug ist, klebt es richtig gut.", "was_klappte_nicht": None, "freigabe_status": "freigegeben", }, { "titel": "Stifteigel fuer den Schreibtisch", "beschreibung_md": ( "Ein kleiner Igel, in dessen Ruecken man Stifte stecken kann. Vorlage " "von Printables, etwas angepasst (groessere Loecher fuer dicke Filzstifte). " "Druck laeuft gerade noch." ), "status_lebenszyklus": "in_arbeit", "abgeschlossen_am": None, "selbsteinschaetzung": None, "selbsteinschaetzung_planung": None, "selbsteinschaetzung_ausfuehrung": None, "was_gelernt": None, "was_klappte_nicht": None, "freigabe_status": "in_arbeit", }, ] def main(): conn = get_db() # ---- Schema-Erweiterung ---- spalten = {row["name"] for row in conn.execute("PRAGMA table_info(projekt)")} angelegt = [] for name, typ in NEUE_SPALTEN: if name not in spalten: conn.execute(f"ALTER TABLE projekt ADD COLUMN {name} {typ}") angelegt.append(name) if angelegt: print(f"[migration] projekt: Spalten ergaenzt: {', '.join(angelegt)}.") else: print("[migration] projekt: alle neuen Spalten existieren bereits.") # ---- Mock-Projekte fuer Test-Biene (Profil 100) ---- profil = conn.execute( "SELECT id, vorname FROM profil WHERE id = 100 AND aktiv = 1" ).fetchone() if not profil: print("[migration] Profil 100 nicht gefunden — Mock-Daten ueberspringen.") conn.commit() conn.close() return schon_da = conn.execute( "SELECT COUNT(*) AS n FROM projekt WHERE profil_id = 100" ).fetchone()["n"] if schon_da > 0: print(f"[migration] Profil 100 hat schon {schon_da} Projekte — Mock-Daten ueberspringen.") conn.commit() conn.close() return for p in MOCK_PROJEKTE: conn.execute( """INSERT INTO projekt (profil_id, titel, beschreibung_md, status_lebenszyklus, abgeschlossen_am, selbsteinschaetzung, selbsteinschaetzung_planung, selbsteinschaetzung_ausfuehrung, was_gelernt, was_klappte_nicht, freigabe_status, sichtbar_in_ag) VALUES (100, :titel, :beschreibung_md, :status_lebenszyklus, :abgeschlossen_am, :selbsteinschaetzung, :selbsteinschaetzung_planung, :selbsteinschaetzung_ausfuehrung, :was_gelernt, :was_klappte_nicht, :freigabe_status, 1)""", p ) conn.commit() print(f"[migration] {len(MOCK_PROJEKTE)} Mock-Projekte fuer Profil 100 ({profil['vorname']}) angelegt.") conn.close() if __name__ == "__main__": main()