Files
notytex/backend/schemas/assessment.py
Bertrand Benjamin 2b08eb534a Migration v1 (Flask) -> v2 (FastAPI + Vue.js) complétée
 Changements majeurs:
- Suppression complète du code Flask legacy
- Migration backend FastAPI vers racine /backend
- Migration frontend Vue.js vers racine /frontend
- Suppression de notytex-v2/ (code monté à la racine)

 Validations:
- Backend démarre correctement (port 8000)
- API /api/v2/health répond healthy
- 99/99 tests unitaires passent
- Frontend configuré avec proxy Vite

📝 Documentation:
- README.md réécrit pour v2
- Instructions de démarrage mises à jour
- .gitignore adapté pour backend/frontend/

🎯 Architecture finale:
notytex/
├── backend/     # FastAPI + SQLAlchemy + Pydantic
├── frontend/    # Vue 3 + Vite + TailwindCSS
├── docs/        # Documentation
└── school_management.db  # Base de données (inchangée)

Jalon 6 complété: Application v2 prête pour utilisation!
2025-11-25 21:09:47 +01:00

237 lines
6.0 KiB
Python

"""
Schemas Pydantic pour Assessment, Exercise, GradingElement.
"""
from datetime import date, datetime
from typing import Optional, List, Union
from pydantic import Field, field_validator
from schemas.common import BaseSchema
# GradingElement schemas
class GradingElementBase(BaseSchema):
"""Schema de base pour GradingElement."""
label: str
description: Optional[str] = None
skill: Optional[str] = None
max_points: float
grading_type: str = "notes"
domain_id: Optional[int] = None
class GradingElementRead(GradingElementBase):
"""Schema pour la lecture d'un GradingElement."""
id: int
exercise_id: int
domain_name: Optional[str] = None
domain_color: Optional[str] = None
# Exercise schemas
class ExerciseBase(BaseSchema):
"""Schema de base pour Exercise."""
title: str
description: Optional[str] = None
order: int = 1
class ExerciseRead(ExerciseBase):
"""Schema pour la lecture d'un Exercise."""
id: int
assessment_id: int
grading_elements: List[GradingElementRead] = []
@property
def total_points(self) -> float:
return sum(e.max_points for e in self.grading_elements)
# Assessment schemas
class AssessmentBase(BaseSchema):
"""Schema de base pour Assessment."""
title: str
description: Optional[str] = None
date: date
trimester: int = Field(ge=1, le=3)
coefficient: float = 1.0
class AssessmentRead(AssessmentBase):
"""Schema pour la lecture d'un Assessment."""
id: int
class_group_id: int
class_name: Optional[str] = None
class AssessmentWithProgress(AssessmentRead):
"""Schema avec progression de correction."""
grading_progress: dict = Field(default_factory=dict)
total_max_points: float = 0
class AssessmentDetail(AssessmentWithProgress):
"""Schema détaillé avec exercices."""
exercises: List[ExerciseRead] = []
class AssessmentList(BaseSchema):
"""Schema pour la liste des évaluations."""
assessments: List[AssessmentWithProgress]
total: int
# Schemas pour les résultats
class StudentScore(BaseSchema):
"""Score d'un élève pour une évaluation."""
student_id: int
student_name: str
total_score: float
total_max_points: float
percentage: float
exercise_scores: dict = Field(default_factory=dict)
has_grades: bool = False # Indique si l'élève a au moins une note
class AssessmentStatistics(BaseSchema):
"""Statistiques d'une évaluation."""
count: int
mean: float
median: float
min: float
max: float
std_dev: float
class HeatmapCell(BaseSchema):
"""Cellule d'une heatmap (élève x compétence/domaine)."""
student_id: int
student_name: str
item_name: str # Nom de la compétence ou du domaine
score: float
max_points: float
percentage: float
color: Optional[str] = None
class HeatmapData(BaseSchema):
"""Données pour une heatmap."""
items: List[str] # Liste des compétences ou domaines
students: List[str] # Liste des noms d'élèves
cells: List[HeatmapCell] # Données par cellule
class AssessmentResults(BaseSchema):
"""Résultats complets d'une évaluation."""
assessment_id: int
assessment_title: str
statistics: AssessmentStatistics
students_scores: List[StudentScore]
histogram_data: List[int] = Field(default_factory=list)
competences_heatmap: Optional[HeatmapData] = None
domains_heatmap: Optional[HeatmapData] = None
# Schemas pour les opérations d'écriture (CRUD)
class GradingElementCreate(BaseSchema):
"""Schema pour créer ou mettre à jour un élément de notation."""
id: Optional[int] = None # Si présent, mise à jour; sinon création
label: str
description: Optional[str] = None
skill: Optional[str] = None
max_points: float
grading_type: str = "notes"
domain_id: Optional[int] = None
class ExerciseCreate(BaseSchema):
"""Schema pour créer ou mettre à jour un exercice avec ses éléments."""
id: Optional[int] = None # Si présent, mise à jour; sinon création
title: str
description: Optional[str] = None
order: int = 1
grading_elements: List[GradingElementCreate] = Field(default_factory=list)
class AssessmentCreate(AssessmentBase):
"""Schema pour créer une évaluation complète (unifiée)."""
class_group_id: int
exercises: List[ExerciseCreate] = Field(default_factory=list)
class AssessmentUpdate(BaseSchema):
"""Schema pour modifier une évaluation."""
title: Optional[str] = None
description: Optional[str] = None
date: Optional[str] = None # Accepter string, convertir après
trimester: Optional[int] = None
coefficient: Optional[float] = None
# Pour modification complète avec exercices
exercises: Optional[List[ExerciseCreate]] = None
@field_validator('date', mode='before')
@classmethod
def parse_date(cls, v):
if v is None:
return None
if isinstance(v, date):
return v.isoformat()
return v
class AssessmentResponse(BaseSchema):
"""Schema de réponse après création/modification."""
id: int
title: str
message: str = ""
# Schemas pour l'envoi de bilans par email
class SendReportsRequest(BaseSchema):
"""Requête pour envoyer des bilans par email."""
student_ids: List[int] = Field(..., min_length=1, description="Liste des IDs d'élèves")
custom_message: Optional[str] = Field(None, description="Message personnalisé du professeur")
include_class_stats: bool = Field(True, description="Inclure les statistiques de classe")
class SendReportResult(BaseSchema):
"""Résultat de l'envoi pour un élève."""
student_id: int
student_name: str
email: Optional[str] = None
success: bool
error_message: Optional[str] = None
class SendReportsResponse(BaseSchema):
"""Réponse de l'envoi de bilans."""
success: bool
total_sent: int
total_failed: int
results: List[SendReportResult]
message: str