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:
84
backend/migrations/Version20260329082334.php
Normal file
84
backend/migrations/Version20260329082334.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260329082334 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Création des tables dénormalisées pour les moyennes élèves et statistiques évaluations';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE student_averages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
student_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
subject_id UUID NOT NULL REFERENCES subjects(id) ON DELETE CASCADE,
|
||||
period_id UUID NOT NULL REFERENCES academic_periods(id) ON DELETE CASCADE,
|
||||
average DECIMAL(4,2),
|
||||
grade_count INT NOT NULL DEFAULT 0,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (student_id, subject_id, period_id)
|
||||
)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_student_averages_tenant ON student_averages(tenant_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_student_averages_student ON student_averages(student_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_student_averages_subject ON student_averages(subject_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE student_general_averages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
student_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
period_id UUID NOT NULL REFERENCES academic_periods(id) ON DELETE CASCADE,
|
||||
average DECIMAL(4,2),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (student_id, period_id)
|
||||
)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_student_general_averages_tenant ON student_general_averages(tenant_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_student_general_averages_student ON student_general_averages(student_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE evaluation_statistics (
|
||||
evaluation_id UUID PRIMARY KEY REFERENCES evaluations(id) ON DELETE CASCADE,
|
||||
average DECIMAL(5,2),
|
||||
min_grade DECIMAL(5,2),
|
||||
max_grade DECIMAL(5,2),
|
||||
median_grade DECIMAL(5,2),
|
||||
graded_count INT NOT NULL DEFAULT 0,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP TABLE IF EXISTS evaluation_statistics');
|
||||
$this->addSql('DROP TABLE IF EXISTS student_general_averages');
|
||||
$this->addSql('DROP TABLE IF EXISTS student_averages');
|
||||
}
|
||||
}
|
||||
56
backend/migrations/Version20260331154510.php
Normal file
56
backend/migrations/Version20260331154510.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20260331154510 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Ajout des appréciations sur les notes et table des modèles d\'appréciations enseignant';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE grades ADD COLUMN appreciation TEXT
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE grades ADD COLUMN appreciation_updated_at TIMESTAMPTZ
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE appreciation_templates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL,
|
||||
teacher_id UUID NOT NULL REFERENCES users(id),
|
||||
title VARCHAR(100) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
category VARCHAR(50),
|
||||
usage_count INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_templates_teacher ON appreciation_templates(teacher_id)
|
||||
SQL);
|
||||
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX idx_templates_tenant ON appreciation_templates(tenant_id)
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP TABLE IF EXISTS appreciation_templates');
|
||||
$this->addSql('ALTER TABLE grades DROP COLUMN IF EXISTS appreciation_updated_at');
|
||||
$this->addSql('ALTER TABLE grades DROP COLUMN IF EXISTS appreciation');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user