Commit Graph

41 Commits

Author SHA1 Message Date
e066ff7420 Migrate app.py from Directus to snakkimo API
- Replace all db_* routes with snakkimo API equivalents
- Stub out Llama AI functions (generate_details, generate_sentence) with 501 error
- Map field names: user_notes↔notes, picture↔picture_link, level↔difficulty_level, statement_de↔positive_sentence_de
- Use word_id as junction_id for M2M deletes (snakkimo uses resource IDs not junction row IDs)
- Normalize db-pictures response to include picture/blurhash/status/design
- Extract unique design values from pictures table for design-options endpoint
- Pair DELETE now also deletes linked statement and question
- FLAG: question words, distractor_words, objects.parent not supported in snakkimo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 15:31:48 +02:00
d02788bd0e feat: CRM-Dashboard, Content-Verwaltung und Wort-Autocomplete
- Home-Seite nach Login mit Begrüßung und 3 Kacheln (Content erstellen, Content verwalten, User verwalten)
- AuthContext speichert User-Profil + Rolle; AdminRoute blockt Nicht-Admins
- Content verwalten (admin-only): Status-Dashboard pro Collection, Liste/Kachel-View, generisches Edit-Formular
- Nur aktive db_-Collections im Dashboard (alte pictures/objects/words/questions entfernt)
- Wort-Autocomplete in DrawIt: ab dem ersten Buchstaben Vorschläge aus db_words, Tastatur-Navigation, Duplikat-Filter
- Backend: /users/me Proxy, db-words/search Endpoint, generische Collection-Endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 23:37:48 +02:00
05c62ac414 feat: Bild in Annotieren-View löschen (Eintrag + Datei aus Directus)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:19:51 +02:00
22e6b0a5a5 fix: Design-PATCH fehlte Bearer-Token + Race Condition bei Fertigstellen
- Neues updateDbPicture() in api.ts mit korrektem Bearer-Header
- Design-onChange nutzt updateDbPicture statt rohem fetch (kein Bearer-Bug mehr)
- finishPicture sendet status + design in einem PATCH (kein Race Condition)
- app.py: get_json(force=True, silent=True) für db-pictures PATCH

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 20:32:55 +02:00
79d1f2ba21 fix: design-options nutzt Admin-Token für Directus /fields/ Endpoint
/fields/ braucht Admin-Rechte – Session-Token des Users hat keinen Zugriff.
DIRECTUS_ADMIN_TOKEN als Konstante (überschreibbar via Env-Var).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 20:20:24 +02:00
255ec51858 fix: design-options fallback wenn Directus fields API nicht erreichbar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 20:15:45 +02:00
5424fea8e1 fix: blurhash cache miss, design dropdown visibility + add Hauptwörter panel
- DrawCanvas: call onImageLoad for cached images (fixes permanent blur)
- DrawIt: reset imageLoaded immediately on navigation (shows blur right away)
- Design dropdown: always visible when picture loaded, no longer gated on designOptions or objects count
- Fertigstellen: always visible when picture loaded, disabled when no objects
- Hauptwörter: new panel above object list for picture-level words (db_words_db_pictures)
- Backend: DELETE /api/directus/db-pictures/<id>/words/<junction_id>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:47:14 +02:00
17918a414b feat: object words in right sidebar + design dropdown
- Words panel moved to right sidebar: shows selected object's words or pending words for new object
- Pending words auto-saved to object after creation
- Remove word chips from left sidebar object cards
- Design dropdown in left sidebar (above Fertigstellen), loads choices dynamically from Directus field metadata
- Include design field in db_pictures GET response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 18:26:38 +02:00
40c36182f1 feat: multi-word per object + {objectID.wordID} placeholders
- Annotate: multiple words per object via db_objects_db_words M2M, word chips with add/remove per object card
- Generate sidebar: objects shown with comma-separated word list as display name
- Generate pair form: all object words as suggestion chips, click inserts {objectId.wordId} at cursor
- Preview resolves {objectId.wordId} → actual word text
- Backend: POST adds single word (no replace), new DELETE /db-objects/<id>/words/<junctionId>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 16:49:24 +02:00
214f8a2019 feat: object label per object + {obj:UUID} sentence placeholders
- Annotate: per-object single label input (M2M via db_objects_db_words), auto-save on blur, remove picture-level word section
- Generate: object chips insert {obj:UUID} at cursor position in question/statement textarea
- Live preview resolves {obj:UUID} → actual object label
- PairsList display also resolves placeholders
- Remove F/A/B word chip system from pair form (replaced by object placeholders)
- Backend: POST /api/directus/db-objects/<id>/words replaces existing word with single label

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 13:04:37 +02:00
2595b8d32e feat: individual word link_to (F/A/B), edit form word management, fix picture-based suggestions
- Words in pair form now linkable individually per word (Frage/Aussage/Beide toggle)
- Edit form includes full word management: view existing words with link indicator, remove/restore, add new words with link_to selector
- Fix word suggestions: load from picture words (db_words_db_pictures) instead of object words (always empty)
- Backend PATCH /api/directus/db-pairs/<id> handles words_add with link_to and words_remove with junction IDs
- Level range 1-100 throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 12:20:21 +02:00
2e6cf094cb feat: object words in left sidebar + suggestions in pair form
- Backend: GET /api/directus/db-objects/<id>/words via db_objects_db_words
- GenerateIt: load objectWords on object select, show as chips in left sidebar
- PairForm: show object words as clickable suggestion chips above word input
  (click to add, greyed out if already added)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 12:07:42 +02:00
