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.
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
<?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 : [0–2.5[, [2.5–5[, [5–7.5[, [7.5–10[, [10–12.5[, [12.5–15[, [15–17.5[, [17.5–20].
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user