364 lines
14 KiB
JavaScript
364 lines
14 KiB
JavaScript
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<nb && userQuestionnaires.ids[i])
|
|
{
|
|
questionnaire=await questionnaireCtrl.searchQuestionnaireById(userQuestionnaires.ids[i]);
|
|
if(questionnaire)
|
|
Questionnaires.push(questionnaire);
|
|
i++;
|
|
}
|
|
return { nbTot: nbTot, questionnaires: Questionnaires };
|
|
}
|
|
exports.getUserQuestionnairesWithoutAnswer = getUserQuestionnairesWithoutAnswer;
|
|
* */
|
|
|
|
/// S'inspirer de la fonction ci-dessous pour la liste des questionnaires proposés à l'utilisateur.
|
|
/*
|
|
// Retourne la liste des questionnaires auxquels un utilisateur a accès, mais n'a pas répondu
|
|
// Ils sont listés par ordre de fraîcheur, les + récents étant en début de liste
|
|
// Un questionnaire de début et un nombre de questionnaires à retourner doivent être fournis (pagination).
|
|
exports.getQuestionnairesWithouAnswerByUser = async(req, res, next) =>
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
* */ |