Checkpoint: Corrections multiples :
- Renommage questionnaire.nom → questionnaire.titre dans QuestionnaireReponse.tsx, AdminQuestionnaireEdit.tsx, questionnaireScheduler.ts, questionnaireExport.ts - Correction des requêtes SQL dans questionnaireDb.ts (suppression de la condition complete inexistante) - Ajout des colonnes valeurMin et valeurMax dans la table questions - Renommage de la colonne nom en titre dans la table questionnaires
This commit is contained in:
328
deployment-package/source_server/rappelDb.ts
Normal file
328
deployment-package/source_server/rappelDb.ts
Normal file
@@ -0,0 +1,328 @@
|
||||
import { getDb } from "./db";
|
||||
import { logsRappels, InsertLogRappel, LogRappel } from "../drizzle/schema";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
|
||||
/**
|
||||
* Enregistre l'envoi d'un rappel dans les logs
|
||||
*/
|
||||
export async function logRappelEnvoye(data: {
|
||||
rappelId: number;
|
||||
sequenceId: number;
|
||||
apprenantId: number;
|
||||
emailDestinataire: string;
|
||||
typeRappel: string;
|
||||
statut: "success" | "failed";
|
||||
messageErreur?: string;
|
||||
dateSequence: Date;
|
||||
}): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) {
|
||||
console.warn("[LogsRappels] Base de données non disponible");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await db.insert(logsRappels).values({
|
||||
rappelId: data.rappelId,
|
||||
sequenceId: data.sequenceId,
|
||||
apprenantId: data.apprenantId,
|
||||
emailDestinataire: data.emailDestinataire,
|
||||
typeRappel: data.typeRappel,
|
||||
dateEnvoi: new Date(),
|
||||
statut: data.statut,
|
||||
messageErreur: data.messageErreur || null,
|
||||
dateSequence: data.dateSequence,
|
||||
} as InsertLogRappel);
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de l'enregistrement du log:", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un rappel a déjà été envoyé à un apprenant pour une séquence donnée
|
||||
*/
|
||||
export async function rappelDejaEnvoye(
|
||||
rappelId: number,
|
||||
sequenceId: number,
|
||||
apprenantId: number
|
||||
): Promise<boolean> {
|
||||
const db = await getDb();
|
||||
if (!db) {
|
||||
console.warn("[LogsRappels] Base de données non disponible");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const logs = await db
|
||||
.select()
|
||||
.from(logsRappels)
|
||||
.where(
|
||||
and(
|
||||
eq(logsRappels.rappelId, rappelId),
|
||||
eq(logsRappels.sequenceId, sequenceId),
|
||||
eq(logsRappels.apprenantId, apprenantId),
|
||||
eq(logsRappels.statut, "success")
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
return logs.length > 0;
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la vérification des doublons:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les logs de rappels pour une séquence donnée
|
||||
*/
|
||||
export async function getLogsRappelsBySequence(sequenceId: number): Promise<LogRappel[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
try {
|
||||
return await db
|
||||
.select()
|
||||
.from(logsRappels)
|
||||
.where(eq(logsRappels.sequenceId, sequenceId))
|
||||
.orderBy(logsRappels.dateEnvoi);
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la récupération des logs:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les logs de rappels échoués récents (dernières 24h)
|
||||
*/
|
||||
export async function getLogsRappelsEchoues(): Promise<LogRappel[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
try {
|
||||
const hier = new Date();
|
||||
hier.setDate(hier.getDate() - 1);
|
||||
|
||||
return await db
|
||||
.select()
|
||||
.from(logsRappels)
|
||||
.where(
|
||||
and(
|
||||
eq(logsRappels.statut, "failed"),
|
||||
// Note: Drizzle ne supporte pas directement les comparaisons de dates
|
||||
// On récupère tous les échecs et on filtre en JS
|
||||
)
|
||||
)
|
||||
.orderBy(logsRappels.dateEnvoi);
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la récupération des échecs:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de rappels envoyés avec succès pour une séquence
|
||||
*/
|
||||
export async function countRappelsEnvoyesSequence(sequenceId: number): Promise<number> {
|
||||
const db = await getDb();
|
||||
if (!db) return 0;
|
||||
|
||||
try {
|
||||
const logs = await db
|
||||
.select()
|
||||
.from(logsRappels)
|
||||
.where(
|
||||
and(
|
||||
eq(logsRappels.sequenceId, sequenceId),
|
||||
eq(logsRappels.statut, "success")
|
||||
)
|
||||
);
|
||||
|
||||
return logs.length;
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors du comptage:", error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les logs de rappels avec filtres
|
||||
*/
|
||||
export async function getLogsRappelsWithFilters(filters: {
|
||||
dateDebut?: Date;
|
||||
dateFin?: Date;
|
||||
sequenceId?: number;
|
||||
statut?: "success" | "failed";
|
||||
email?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<LogRappel[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
try {
|
||||
let query = db.select().from(logsRappels);
|
||||
|
||||
const conditions = [];
|
||||
if (filters.sequenceId) {
|
||||
conditions.push(eq(logsRappels.sequenceId, filters.sequenceId));
|
||||
}
|
||||
if (filters.statut) {
|
||||
conditions.push(eq(logsRappels.statut, filters.statut));
|
||||
}
|
||||
if (filters.email) {
|
||||
// Note: Drizzle ne supporte pas LIKE directement, on filtre en JS
|
||||
}
|
||||
|
||||
if (conditions.length > 0) {
|
||||
query = query.where(and(...conditions)) as any;
|
||||
}
|
||||
|
||||
let logs = await query.orderBy(logsRappels.dateEnvoi).limit(filters.limit || 100).offset(filters.offset || 0);
|
||||
|
||||
// Filtrer par email si nécessaire
|
||||
if (filters.email) {
|
||||
logs = logs.filter(log => log.emailDestinataire.toLowerCase().includes(filters.email!.toLowerCase()));
|
||||
}
|
||||
|
||||
// Filtrer par date si nécessaire
|
||||
if (filters.dateDebut) {
|
||||
logs = logs.filter(log => new Date(log.dateEnvoi) >= filters.dateDebut!);
|
||||
}
|
||||
if (filters.dateFin) {
|
||||
logs = logs.filter(log => new Date(log.dateEnvoi) <= filters.dateFin!);
|
||||
}
|
||||
|
||||
return logs;
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la récupération avec filtres:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule les statistiques des rappels
|
||||
*/
|
||||
export async function getStatsRappels(): Promise<{
|
||||
totalEnvoyes: number;
|
||||
totalSucces: number;
|
||||
totalEchecs: number;
|
||||
tauxSucces: number;
|
||||
emailsProblematiques: Array<{ email: string; nbEchecs: number }>;
|
||||
}> {
|
||||
const db = await getDb();
|
||||
if (!db) {
|
||||
return {
|
||||
totalEnvoyes: 0,
|
||||
totalSucces: 0,
|
||||
totalEchecs: 0,
|
||||
tauxSucces: 0,
|
||||
emailsProblematiques: [],
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const logs = await db.select().from(logsRappels);
|
||||
|
||||
const totalEnvoyes = logs.length;
|
||||
const totalSucces = logs.filter(l => l.statut === "success").length;
|
||||
const totalEchecs = logs.filter(l => l.statut === "failed").length;
|
||||
const tauxSucces = totalEnvoyes > 0 ? (totalSucces / totalEnvoyes) * 100 : 0;
|
||||
|
||||
// Compter les échecs par email
|
||||
const echecsParEmail = new Map<string, number>();
|
||||
logs.filter(l => l.statut === "failed").forEach(log => {
|
||||
const count = echecsParEmail.get(log.emailDestinataire) || 0;
|
||||
echecsParEmail.set(log.emailDestinataire, count + 1);
|
||||
});
|
||||
|
||||
const emailsProblematiques = Array.from(echecsParEmail.entries())
|
||||
.map(([email, nbEchecs]) => ({ email, nbEchecs }))
|
||||
.sort((a, b) => b.nbEchecs - a.nbEchecs)
|
||||
.slice(0, 10); // Top 10
|
||||
|
||||
return {
|
||||
totalEnvoyes,
|
||||
totalSucces,
|
||||
totalEchecs,
|
||||
tauxSucces,
|
||||
emailsProblematiques,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors du calcul des statistiques:", error);
|
||||
return {
|
||||
totalEnvoyes: 0,
|
||||
totalSucces: 0,
|
||||
totalEchecs: 0,
|
||||
tauxSucces: 0,
|
||||
emailsProblematiques: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'évolution des envois par jour
|
||||
*/
|
||||
export async function getEvolutionEnvois(): Promise<Array<{
|
||||
date: string;
|
||||
nbEnvoyes: number;
|
||||
nbSucces: number;
|
||||
nbEchecs: number;
|
||||
}>> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
try {
|
||||
const logs = await db.select().from(logsRappels).orderBy(logsRappels.dateEnvoi);
|
||||
|
||||
// Grouper par jour
|
||||
const parJour = new Map<string, { nbEnvoyes: number; nbSucces: number; nbEchecs: number }>();
|
||||
|
||||
logs.forEach(log => {
|
||||
const dateStr = new Date(log.dateEnvoi).toISOString().split('T')[0];
|
||||
const stats = parJour.get(dateStr) || { nbEnvoyes: 0, nbSucces: 0, nbEchecs: 0 };
|
||||
stats.nbEnvoyes++;
|
||||
if (log.statut === "success") stats.nbSucces++;
|
||||
else stats.nbEchecs++;
|
||||
parJour.set(dateStr, stats);
|
||||
});
|
||||
|
||||
return Array.from(parJour.entries())
|
||||
.map(([date, stats]) => ({ date, ...stats }))
|
||||
.sort((a, b) => a.date.localeCompare(b.date));
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la récupération de l'évolution:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les logs échoués à réessayer
|
||||
*/
|
||||
export async function getLogsAReessayer(): Promise<LogRappel[]> {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
try {
|
||||
const maintenant = new Date();
|
||||
const logs = await db
|
||||
.select()
|
||||
.from(logsRappels)
|
||||
.where(
|
||||
and(
|
||||
eq(logsRappels.statut, "failed"),
|
||||
// Limiter à 3 tentatives maximum
|
||||
)
|
||||
);
|
||||
|
||||
// Filtrer en JS pour les conditions complexes
|
||||
return logs.filter(log => {
|
||||
if (log.nbTentatives >= 3) return false;
|
||||
if (!log.prochainEssai) return true; // Premier essai
|
||||
return new Date(log.prochainEssai) <= maintenant;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[LogsRappels] Erreur lors de la récupération des logs à réessayer:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user