Vier zusammenhaengende Iterationen, die das Funktions-Geruest der AG aufbauen.
Alle 6 Migrationen sind idempotent und legen Schema-Erweiterungen + initiale
Seeds an (Mock-Projekte nur, falls Profil 100 existiert — auf prod nicht).
Software-Sektion (Etappe 3):
- software_seite-Tabelle: schlanker Karten-Katalog (slug, titel, urls,
plattform, kategorie, sortierung, Cover-BLOB)
- 7 Initial-Karten (Bambu Studio/Handy, Printables, MakerWorld,
TinkerCAD, Onshape, Gridfinity-Generator)
- Admin-Verwaltung: Liste, Anlegen-Form, Bearbeiten, Aktiv-Toggle,
Hart-Loeschen, Bild-Upload via Crop-Tool (Strg+V/Drag&Drop, 400x400)
Drucker-Verwaltung (Etappe 5-vorgezogen):
- drucker.freigegeben + gesperrt_grund (Lehrer-Sperre fuer Reparatur)
- drucker.manual_url + troubleshooting_url (Override fuer Modell-Defaults)
- SuS-View /drucker (eingeloggt): Kacheln + Sperre-Banner + Belegung
- Admin /admin/drucker: Status-Override (idle/printing/failed/...),
Sperren/Freigeben mit Grund, Inline-Edit fuer URLs
Slot-System / Reservierung (Etappe 7-vorgezogen):
- profil.reservieren_freigeschaltet (Lehrer schaltet manuell frei,
ersetzt L1-Gate-Automatismus)
- services/slots.py: Konflikt-Check, Belegungs-Helper
- SuS-Routen /cockpit/slot/neu (mit Konflikt-Check), /cockpit/slots,
Stornieren
- Admin /admin/slots Uebersicht + /admin/slot/neu (Lehrer reserviert
fuer SuS) + Hart-Loeschen + Profil-Freigabe-Toggle
Selbstlern-Loop (Etappe 6 — Kern):
- projekt-Spalten: 3-Achsen-Eval (Gesamt + Planung + Ausfuehrung),
was_klappte_nicht, cover_blob, modellier_software_slug,
drucker_id_used, makerworld_url
- Regel: max. 1 aktives Projekt pro SuS, neues nur nach Freigabe
(auto bei zufrieden/mittel, Lehrer-Review bei gelernt)
- SuS-Werkstatt-Sektion: Titel + Beschreibung + Software-Dropdown +
Drucker-Dropdown + MakerWorld-URL + Files-Upload (drag&drop,
base64, max 30 MB)
- Cover-Bild via Crop-Tool (Re-use admin_software_bild-Pattern)
- Abschluss-Block: Pflicht Cover + mindestens 1 Datei
- Eval-Form: visuelle 3x3 Radio-Karten + 2 Freitexte
- Lehrer-Review: /admin/projekte mit Filter (wartet/in_arbeit/
abgeschlossen/rueckfrage), Aktionen freigeben/rueckfrage/loeschen
- Oeffentlicher Katalog /projekte: Sortier-Tabelle (Titel/Person/
Status/Erfolg/Datum), aufklappbare Detail mit Cover/Beschreibung/
Datei-Downloads/MakerWorld-Link
- Wiederhol-Logik: "Sowas mache ich auch" via Verknuepfung
(vorlage_projekt_id), bidirektionale Lineage-Banner
Cockpit-Refactoring:
- Projekt-Karte ganz oben (vor Onboarding) mit "Hier weitermachen"
- Check-In-Quick-Actions: nach Auswahl direkt zu /cockpit/projekt/neu,
/drucker, /lektionen, /cockpit/projekt/<aktiv>
- "Modus aendern"-Button im Eingecheckt-Banner
- Admin-Dashboard-Hub mit Karten: SuS-Profile, Software, Lektionen,
Drucker, Reservierungen, Projekte (Badge bei wartenden), Alph
Bug-Fixes:
- Admin-Login redirected jetzt auf /admin/dashboard statt /admin/profile
- app.py: neue Jinja-Filter iso_format + dauer_min
- .gitignore: .claude/ ausschliessen (lokale launch.json)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
165 lines
6.4 KiB
Python
165 lines
6.4 KiB
Python
"""Migration 2026-05-25: Software-Karten initial befuellen.
|
|
|
|
Etappe 3 — schlankes Karten-Konzept (siehe docs/decisions.md, neue Entscheidung
|
|
zur Software-Sektion). Wir halten uns raus: SuS haben die Software auf den
|
|
Schul-PCs / im Browser und machen eigene Erfahrungen. Wir liefern nur die
|
|
Auswahl und ein paar Tutorials.
|
|
|
|
Inhalte pro Karte: 1-2 Satz Beschreibung, Hauptlink, optional Tutorial-Link.
|
|
Apps zusaetzlich App-Store + Play-Store-URLs (QR-Codes folgen in Folge-Schritt).
|
|
|
|
Idempotent: INSERT OR REPLACE auf slug. Achtung: ueberschreibt Inline-Edits
|
|
des Lehrers. Nur fuer Initial-Seed gedacht.
|
|
"""
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
from database import get_db, init_db
|
|
|
|
|
|
SOFTWARE = [
|
|
# ---------- Slicer (Pflicht) ----------
|
|
{
|
|
"slug": "bambu-studio",
|
|
"titel": "Bambu Studio",
|
|
"kurzbeschreibung": "Slicer fuer unsere Drucker. Modell oeffnen, Drucker waehlen, drucken.",
|
|
"url": "https://bambulab.com/de/download/studio",
|
|
"tutorial_url": "https://wiki.bambulab.com/en/software/bambu-studio/studio-quick-start",
|
|
"plattform": "Windows / macOS / Linux",
|
|
"kategorie": "software",
|
|
"sortierung": 10,
|
|
},
|
|
# ---------- App ----------
|
|
{
|
|
"slug": "bambu-handy",
|
|
"titel": "Bambu Handy",
|
|
"kurzbeschreibung": "Drucker live am Handy beobachten, pausieren, Kamera ansehen.",
|
|
"url": "https://bambulab.com/de/download",
|
|
"tutorial_url": None,
|
|
"plattform": "iOS + Android",
|
|
"app_store_url": "https://apps.apple.com/de/app/bambu-handy/id1620054970",
|
|
"play_store_url": "https://play.google.com/store/apps/details?id=com.bambulab.bambu_link",
|
|
"kategorie": "app",
|
|
"sortierung": 20,
|
|
},
|
|
# ---------- Modell-Plattformen ----------
|
|
{
|
|
"slug": "printables",
|
|
"titel": "Printables",
|
|
"kurzbeschreibung": "Modell-Plattform von Prusa. Werbefrei, deutsche UI — erste Adresse fuer fertige Modelle.",
|
|
"url": "https://www.printables.com",
|
|
"tutorial_url": "https://www.printables.com/education",
|
|
"plattform": "Browser",
|
|
"kategorie": "software",
|
|
"sortierung": 30,
|
|
},
|
|
{
|
|
"slug": "makerworld",
|
|
"titel": "MakerWorld",
|
|
"kurzbeschreibung": "Bambu-eigene Modell-Plattform mit Customizer — Modelle mit eigenem Namen oder Wunschmassen.",
|
|
"url": "https://makerworld.com",
|
|
"tutorial_url": "https://makerworld.com/en/models/1320280", # Parametric Name Keychain als Erst-Customizer
|
|
"plattform": "Browser (Konto noetig)",
|
|
"kategorie": "software",
|
|
"sortierung": 40,
|
|
},
|
|
# ---------- CAD ----------
|
|
{
|
|
"slug": "tinkercad",
|
|
"titel": "TinkerCAD",
|
|
"kurzbeschreibung": "Visuelles 3D-CAD im Browser. Grundkoerper ziehen, kombinieren, exportieren. Einstieg ins eigene Modellieren.",
|
|
"url": "https://www.tinkercad.com",
|
|
"tutorial_url": "https://www.tinkercad.com/learn",
|
|
"plattform": "Browser",
|
|
"kategorie": "software",
|
|
"sortierung": 50,
|
|
},
|
|
# OpenSCAD bewusst nicht aufgenommen — solange wir keine Lektion dafuer
|
|
# haben, wuerden wir's nur als Karte "in den Raum stellen". Wenn spaeter
|
|
# eine OpenSCAD-Lektion entsteht (Bruecke zur Informatik), kann die Karte
|
|
# via Admin-GUI oder Folge-Migration nachgezogen werden.
|
|
{
|
|
"slug": "onshape",
|
|
"titel": "Onshape",
|
|
"kurzbeschreibung": "Professionelles CAD im Browser. Was Profis nutzen — fuer Schulen kostenlos, ab 13 Jahren.",
|
|
"url": "https://www.onshape.com/de/education/",
|
|
"tutorial_url": "https://www.youtube.com/watch?v=VyVqG8ZPvaM", # stolz3D Anfaenger Grundkurs
|
|
"plattform": "Browser + iOS + Android",
|
|
"kategorie": "software",
|
|
"sortierung": 70,
|
|
},
|
|
# ---------- Online-Generatoren ----------
|
|
{
|
|
"slug": "gridfinity",
|
|
"titel": "Gridfinity Builder",
|
|
"kurzbeschreibung": "Online-Generator fuer Gridfinity-Boxen — modulare Werkstatt-Ordnungssysteme nach Mass.",
|
|
"url": "https://gridfinity.perplexinglabs.com/pr/gridfinity-rebuilt/0/1",
|
|
"tutorial_url": None,
|
|
"plattform": "Browser",
|
|
"kategorie": "generator",
|
|
"sortierung": 80,
|
|
},
|
|
]
|
|
|
|
|
|
# Felder, die als Spalten gepflegt werden (alle anderen Keys werden ignoriert)
|
|
SPALTEN = (
|
|
"slug", "titel", "kurzbeschreibung", "url", "tutorial_url",
|
|
"plattform", "app_store_url", "play_store_url", "bild_pfad",
|
|
"kategorie", "sortierung",
|
|
)
|
|
|
|
|
|
def main():
|
|
init_db()
|
|
conn = get_db()
|
|
angelegt = 0
|
|
aktualisiert = 0
|
|
for s in SOFTWARE:
|
|
# Default-Werte fuer optionale Felder, damit das UPDATE/INSERT einheitlich ist
|
|
werte = {sp: s.get(sp) for sp in SPALTEN}
|
|
|
|
existiert = conn.execute(
|
|
"SELECT 1 FROM software_seite WHERE slug = ?", (werte["slug"],)
|
|
).fetchone()
|
|
if existiert:
|
|
conn.execute(
|
|
"""UPDATE software_seite SET
|
|
titel = :titel,
|
|
kurzbeschreibung = :kurzbeschreibung,
|
|
url = :url,
|
|
tutorial_url = :tutorial_url,
|
|
plattform = :plattform,
|
|
app_store_url = :app_store_url,
|
|
play_store_url = :play_store_url,
|
|
bild_pfad = :bild_pfad,
|
|
kategorie = :kategorie,
|
|
sortierung = :sortierung,
|
|
aktiv = 1
|
|
WHERE slug = :slug""",
|
|
werte
|
|
)
|
|
aktualisiert += 1
|
|
else:
|
|
conn.execute(
|
|
"""INSERT INTO software_seite
|
|
(slug, titel, kurzbeschreibung, url, tutorial_url,
|
|
plattform, app_store_url, play_store_url, bild_pfad,
|
|
kategorie, sortierung, aktiv)
|
|
VALUES
|
|
(:slug, :titel, :kurzbeschreibung, :url, :tutorial_url,
|
|
:plattform, :app_store_url, :play_store_url, :bild_pfad,
|
|
:kategorie, :sortierung, 1)""",
|
|
werte
|
|
)
|
|
angelegt += 1
|
|
conn.commit()
|
|
conn.close()
|
|
print(f"[migration] software_seite: {angelegt} angelegt, {aktualisiert} aktualisiert "
|
|
f"({len(SOFTWARE)} Karten total).")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|