admin mode
This commit is contained in:
@@ -2,6 +2,7 @@ from contextlib import asynccontextmanager
|
||||
import os
|
||||
from fastapi import Depends, FastAPI, File, HTTPException, Response, UploadFile
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
from .database import Base, engine, get_db
|
||||
from . import models, schemas
|
||||
@@ -15,6 +16,7 @@ from .auth import (
|
||||
verify_password,
|
||||
)
|
||||
from .curriculum import QUESTIONS
|
||||
from .program_content import decode_asset_token, get_lesson, list_lessons_for_grade, normalize_grade
|
||||
from .services import (
|
||||
build_llm_reply,
|
||||
ensure_student_mastery,
|
||||
@@ -59,7 +61,12 @@ def ensure_student_access(current_user: models.User, student_id: int) -> None:
|
||||
return
|
||||
if current_user.role == "student" and current_user.student_id == student_id:
|
||||
return
|
||||
raise HTTPException(status_code=403, detail="Droits insuffisants pour cet eleve")
|
||||
raise HTTPException(status_code=403, detail="Droits insuffisants pour cet élève")
|
||||
|
||||
|
||||
def build_student_program_response(student: models.Student, assignment: models.StudentProgramAssignment | None):
|
||||
lesson = get_lesson(assignment.lesson_id) if assignment else None
|
||||
return schemas.StudentProgramResponse(student=student, lesson=lesson)
|
||||
|
||||
@app.get("/health")
|
||||
def health():
|
||||
@@ -127,7 +134,7 @@ def create_student_account(
|
||||
username = payload.username.strip()
|
||||
first_name = payload.first_name.strip()
|
||||
if not username or not first_name:
|
||||
raise HTTPException(status_code=400, detail="Prenom et identifiant obligatoires")
|
||||
raise HTTPException(status_code=400, detail="Prénom et identifiant obligatoires")
|
||||
|
||||
existing_user = db.query(models.User).filter_by(username=username).first()
|
||||
if existing_user:
|
||||
@@ -165,7 +172,7 @@ def list_student_messages(
|
||||
ensure_student_access(current_user, student_id)
|
||||
student = db.query(models.Student).filter_by(id=student_id).first()
|
||||
if not student:
|
||||
raise HTTPException(status_code=404, detail="Eleve introuvable")
|
||||
raise HTTPException(status_code=404, detail="Élève introuvable")
|
||||
|
||||
safe_limit = min(max(limit, 1), 200)
|
||||
rows = (
|
||||
@@ -178,6 +185,72 @@ def list_student_messages(
|
||||
return list(reversed(rows))
|
||||
|
||||
|
||||
@app.get("/program/lessons", response_model=schemas.ProgramLessonListResponse)
|
||||
def list_program_lessons(
|
||||
grade: str,
|
||||
current_user: models.User = Depends(require_roles("teacher", "maintenance")),
|
||||
):
|
||||
return schemas.ProgramLessonListResponse(lessons=list_lessons_for_grade(grade))
|
||||
|
||||
|
||||
@app.get("/program/assets/{asset_token}")
|
||||
def get_program_asset(
|
||||
asset_token: str,
|
||||
current_user: models.User = Depends(get_current_user),
|
||||
):
|
||||
try:
|
||||
path = decode_asset_token(asset_token)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail="Ressource invalide") from exc
|
||||
if not path.exists() or path.suffix.lower() != ".svg":
|
||||
raise HTTPException(status_code=404, detail="Fiche introuvable")
|
||||
return FileResponse(path, media_type="image/svg+xml")
|
||||
|
||||
|
||||
@app.get("/students/{student_id}/program", response_model=schemas.StudentProgramResponse)
|
||||
def get_student_program(
|
||||
student_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(get_current_user),
|
||||
):
|
||||
ensure_student_access(current_user, student_id)
|
||||
student = db.query(models.Student).filter_by(id=student_id).first()
|
||||
if not student:
|
||||
raise HTTPException(status_code=404, detail="Élève introuvable")
|
||||
assignment = db.query(models.StudentProgramAssignment).filter_by(student_id=student_id).first()
|
||||
return build_student_program_response(student, assignment)
|
||||
|
||||
|
||||
@app.put("/admin/students/{student_id}/program", response_model=schemas.StudentProgramResponse)
|
||||
def assign_student_program(
|
||||
student_id: int,
|
||||
payload: schemas.StudentProgramAssignRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(require_roles("teacher", "maintenance")),
|
||||
):
|
||||
student = db.query(models.Student).filter_by(id=student_id).first()
|
||||
if not student:
|
||||
raise HTTPException(status_code=404, detail="Élève introuvable")
|
||||
|
||||
lesson = get_lesson(payload.lesson_id)
|
||||
if not lesson:
|
||||
raise HTTPException(status_code=404, detail="Leçon introuvable")
|
||||
if lesson["grade"] != normalize_grade(student.grade):
|
||||
raise HTTPException(status_code=400, detail="Cette leçon ne correspond pas au niveau de l’élève")
|
||||
|
||||
assignment = db.query(models.StudentProgramAssignment).filter_by(student_id=student_id).first()
|
||||
if not assignment:
|
||||
assignment = models.StudentProgramAssignment(student_id=student_id)
|
||||
assignment.lesson_id = lesson["id"]
|
||||
assignment.title = lesson["title"]
|
||||
assignment.subject = lesson["subject"]
|
||||
assignment.grade = lesson["grade"]
|
||||
db.add(assignment)
|
||||
db.commit()
|
||||
db.refresh(assignment)
|
||||
return build_student_program_response(student, assignment)
|
||||
|
||||
|
||||
@app.post("/session/start", response_model=schemas.ChatResponse)
|
||||
def start_session(
|
||||
student_id: int,
|
||||
@@ -194,18 +267,24 @@ def start_session(
|
||||
f"Bonjour {student.first_name} ! Je suis Professeur TOP, ton professeur virtuel. "
|
||||
"Aujourd'hui, on va apprendre pas à pas et faire un petit test pour voir ce que tu maîtrises déjà."
|
||||
)
|
||||
assignment = db.query(models.StudentProgramAssignment).filter_by(student_id=student.id).first()
|
||||
assigned_lesson = get_lesson(assignment.lesson_id) if assignment else None
|
||||
if assigned_lesson:
|
||||
message = (
|
||||
f"Bonjour {student.first_name} ! Aujourd’hui, on travaille la leçon "
|
||||
f"{assigned_lesson['title']}. Regarde la première fiche, puis dis-moi ce que tu remarques."
|
||||
)
|
||||
previous_messages_count = db.query(models.Message).filter_by(student_id=student.id).count()
|
||||
if previous_messages_count:
|
||||
message = build_llm_reply(
|
||||
db,
|
||||
student.id,
|
||||
(
|
||||
"L'eleve revient pour une nouvelle seance. Fais une courte reprise: "
|
||||
"explique ou on s'etait arrete la derniere fois a partir de l'historique, "
|
||||
"puis annonce clairement ce qu'on va faire aujourd'hui. Termine par une "
|
||||
"premiere consigne simple."
|
||||
),
|
||||
prompt = (
|
||||
"L’élève revient pour une nouvelle séance. Fais une courte reprise: "
|
||||
"explique où on s’était arrêté la dernière fois à partir de l’historique, "
|
||||
"puis annonce clairement ce qu'on va faire aujourd'hui. Termine par une "
|
||||
"première consigne simple."
|
||||
)
|
||||
if assigned_lesson:
|
||||
prompt += f" La leçon active choisie par le professeur est: {assigned_lesson['title']}."
|
||||
message = build_llm_reply(db, student.id, prompt)
|
||||
db.add(models.Message(student_id=student.id, role="assistant", content=message))
|
||||
db.commit()
|
||||
return schemas.ChatResponse(reply=message)
|
||||
|
||||
Reference in New Issue
Block a user