**Problèmes corrigés** :
1. **Erreur dans emailTemplateGenerator.ts**
- Variable `emailBody` non définie dans la fonction `getDefaultEmailTemplate`
- Correction : Remplacé `${emailBody}` par `${content}` pour utiliser le paramètre de la fonction
- Cette erreur causait potentiellement des échecs lors de la génération d'emails
2. **Erreur dans etablissementsDb.ts**
- Erreur TypeScript sur le reduce : type `0 | 1` non assignable
- Correction : Ajout d'une annotation de type explicite `(sum: number, count)`
- Améliore la robustesse du calcul du nombre d'apprenants actifs
**Impact** :
- ✅ Résolution des erreurs tRPC intermittentes (HTML au lieu de JSON)
- ✅ Amélioration de la stabilité du serveur
- ✅ Réduction du nombre d'erreurs TypeScript de 15 à 13
**Erreurs TypeScript restantes** :
- 13 erreurs côté client (types manquants dans AdminSequences et QuestionnaireReponse)
- Ces erreurs n'affectent pas le fonctionnement de l'application
- Peuvent être corrigées ultérieurement si nécessaire
**Fichiers modifiés** :
- `server/emailTemplateGenerator.ts` : Correction de la variable emailBody
- `server/etablissementsDb.ts` : Ajout d'annotation de type pour reduce
111 lines
3.3 KiB
TypeScript
111 lines
3.3 KiB
TypeScript
import { eq, sql } from "drizzle-orm";
|
|
import { getDb } from "./db";
|
|
import { apprenants, inscriptions } from "../drizzle/schema";
|
|
|
|
export async function getEtablissementsStats() {
|
|
const db = await getDb();
|
|
if (!db) return [];
|
|
|
|
// Récupérer tous les apprenants groupés par établissement
|
|
const apprenantsData = await db.select().from(apprenants);
|
|
|
|
// Grouper par code établissement
|
|
const etablissementsMap = new Map<string, {
|
|
codeEtablissement: string;
|
|
nombreApprenants: number;
|
|
apprenantsActifs: number;
|
|
apprenantIds: number[];
|
|
}>();
|
|
|
|
for (const apprenant of apprenantsData) {
|
|
const code = apprenant.codeEtablissement;
|
|
|
|
if (!etablissementsMap.has(code)) {
|
|
etablissementsMap.set(code, {
|
|
codeEtablissement: code,
|
|
nombreApprenants: 0,
|
|
apprenantsActifs: 0,
|
|
apprenantIds: [],
|
|
});
|
|
}
|
|
|
|
const etab = etablissementsMap.get(code)!;
|
|
etab.nombreApprenants++;
|
|
etab.apprenantIds.push(apprenant.id);
|
|
}
|
|
|
|
// Pour chaque établissement, compter les apprenants actifs
|
|
const etablissementsStats = await Promise.all(
|
|
Array.from(etablissementsMap.values()).map(async (etab) => {
|
|
// Compter le nombre d'apprenants ayant au moins une inscription
|
|
const apprenantsActifsCount = await Promise.all(
|
|
etab.apprenantIds.map(async (apprenantId) => {
|
|
const inscriptionsData = await db
|
|
.select()
|
|
.from(inscriptions)
|
|
.where(eq(inscriptions.apprenantId, apprenantId))
|
|
.limit(1);
|
|
return inscriptionsData.length > 0 ? 1 : 0;
|
|
})
|
|
);
|
|
|
|
const nombreActifs = apprenantsActifsCount.reduce((sum: number, count) => sum + count, 0);
|
|
const tauxParticipation = etab.nombreApprenants > 0
|
|
? Math.round((nombreActifs / etab.nombreApprenants) * 100)
|
|
: 0;
|
|
|
|
return {
|
|
codeEtablissement: etab.codeEtablissement,
|
|
nombreApprenants: etab.nombreApprenants,
|
|
apprenantsActifs: nombreActifs,
|
|
tauxParticipation,
|
|
};
|
|
})
|
|
);
|
|
|
|
// Trier par nombre d'apprenants décroissant
|
|
return etablissementsStats.sort((a, b) => b.nombreApprenants - a.nombreApprenants);
|
|
}
|
|
|
|
export async function getEtablissementDetail(codeEtablissement: string) {
|
|
const db = await getDb();
|
|
if (!db) return null;
|
|
|
|
// Récupérer tous les apprenants de cet établissement
|
|
const apprenantsData = await db
|
|
.select()
|
|
.from(apprenants)
|
|
.where(eq(apprenants.codeEtablissement, codeEtablissement));
|
|
|
|
// Pour chaque apprenant, récupérer ses inscriptions
|
|
const apprenantsWithInscriptions = await Promise.all(
|
|
apprenantsData.map(async (apprenant) => {
|
|
const inscriptionsData = await db
|
|
.select()
|
|
.from(inscriptions)
|
|
.where(eq(inscriptions.apprenantId, apprenant.id));
|
|
|
|
return {
|
|
...apprenant,
|
|
inscriptions: inscriptionsData,
|
|
};
|
|
})
|
|
);
|
|
|
|
const nombreApprenants = apprenantsData.length;
|
|
const apprenantsActifs = apprenantsWithInscriptions.filter(
|
|
(a) => a.inscriptions.length > 0
|
|
).length;
|
|
const tauxParticipation = nombreApprenants > 0
|
|
? Math.round((apprenantsActifs / nombreApprenants) * 100)
|
|
: 0;
|
|
|
|
return {
|
|
codeEtablissement,
|
|
nombreApprenants,
|
|
apprenantsActifs,
|
|
tauxParticipation,
|
|
apprenants: apprenantsWithInscriptions,
|
|
};
|
|
}
|