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:
58
frontend/e2e/helpers.ts
Normal file
58
frontend/e2e/helpers.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export const projectRoot = join(__dirname, '../..');
|
||||
export const composeFile = join(projectRoot, 'compose.yaml');
|
||||
|
||||
export function execWithRetry(command: string, maxRetries = 3): string {
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
return execSync(command, { encoding: 'utf-8' });
|
||||
} catch (error) {
|
||||
if (attempt === maxRetries) throw error;
|
||||
// Wait before retry: 1s, 2s, 3s
|
||||
execSync(`sleep ${attempt}`);
|
||||
}
|
||||
}
|
||||
throw new Error('Unreachable');
|
||||
}
|
||||
|
||||
export function runSql(sql: string) {
|
||||
execWithRetry(
|
||||
`docker compose -f "${composeFile}" exec -T php php bin/console dbal:run-sql "${sql}" 2>&1`
|
||||
);
|
||||
}
|
||||
|
||||
export function clearCache() {
|
||||
try {
|
||||
execWithRetry(
|
||||
`docker compose -f "${composeFile}" exec -T php php bin/console cache:pool:clear paginated_queries.cache 2>&1`
|
||||
);
|
||||
} catch {
|
||||
// Cache pool may not exist
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveDeterministicIds(tenantId: string): { schoolId: string; academicYearId: string } {
|
||||
const output = execWithRetry(
|
||||
`docker compose -f "${composeFile}" exec -T php php -r '` +
|
||||
`require "/app/vendor/autoload.php"; ` +
|
||||
`$t="${tenantId}"; $ns="6ba7b814-9dad-11d1-80b4-00c04fd430c8"; ` +
|
||||
`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"school-$t")->toString()."\\n"; ` +
|
||||
`$m=(int)date("n"); $s=$m>=9?(int)date("Y"):(int)date("Y")-1; $e=$s+1; ` +
|
||||
`echo Ramsey\\Uuid\\Uuid::uuid5($ns,"$t:$s-$e")->toString();` +
|
||||
`' 2>&1`
|
||||
).trim();
|
||||
const [schoolId, academicYearId] = output.split('\n');
|
||||
return { schoolId: schoolId!, academicYearId: academicYearId! };
|
||||
}
|
||||
|
||||
export function createTestUser(tenant: string, email: string, password: string, role: string) {
|
||||
execWithRetry(
|
||||
`docker compose -f "${composeFile}" exec -T php php bin/console app:dev:create-test-user --tenant=${tenant} --email=${email} --password=${password} --role=${role} 2>&1`
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user