fix: reimport dict-cursor bug + add admin cleanup endpoint
Problem 1: Reimport fails completely (all exercises fail) Root cause: Line 465 existing[0] instead of existing['id'] Fix: Changed to existing['id'] for dict-cursor compatibility Problem 2: 221 exercises skipped, only 2 in database Root cause: Orphaned wiki_import_references from failed imports Fix: New admin endpoint to manually cleanup orphaned references New endpoints: - DELETE /api/import/mediawiki/admin/cleanup-orphaned-references Deletes all references where local_id doesn't exist in target table Returns count of deleted references per type - GET /api/import/mediawiki/admin/stats Shows references vs. actual count + orphaned count Helps diagnose import state issues Usage: 1. Call cleanup endpoint to remove orphaned references 2. Run normal import (reimport=false) 3. Should import all previously failed exercises
This commit is contained in:
parent
3a4160fd1c
commit
97a7fe7cba
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
95
backend/routers/import_wiki_admin.py
Normal file
95
backend/routers/import_wiki_admin.py
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user