aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
146 lines
4.6 KiB
Python
146 lines
4.6 KiB
Python
"""
|
|
quiXzoom Academy API endpoints for LandveX Admin
|
|
"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from pydantic import BaseModel
|
|
from typing import List, Optional
|
|
from datetime import datetime
|
|
from app.core.security import get_current_user
|
|
from app.models.user import User
|
|
|
|
router = APIRouter(prefix="/academy", tags=["academy"])
|
|
|
|
# Models
|
|
class LessonCompleteRequest(BaseModel):
|
|
lesson_id: str
|
|
module_id: str
|
|
score: Optional[int] = None
|
|
|
|
class ProgressResponse(BaseModel):
|
|
user_id: str
|
|
completed_lessons: List[str]
|
|
current_module: int
|
|
total_progress: float
|
|
certificate_earned: bool
|
|
certificate_date: Optional[str] = None
|
|
|
|
class CertificateResponse(BaseModel):
|
|
pdf_url: str
|
|
verification_code: str
|
|
issue_date: str
|
|
|
|
# In-memory storage (replace with DB in production)
|
|
academy_progress = {}
|
|
|
|
LESSONS_PER_MODULE = {
|
|
"module1": 4,
|
|
"module2": 4,
|
|
"module3": 3,
|
|
"module4": 3,
|
|
"module5": 3,
|
|
"module6": 4
|
|
}
|
|
|
|
TOTAL_LESSONS = sum(LESSONS_PER_MODULE.values())
|
|
|
|
@router.get("/progress", response_model=ProgressResponse)
|
|
async def get_progress(current_user: User = Depends(get_current_user)):
|
|
"""Get user's academy progress"""
|
|
user_id = str(current_user.id)
|
|
progress = academy_progress.get(user_id, {
|
|
"completed_lessons": [],
|
|
"current_module": 1
|
|
})
|
|
|
|
completed = len(progress["completed_lessons"])
|
|
total_progress = (completed / TOTAL_LESSONS) * 100
|
|
|
|
certificate_earned = completed >= TOTAL_LESSONS
|
|
|
|
return ProgressResponse(
|
|
user_id=user_id,
|
|
completed_lessons=progress["completed_lessons"],
|
|
current_module=progress["current_module"],
|
|
total_progress=round(total_progress, 1),
|
|
certificate_earned=certificate_earned,
|
|
certificate_date=datetime.utcnow().isoformat() if certificate_earned else None
|
|
)
|
|
|
|
@router.post("/complete", status_code=status.HTTP_200_OK)
|
|
async def complete_lesson(
|
|
request: LessonCompleteRequest,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Mark a lesson as completed"""
|
|
user_id = str(current_user.id)
|
|
|
|
if user_id not in academy_progress:
|
|
academy_progress[user_id] = {
|
|
"completed_lessons": [],
|
|
"current_module": 1
|
|
}
|
|
|
|
progress = academy_progress[user_id]
|
|
|
|
if request.lesson_id not in progress["completed_lessons"]:
|
|
progress["completed_lessons"].append(request.lesson_id)
|
|
|
|
# Update current module
|
|
module_num = int(request.module_id.replace("module", ""))
|
|
module_lessons = [f"m{module_num}l{i}" for i in range(1, LESSONS_PER_MODULE[request.module_id] + 1)]
|
|
|
|
if all(l in progress["completed_lessons"] for l in module_lessons):
|
|
progress["current_module"] = min(module_num + 1, 6)
|
|
|
|
return {
|
|
"success": True,
|
|
"lesson_id": request.lesson_id,
|
|
"progress": len(progress["completed_lessons"]) / TOTAL_LESSONS * 100
|
|
}
|
|
|
|
@router.get("/certificate", response_model=CertificateResponse)
|
|
async def get_certificate(current_user: User = Depends(get_current_user)):
|
|
"""Generate certificate PDF"""
|
|
user_id = str(current_user.id)
|
|
progress = academy_progress.get(user_id, {"completed_lessons": []})
|
|
|
|
if len(progress["completed_lessons"]) < TOTAL_LESSONS:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Certificate not yet earned. Complete all lessons first."
|
|
)
|
|
|
|
# Generate verification code
|
|
verification = f"QX-{user_id[:8].upper()}-{datetime.utcnow().strftime('%Y%m%d')}"
|
|
|
|
return CertificateResponse(
|
|
pdf_url=f"/api/academy/certificate/{user_id}/download",
|
|
verification_code=verification,
|
|
issue_date=datetime.utcnow().isoformat()
|
|
)
|
|
|
|
@router.get("/stats")
|
|
async def get_academy_stats(current_user: User = Depends(get_current_user)):
|
|
"""Get academy statistics (admin only)"""
|
|
if not current_user.is_admin:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Admin access required"
|
|
)
|
|
|
|
total_users = len(academy_progress)
|
|
completed_users = sum(
|
|
1 for p in academy_progress.values()
|
|
if len(p["completed_lessons"]) >= TOTAL_LESSONS
|
|
)
|
|
|
|
return {
|
|
"total_enrolled": total_users,
|
|
"completed_all": completed_users,
|
|
"completion_rate": round(completed_users / total_users * 100, 1) if total_users > 0 else 0,
|
|
"average_progress": round(
|
|
sum(len(p["completed_lessons"]) for p in academy_progress.values()) / total_users / TOTAL_LESSONS * 100, 1
|
|
) if total_users > 0 else 0
|
|
}
|