Files
Classeo/backend/src/Scolarite/Domain/Service/TeacherStatisticsCalculator.php
Mathias STRASSER 18c54e6d67
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-11 20:52:41 +02:00

155 lines
3.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Scolarite\Domain\Service;
use function abs;
use function array_fill;
use function array_values;
use function count;
use function floor;
use function max;
use function min;
use function round;
final class TeacherStatisticsCalculator
{
/**
* Répartition des notes en 8 bins de largeur 2.5 points (sur /20).
* Bins : [02.5[, [2.55[, [57.5[, [7.510[, [1012.5[, [12.515[, [1517.5[, [17.520].
*
* @param list<float> $normalizedValues Notes normalisées sur /20
*
* @return list<int> 8 éléments
*/
public function calculateDistribution(array $normalizedValues): array
{
/** @var list<int> $bins */
$bins = array_fill(0, 8, 0);
foreach ($normalizedValues as $value) {
$binIndex = min(7, max(0, (int) floor($value / 2.5)));
++$bins[$binIndex];
}
return array_values($bins);
}
/**
* Taux de réussite : pourcentage de notes >= seuil.
*
* @param list<float> $normalizedValues Notes normalisées sur /20
* @param float $threshold Seuil de réussite (défaut : 10.0)
*/
public function calculateSuccessRate(array $normalizedValues, float $threshold = 10.0): float
{
if ($normalizedValues === []) {
return 0.0;
}
$count = count($normalizedValues);
$above = 0;
foreach ($normalizedValues as $value) {
if ($value >= $threshold) {
++$above;
}
}
return round($above * 100.0 / $count, 1);
}
/**
* Régression linéaire simple (moindres carrés) pour la ligne de tendance.
*
* @param list<array{0: int|float, 1: float}> $points [[x, y], ...]
*/
public function calculateTrendLine(array $points): ?TrendResult
{
$n = count($points);
if ($n < 2) {
return null;
}
$sumX = 0.0;
$sumY = 0.0;
$sumXY = 0.0;
$sumX2 = 0.0;
foreach ($points as [$x, $y]) {
$sumX += $x;
$sumY += $y;
$sumXY += $x * $y;
$sumX2 += $x * $x;
}
$denominator = $n * $sumX2 - $sumX * $sumX;
if (abs($denominator) < 1e-10) {
return new TrendResult(slope: 0.0, intercept: $sumY / $n);
}
$slope = ($n * $sumXY - $sumX * $sumY) / $denominator;
$intercept = ($sumY - $slope * $sumX) / $n;
return new TrendResult(
slope: round($slope, 4),
intercept: round($intercept, 4),
);
}
/**
* Détecte la tendance à partir de moyennes périodiques (trimestres).
* Compare la dernière moyenne à la première avec un seuil de 1 point.
*
* @param list<float> $periodicAverages Moyennes par période chronologique
*
* @return 'improving'|'stable'|'declining'
*/
public function detectTrend(array $periodicAverages): string
{
if (count($periodicAverages) < 2) {
return 'stable';
}
$first = $periodicAverages[0];
$last = $periodicAverages[count($periodicAverages) - 1];
$diff = $last - $first;
if ($diff > 1.0) {
return 'improving';
}
if ($diff < -1.0) {
return 'declining';
}
return 'stable';
}
/**
* Calcule le percentile d'une valeur parmi un ensemble d'autres valeurs.
* Indique le % de valeurs inférieures à la valeur donnée.
*
* @param list<float> $otherValues
*/
public function calculatePercentile(float $value, array $otherValues): float
{
if ($otherValues === []) {
return 100.0;
}
$below = 0;
foreach ($otherValues as $other) {
if ($other < $value) {
++$below;
}
}
return round($below * 100.0 / count($otherValues), 1);
}
}