8bcb3b9168 feat: edit and delete pairs in GenerateIt
- Backend: PATCH /api/directus/db-pairs/<id> updates level, statement, question
  (creates question if new, removes if cleared)
- Backend: DELETE /api/directus/db-pairs/<id> removes pair + all junctions,
  questions and statements
- Frontend: inline edit form per pair (level slider, statement, question)
- Frontend: delete button per pair with confirm dialog
- api.ts: updateDbPair, deleteDbPair

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 11:58:57 +02:00
7c983a7460 refactor: migrate to new db_* Directus collections
- DrawIt: load db_pictures (status=draft), create db_objects/db_words
  with blurhash placeholder, finish sets status=objects_created
- GenerateIt: load db_pictures (status=objects_created), right panel
  replaced with manual QA pairs (db_pairs + db_question + db_statement)
- Backend: new routes for db_pictures, db_objects, db_words, db_pairs
- Types/API: full db_* type definitions and API helpers
- Directus: user_notes field in db_objects, M2M db_words<->db_pictures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 08:03:23 +02:00
Tim Leikauf
5357805530 refactor: generate_questions auf natives Directus M2M umgestellt
- Dedup via deep field query auf Object (1 GET statt 2 Junction-GETs)
- Wörter batch-linked via PATCH objects/{id}.linked_words create
- Fragen batch-linked via PATCH objects/{id}.linked_questions create
- related_words + distractor_words via PATCH questions/{id} create
- Keine direkten Junction-Table-POSTs mehr

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 22:19:42 +02:00
Tim Leikauf
9c8ec853f6 refactor: replace _ensure_junction/_ensure_link with batch Directus ops in generate_questions
- Remove per-call _ensure_junction (junction tables already exist)
- Load existing word/question links upfront (2 GET requests instead of N)
- Batch POST all new words_objects links in a single request
- Batch POST related_words and distractor_words per new question
- Eliminates O(N) serial GET+POST pattern in favour of O(1) upfront dedup + batch writes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 22:14:28 +02:00
Tim Leikauf
a622ac49df refactor(words): words_pictures auf natives Directus M2M umgestellt
- GET: Deep-Query über pictures.linked_words statt manuelle Junction-Abfrage
- POST: PATCH /items/pictures/{id} mit linked_words.create statt _ensure_link
- _ensure_junction/_ensure_link für words_pictures entfernt
- Setup-Logik in _setup_words_pictures() ausgelagert (idempotent)
- Batch-Insert aller neuen Links in einem einzigen PATCH-Call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 22:00:55 +02:00
Tim Leikauf
622907d426 fix(words): Relations-Setup automatisch beim ersten Word-Save
words_pictures M2M-Relationen werden idempotent beim POST eingerichtet –
kein manueller Setup-Call nötig.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:52:09 +02:00
Tim Leikauf
5e0de3014e feat(setup): /api/setup-words-pictures – M2M-Relation words↔pictures einrichten
Einmaliger Setup-Endpoint: setzt special=m2m auf Alias-Felder,
erstellt Relations für words_pictures Junction in Directus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:44:53 +02:00
Tim Leikauf
cc782c0ef0 fix(words): bestehendes Wort → Level updaten + Picture-Link immer setzen
_find_or_create_word gibt is_new zurück; bei is_new=False wird das Level
via PATCH aktualisiert. _ensure_link läuft immer → Picture-Junction wird
auch für bereits existierende Wörter angelegt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:30:41 +02:00
Tim Leikauf
84186110e7 feat(annotate): Words-Frame – Safe Words mit Level per Bild speichern
- Rechte Sidebar in zwei Frames aufgeteilt: Objects (bisherig) + Words (neu)
- Words-Frame: Wörter + Level (1–100) per Bild anlegen, dedupliziert via words_pictures Junction
- Pending-Words in Primary-Farbe mit inline Level-Edit, gespeicherte Words in neutralem Grau
- Save-Button speichert alle pending Words nach Directus (status=draft, title_de, level, picture-Link)
- Automatisches Laden der Bild-Words bei Bildwechsel
- Backend: GET/POST /api/directus/pictures/<pic_id>/words (words_pictures Junction, _find_or_create_word)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:05:19 +02:00
f9b3714705 Debug: Purge-Details im Alert anzeigen (Status-Breakdown pro Collection)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 21:05:48 +02:00
50d5377fb5 Fix: Archived Directus items als gelöscht behandeln
Directus archiviert Items beim Löschen (status=archived) statt hart zu löschen.
- Questions/Words-Query filtert archived heraus (filter[status][_neq]=archived)
- Purge-Endpoints behandeln archived items als nicht-existent → Junction-Zeilen werden entfernt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:47:48 +02:00
ae10e60897 Globale Orphan-Bereinigung: /api/purge-all-orphans + UI-Button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:42:13 +02:00
8f01c0396e Orphan-Junction-Cleanup + Refresh-Button für Fragen/Wörter
- Backend: DELETE question/word räumt alle Junction-Zeilen mit auf
- Backend: /purge-orphans bereinigt verwaiste Junctions per Objekt
- Frontend: reloadQW ruft purgeOrphans vor dem Neu-Laden auf
- Frontend: ↺-Button in Wörter- und Fragen-Sidebar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:38:26 +02:00
c2a2b0e6f7 Fix distractor_words special=m2m in Directus via eigenen Endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:25:09 +02:00
434dbb2b4a Setup-Schema: distractor_words special=m2m per PATCH forcieren
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:20:15 +02:00
47af0d705c short_answer_de/en/se Felder + Distractor-Wörter in Fragen-Sidebar
- Directus: questions.short_answer_de/en/se Text-Felder angelegt
- Backend: short_answer_de beim Erstellen speichern
- Backend: get_object_questions_list gibt short_answer_de + distractor_words zurück
- Frontend: Sidebar zeigt Kurzantwort (blau) + Ablenker-Chips pro Frage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:15:14 +02:00
f4a4b40914 Mehrwörtige Wort-Einträge verhindern + orange highlighten
- Backend: _sanitize_word() splittet Komma-Listen und filtert Mehrwörter raus
- Frontend: Chips mit Leerzeichen/Komma werden orange markiert (Tooltip)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 11:02:15 +02:00
a37189ce3c Fix: 2-Schritt-Query für questions/words ohne konfigurierte Directus-Relationen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:49:53 +02:00
1bc6d8b30f Add /api/setup-schema endpoint für Directus M2M Relationen
Konfiguriert alle fehlenden Directus-Relationen:
- questions.object → objects (FK fix)
- questions.short_answer → words (FK fix)
- questions ↔ objects M2M via questions_objects
- words ↔ objects M2M via words_objects
- questions ↔ words (distractor) M2M via questions_distractor_words
- words.linked_questions backref via questions_words

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:43:41 +02:00
0f83210aec Fragen & Wörter nach Generate it anzeigen und löschen können
- Neue Endpoints: GET /api/object/<id>/questions+words, DELETE /api/question/<id>, DELETE /api/word/<id>
- GenerateIt: Wörter-Sidebar mit ×-Chips, Fragen-Sidebar mit Level-Badge und ×
- Laden beim Objekt-Wechsel und nach Generate it

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:32:22 +02:00
2755632524 Optimize generate_questions: word cache + gunicorn timeout 300s
- Cache all words globally across levels (avoids 500+ Directus calls)
- Add Procfile with --timeout 300 to prevent gunicorn killing long requests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:27:12 +02:00
0360bcd1e6 Generate it / Publish it: Claude Haiku integration + Generate page redesign
- GenerateIt page: objects sidebar, readOnly canvas, collapsible prompt bar
- Generate it: calls Claude Haiku, saves questions/words to Directus as draft
- Publish it: promotes draft questions/words to published
- Deduplication: links existing words/questions instead of duplicating
- GenerateObjectsList: tree view with user_notes labels
- DrawCanvas: readOnly prop to disable mouse interaction
- api.ts: generateQuestions + publishQuestions endpoints
- requirements.txt: anthropic==0.40.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 10:16:28 +02:00
a42fadef09 Fertigstellen-Button + drawing_created Status-Flow
- DrawIt: Button "Fertigstellen" unter Objektliste setzt Picture-Status auf drawing_created
- Bild verschwindet danach aus der Annotieren-Ansicht
- GenerateIt: lädt jetzt Directus-Bilder mit status=drawing_created
- GenerateIt: zeigt Bild-Vorschau + Directus-Objekte
- app.py: PATCH-Endpunkt für Pictures + Status-Parameter im GET

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:49:13 +02:00
e18d9a5796 bbox/polygon durch selections ersetzen
- bbox und polygon Felder in Directus versteckt (Daten bleiben)
- Alle Auswahlen laufen nur noch über das selections-Feld
- CanvasObject, DirectusObject, API und Zeichenlogik umgestellt
- Objekte mit mehreren Auswahlen werden korrekt auf Canvas gezeichnet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:36:22 +02:00
807c733770 Mehrere Auswahlen in einem Objekt speichern
- Neues JSON-Feld 'selections' in Directus objects-Collection
- Alle Auswahlen (bbox/polygon) landen in einem einzigen Objekt
- Erste Auswahl bleibt weiterhin in bbox/polygon für Kompatibilität
- GET-Proxy liefert selections-Feld mit aus

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:23:22 +02:00
4cd8a63a3d Polygon-Schließen per Button + M2M-Setup-Route entfernt
- Polygon kann nun mit ≥2 Punkten über den Button geschlossen werden
- Button zeigt "Polygon schließen & hinzufügen" solange Polygon offen ist
- Automatisches Schließen (Verbindung zum Startpunkt) beim Klick
- Einmalige Setup-Route /api/directus/setup-m2m entfernt (nicht mehr benötigt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:53:14 +02:00
343d6a2389 Objekte direkt in Directus speichern + neuer Annotationsworkflow
- DirectusObject Typ + CanvasObject Interface in types.ts
- DrawCanvas nutzt CanvasObject (generisch, nicht mehr ObjectMeta-gebunden)
- Flask: /api/directus/objects (GET/POST), /api/directus/objects/<id> (PATCH/DELETE)
- Flask: /api/directus/setup-m2m (einmalig: m2m für categories/questions)
- api.ts: getDirectusObjects, createDirectusObject, updateDirectusObject, deleteDirectusObject
- DrawIt: Objekte werden in Directus gespeichert (mit picture, bbox/polygon, user_notes, parent)
- DrawIt: Linke Sidebar zeigt Objektliste mit Notizen-Editor und Löschen-Button
- DrawIt: Rechte Sidebar: Modus, user_notes Textarea, Parent-Dropdown, Auswahlen
- Directus: user_notes Feld (textarea), action/resolution/confidence/media versteckt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 20:26:46 +02:00
278289a380 Fix: Directus-Auth via Flask-Proxy (CORS umgehen)
Login und Bildliste laufen jetzt über /api/directus/* statt direkt
zu db.hejyou.com – kein CORS-Problem mehr im Browser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 11:28:11 +02:00
5d47482d2a Erster Commit 2026-04-23 22:10:45 +02:00