From 693475128cb4c285fb049184045733404e358663 Mon Sep 17 00:00:00 2001 From: team3 Date: Fri, 12 Jun 2026 07:44:39 +0200 Subject: [PATCH] =?UTF-8?q?Elemente:=20Aufgabe/L=C3=B6sung-Felder=20entfer?= =?UTF-8?q?nt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/database.py | 11 +----- backend/generator.py | 12 +++--- backend/models.py | 8 +--- frontend/src/components/ElementsSidebar.vue | 43 +++------------------ templates/Prompt/Element-Chat.md | 4 +- templates/Prompt/Element-Check.md | 6 +-- templates/Prompt/Element-Create.md | 4 +- templates/Prompt/Element-Stil.md | 5 +-- templates/Prompt/Element-Verify.md | 2 +- 9 files changed, 23 insertions(+), 72 deletions(-) diff --git a/backend/database.py b/backend/database.py index de6deac..151acdf 100644 --- a/backend/database.py +++ b/backend/database.py @@ -42,8 +42,6 @@ CREATE TABLE IF NOT EXISTS elements ( description TEXT NOT NULL DEFAULT '', examples TEXT NOT NULL DEFAULT '[]', hints TEXT NOT NULL DEFAULT '[]', - aufgabe TEXT NOT NULL DEFAULT '', - loesung TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ) @@ -70,11 +68,6 @@ async def init_db(): await db.execute("ALTER TABLE guides ADD COLUMN step INTEGER") except aiosqlite.OperationalError: pass - for col in ("aufgabe", "loesung"): # Migration für Elemente ohne Aufgabe/Lösung - try: - await db.execute(f"ALTER TABLE elements ADD COLUMN {col} TEXT NOT NULL DEFAULT ''") - except aiosqlite.OperationalError: - pass await db.execute( "UPDATE guides SET status = 'error', progress = NULL, error_msg = 'Server-Neustart' " "WHERE status IN ('queued', 'generating')" @@ -173,8 +166,8 @@ def _element_row(row, cursor) -> dict: async def create_element(element: dict) -> dict: db = await get_db() await db.execute( - """INSERT INTO elements (id, topic, title, description, examples, hints, aufgabe, loesung, created_at, updated_at) - VALUES (:id, :topic, :title, :description, :examples, :hints, :aufgabe, :loesung, :created_at, :updated_at)""", + """INSERT INTO elements (id, topic, title, description, examples, hints, created_at, updated_at) + VALUES (:id, :topic, :title, :description, :examples, :hints, :created_at, :updated_at)""", {**element, "examples": json.dumps(element["examples"], ensure_ascii=False), "hints": json.dumps(element["hints"], ensure_ascii=False)}, ) diff --git a/backend/generator.py b/backend/generator.py index b81fb7f..8168423 100644 --- a/backend/generator.py +++ b/backend/generator.py @@ -1396,8 +1396,6 @@ def _element_fields(data: dict) -> dict | None: "description": str(data.get("description", "")).strip(), "examples": listen["examples"], "hints": listen["hints"], - "aufgabe": str(data.get("aufgabe", "")).strip(), - "loesung": str(data.get("loesung", "")).strip(), } @@ -1420,7 +1418,7 @@ def _topic_context(topic: str, limit: int = 12000) -> str: async def generate_element(topic: str, hint: str, provider: str = DEFAULT_PROVIDER) -> dict: """Erstellt Element-Felder per KI. Fallback: nur Titel aus dem Stichwort.""" - fallback = {"title": hint.strip() or "Neues Element", "description": "", "examples": [], "hints": [], "aufgabe": "", "loesung": ""} + fallback = {"title": hint.strip() or "Neues Element", "description": "", "examples": [], "hints": []} try: prompt = _prompt( "Element-Create", @@ -1456,7 +1454,7 @@ def _parse_suggestions(stdout: str) -> list[dict] | None: text = str(s.get("text", "")).strip() target = s.get("target") content = str(s.get("content", "")).strip() - if text and content and target in ("description", "examples", "hints", "aufgabe", "loesung"): + if text and content and target in ("description", "examples", "hints"): if target == "examples": content = _fence(content) suggestions.append({"text": text, "target": target, "content": content}) @@ -1467,7 +1465,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list """Zweischrittige Prüfung auf fehlende Infos: Recherche → Verifizieren. None bei Fehler.""" try: element_json = json.dumps( - {k: element[k] for k in ("title", "description", "examples", "hints", "aufgabe", "loesung")}, + {k: element[k] for k in ("title", "description", "examples", "hints")}, ensure_ascii=False, indent=1, ) context = _topic_context(element["topic"]) @@ -1504,7 +1502,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list def _element_json(element: dict) -> str: return json.dumps( - {k: element[k] for k in ("title", "description", "examples", "hints", "aufgabe", "loesung")}, + {k: element[k] for k in ("title", "description", "examples", "hints")}, ensure_ascii=False, indent=1, ) @@ -1520,7 +1518,7 @@ def _validate_change(c, element: dict) -> dict | None: content = str(c.get("content", "")).strip() if not text or action not in ("entfernen", "anpassen", "hinzufuegen"): return None - if target not in ("title", "description", "examples", "hints", "aufgabe", "loesung"): + if target not in ("title", "description", "examples", "hints"): return None if action in ("anpassen", "hinzufuegen") and not content: return None diff --git a/backend/models.py b/backend/models.py index 5c5400c..7d0b4c1 100644 --- a/backend/models.py +++ b/backend/models.py @@ -86,8 +86,6 @@ class ElementResponse(BaseModel): description: str = "" examples: list[str] = [] hints: list[str] = [] - aufgabe: str = "" - loesung: str = "" created_at: str updated_at: str @@ -103,8 +101,6 @@ class ElementUpdateRequest(BaseModel): description: str | None = None examples: list[str] | None = None hints: list[str] | None = None - aufgabe: str | None = None - loesung: str | None = None class ElementCheckRequest(BaseModel): @@ -113,7 +109,7 @@ class ElementCheckRequest(BaseModel): class ElementSuggestion(BaseModel): text: str - target: Literal["description", "examples", "hints", "aufgabe", "loesung"] + target: Literal["description", "examples", "hints"] content: str @@ -124,7 +120,7 @@ class ElementCheckResponse(BaseModel): class ElementStyleChange(BaseModel): text: str action: Literal["entfernen", "anpassen", "hinzufuegen"] - target: Literal["title", "description", "examples", "hints", "aufgabe", "loesung"] + target: Literal["title", "description", "examples", "hints"] index: int | None = None content: str = "" diff --git a/frontend/src/components/ElementsSidebar.vue b/frontend/src/components/ElementsSidebar.vue index 69b0ef5..18a702e 100644 --- a/frontend/src/components/ElementsSidebar.vue +++ b/frontend/src/components/ElementsSidebar.vue @@ -18,7 +18,7 @@ const query = ref('') const creating = ref(false) const selected = ref(null) const tab = ref('overview') // 'overview' | 'chat' | 'edit' -const edit = ref({ title: '', description: '', examples: [], hints: [], aufgabe: '', loesung: '' }) +const edit = ref({ title: '', description: '', examples: [], hints: [] }) const savingEdit = ref(false) watch(() => props.topic, load, { immediate: true }) @@ -93,8 +93,6 @@ function loadEdit() { description: selected.value.description, examples: [...selected.value.examples], hints: [...selected.value.hints], - aufgabe: selected.value.aufgabe || '', - loesung: selected.value.loesung || '', } } @@ -112,8 +110,6 @@ async function saveEdit() { description: edit.value.description, examples: edit.value.examples.filter((s) => s.trim()), hints: edit.value.hints.filter((s) => s.trim()), - aufgabe: edit.value.aufgabe, - loesung: edit.value.loesung, }) selected.value = updated const idx = elements.value.findIndex((e) => e.id === updated.id) @@ -250,19 +246,17 @@ async function applyStyleChange(i) { const c = styleChanges.value[i] applyingStyle.value = true try { - const STRING_TARGETS = ['title', 'description', 'aufgabe', 'loesung'] + const STRING_TARGETS = ['title', 'description'] const fields = { title: selected.value.title, description: selected.value.description, examples: [...selected.value.examples], hints: [...selected.value.hints], - aufgabe: selected.value.aufgabe || '', - loesung: selected.value.loesung || '', } if (c.action === 'entfernen') fields[c.target].splice(c.index, 1) else if (c.action === 'hinzufuegen') { if (c.target === 'title') fields.title = c.content - else if (c.target === 'description' || c.target === 'aufgabe' || c.target === 'loesung') + else if (c.target === 'description') fields[c.target] = fields[c.target] ? fields[c.target] + '\n\n' + c.content : c.content else fields[c.target].push(c.content) } else if (STRING_TARGETS.includes(c.target)) fields[c.target] = c.content @@ -448,25 +442,6 @@ async function send() { /> -
-

