import { eq, sql, and, gte, lte } from "drizzle-orm"; import { getDb } from "./db"; import { questionnaires, envoisQuestionnaires, reponsesQuestionnaires, sequences, formations, formateurs } from "../drizzle/schema"; /** * Statistiques de taux de réponse par formation */ export async function getStatsByFormation() { const db = await getDb(); if (!db) throw new Error("Database not available"); const results = await db .select({ formationId: formations.id, formationNom: formations.nom, totalEnvois: sql`COUNT(DISTINCT ${envoisQuestionnaires.id})`, totalReponses: sql`COUNT(DISTINCT CASE WHEN ${envoisQuestionnaires.dateReponse} IS NOT NULL THEN ${envoisQuestionnaires.id} END)`, }) .from(envoisQuestionnaires) .innerJoin(sequences, eq(envoisQuestionnaires.sequenceId, sequences.id)) .innerJoin(formations, eq(sequences.formationId, formations.id)) .groupBy(formations.id, formations.nom); return results.map(r => ({ formationId: r.formationId, formationNom: r.formationNom, totalEnvois: Number(r.totalEnvois), totalReponses: Number(r.totalReponses), tauxReponse: Number(r.totalEnvois) > 0 ? Math.round((Number(r.totalReponses) / Number(r.totalEnvois)) * 100) : 0 })); } /** * Évolution temporelle des réponses (par mois) */ export async function getEvolutionTemporelle(startDate?: Date, endDate?: Date) { const db = await getDb(); if (!db) throw new Error("Database not available"); let query = db .select({ mois: sql`DATE_FORMAT(${envoisQuestionnaires.dateEnvoi}, '%Y-%m')`, totalEnvois: sql`COUNT(DISTINCT ${envoisQuestionnaires.id})`, totalReponses: sql`COUNT(DISTINCT CASE WHEN ${envoisQuestionnaires.dateReponse} IS NOT NULL THEN ${envoisQuestionnaires.id} END)`, }) .from(envoisQuestionnaires); // Filtrer par dates si spécifiées const conditions = []; if (startDate) { conditions.push(gte(envoisQuestionnaires.dateEnvoi, startDate)); } if (endDate) { conditions.push(lte(envoisQuestionnaires.dateEnvoi, endDate)); } if (conditions.length > 0) { query = query.where(and(...conditions)) as any; } const results = await query.groupBy(sql`DATE_FORMAT(${envoisQuestionnaires.dateEnvoi}, '%Y-%m')`); return results.map(r => ({ mois: r.mois, totalEnvois: Number(r.totalEnvois), totalReponses: Number(r.totalReponses), tauxReponse: Number(r.totalEnvois) > 0 ? Math.round((Number(r.totalReponses) / Number(r.totalEnvois)) * 100) : 0 })); } /** * Comparaison par formateur */ export async function getStatsByFormateur() { const db = await getDb(); if (!db) throw new Error("Database not available"); const results = await db .select({ formateurId: formateurs.id, formateurNom: formateurs.nom, totalEnvois: sql`COUNT(DISTINCT ${envoisQuestionnaires.id})`, totalReponses: sql`COUNT(DISTINCT CASE WHEN ${envoisQuestionnaires.dateReponse} IS NOT NULL THEN ${envoisQuestionnaires.id} END)`, moyenneSatisfaction: sql`AVG(CASE WHEN ${reponsesQuestionnaires.complete} = 1 THEN (SELECT AVG(CAST(rq.reponse AS DECIMAL(10,2))) FROM reponsesQuestions rq INNER JOIN questions q ON rq.questionId = q.id WHERE rq.reponseQuestionnaireId = ${reponsesQuestionnaires.id} AND q.type = 'echelle' AND rq.reponse REGEXP '^[0-9]+$') END)`, }) .from(envoisQuestionnaires) .innerJoin(sequences, eq(envoisQuestionnaires.sequenceId, sequences.id)) .leftJoin(formateurs, eq(sequences.formateurId, formateurs.id)) .leftJoin(reponsesQuestionnaires, and( eq(envoisQuestionnaires.apprenantId, reponsesQuestionnaires.apprenantId), eq(envoisQuestionnaires.sequenceId, reponsesQuestionnaires.sequenceId) )) .where(sql`${formateurs.id} IS NOT NULL`) .groupBy(formateurs.id, formateurs.nom); return results.map(r => ({ formateurId: r.formateurId, formateurNom: r.formateurNom || "Non spécifié", totalEnvois: Number(r.totalEnvois), totalReponses: Number(r.totalReponses), tauxReponse: Number(r.totalEnvois) > 0 ? Math.round((Number(r.totalReponses) / Number(r.totalEnvois)) * 100) : 0, moyenneSatisfaction: r.moyenneSatisfaction ? Number(r.moyenneSatisfaction).toFixed(1) : null })); } /** * Statistiques globales pour le tableau de bord */ export async function getStatsGlobales() { const db = await getDb(); if (!db) throw new Error("Database not available"); const [stats] = await db .select({ totalQuestionnaires: sql`COUNT(DISTINCT ${questionnaires.id})`, totalEnvois: sql`COUNT(DISTINCT ${envoisQuestionnaires.id})`, totalReponses: sql`COUNT(DISTINCT CASE WHEN ${envoisQuestionnaires.dateReponse} IS NOT NULL THEN ${envoisQuestionnaires.id} END)`, tauxReponseGlobal: sql`ROUND( COUNT(DISTINCT CASE WHEN ${envoisQuestionnaires.dateReponse} IS NOT NULL THEN ${envoisQuestionnaires.id} END) * 100.0 / NULLIF(COUNT(DISTINCT ${envoisQuestionnaires.id}), 0), 1 )`, }) .from(questionnaires) .leftJoin(envoisQuestionnaires, eq(questionnaires.id, envoisQuestionnaires.questionnaireId)); return { totalQuestionnaires: Number(stats.totalQuestionnaires), totalEnvois: Number(stats.totalEnvois), totalReponses: Number(stats.totalReponses), tauxReponseGlobal: Number(stats.tauxReponseGlobal) || 0 }; }