392 lines
11 KiB
Markdown
392 lines
11 KiB
Markdown
|
|
# SMW-Importer Gap-Analyse & Umsetzungsplanung
|
||
|
|
|
||
|
|
**Datum:** 2026-04-24
|
||
|
|
**Status:** Requirement Analysis Complete
|
||
|
|
**Nächste Schritte:** User-Approval → Implementation
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Executive Summary
|
||
|
|
|
||
|
|
**Ziel:** SMW-Importer fertigstellen inkl. Frontend, saubere Datenbank, Issue #53 Compliance
|
||
|
|
|
||
|
|
**Aktueller Status:**
|
||
|
|
- ✅ Backend komplett (smw_client.py, smw_mapper.py, import_wiki.py)
|
||
|
|
- ✅ Migration 018 (wiki_import_tracking) deployed
|
||
|
|
- ✅ Issue #53 konform (keine fachliche Interpretation im Import)
|
||
|
|
- ✅ Duplikats-Handling via wiki_import_references
|
||
|
|
- ❌ Migrations 014-017 fehlen (Blocker für saubere DB)
|
||
|
|
- ❌ exercises.py Router nutzt Legacy-Felder (Inkonsistenz)
|
||
|
|
- ❌ Frontend für Importer fehlt komplett
|
||
|
|
|
||
|
|
**Kritischer Pfad:**
|
||
|
|
1. Migrations 014-017 anlegen (DB-Konsistenz)
|
||
|
|
2. exercises.py auf M:N umstellen (Legacy-Cleanup)
|
||
|
|
3. Frontend für Importer (Admin-UI)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Issue #53 Compliance Check (Mitai → Shinkan)
|
||
|
|
|
||
|
|
### 2.1 Was ist Issue #53?
|
||
|
|
|
||
|
|
**Quelle:** `c:\Dev\mitai-jinkendo\docs\issues\issue-53-phase-0c-multi-layer-architecture.md`
|
||
|
|
|
||
|
|
**Kern-Prinzip:**
|
||
|
|
```
|
||
|
|
┌────────────────────────────────────────────────┐
|
||
|
|
│ Layer 1: DATA LAYER │
|
||
|
|
│ - Pure data retrieval + calculation logic │
|
||
|
|
│ - Returns: Structured data (dict/list/float) │
|
||
|
|
│ - No formatting, no strings │
|
||
|
|
└──────────────────┬─────────────────────────────┘
|
||
|
|
│
|
||
|
|
┌───────────┴──────────┐
|
||
|
|
│ │
|
||
|
|
▼ ▼
|
||
|
|
┌──────────────┐ ┌─────────────────────┐
|
||
|
|
│ Layer 2a: │ │ Layer 2b: │
|
||
|
|
│ KI LAYER │ │ VISUALIZATION LAYER │
|
||
|
|
└──────────────┘ └─────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
**Import-Regeln (ARCHITECTURE.md §8):**
|
||
|
|
- ✅ **Erlaubt:** Typ-/Einheits-Konvertierung, Zuordnung CSV→Feld, Duplikat-Logik
|
||
|
|
- ❌ **Verboten:** Fachliche Interpretation, Aggregation von "Bedeutung", Metriken für Auswertung
|
||
|
|
|
||
|
|
### 2.2 Shinkan SMW-Importer Compliance
|
||
|
|
|
||
|
|
**✅ KONFORM:**
|
||
|
|
```python
|
||
|
|
# smw_mapper.py - Nur Feld-Zuordnung
|
||
|
|
EXERCISE_PROPERTY_MAP = {
|
||
|
|
"Übungsbezeichnung": "title_override",
|
||
|
|
"Ziel": "goal",
|
||
|
|
"Durchführung": "execution",
|
||
|
|
"Plandauer": "duration_raw", # String → Int Konvertierung
|
||
|
|
"Übungstyp": "focus_area_names", # Name → ID Lookup
|
||
|
|
}
|
||
|
|
|
||
|
|
# Typ-Konvertierung (erlaubt)
|
||
|
|
duration_min = safe_int(raw_value.get("Plandauer"))
|
||
|
|
|
||
|
|
# Keine fachliche Interpretation ✅
|
||
|
|
# Keine Metriken-Berechnung ✅
|
||
|
|
```
|
||
|
|
|
||
|
|
**✅ Duplikats-Handling:**
|
||
|
|
```python
|
||
|
|
# import_wiki.py - reimport-safe
|
||
|
|
if not reimport:
|
||
|
|
cur.execute(
|
||
|
|
"SELECT id FROM wiki_import_references WHERE wiki_page_title = %s",
|
||
|
|
(page_title, import_type)
|
||
|
|
)
|
||
|
|
if cur.fetchone():
|
||
|
|
stats["skipped"] += 1
|
||
|
|
continue
|
||
|
|
```
|
||
|
|
|
||
|
|
**✅ M:N Katalog-Zuordnung:**
|
||
|
|
```python
|
||
|
|
# _assign_exercise_catalogs() - lookup only, keine Berechnung
|
||
|
|
cur.execute("SELECT id FROM focus_areas WHERE name ILIKE %s", (name,))
|
||
|
|
if row:
|
||
|
|
cur.execute(
|
||
|
|
"INSERT INTO exercise_focus_areas (exercise_id, focus_area_id, is_primary) ..."
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fazit:** SMW-Importer ist Issue #53 konform. KEIN Refactoring nötig.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Datenbank-Inkonsistenz: Legacy vs. M:N
|
||
|
|
|
||
|
|
### 3.1 Problem
|
||
|
|
|
||
|
|
**Migration 005** (deployed) definiert Legacy-Felder:
|
||
|
|
```sql
|
||
|
|
CREATE TABLE exercises (
|
||
|
|
...
|
||
|
|
age_groups JSONB, -- ["minis", "kinder", "erwachsene"]
|
||
|
|
focus_area VARCHAR(100), -- karate, selbstverteidigung, gewaltschutz
|
||
|
|
secondary_areas JSONB,
|
||
|
|
...
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration 008** (deployed) definiert M:N Tabellen:
|
||
|
|
```sql
|
||
|
|
CREATE TABLE exercise_focus_areas (...);
|
||
|
|
CREATE TABLE exercise_age_groups (...);
|
||
|
|
CREATE TABLE exercise_target_groups (...);
|
||
|
|
```
|
||
|
|
|
||
|
|
**exercises.py Router** (aktuell) nutzt BEIDE:
|
||
|
|
```python
|
||
|
|
# Legacy beim CREATE
|
||
|
|
data.get('age_groups'), # JSONB
|
||
|
|
data.get('focus_area'), # VARCHAR
|
||
|
|
|
||
|
|
# M:N beim GET
|
||
|
|
SELECT fa.name FROM exercise_focus_areas efa
|
||
|
|
JOIN focus_areas fa ON efa.focus_area_id = fa.id
|
||
|
|
```
|
||
|
|
|
||
|
|
**Migration 014** (geplant) würde Legacy-Felder droppen:
|
||
|
|
```sql
|
||
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS age_groups;
|
||
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS focus_area;
|
||
|
|
ALTER TABLE exercises DROP COLUMN IF EXISTS secondary_areas;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 Auswirkung
|
||
|
|
|
||
|
|
**Wenn Migration 014 jetzt ausgeführt wird:**
|
||
|
|
- ✅ SMW-Importer funktioniert (nutzt nur M:N)
|
||
|
|
- ❌ exercises.py CREATE bricht (INSERT referenziert gedropte Spalten)
|
||
|
|
- ❌ exercises.py GET funktioniert (nutzt M:N)
|
||
|
|
|
||
|
|
**Lösung:** exercises.py auf M:N umstellen BEVOR Migration 014
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Fehlende Migrations 014-017
|
||
|
|
|
||
|
|
### 4.1 Inhalt
|
||
|
|
|
||
|
|
**Migration 014: Variant Progression + Search + Legacy Cleanup**
|
||
|
|
- Variant Progression (progression_level, sequence_order, prerequisite_variant_id)
|
||
|
|
- Search Vector (tsvector für Volltext-Suche)
|
||
|
|
- Legacy DROP (age_groups, focus_area, secondary_areas)
|
||
|
|
|
||
|
|
**Migration 015: Semantic Matching (OPTIONAL - Phase 2)**
|
||
|
|
- exercise_embeddings Tabelle für Vector Search
|
||
|
|
|
||
|
|
**Migration 016: Saved Searches**
|
||
|
|
- saved_exercise_searches Tabelle
|
||
|
|
|
||
|
|
**Migration 017: Exercise Blocks**
|
||
|
|
- exercise_blocks + exercise_block_items
|
||
|
|
- Template Blocks (is_template, is_placeholder, placeholder_criteria)
|
||
|
|
|
||
|
|
### 4.2 SQL-Dateien
|
||
|
|
|
||
|
|
**Status:** Spec-Inhalt existiert in `EXERCISES_DATABASE_FINAL.md`, muss als .sql extrahiert werden
|
||
|
|
|
||
|
|
**Action Items:**
|
||
|
|
- [ ] 014_variant_progression_search_legacy.sql anlegen
|
||
|
|
- [ ] 015_semantic_matching.sql anlegen (OPTIONAL)
|
||
|
|
- [ ] 016_saved_searches.sql anlegen
|
||
|
|
- [ ] 017_exercise_blocks.sql anlegen
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Frontend für SMW-Importer
|
||
|
|
|
||
|
|
### 5.1 Fehlende Komponenten
|
||
|
|
|
||
|
|
**Admin-Seite:**
|
||
|
|
- `frontend/src/pages/MediaWikiImportPage.jsx` (NEU)
|
||
|
|
|
||
|
|
**API-Funktionen:**
|
||
|
|
- `frontend/src/utils/api.js` - Wiki-Import Funktionen ergänzen
|
||
|
|
|
||
|
|
**Navigation:**
|
||
|
|
- Admin-Navigation erweitern (Link zu Import-Seite)
|
||
|
|
|
||
|
|
### 5.2 UI-Requirements
|
||
|
|
|
||
|
|
**3-Tab Layout:**
|
||
|
|
1. **Preview Tab**
|
||
|
|
- Kategorie-Auswahl (Übungen, Fähigkeiten, Methoden)
|
||
|
|
- Limit-Auswahl (10, 50, 100)
|
||
|
|
- Preview-Button → zeigt Tabelle mit:
|
||
|
|
- Wiki-Seitentitel
|
||
|
|
- Already Imported (Ja/Nein)
|
||
|
|
- Last Imported (Datum)
|
||
|
|
- Mapped Fields (Accordion)
|
||
|
|
- Warnings/Errors
|
||
|
|
|
||
|
|
2. **Execute Tab**
|
||
|
|
- Import-Typ (Exercise, Skill, Method)
|
||
|
|
- Kategorie-Input
|
||
|
|
- Reimport-Existing (Checkbox)
|
||
|
|
- Dry-Run (Checkbox)
|
||
|
|
- Limit (Optional)
|
||
|
|
- Execute-Button → startet Background-Task
|
||
|
|
- Status-Display (Running, Completed, Failed)
|
||
|
|
- Progress (Imported/Skipped/Failed Counts)
|
||
|
|
|
||
|
|
3. **History Tab**
|
||
|
|
- Liste aller Import-Logs (neueste zuerst)
|
||
|
|
- Spalten: Type, Category, Status, Items (Total/Imported/Skipped/Failed), Date
|
||
|
|
- Expandable Row für Error-Details
|
||
|
|
|
||
|
|
**API-Endpoints (bereits vorhanden):**
|
||
|
|
- `GET /api/import/mediawiki/preview?category=Übungen&import_type=exercise&limit=10`
|
||
|
|
- `POST /api/import/mediawiki/execute` (Body: category, import_type, reimport, dry_run, limit)
|
||
|
|
- `GET /api/import/mediawiki/status/{log_id}`
|
||
|
|
- `GET /api/import/mediawiki/logs`
|
||
|
|
- `DELETE /api/import/mediawiki/references/{ref_id}` (Force Reimport)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Umsetzungsplan (priorisiert)
|
||
|
|
|
||
|
|
### Phase 1: Datenbank-Konsistenz (BLOCKER)
|
||
|
|
|
||
|
|
**1.1 Migrations anlegen**
|
||
|
|
- [ ] Migration 014 als .sql (Variant + Search + **Legacy DROP**)
|
||
|
|
- [ ] Migration 015 als .sql (Semantic Matching) - OPTIONAL, kann warten
|
||
|
|
- [ ] Migration 016 als .sql (Saved Searches)
|
||
|
|
- [ ] Migration 017 als .sql (Exercise Blocks)
|
||
|
|
|
||
|
|
**Aufwand:** 2-3h (SQL aus Spec extrahieren, testen)
|
||
|
|
|
||
|
|
**1.2 exercises.py Router überarbeiten**
|
||
|
|
- [ ] CREATE: Legacy-Felder entfernen, nur M:N nutzen
|
||
|
|
- [ ] UPDATE: Legacy-Felder entfernen
|
||
|
|
- [ ] Query-Filter: focus_area String-Filter durch M:N-Join ersetzen
|
||
|
|
|
||
|
|
**Aufwand:** 1-2h
|
||
|
|
|
||
|
|
**1.3 Deployment**
|
||
|
|
- [ ] Migrations 014-017 deployen (dev → prod)
|
||
|
|
- [ ] exercises.py deployen
|
||
|
|
- [ ] Testen: SMW-Import funktioniert
|
||
|
|
|
||
|
|
**Aufwand:** 1h
|
||
|
|
|
||
|
|
**Total Phase 1:** 4-6h
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 2: SMW-Importer Frontend
|
||
|
|
|
||
|
|
**2.1 API-Funktionen**
|
||
|
|
- [ ] `api.js` erweitern:
|
||
|
|
- `previewMediaWikiImport(category, importType, limit)`
|
||
|
|
- `executeMediaWikiImport(category, importType, reimport, dryRun, limit)`
|
||
|
|
- `getMediaWikiImportStatus(logId)`
|
||
|
|
- `listMediaWikiImportLogs()`
|
||
|
|
- `deleteMediaWikiImportReference(refId)`
|
||
|
|
|
||
|
|
**Aufwand:** 0.5h
|
||
|
|
|
||
|
|
**2.2 MediaWikiImportPage.jsx**
|
||
|
|
- [ ] 3-Tab Layout (Preview, Execute, History)
|
||
|
|
- [ ] Preview: Kategorie-Auswahl, Preview-Tabelle mit Accordions
|
||
|
|
- [ ] Execute: Form mit Status-Polling
|
||
|
|
- [ ] History: Tabelle mit Expandable Rows
|
||
|
|
|
||
|
|
**Aufwand:** 3-4h
|
||
|
|
|
||
|
|
**2.3 Navigation**
|
||
|
|
- [ ] Admin-Navigation erweitern (Link zu Import-Seite)
|
||
|
|
|
||
|
|
**Aufwand:** 0.5h
|
||
|
|
|
||
|
|
**Total Phase 2:** 4-5h
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Phase 3: Testing & Refinement
|
||
|
|
|
||
|
|
**3.1 Integration Testing**
|
||
|
|
- [ ] Dev-System: Import von BINGO + Speed Jab Drills
|
||
|
|
- [ ] Prüfen: Alle M:N Tabellen korrekt befüllt
|
||
|
|
- [ ] Prüfen: Reimport überschreibt korrekt
|
||
|
|
- [ ] Prüfen: Duplikats-Handling funktioniert
|
||
|
|
|
||
|
|
**Aufwand:** 1-2h
|
||
|
|
|
||
|
|
**3.2 Prod Deployment**
|
||
|
|
- [ ] SMW-Importer deployen (dev → prod)
|
||
|
|
- [ ] Erste echte Imports durchführen
|
||
|
|
|
||
|
|
**Aufwand:** 1h
|
||
|
|
|
||
|
|
**Total Phase 3:** 2-3h
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. Gesamt-Aufwand
|
||
|
|
|
||
|
|
| Phase | Tasks | Aufwand |
|
||
|
|
|-------|-------|---------|
|
||
|
|
| Phase 1: DB-Konsistenz | Migrations + Router | 4-6h |
|
||
|
|
| Phase 2: Frontend | UI + API | 4-5h |
|
||
|
|
| Phase 3: Testing | Integration + Deploy | 2-3h |
|
||
|
|
| **TOTAL** | | **10-14h** |
|
||
|
|
|
||
|
|
**Empfehlung:** Phase 1 zuerst (kritisch für DB-Konsistenz), dann Phase 2+3
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. Offene Fragen
|
||
|
|
|
||
|
|
**Q1:** Sollen wir Migration 015 (Semantic Matching) jetzt anlegen oder später?
|
||
|
|
- **Pro:** Komplett-System
|
||
|
|
- **Con:** OPTIONAL, braucht Vector-Embeddings (nicht kritisch)
|
||
|
|
- **Empfehlung:** SKIPPEN, erst bei Bedarf
|
||
|
|
|
||
|
|
**Q2:** Soll exercises.py komplett refactored werden oder nur Legacy-Felder entfernen?
|
||
|
|
- **Empfehlung:** Nur Legacy-Felder entfernen (minimal invasiv)
|
||
|
|
|
||
|
|
**Q3:** Data Layer in Shinkan anlegen (wie Mitai)?
|
||
|
|
- **Status:** Shinkan hat KEIN `data_layer/` Verzeichnis
|
||
|
|
- **Empfehlung:** SPÄTER, nicht Blocker für Importer
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. Issue #53 Learnings für Shinkan
|
||
|
|
|
||
|
|
**Aus Mitai übernehmen:**
|
||
|
|
1. ✅ Import darf nur Feld-Zuordnung + Typ-Konvertierung (bereits konform)
|
||
|
|
2. ✅ Keine fachliche Interpretation im Import (bereits konform)
|
||
|
|
3. ⏸️ Data Layer für Metriken-Berechnung (SPÄTER, separate Task)
|
||
|
|
|
||
|
|
**NICHT übernehmen:**
|
||
|
|
- ❌ Komplett-Refactoring jetzt (zu großer Scope)
|
||
|
|
- ❌ Chart-Endpoints jetzt (kein Bedarf)
|
||
|
|
|
||
|
|
**Fazit:** Shinkan Import ist bereits Issue #53 konform. Kein Action-Item.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. Nächste Schritte (User-Approval erforderlich)
|
||
|
|
|
||
|
|
**Option A: Komplett-Durchlauf (empfohlen)**
|
||
|
|
1. Migrations 014-017 anlegen
|
||
|
|
2. exercises.py auf M:N umstellen
|
||
|
|
3. Frontend für Importer bauen
|
||
|
|
4. Testen + Deployen
|
||
|
|
|
||
|
|
**Aufwand:** 10-14h
|
||
|
|
|
||
|
|
**Option B: Minimaler Pfad (schnell, aber unvollständig)**
|
||
|
|
1. Nur Migration 014 anlegen (Legacy DROP)
|
||
|
|
2. exercises.py minimal fixen
|
||
|
|
3. Frontend SPÄTER
|
||
|
|
|
||
|
|
**Aufwand:** 4-6h, aber Frontend fehlt
|
||
|
|
|
||
|
|
**Option C: User entscheidet Priorität**
|
||
|
|
- Welche Phase zuerst?
|
||
|
|
- Migration 015 (Semantic Matching) jetzt oder später?
|
||
|
|
- exercises.py komplett refactorn oder minimal?
|
||
|
|
|
||
|
|
**Empfehlung:** Option A (Komplett-Durchlauf), weil:
|
||
|
|
- Saubere Datenbank (keine Legacy-Felder)
|
||
|
|
- Vollständiger Importer (Backend + Frontend)
|
||
|
|
- Issue #53 konform
|
||
|
|
- Basis für zukünftige Features
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Autor:** Claude Code
|
||
|
|
**Version:** 1.0
|
||
|
|
**Status:** Pending User-Approval
|