const { QueryTypes } = require("sequelize"); const config = require("../config/main.js"); const configTpl = require("../views/"+config.theme+"/config/"+config.availableLangs[0]+".js"); const tool = require("../tools/main"); const toolFile = require("../tools/file"); const groupCtrl = require("./group"); const questionnaireCtrl = require("./questionnaire"); const subscriptionCtrl = require("./subscription"); const txtAnswers = require("../lang/"+config.adminLang+"/answer"); const txtGeneral = require("../lang/"+config.adminLang+"/general"); // Enregistrement d'une réponse à un questionnaire exports.create = async (req, res, next) => { try { const db = require("../models/index"); req.body.UserId=req.connectedUser.User.id; await saveAnswerToQuestionnaire(req.body); // J'en profite pour remettre les pendules à l'heure ! (revoir pour appeler un contrôleur des users) db["User"].update({ timeDifference: req.body.timeDifference }, { where: { id : req.connectedUser.User.id }, limit:1 }); res.status(201).json({ message: txtAnswers.responseSavedMessage }); next(); } catch(e) { next(e); } } // Enregistrement d'une réponse à un groupe de questionnaires exports.createInGroup = async (req, res, next) => { try { const db = require("../models/index"); req.body.UserId=req.connectedUser.User.id; await saveAnswerToGroup(req.body); // J'en profite pour remettre les pendules à l'heure ! db["User"].update({ timeDifference: req.body.timeDifference }, { where: { id : req.connectedUser.User.id }, limit:1 }); res.status(201).json({ message: txtAnswers.responseSavedMessage }); next(); } catch(e) { next(e); } } // Retourne les réponses d'un utilisateur pour un questionnaire donné exports.getAnswersByQuestionnaire = async(req, res, next) => { try { const answers=await getUserAnswersByQuestionnaire(req.params.userId, req.params.questionnaireId); res.status(200).json(answers); next(); } catch(e) { next(e); } } // Retourne les réponses d'un utilisateur pour un groupe de questionnaires donné exports.getAnswersByGroup = async(req, res, next) => { try { const answers=await getUserAnswersByGroup(req.params.userId, req.params.groupId); res.status(200).json(answers); next(); } catch(e) { next(e); } } // Retourne les statistiques de l'utilisateur exports.getStatsByUser = async(req, res, next) => { try { const stats=await getUserStatsAnswers(req.params.userId); // + stats générales des questionnaires et groupes pour comparaison : stats.questionnaires=await questionnaireCtrl.getStatsQuestionnaires(); stats.groups=await groupCtrl.getStatsGroups(); res.status(200).json(stats); } catch(e) { next(e); } } // FONCTIONS UTILITAIRES // Enregistre la réponse à un questionnaire autonome const saveAnswerToQuestionnaire = async (req) => { const db = require("../models/index"); const checkQuestionnaireAccess=await subscriptionCtrl.checkQuestionnaireAccess(req.UserId, req.QuestionnaireId); if(checkQuestionnaireAccess) // Si l'utilisateur a déjà accès à ce questionnaire, j'enregistre juste sa réponse await db["Answer"].create({ ...req }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] }); else { await Promise.all([ db["QuestionnaireAccess"].create({ ...req }, { fields: ["QuestionnaireId", "UserId"] }), db["Answer"].create({ ...req }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] }) ]); } await creaUserStatsAnwsersJson(req.UserId); await creaUserAnswersJson(req.UserId); return true; } exports.saveAnswerToQuestionnaire = saveAnswerToQuestionnaire; // Enregistre la réponse à un groupe de questionnaires const saveAnswerToGroup = async (req) => { const db = require("../models/index"); const answer = await db["Answer"].create({ ...req }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "GroupId", "UserId"] }); // Si il a répondu à un quiz groupé, l'utilisateur est considéré comme ayant lu tous les éléments du groupe const group = await groupCtrl.searchGroupById(req.GroupId); for(let i in group.Questionnaires) { if(await subscriptionCtrl.checkQuestionnaireAccess(req.UserId, group.Questionnaires[i].id) === false) { req.QuestionnaireId = group.Questionnaires[i].id; await db["QuestionnaireAccess"].create({ ...req }, { fields: ["QuestionnaireId", "UserId"] }); } } await creaUserStatsAnwsersJson(req.UserId); await creaUserAnswersJson(req.UserId); return true; } exports.saveAnswerToGroup = saveAnswerToGroup; // Créer la liste des réponses d'un utilisateur // !! à surveiller car fichier pouvant devenir gros ! mais utile pour future SVG côté client ? const creaUserAnswersJson = async (UserId) => { const db = require("../models/index"); const userAnswers=await db.sequelize.query("SELECT `QuestionnaireId`, `GroupId`, `nbQuestions`, `nbCorrectAnswers`, `duration`, `createdAt` FROM `Answers` WHERE `UserId`=:id ORDER BY `GroupId` DESC, `QuestionnaireId` DESC, `createdAt` DESC", { replacements: { id: UserId }, type: QueryTypes.SELECT }); if(userAnswers) { await toolFile.createJSON(config.dirCacheUsersAnswers, UserId, userAnswers); return userAnswers; } else return false; } exports.creaUserAnswersJson = creaUserAnswersJson; // Retourne les réponses d'un utilisateurs à un questionnaire simple. const getUserAnswersByQuestionnaire = async (UserId, QuestionnaireId) => { let userAnswers=await toolFile.readJSON(config.dirCacheUsersAnswers, UserId); if(!userAnswers) userAnswers=await creaUserAnswersJson(UserId); if(!userAnswers) return false; const answers=[]; for(let i in userAnswers) { if(userAnswers[i].QuestionnaireId == QuestionnaireId) // pas "===", car type de données pouvant être différents answers.push(userAnswers[i]); else if(answers.length !== 0) // les réponses étant groupées par QuestionnaireId, je peux sortir de la boucle break; } return answers; } // Retourne les réponses d'un utilisateurs à un groupe de questionnaires. const getUserAnswersByGroup = async (UserId, GroupId) => { let userAnswers=await toolFile.readJSON(config.dirCacheUsersAnswers, UserId); if(!userAnswers) userAnswers=await creaUserAnswersJson(UserId); if(!userAnswers) return false; const answers=[]; for(let i in userAnswers) { if(userAnswers[i].GroupId == GroupId)// pas "===" car type de données pouvant être différents answers.push(userAnswers[i]); else if(answers.length !== 0) // les réponses étant groupées par GroupId, je peux sortir de la boucle break; } return answers; } // À combien de questionnaire l'utilisateur a-t-il répondu ? et quel est son résultat moyen ? const creaUserStatsAnwsersJson = async (UserId) => { const db = require("../models/index"); const getUserAnswers = await db["Answer"].findAll({ where: { UserId : UserId }, attributes: ["id"] }); const getUserQuestionnaires = await db.sequelize.query("SELECT DISTINCT QuestionnaireId FROM Answers WHERE UserId=:id AND QuestionnaireId IS NOT NULL", { replacements: { id: UserId }, type: QueryTypes.SELECT }); const getUserQuestionnairesGroup = await db.sequelize.query("SELECT DISTINCT GroupId FROM Answers WHERE UserId=:id AND GroupId IS NOT NULL", { replacements: { id: UserId }, type: QueryTypes.SELECT }); const getUserStats = await db.sequelize.query("SELECT ROUND(AVG(nbCorrectAnswers/nbQuestions) *100) as avgCorrectAnswers, ROUND(AVG(duration)) as avgDuration FROM Answers GROUP BY UserId HAVING UserId=:id", { replacements: { id: UserId }, type: QueryTypes.SELECT }); if(getUserAnswers && getUserQuestionnaires && getUserQuestionnairesGroup) { const stats = { nbAnswers : getUserAnswers.length, nbQuestionnaires : getUserQuestionnaires.length+getUserQuestionnairesGroup.length } if(getUserStats && getUserAnswers.length !== 0) { stats.avgCorrectAnswers=getUserStats[0].avgCorrectAnswers; stats.avgDuration=getUserStats[0].avgDuration; } await toolFile.createJSON(config.dirCacheUsersAnswers, "stats"+UserId, stats); return stats; } else return false; } exports.creaUserStatsAnwsersJson = creaUserStatsAnwsersJson; // Retourne les données créées par la fonction précédente : const getUserStatsAnswers = async (UserId) => { let userStats=await toolFile.readJSON(config.dirCacheUsersAnswers, "stats"+UserId); if(!userStats) userStats=await creaUserStatsAnwsersJson(UserId); if(!userStats) return false; else return userStats; } // À combien de questionnaire les utilisateurs ont-ils répondu ces dernières 24H ? depuis le début ? const getStatsAnswers = async () => { const db = require("../models/index"); const getAnswers24H = await db.sequelize.query("SELECT `id` FROM `Answers` WHERE `createdAt` > ADDDATE(NOW(), -1)", { type: QueryTypes.SELECT }); const getAnswersTot = await db.sequelize.query("SELECT `id` FROM `Answers`", { type: QueryTypes.SELECT }); if(getAnswers24H && getAnswersTot) { const stats = { nbAnswers24H : getAnswers24H.length, nbAnswersTot : getAnswersTot.length } return stats; } else return false; } exports.getStatsAnswers = getStatsAnswers; /* // Créer la liste des questionnaires/éléments de questionnaire proposés à l'utilisateur, mais auxquels il n'a pas encore répondu // remplacer pour la liste des derniers éléments ou quizs envoyés ? const creaUserQuestionnairesWithoutAnswerJson = async (UserId) => { UserId=tool.trimIfNotNull(UserId); if(UserId===null) return false; const db = require("../models/index"); const userQuestionnaires=await db.sequelize.query("SELECT `QuestionnaireId` FROM `QuestionnaireAccesses` WHERE `UserId`=:id AND `QuestionnaireId` NOT IN (SELECT DISTINCT `QuestionnaireId` FROM Answers WHERE `UserId`=:id) ORDER BY `createdAt` DESC ", { replacements: { id: UserId }, type: QueryTypes.SELECT }); if(userQuestionnaires) { const questionnairesId=[];// les ids suffisent et allègent le fichier for(i in userQuestionnaires) questionnairesId.push(userQuestionnaires[i].QuestionnaireId); await toolFile.createJSON(config.dirCacheUsersQuestionnaires+"/without", UserId, { ids: questionnairesId }); return { ids: questionnairesId }; } else return false; } exports.creaUserQuestionnairesWithoutAnswerJson = creaUserQuestionnairesWithoutAnswerJson; // Retourne les données créées par la fonction précédente const getUserQuestionnairesWithoutAnswer = async (UserId, begin=0, nb=10) => { UserId=tool.trimIfNotNull(UserId); if(UserId===null) return false; let userQuestionnaires=await toolFile.readJSON(config.dirCacheUsersQuestionnaires+"/without", UserId); if(!userQuestionnaires) userQuestionnaires=await creaUserQuestionnairesWithoutAnswerJson(UserId); if(!userQuestionnaires) return false; let questionnaire, Questionnaires=[], i=begin; const nbTot=userQuestionnaires.ids.length; if(nb===0) nb=nbTot;// peut être = 0 si l'utilisateur est à jour while(i { try { let datas; if(req.params.id === undefined || req.params.begin === undefined || req.params.nb === undefined) { const err=new Error; err.message=txtGeneral.neededParams; throw err; } else datas=await getUserQuestionnairesWithoutAnswer(req.params.id, req.params.begin, req.params.nb); if(datas!==false) { if(req.params.output!==undefined && req.params.output=="html") { if(datas.questionnaires.length!=0) { const pug = require("pug"); const striptags = require("striptags"); const txtIllustration= require("../lang/"+config.adminLang+"/illustration"); const compiledFunction = pug.compileFile("./views/"+config.theme+"/includes/listing-questionnaires.pug"); const pageDatas= { tool: tool, striptags: striptags, txtGeneral: txtGeneral, txtIllustration: txtIllustration, questionnaires: datas.questionnaires, nbQuestionnairesList:configTpl.nbQuestionnairesUserHomePage } datas.html=await compiledFunction(pageDatas); } else datas.html=""; res.status(200).json(datas); } else res.status(200).json(datas); } else res.status(404).json(txtQuestionnaire.notFound); next(); } catch(e) { next(e); } } * */