feat: Calculer automatiquement les moyennes après chaque saisie de notes
Les enseignants ont besoin de moyennes à jour immédiatement après la publication ou modification des notes, sans attendre un batch nocturne. Le système recalcule via Domain Events synchrones : statistiques d'évaluation (min/max/moyenne/médiane), moyennes matières pondérées (normalisation /20), et moyenne générale par élève. Les résultats sont stockés dans des tables dénormalisées avec cache Redis (TTL 5 min). Trois endpoints API exposent les données avec contrôle d'accès par rôle. Une commande console permet le backfill des données historiques au déploiement.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Domain\Model\Grade\AppreciationTemplate;
|
||||
use App\Scolarite\Domain\Model\Grade\AppreciationTemplateId;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface AppreciationTemplateRepository
|
||||
{
|
||||
public function save(AppreciationTemplate $template): void;
|
||||
|
||||
public function findById(AppreciationTemplateId $id, TenantId $tenantId): ?AppreciationTemplate;
|
||||
|
||||
/** @return array<AppreciationTemplate> */
|
||||
public function findByTeacher(UserId $teacherId, TenantId $tenantId): array;
|
||||
|
||||
public function delete(AppreciationTemplateId $id, TenantId $tenantId): void;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyEvaluation;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyEvaluationId;
|
||||
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
|
||||
|
||||
interface CompetencyEvaluationRepository
|
||||
{
|
||||
public function save(CompetencyEvaluation $competencyEvaluation): void;
|
||||
|
||||
public function findById(CompetencyEvaluationId $id): ?CompetencyEvaluation;
|
||||
|
||||
/** @return array<CompetencyEvaluation> */
|
||||
public function findByEvaluation(EvaluationId $evaluationId): array;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyFramework;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyFrameworkId;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface CompetencyFrameworkRepository
|
||||
{
|
||||
public function save(CompetencyFramework $framework): void;
|
||||
|
||||
public function findById(CompetencyFrameworkId $id, TenantId $tenantId): ?CompetencyFramework;
|
||||
|
||||
public function findDefault(TenantId $tenantId): ?CompetencyFramework;
|
||||
|
||||
/** @return array<CompetencyFramework> */
|
||||
public function findByTenant(TenantId $tenantId): array;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Competency\Competency;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyFrameworkId;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyId;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface CompetencyRepository
|
||||
{
|
||||
public function save(Competency $competency): void;
|
||||
|
||||
public function findById(CompetencyId $id, TenantId $tenantId): ?Competency;
|
||||
|
||||
/** @return array<Competency> */
|
||||
public function findByFramework(CompetencyFrameworkId $frameworkId): array;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Competency\CustomCompetencyLevel;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface CustomCompetencyLevelRepository
|
||||
{
|
||||
public function save(CustomCompetencyLevel $level): void;
|
||||
|
||||
/** @return array<CustomCompetencyLevel> */
|
||||
public function findByTenant(TenantId $tenantId): array;
|
||||
|
||||
public function hasByTenant(TenantId $tenantId): bool;
|
||||
}
|
||||
@@ -5,11 +5,13 @@ declare(strict_types=1);
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Administration\Domain\Model\SchoolClass\ClassId;
|
||||
use App\Administration\Domain\Model\Subject\SubjectId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Domain\Exception\EvaluationNotFoundException;
|
||||
use App\Scolarite\Domain\Model\Evaluation\Evaluation;
|
||||
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
use DateTimeImmutable;
|
||||
|
||||
interface EvaluationRepository
|
||||
{
|
||||
@@ -28,4 +30,16 @@ interface EvaluationRepository
|
||||
|
||||
/** @return array<Evaluation> */
|
||||
public function findByClass(ClassId $classId, TenantId $tenantId): array;
|
||||
|
||||
/** @return array<Evaluation> Toutes les évaluations publiées (notes visibles) */
|
||||
public function findAllWithPublishedGrades(TenantId $tenantId): array;
|
||||
|
||||
/** @return array<Evaluation> Évaluations dont les notes sont publiées */
|
||||
public function findWithPublishedGradesBySubjectAndClassInDateRange(
|
||||
SubjectId $subjectId,
|
||||
ClassId $classId,
|
||||
DateTimeImmutable $startDate,
|
||||
DateTimeImmutable $endDate,
|
||||
TenantId $tenantId,
|
||||
): array;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Scolarite\Domain\Model\Evaluation\ClassStatistics;
|
||||
use App\Scolarite\Domain\Model\Evaluation\EvaluationId;
|
||||
|
||||
interface EvaluationStatisticsRepository
|
||||
{
|
||||
public function save(EvaluationId $evaluationId, ClassStatistics $statistics): void;
|
||||
|
||||
public function findByEvaluation(EvaluationId $evaluationId): ?ClassStatistics;
|
||||
|
||||
public function delete(EvaluationId $evaluationId): void;
|
||||
}
|
||||
@@ -14,6 +14,8 @@ interface GradeRepository
|
||||
{
|
||||
public function save(Grade $grade): void;
|
||||
|
||||
public function saveAppreciation(Grade $grade): void;
|
||||
|
||||
/** @throws GradeNotFoundException */
|
||||
public function get(GradeId $id, TenantId $tenantId): Grade;
|
||||
|
||||
@@ -22,5 +24,12 @@ interface GradeRepository
|
||||
/** @return array<Grade> */
|
||||
public function findByEvaluation(EvaluationId $evaluationId, TenantId $tenantId): array;
|
||||
|
||||
/**
|
||||
* @param array<EvaluationId> $evaluationIds
|
||||
*
|
||||
* @return array<string, list<Grade>> Clé = evaluationId (string)
|
||||
*/
|
||||
public function findByEvaluations(array $evaluationIds, TenantId $tenantId): array;
|
||||
|
||||
public function hasGradesForEvaluation(EvaluationId $evaluationId, TenantId $tenantId): bool;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Administration\Domain\Model\Subject\SubjectId;
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface StudentAverageRepository
|
||||
{
|
||||
public function saveSubjectAverage(
|
||||
TenantId $tenantId,
|
||||
UserId $studentId,
|
||||
SubjectId $subjectId,
|
||||
string $periodId,
|
||||
float $average,
|
||||
int $gradeCount,
|
||||
): void;
|
||||
|
||||
public function saveGeneralAverage(
|
||||
TenantId $tenantId,
|
||||
UserId $studentId,
|
||||
string $periodId,
|
||||
float $average,
|
||||
): void;
|
||||
|
||||
/** @return list<float> Moyennes matières d'un élève pour une période */
|
||||
public function findSubjectAveragesForStudent(
|
||||
UserId $studentId,
|
||||
string $periodId,
|
||||
TenantId $tenantId,
|
||||
): array;
|
||||
|
||||
/** @return list<array{subjectId: string, subjectName: string|null, average: float, gradeCount: int}> */
|
||||
public function findDetailedSubjectAveragesForStudent(
|
||||
UserId $studentId,
|
||||
string $periodId,
|
||||
TenantId $tenantId,
|
||||
): array;
|
||||
|
||||
public function findGeneralAverageForStudent(
|
||||
UserId $studentId,
|
||||
string $periodId,
|
||||
TenantId $tenantId,
|
||||
): ?float;
|
||||
|
||||
public function deleteSubjectAverage(
|
||||
UserId $studentId,
|
||||
SubjectId $subjectId,
|
||||
string $periodId,
|
||||
TenantId $tenantId,
|
||||
): void;
|
||||
|
||||
public function deleteGeneralAverage(
|
||||
UserId $studentId,
|
||||
string $periodId,
|
||||
TenantId $tenantId,
|
||||
): void;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Scolarite\Domain\Repository;
|
||||
|
||||
use App\Administration\Domain\Model\User\UserId;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyEvaluationId;
|
||||
use App\Scolarite\Domain\Model\Competency\CompetencyId;
|
||||
use App\Scolarite\Domain\Model\Competency\StudentCompetencyResult;
|
||||
use App\Shared\Domain\Tenant\TenantId;
|
||||
|
||||
interface StudentCompetencyResultRepository
|
||||
{
|
||||
public function save(StudentCompetencyResult $result): void;
|
||||
|
||||
public function delete(StudentCompetencyResult $result): void;
|
||||
|
||||
/** @return array<StudentCompetencyResult> */
|
||||
public function findByCompetencyEvaluation(
|
||||
CompetencyEvaluationId $competencyEvaluationId,
|
||||
TenantId $tenantId,
|
||||
): array;
|
||||
|
||||
public function findByCompetencyEvaluationAndStudent(
|
||||
CompetencyEvaluationId $competencyEvaluationId,
|
||||
UserId $studentId,
|
||||
TenantId $tenantId,
|
||||
): ?StudentCompetencyResult;
|
||||
|
||||
/** @return array<StudentCompetencyResult> */
|
||||
public function findByStudentAndCompetency(
|
||||
UserId $studentId,
|
||||
CompetencyId $competencyId,
|
||||
TenantId $tenantId,
|
||||
): array;
|
||||
}
|
||||
Reference in New Issue
Block a user