import ExcelJS from 'exceljs'; import { jsPDF } from 'jspdf'; import { getQuestionnaireById, getQuestionsByQuestionnaireId, getQuestionnaireStats, getReponsesDetailleesQuestionnaire } from './questionnaireDb'; import { getDb } from './db'; import { sql } from 'drizzle-orm'; /** * Génère un fichier Excel avec les statistiques du questionnaire */ export async function exportQuestionnaireToExcel(questionnaireId: number): Promise { const questionnaire = await getQuestionnaireById(questionnaireId); const stats = await getQuestionnaireStats(questionnaireId); const questionsData = await getQuestionsByQuestionnaireId(questionnaireId); const reponsesData = await getReponsesDetailleesQuestionnaire(questionnaireId); if (!questionnaire || !stats) { throw new Error('Questionnaire introuvable'); } const workbook = new ExcelJS.Workbook(); // Feuille 1: Statistiques globales const statsSheet = workbook.addWorksheet('Statistiques'); statsSheet.addRow(['Questionnaire', questionnaire.nom]); statsSheet.addRow(['Type', questionnaire.type]); statsSheet.addRow(['']); statsSheet.addRow(['Nombre d\'envois', stats.totalEnvois]); statsSheet.addRow(['Nombre de réponses', stats.totalReponses]); statsSheet.addRow(['Taux de réponse', `${stats.tauxReponse.toFixed(1)}%`]); statsSheet.addRow(['']); // Style pour l'en-tête statsSheet.getColumn(1).width = 30; statsSheet.getColumn(2).width = 40; statsSheet.getRow(1).font = { bold: true, size: 14 }; // Feuille 2: Statistiques par question const questionsSheet = workbook.addWorksheet('Par question'); questionsSheet.addRow(['Question', 'Type', 'Ordre']); questionsSheet.getRow(1).font = { bold: true }; questionsSheet.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } }; questionsData.forEach((q: any) => { questionsSheet.addRow([ q.texte, q.typeQuestion, q.ordre ]); }); questionsSheet.getColumn(1).width = 60; questionsSheet.getColumn(2).width = 20; questionsSheet.getColumn(3).width = 10; // Feuille 3: Réponses brutes const reponsesSheet = workbook.addWorksheet('Réponses brutes'); reponsesSheet.addRow(['Date réponse', 'Apprenant ID', 'Séquence ID', 'Question ID', 'Réponse numérique', 'Réponse texte']); reponsesSheet.getRow(1).font = { bold: true }; reponsesSheet.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } }; reponsesData.forEach((r: any) => { reponsesSheet.addRow([ r.dateReponse ? new Date(r.dateReponse).toLocaleDateString('fr-FR') : '', r.apprenantId, r.sequenceId, r.questionId, r.reponseNumerique || '', r.reponseTexte || '' ]); }); reponsesSheet.getColumn(1).width = 15; reponsesSheet.getColumn(2).width = 15; reponsesSheet.getColumn(3).width = 15; reponsesSheet.getColumn(4).width = 15; reponsesSheet.getColumn(5).width = 20; reponsesSheet.getColumn(6).width = 50; // Générer le buffer const buffer = await workbook.xlsx.writeBuffer(); return Buffer.from(buffer); } /** * Génère un fichier PDF avec les statistiques du questionnaire */ export async function exportQuestionnaireToPDF(questionnaireId: number): Promise { const questionnaire = await getQuestionnaireById(questionnaireId); const stats = await getQuestionnaireStats(questionnaireId); const questionsData = await getQuestionsByQuestionnaireId(questionnaireId); if (!questionnaire || !stats) { throw new Error('Questionnaire introuvable'); } const doc = new jsPDF(); let yPos = 20; // Titre doc.setFontSize(18); doc.setFont('helvetica', 'bold'); doc.text(questionnaire.nom, 20, yPos); yPos += 10; // Type doc.setFontSize(12); doc.setFont('helvetica', 'normal'); doc.text(`Type: ${questionnaire.type}`, 20, yPos); yPos += 15; // Statistiques globales doc.setFont('helvetica', 'bold'); doc.text('Statistiques globales', 20, yPos); yPos += 8; doc.setFont('helvetica', 'normal'); doc.text(`Nombre d'envois: ${stats.totalEnvois}`, 20, yPos); yPos += 6; doc.text(`Nombre de réponses: ${stats.totalReponses}`, 20, yPos); yPos += 6; doc.text(`Taux de réponse: ${stats.tauxReponse.toFixed(1)}%`, 20, yPos); yPos += 15; // Liste des questions doc.setFont('helvetica', 'bold'); doc.text('Questions du questionnaire', 20, yPos); yPos += 10; questionsData.forEach((q: any, index: number) => { // Vérifier si on doit ajouter une nouvelle page if (yPos > 250) { doc.addPage(); yPos = 20; } doc.setFont('helvetica', 'bold'); doc.setFontSize(11); const questionText = `${index + 1}. ${q.texte}`; const lines = doc.splitTextToSize(questionText, 170); doc.text(lines, 20, yPos); yPos += lines.length * 6; doc.setFont('helvetica', 'normal'); doc.setFontSize(10); doc.text(`Type: ${q.typeQuestion}`, 25, yPos); yPos += 8; }); // Pied de page const pageCount = doc.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); doc.setFontSize(8); doc.setFont('helvetica', 'normal'); doc.text( `Page ${i} sur ${pageCount} - Généré le ${new Date().toLocaleDateString('fr-FR')}`, 20, 290 ); } return Buffer.from(doc.output('arraybuffer')); }