Aufgabe

-
- -
-
-

Lösung

-
- -
-

Prüft auf fehlende Infos…

Prüft den Stil…

@@ -522,12 +497,6 @@ async function send() {
- - - - - - @@ -786,13 +755,11 @@ async function send() { color: var(--text); } -.el-hints-block, -.el-task-block { +.el-hints-block { margin-top: 0.9rem; } -.el-hints-block h4, -.el-task-block h4 { +.el-hints-block h4 { margin: 0 0 0.35rem; font-size: 0.72rem; font-weight: 700; diff --git a/templates/Prompt/Element-Chat.md b/templates/Prompt/Element-Chat.md index bd3947e..215b1f5 100644 --- a/templates/Prompt/Element-Chat.md +++ b/templates/Prompt/Element-Chat.md @@ -17,8 +17,8 @@ Umfang: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH. Markdown: `inline-code` fü Jeder Vorschlag: - text: kurz, was geändert wird (max. 12 Wörter, reiner Text) - action: "entfernen" | "anpassen" | "hinzufuegen" -- target: "title" | "description" | "examples" | "hints" | "aufgabe" | "loesung" -- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description/aufgabe/loesung und hinzufuegen: null) +- target: "title" | "description" | "examples" | "hints" +- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description und hinzufuegen: null) - content: der neue vollständige Inhalt (bei entfernen: leer) "entfernen" nur für examples/hints. Nur Vorschläge machen, die die Nutzer-Anweisung verlangt. diff --git a/templates/Prompt/Element-Check.md b/templates/Prompt/Element-Check.md index 74e4fca..1cdd5dd 100644 --- a/templates/Prompt/Element-Check.md +++ b/templates/Prompt/Element-Check.md @@ -6,12 +6,12 @@ AKTUELLES ELEMENT (JSON): KONTEXT (Auszüge aus dem Themen-Material): {context} -RECHERCHE — sammle breit alle Kandidaten: fehlende Kernaussagen, wichtige Varianten, typische Stolperfallen, Best Practices. Fehlt eine Übungsaufgabe (aufgabe) oder deren Lösung (loesung), schlage sie vor. Lieber einen Kandidaten zu viel als einen zu wenig — die Bewertung passiert in einem zweiten Schritt. Nichts vorschlagen, was das Element schon enthält. +RECHERCHE — sammle breit alle Kandidaten: fehlende Kernaussagen, wichtige Varianten, typische Stolperfallen, Best Practices. Lieber einen Kandidaten zu viel als einen zu wenig — die Bewertung passiert in einem zweiten Schritt. Nichts vorschlagen, was das Element schon enthält. Jeder Kandidat: - text: kurze Beschreibung der Lücke (max. 12 Wörter, reiner Text) -- target: "description" | "examples" | "hints" | "aufgabe" | "loesung" -- content: fertiger Inhalt zum Einfügen. SO KURZ WIE MÖGLICH, so lang wie nötig. Markdown: `inline-code` für Bezeichner, examples als Codeblock mit Sprachangabe (```sprache), beginnend mit kurzem Kommentar zur Variante (z. B. ``), hints nur wenn WICHTIG oder NÜTZLICH, im Telegrammstil (nur Kernaussage, z. B. "Keine Blockelemente in `

`."). aufgabe: EINE konkrete, in Minuten lösbare Übungsaufgabe; loesung: die knappe Musterlösung dazu. Tags/Bezeichner im Fließtext IMMER in Backticks. +- target: "description" | "examples" | "hints" +- content: fertiger Inhalt zum Einfügen. SO KURZ WIE MÖGLICH, so lang wie nötig. Markdown: `inline-code` für Bezeichner, examples als Codeblock mit Sprachangabe (```sprache), beginnend mit kurzem Kommentar zur Variante (z. B. ``), hints nur wenn WICHTIG oder NÜTZLICH, im Telegrammstil (nur Kernaussage, z. B. "Keine Blockelemente in `

`."). Tags/Bezeichner im Fließtext IMMER in Backticks. Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text: {{"suggestions": [{{"text": "...", "target": "hints", "content": "..."}}]}} diff --git a/templates/Prompt/Element-Create.md b/templates/Prompt/Element-Create.md index 85bccad..388c375 100644 --- a/templates/Prompt/Element-Create.md +++ b/templates/Prompt/Element-Create.md @@ -11,8 +11,6 @@ Erstelle GENAU EIN Element zum Stichwort: 2. description — was es ist und wozu: MAXIMAL 1–2 Sätze 3. examples — GENAU EIN Beispiel: KURZ und SIMPEL, wenige Zeilen Code, das Minimalbeispiel, keine Realwelt-Komplexität. Beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. ``, `// Mit Default-Wert`), der die Variante benennt. 4. hints — IMMER leere Liste. Hinweise ergänzt der Nutzer später selbst. (Falls je gefordert: TELEGRAMMSTIL, max. 10 Wörter.) -5. aufgabe — GENAU EINE kleine Übungsaufgabe zum Konzept: konkret, in wenigen Minuten lösbar, prüft das Verständnis. Markdown, Code als Codeblock mit Sprachangabe. -6. loesung — die Musterlösung zur Aufgabe: knapp, nachvollziehbar, Schritt für Schritt nur wo nötig. Code als Codeblock mit Sprachangabe. Das Element ist ATOMAR: allein verständlich, ohne dass der Leser etwas anderes gelesen hat. Benutzte Begriffe in einem Halbsatz auflösen. @@ -23,4 +21,4 @@ Tonalität: klares Deutsch, direkt, praxisorientiert. Fachbegriffe beim ersten A Markdown in description und examples: normale Absätze, `inline-code` für Bezeichner, Codeblöcke mit Sprachangabe (```sprache), **fett** sparsam für Kernaussagen. Keine Überschriften. Code-Beispiele IMMER als Codeblock, nie als Inline-Code. Bezeichner, Tags und Befehle (z. B. `

`, `git add`) im Fließtext IMMER in Backticks — nie nackt. Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text: -{{"title": "...", "description": "...", "examples": ["```sprache\n...\n```"], "hints": [], "aufgabe": "...", "loesung": "..."}} +{{"title": "...", "description": "...", "examples": ["```sprache\n...\n```"], "hints": []}} diff --git a/templates/Prompt/Element-Stil.md b/templates/Prompt/Element-Stil.md index 8256129..1e80e9f 100644 --- a/templates/Prompt/Element-Stil.md +++ b/templates/Prompt/Element-Stil.md @@ -8,7 +8,6 @@ STIL-REGELN: 2. description — was es ist und wozu: MAXIMAL 1–2 Sätze 3. examples — KURZ und SIMPEL: wenige Zeilen Code, Minimalbeispiel, keine Realwelt-Komplexität. Ein Beispiel pro relevanter Variante, geordnet vom Üblichen zum Speziellen. Als Codeblock mit Sprachangabe (```sprache), nie als Inline-Code. Jedes Beispiel beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. ``), der die Variante benennt. 4. hints — jeder Hinweis muss WICHTIG oder NÜTZLICH sein: Stolperfalle, Merksatz oder Best Practice mit echtem Praxiswert. Selbstverständliches, Nischenwissen und Redundantes zum Element entfernen. Telegrammstil: nur die Kernaussage, Füllverben und Herleitungen streichen. - aufgabe — EINE konkrete, in Minuten lösbare Übungsaufgabe, die das Verständnis prüft. loesung — knappe, nachvollziehbare Musterlösung dazu. Beide: Code als Codeblock mit Sprachangabe. Vorher: "Browser fügen standardmäßig vertikalen Abstand vor und nach `

` ein — anpassbar mit `margin`." Nachher: "Browser-Abstand um `

` per `margin` anpassbar." 5. Umfang: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH. Jedes Wort muss seinen Platz verdienen — Füllwörter, Nebensätze ohne Informationswert und Selbstverständliches streichen. Aber: Kürze nie auf Kosten der Verständlichkeit oder Korrektheit. @@ -18,8 +17,8 @@ STIL-REGELN: Schlage für jeden Stil-Verstoß GENAU EINE Änderung vor: - text: kurz, was und warum (max. 12 Wörter, reiner Text) - action: "entfernen" | "anpassen" | "hinzufuegen" -- target: "title" | "description" | "examples" | "hints" | "aufgabe" | "loesung" -- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description/aufgabe/loesung: null; bei hinzufuegen: null) +- target: "title" | "description" | "examples" | "hints" +- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description: null; bei hinzufuegen: null) - content: der neue/vollständige Inhalt (bei entfernen: leer) "entfernen" nur für examples/hints. "hinzufuegen" sparsam — nur wenn eine Stil-Regel es verlangt (z. B. fehlender Varianten-Kommentar gehört zu "anpassen", nicht "hinzufuegen"). Erfüllt etwas die Regeln schon: NICHT anfassen. diff --git a/templates/Prompt/Element-Verify.md b/templates/Prompt/Element-Verify.md index eb94799..fa9a74a 100644 --- a/templates/Prompt/Element-Verify.md +++ b/templates/Prompt/Element-Verify.md @@ -13,7 +13,7 @@ Prüfe JEDEN Kandidaten kritisch: 1. WICHTIG? Muss ein Lerner das wissen? Nice-to-haves und Nischenwissen ablehnen. 2. REDUNDANT? Steckt die Info schon im Element oder in einem anderen Kandidaten? Ablehnen bzw. Duplikate zusammenführen. 3. KORREKT? Fachlich falsch oder irreführend → ablehnen. -4. PASST das target ("description" | "examples" | "hints" | "aufgabe" | "loesung")? Sonst korrigieren. Höchstens EIN Vorschlag je für "aufgabe" und "loesung". +4. PASST das target ("description" | "examples" | "hints")? Sonst korrigieren. Behalte nur Kandidaten, die alle Prüfungen bestehen. Verbessere dabei content auf die Stil-Regeln: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH; `inline-code` für Bezeichner; examples als Codeblock mit Sprachangabe und kurzem Varianten-Kommentar; hints im Telegrammstil (nur Kernaussage, Kürze nie auf Kosten der Verständlichkeit).