Files
notytex/backend/domain/value_objects/statistics.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

111 lines
2.6 KiB
Python

"""
Value Objects pour les statistiques.
"""
from dataclasses import dataclass
from typing import List
@dataclass(frozen=True)
class StatisticsResult:
"""
Résultat des calculs statistiques.
Attributes:
count: Nombre de valeurs
mean: Moyenne
median: Médiane
min: Minimum
max: Maximum
std_dev: Écart-type
"""
count: int
mean: float
median: float
min: float
max: float
std_dev: float
def to_dict(self) -> dict:
"""Convertit en dictionnaire pour la sérialisation."""
return {
"count": self.count,
"mean": self.mean,
"median": self.median,
"min": self.min,
"max": self.max,
"std_dev": self.std_dev
}
@classmethod
def empty(cls) -> "StatisticsResult":
"""Crée un résultat vide."""
return cls(
count=0,
mean=0.0,
median=0.0,
min=0.0,
max=0.0,
std_dev=0.0
)
@dataclass(frozen=True)
class HistogramBin:
"""
Un bin d'histogramme.
Attributes:
range_start: Début de l'intervalle
range_end: Fin de l'intervalle
count: Nombre de valeurs dans l'intervalle
"""
range_start: float
range_end: float
count: int
@property
def label(self) -> str:
"""Génère le label pour l'affichage."""
if self.range_end == float('inf'):
return f"{int(self.range_start)}+"
return f"{int(self.range_start)}-{int(self.range_end)}"
def to_dict(self) -> dict:
"""Convertit en dictionnaire pour la sérialisation."""
return {
"range": self.label,
"count": self.count
}
@dataclass
class HistogramData:
"""
Données d'histogramme.
Attributes:
bins: Liste des bins
max_count: Nombre maximum dans un bin (pour l'affichage)
"""
bins: List[HistogramBin]
@property
def max_count(self) -> int:
"""Retourne le count maximum parmi tous les bins."""
if not self.bins:
return 0
return max(bin.count for bin in self.bins)
@property
def total_count(self) -> int:
"""Retourne le nombre total d'éléments."""
return sum(bin.count for bin in self.bins)
def to_list(self) -> List[int]:
"""Convertit en liste simple de counts (pour Chart.js)."""
return [bin.count for bin in self.bins]
def to_dict_list(self) -> List[dict]:
"""Convertit en liste de dictionnaires."""
return [bin.to_dict() for bin in self.bins]