diff --git a/backend/main.py b/backend/main.py index 37e379a..c45b218 100644 --- a/backend/main.py +++ b/backend/main.py @@ -70,7 +70,7 @@ def read_root(): } # Register routers -from routers import auth, profiles, exercises, clubs, skills, training_planning, catalogs, import_wiki +from routers import auth, profiles, exercises, clubs, skills, training_planning, catalogs, import_wiki, import_wiki_admin app.include_router(auth.router) app.include_router(profiles.router) @@ -80,6 +80,7 @@ app.include_router(skills.router) app.include_router(training_planning.router) app.include_router(catalogs.router) app.include_router(import_wiki.router) +app.include_router(import_wiki_admin.router) if __name__ == "__main__": import uvicorn diff --git a/backend/routers/import_wiki.py b/backend/routers/import_wiki.py index 9d4475b..fc24287 100644 --- a/backend/routers/import_wiki.py +++ b/backend/routers/import_wiki.py @@ -462,7 +462,7 @@ def _upsert_exercise(mapped: dict, reimport: bool) -> Optional[int]: equipment = _json.dumps(mapped.get("equipment", [])) if mapped.get("equipment") else None if existing and reimport: - ex_id = existing[0] + ex_id = existing['id'] cur.execute( """UPDATE exercises SET title = %s, summary = %s, goal = %s, execution = %s, diff --git a/backend/routers/import_wiki_admin.py b/backend/routers/import_wiki_admin.py new file mode 100644 index 0000000..c26d8d1 --- /dev/null +++ b/backend/routers/import_wiki_admin.py @@ -0,0 +1,95 @@ +""" +Admin-Hilfsfunktionen für MediaWiki-Import +""" +from fastapi import APIRouter, Depends, HTTPException +from db import get_db, get_cursor +from routers.import_wiki import require_admin + +router = APIRouter(prefix="/api/import/mediawiki/admin", tags=["import-admin"]) + + +@router.delete("/cleanup-orphaned-references") +def cleanup_orphaned_references(session: dict = Depends(require_admin)): + """ + Löscht verwaiste Import-Referenzen (Referenz existiert, aber Übung fehlt). + Nützlich nach fehlgeschlagenen Imports. + """ + deleted = {"exercises": 0, "skills": 0, "methods": 0} + + with get_db() as conn: + cur = get_cursor(conn) + + # Exercises + cur.execute(""" + DELETE FROM wiki_import_references + WHERE content_type = 'exercise' + AND local_id NOT IN (SELECT id FROM exercises) + """) + deleted["exercises"] = cur.rowcount + + # Skills + cur.execute(""" + DELETE FROM wiki_import_references + WHERE content_type = 'skill' + AND local_id NOT IN (SELECT id FROM skills) + """) + deleted["skills"] = cur.rowcount + + # Methods + cur.execute(""" + DELETE FROM wiki_import_references + WHERE content_type = 'method' + AND local_id NOT IN (SELECT id FROM training_methods) + """) + deleted["methods"] = cur.rowcount + + conn.commit() + + return { + "ok": True, + "deleted": deleted, + "message": f"Deleted {sum(deleted.values())} orphaned references" + } + + +@router.get("/stats") +def get_import_stats(session: dict = Depends(require_admin)): + """Statistik über Import-Referenzen vs. tatsächliche Einträge.""" + with get_db() as conn: + cur = get_cursor(conn) + + # Exercises + cur.execute("SELECT COUNT(*) as count FROM wiki_import_references WHERE content_type = 'exercise'") + refs_exercises = cur.fetchone()['count'] + cur.execute("SELECT COUNT(*) as count FROM exercises WHERE import_source = 'mediawiki'") + actual_exercises = cur.fetchone()['count'] + + # Skills + cur.execute("SELECT COUNT(*) as count FROM wiki_import_references WHERE content_type = 'skill'") + refs_skills = cur.fetchone()['count'] + cur.execute("SELECT COUNT(*) as count FROM skills") + actual_skills = cur.fetchone()['count'] + + # Methods + cur.execute("SELECT COUNT(*) as count FROM wiki_import_references WHERE content_type = 'method'") + refs_methods = cur.fetchone()['count'] + cur.execute("SELECT COUNT(*) as count FROM training_methods") + actual_methods = cur.fetchone()['count'] + + return { + "exercises": { + "references": refs_exercises, + "actual": actual_exercises, + "orphaned": refs_exercises - actual_exercises + }, + "skills": { + "references": refs_skills, + "actual": actual_skills, + "orphaned": refs_skills - actual_skills + }, + "methods": { + "references": refs_methods, + "actual": actual_methods, + "orphaned": refs_methods - actual_methods + } + }