✨ 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!
248 lines
6.4 KiB
Python
248 lines
6.4 KiB
Python
"""
|
|
Schemas Pydantic pour la configuration.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional, List
|
|
|
|
from pydantic import Field
|
|
|
|
from schemas.common import BaseSchema
|
|
|
|
|
|
class CompetenceRead(BaseSchema):
|
|
"""Schema pour la lecture d'une compétence."""
|
|
|
|
id: int
|
|
name: str
|
|
color: str
|
|
icon: str
|
|
order_index: int
|
|
|
|
|
|
class CompetenceList(BaseSchema):
|
|
"""Liste des compétences."""
|
|
|
|
competences: List[CompetenceRead]
|
|
|
|
|
|
class DomainRead(BaseSchema):
|
|
"""Schema pour la lecture d'un domaine."""
|
|
|
|
id: int
|
|
name: str
|
|
color: str
|
|
description: Optional[str] = None
|
|
|
|
|
|
class DomainList(BaseSchema):
|
|
"""Liste des domaines."""
|
|
|
|
domains: List[DomainRead]
|
|
|
|
|
|
class ScaleValueRead(BaseSchema):
|
|
"""Schema pour une valeur d'échelle de compétence."""
|
|
|
|
value: str
|
|
label: str
|
|
color: str
|
|
included_in_total: bool
|
|
|
|
|
|
class ScaleRead(BaseSchema):
|
|
"""Échelle de compétences complète."""
|
|
|
|
values: List[ScaleValueRead]
|
|
|
|
|
|
class AppConfigRead(BaseSchema):
|
|
"""Configuration de l'application."""
|
|
|
|
school_year: str
|
|
school_name: str
|
|
default_grading_system: str
|
|
|
|
|
|
class FullConfigRead(BaseSchema):
|
|
"""Configuration complète."""
|
|
|
|
app_config: AppConfigRead
|
|
competences: List[CompetenceRead]
|
|
domains: List[DomainRead]
|
|
scale: ScaleRead
|
|
|
|
|
|
# Schemas de création/mise à jour
|
|
|
|
class CompetenceCreate(BaseSchema):
|
|
"""Schema pour la création d'une compétence."""
|
|
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
color: str = Field(..., pattern=r'^#[0-9a-fA-F]{6}$')
|
|
icon: str = Field(default="star")
|
|
order_index: Optional[int] = None
|
|
|
|
|
|
class CompetenceUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour d'une compétence."""
|
|
|
|
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
color: Optional[str] = Field(None, pattern=r'^#[0-9a-fA-F]{6}$')
|
|
icon: Optional[str] = None
|
|
order_index: Optional[int] = None
|
|
|
|
|
|
class DomainCreate(BaseSchema):
|
|
"""Schema pour la création d'un domaine."""
|
|
|
|
name: str = Field(..., min_length=1, max_length=100)
|
|
color: str = Field(..., pattern=r'^#[0-9a-fA-F]{6}$')
|
|
description: Optional[str] = None
|
|
|
|
|
|
class DomainUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour d'un domaine."""
|
|
|
|
name: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
color: Optional[str] = Field(None, pattern=r'^#[0-9a-fA-F]{6}$')
|
|
description: Optional[str] = None
|
|
|
|
|
|
class ScaleValueCreate(BaseSchema):
|
|
"""Schema pour la création d'une valeur d'échelle."""
|
|
|
|
value: str = Field(..., min_length=1, max_length=10)
|
|
label: str = Field(..., min_length=1, max_length=100)
|
|
color: str = Field(..., pattern=r'^#[0-9a-fA-F]{6}$')
|
|
included_in_total: bool = True
|
|
|
|
|
|
class ScaleValueUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour d'une valeur d'échelle."""
|
|
|
|
value: Optional[str] = Field(None, min_length=1, max_length=10)
|
|
label: Optional[str] = Field(None, min_length=1, max_length=100)
|
|
color: Optional[str] = Field(None, pattern=r'^#[0-9a-fA-F]{6}$')
|
|
included_in_total: Optional[bool] = None
|
|
|
|
|
|
class AppConfigUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour de la configuration générale."""
|
|
|
|
school_year: Optional[str] = None
|
|
school_name: Optional[str] = None
|
|
default_grading_system: Optional[str] = Field(None, pattern=r'^(notes|score)$')
|
|
|
|
|
|
class ConfigResponse(BaseSchema):
|
|
"""Réponse pour les opérations CRUD de configuration."""
|
|
|
|
success: bool
|
|
message: str
|
|
|
|
|
|
# SMTP Email Configuration schemas
|
|
|
|
class SMTPConfigRead(BaseSchema):
|
|
"""Configuration SMTP pour l'envoi d'emails."""
|
|
|
|
host: str = ""
|
|
port: int = 587
|
|
username: str = ""
|
|
use_tls: bool = True
|
|
from_name: str = "Notytex"
|
|
from_address: str = ""
|
|
is_configured: bool = False # Indique si la config est complète
|
|
|
|
|
|
class SMTPConfigUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour de la configuration SMTP."""
|
|
|
|
host: Optional[str] = None
|
|
port: Optional[int] = Field(None, ge=1, le=65535)
|
|
username: Optional[str] = None
|
|
password: Optional[str] = None # Envoyé mais non retourné
|
|
use_tls: Optional[bool] = None
|
|
from_name: Optional[str] = None
|
|
from_address: Optional[str] = Field(None, pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
|
|
|
|
|
class SMTPTestRequest(BaseSchema):
|
|
"""Requête pour tester la configuration SMTP."""
|
|
|
|
test_email: str = Field(..., pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
|
|
|
|
|
class SMTPTestResponse(BaseSchema):
|
|
"""Réponse du test de configuration SMTP."""
|
|
|
|
success: bool
|
|
message: str
|
|
|
|
|
|
# Notes Gradient Configuration schemas
|
|
|
|
class NotesGradientRead(BaseSchema):
|
|
"""Configuration du dégradé de couleurs pour les notes."""
|
|
|
|
min_color: str = "#dc2626" # Rouge pour note 0
|
|
max_color: str = "#059669" # Vert pour note max
|
|
enabled: bool = False
|
|
|
|
|
|
class NotesGradientUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour du dégradé des notes."""
|
|
|
|
min_color: Optional[str] = Field(None, pattern=r'^#[0-9a-fA-F]{6}$')
|
|
max_color: Optional[str] = Field(None, pattern=r'^#[0-9a-fA-F]{6}$')
|
|
enabled: Optional[bool] = None
|
|
|
|
|
|
# Scale batch update
|
|
|
|
class ScaleValueBatchItem(BaseSchema):
|
|
"""Un item pour la mise à jour batch de l'échelle."""
|
|
|
|
value: str
|
|
label: str
|
|
color: str = Field(..., pattern=r'^#[0-9a-fA-F]{6}$')
|
|
included_in_total: bool = True
|
|
|
|
|
|
class ScaleBatchUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour batch de l'échelle."""
|
|
|
|
values: List[ScaleValueBatchItem]
|
|
|
|
|
|
# Database Configuration schemas
|
|
|
|
class DatabaseConfigRead(BaseSchema):
|
|
"""Configuration de la base de données."""
|
|
|
|
path: str # Chemin configuré (peut être relatif)
|
|
resolved_path: str # Chemin absolu résolu
|
|
exists: bool # Si le fichier existe
|
|
size_bytes: Optional[int] = None # Taille du fichier si existe
|
|
size_human: Optional[str] = None # Taille lisible (ex: "2.5 MB")
|
|
|
|
|
|
class DatabaseConfigUpdate(BaseSchema):
|
|
"""Schema pour la mise à jour du chemin de la base de données.
|
|
|
|
Note: Cette modification nécessite un redémarrage de l'application.
|
|
Le nouveau chemin sera utilisé au prochain démarrage.
|
|
"""
|
|
|
|
path: str = Field(..., min_length=1, description="Chemin vers le fichier SQLite")
|
|
|
|
|
|
class DatabaseConfigResponse(BaseSchema):
|
|
"""Réponse après mise à jour de la configuration de base de données."""
|
|
|
|
success: bool
|
|
message: str
|
|
requires_restart: bool = True
|
|
new_path: Optional[str] = None
|