Ajout de la possibilité de répondre à un groupe de questions.
This commit is contained in:
parent
fe713480a0
commit
ca4be71a89
@ -9,7 +9,6 @@ module.exports =
|
||||
previewQuestionnaireRoutes: "/preview",
|
||||
publishedQuestionnaireRoutes: "/quiz/",
|
||||
regenerateHTML: "/htmlregenerated",
|
||||
saveAnswersRoute: "/answer/",
|
||||
searchAdminQuestionnairesRoute : "/searchadmin",
|
||||
searchQuestionnairesRoute : "/search",
|
||||
// -- groupes :
|
||||
@ -24,8 +23,9 @@ module.exports =
|
||||
// -- answers :
|
||||
getAdminStats: "/getadminstats/",
|
||||
getPreviousAnswers: "/user/answers/",
|
||||
getQuestionnairesWithoutAnswer: "/withoutanswer/user/",
|
||||
getStatsAnswers : "/user/anwswers/stats/",
|
||||
/// getQuestionnairesWithoutAnswer: "/withoutanswer/user/", -> ne sert plus ! à remplacer pour liste derniers quizs
|
||||
getStatsAnswers : "/user/anwswers/stats/",// fonctionne aussi pour les groupes
|
||||
saveAnswersRoute: "/answer/",// idem
|
||||
// forms : à compléter avec valeurs par défaut, etc. cf modèle
|
||||
Questionnaire :
|
||||
{
|
||||
|
@ -6,10 +6,11 @@ const configTpl = require("../views/"+config.theme+"/config/"+config.availableLa
|
||||
const tool = require("../tools/main");
|
||||
const toolFile = require("../tools/file");
|
||||
|
||||
const subscriptionCtrl = require("./subscription");
|
||||
const groupCtrl = require("./group");
|
||||
const questionnaireCtrl = require("./questionnaire");
|
||||
const subscriptionCtrl = require("./subscription");
|
||||
|
||||
const txt = require("../lang/"+config.adminLang+"/answer");
|
||||
const txtAnswers = require("../lang/"+config.adminLang+"/answer");
|
||||
const txtGeneral = require("../lang/"+config.adminLang+"/general");
|
||||
|
||||
// Enregistrement d'une réponse à un questionnaire
|
||||
@ -18,33 +19,39 @@ exports.create = async (req, res, next) =>
|
||||
try
|
||||
{
|
||||
const db = require("../models/index");
|
||||
const checkQuestionnaireAccess=await subscriptionCtrl.checkQuestionnaireAccess(req.connectedUser.User.id, req.body.QuestionnaireId);
|
||||
req.body.UserId=req.connectedUser.User.id;
|
||||
if(checkQuestionnaireAccess) // l'utilisateur a déjà accès à ce questionnaire
|
||||
await db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] });
|
||||
else
|
||||
{
|
||||
await Promise.all([
|
||||
db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] }),
|
||||
db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] })
|
||||
]);
|
||||
}
|
||||
// j'en profite pour remettre les pendules à l'heure !
|
||||
await saveAnswerToQuestionnaire(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 });
|
||||
creaUserStatsAnwsersJson(req.body.UserId);
|
||||
creaUserQuestionnairesWithoutAnswerJson(req.body.UserId);
|
||||
creaUserAnswersJson(req.body.UserId);
|
||||
res.status(201).json({ message: txt.responseSavedMessage });
|
||||
res.status(201).json({ message: txtAnswers.responseSavedMessage });
|
||||
next();
|
||||
}
|
||||
catch(e)
|
||||
{ // à priori, l'utilisateur ne peut pas avoir envoyé de données incorrectes, donc erreur application pour admin
|
||||
{
|
||||
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é
|
||||
// Si fichier réponses devient trop gros, passer par sql ?
|
||||
exports.getAnswersByQuestionnaire = async(req, res, next) =>
|
||||
{
|
||||
try
|
||||
@ -59,14 +66,30 @@ exports.getAnswersByQuestionnaire = async(req, res, next) =>
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// J'ajoute les stats générales des questionnaires pour comparaison :
|
||||
stats.general=await questionnaireCtrl.getStatsQuestionnaires();
|
||||
// + 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)
|
||||
@ -75,71 +98,58 @@ exports.getStatsByUser = async(req, res, next) =>
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FONCTIONS UTILITAIRES
|
||||
|
||||
// Enregistre la réponse à un questionnaire
|
||||
const saveAnswerToQuestionnaire = async (req) =>
|
||||
{
|
||||
const db = require("../models/index");
|
||||
const checkQuestionnaireAccess=await subscriptionCtrl.checkQuestionnaireAccess(req.UserId, req.QuestionnaireId);
|
||||
if(checkQuestionnaireAccess) // 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"] });
|
||||
const group = 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`,`nbQuestions`,`nbCorrectAnswers`,`duration`,`createdAt` FROM `Answers` WHERE `UserId`=:id ORDER BY `QuestionnaireId` DESC, `createdAt` DESC", { replacements: { id: UserId }, type: QueryTypes.SELECT });
|
||||
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);// à surveiller car fichier pouvant devenir gros ! mais utile pour SVG côté client
|
||||
await toolFile.createJSON(config.dirCacheUsersAnswers, UserId, userAnswers);
|
||||
return userAnswers;
|
||||
}
|
||||
else
|
||||
@ -147,7 +157,7 @@ const creaUserAnswersJson = async (UserId) =>
|
||||
}
|
||||
exports.creaUserAnswersJson = creaUserAnswersJson;
|
||||
|
||||
// Retourne les réponses d'un utilisateurs à un questionnaire
|
||||
// Retourne les réponses d'un utilisateurs à un questionnaire simple.
|
||||
const getUserAnswersByQuestionnaire = async (UserId, QuestionnaireId) =>
|
||||
{
|
||||
let userAnswers=await toolFile.readJSON(config.dirCacheUsersAnswers, UserId);
|
||||
@ -158,29 +168,49 @@ const getUserAnswersByQuestionnaire = async (UserId, QuestionnaireId) =>
|
||||
const answers=[];
|
||||
for(let i in userAnswers)
|
||||
{
|
||||
if(userAnswers[i].QuestionnaireId==QuestionnaireId)// pas "===" car type de données pouvant être différents
|
||||
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 classées par QuestionnaireId, je peux sortir de la boucle
|
||||
else if(answers.length !== 0) // les réponses étant classées par QuestionnaireId, je peux sortir de la boucle
|
||||
break;
|
||||
}
|
||||
return answers;
|
||||
}
|
||||
|
||||
// À combien de questionnaire l'utilisateur a-t'il répondu, quelle est son résultat moyen ?
|
||||
// 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 classé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["Answer"].findAll({ attributes: [[db.sequelize.fn('DISTINCT', db.sequelize.col('QuestionnaireId')), 'id']], where: { UserId : UserId }});
|
||||
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)
|
||||
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
|
||||
nbQuestionnaires : getUserQuestionnaires.length+getUserQuestionnairesGroup.length
|
||||
}
|
||||
if(getUserStats && getUserAnswers.length!=0)
|
||||
if(getUserStats && getUserAnswers.length !== 0)
|
||||
{
|
||||
stats.avgCorrectAnswers=getUserStats[0].avgCorrectAnswers;
|
||||
stats.avgDuration=getUserStats[0].avgDuration;
|
||||
@ -193,7 +223,7 @@ const creaUserStatsAnwsersJson = async (UserId) =>
|
||||
}
|
||||
exports.creaUserStatsAnwsersJson = creaUserStatsAnwsersJson;
|
||||
|
||||
// Retourne les données créées par la fonction précédente
|
||||
// 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);
|
||||
@ -205,7 +235,7 @@ const getUserStatsAnswers = async (UserId) =>
|
||||
return userStats;
|
||||
}
|
||||
|
||||
// À combien de questionnaire les utilisateurs ont-ils répondu ces dernières 24 ? depuis le début ?
|
||||
// À 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");
|
||||
@ -225,7 +255,9 @@ const getStatsAnswers = async () =>
|
||||
}
|
||||
exports.getStatsAnswers = getStatsAnswers;
|
||||
|
||||
// Créer la liste des questionnaires proposés à l'utilisateur, mais auxquels il n'a pas encore répondu
|
||||
/*
|
||||
// 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);
|
||||
@ -270,4 +302,62 @@ const getUserQuestionnairesWithoutAnswer = async (UserId, begin=0, nb=10) =>
|
||||
}
|
||||
return { nbTot: nbTot, questionnaires: Questionnaires };
|
||||
}
|
||||
exports.getUserQuestionnairesWithoutAnswer = getUserQuestionnairesWithoutAnswer;
|
||||
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);
|
||||
}
|
||||
}
|
||||
* */
|
@ -62,7 +62,8 @@ exports.getGodfatherId = async (req, res, next) =>
|
||||
}
|
||||
|
||||
// Contrôleur traitant les données envoyées pour une inscription
|
||||
// Les CGU doivent être acceptées et une adresse e-mail envoyée. Le reste peut être adapté sur la page de validation.
|
||||
// Les CGU doivent être acceptées et une adresse e-mail envoyée.
|
||||
// Le reste peut être adapté sur la page de validation de l'inscription.
|
||||
exports.signup = async (req, res, next) =>
|
||||
{
|
||||
try
|
||||
@ -76,7 +77,6 @@ exports.signup = async (req, res, next) =>
|
||||
{
|
||||
// Un mot de passe temporaire et non communiqué est généré :
|
||||
req.body.passwordTemp=tool.getPassword(8, 12);
|
||||
console.log(req.body.passwordTemp);
|
||||
req.body.password=await bcrypt.hash(req.body.passwordTemp, config.bcryptSaltRounds);
|
||||
// Un pseudo temporaire est créé en utilisant la partie de l'e-mail précédant le "@" :
|
||||
const lastIndex=req.body.email.indexOf("@");
|
||||
@ -85,14 +85,11 @@ exports.signup = async (req, res, next) =>
|
||||
req.body.name=req.body.email.substring(0, lastIndex);
|
||||
const user=await db["User"].create({ ...req.body }, { fields: ["name", "email", "password", "timeDifference"] });
|
||||
req.body.UserId=user.id;
|
||||
// si l'utilisateur a répondu à un quiz avant de créer son compte, on enregistre son résultat :
|
||||
// Si l'utilisateur a répondu à un quiz (ou groupe de quizs) avant de créer son compte, on enregistre son résultat :
|
||||
if(req.body.QuestionnaireId)
|
||||
{
|
||||
await Promise.all([
|
||||
db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] }),
|
||||
db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] })
|
||||
]); // pas nécessaire ici de créer le cache JSON, car il sera créé lors de la première connexion au compte.
|
||||
}
|
||||
await answerCtrl.saveAnswerToQuestionnaire(req.body);
|
||||
else if(req.body.GroupId)
|
||||
await answerCtrl.saveAnswerToGroup(req.body);
|
||||
await sendValidationLink(user);
|
||||
res.status(201).json({ message: txt.mailValidationMessage });
|
||||
next();
|
||||
@ -312,23 +309,18 @@ exports.login = async (req, res, next) =>
|
||||
const now=new Date();
|
||||
const timeDifference=req.body.timeDifference;
|
||||
db["User"].update({ connectedAt: now, timeDifference: timeDifference }, { where: { id : user.id }, limit:1 });
|
||||
creaUserJson(user.id);
|
||||
await creaUserJson(user.id);
|
||||
// Connexion à rallonge uniquement possible pour utilisateur de base :
|
||||
let loginTime=config.tokenConnexionMinTimeInHours;
|
||||
if((req.body.keepConnected==="true") && (user.status==="user"))
|
||||
if((req.body.keepConnected === "true") && (user.status === "user"))
|
||||
loginTime=config.tokenConnexionMaxTimeInDays;
|
||||
// Si des données concernant un quiz ont été transmises, on les enregistre ici :
|
||||
// Si l'utilisateur a répondu à un quiz (ou groupe de quizs) avant de se connecter, on enregistre son résultat.
|
||||
// Uniquement pour les utilisateurs de base.
|
||||
req.body.UserId=user.id;
|
||||
if(req.body.QuestionnaireId)
|
||||
{
|
||||
const access=await subscriptionCtrl.checkQuestionnaireAccess(user.id, req.body.QuestionnaireId);
|
||||
if(!access)
|
||||
await db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] });
|
||||
await db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] });
|
||||
answerCtrl.creaUserAnswersJson(req.body.UserId);
|
||||
answerCtrl.creaUserQuestionnairesWithoutAnswerJson(req.body.UserId);
|
||||
answerCtrl.creaUserStatsAnwsersJson(req.body.UserId);
|
||||
}
|
||||
if(req.body.QuestionnaireId && user.status === "user")
|
||||
await answerCtrl.saveAnswerToQuestionnaire(req.body);
|
||||
else if(req.body.GroupId && user.status === "user")
|
||||
await answerCtrl.saveAnswerToGroup(req.body);
|
||||
res.status(200).json(
|
||||
{
|
||||
userId: user.id,
|
||||
@ -404,8 +396,8 @@ exports.checkLoginLink = async (req, res, next) =>
|
||||
try
|
||||
{
|
||||
const db = require("../models/index");
|
||||
const userDatas= await checkTokenUser(req.body.t);
|
||||
if(userDatas.User.status!=="user")
|
||||
const userDatas = await checkTokenUser(req.body.t);
|
||||
if(userDatas.User.status !== "user")
|
||||
res.status(403).json({ errors: [txtGeneral.notAllowed] });
|
||||
else if(userDatas.Subscription)
|
||||
{
|
||||
@ -416,18 +408,12 @@ exports.checkLoginLink = async (req, res, next) =>
|
||||
let loginTime=config.tokenConnexionMinTimeInHours;
|
||||
if(userDatas.decodedToken.keepConnected===true)
|
||||
loginTime=config.tokenConnexionMaxTimeInDays;
|
||||
// si des données concernant un quiz ont été transmises, je les enregistre ici :
|
||||
// Si l'utilisateur a répondu à un quiz (ou groupe de quizs) avant de se connecter, on enregistre son résultat :
|
||||
req.body.UserId=userDatas.User.id;
|
||||
if(req.body.QuestionnaireId)
|
||||
{
|
||||
req.body.UserId=userDatas.User.id;
|
||||
const access=await subscriptionCtrl.checkQuestionnaireAccess(userDatas.User.id, req.body.QuestionnaireId);
|
||||
if(!access)
|
||||
await db["QuestionnaireAccess"].create({ ...req.body }, { fields: ["QuestionnaireId", "UserId"] });
|
||||
await db["Answer"].create({ ...req.body }, { fields: ["nbQuestions", "nbCorrectAnswers", "duration", "QuestionnaireId", "UserId"] });
|
||||
answerCtrl.creaUserAnswersJson(req.body.UserId);
|
||||
answerCtrl.creaUserStatsAnwsersJson(req.body.UserId);
|
||||
answerCtrl.creaUserQuestionnairesWithoutAnswerJson(req.body.UserId);
|
||||
}
|
||||
await answerCtrl.saveAnswerToQuestionnaire(req.body);
|
||||
else if(req.body.GroupId)
|
||||
await answerCtrl.saveAnswerToGroup(req.body);
|
||||
res.status(200).json(
|
||||
{
|
||||
userId: userDatas.User.id,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN QUIZ
|
||||
// -- GESTION DU FORMULAIRE PERMETTANT D'AFFICHER ET RÉPONDRE À UN GROUPE DE QUIZS
|
||||
|
||||
/// Il n'est pas nécessaire d'être connecté pour répondre au quiz et voir son résultat.
|
||||
/// Mais si pas connecté, on propose à l'internaute de se connecter ou de créer un compte pour sauvegarder son résultat.
|
||||
@ -8,7 +8,7 @@
|
||||
// Fichier de configuration tirés du backend :
|
||||
import { apiUrl, availableLangs, theme } from "../../config/instance.js";
|
||||
const lang=availableLangs[0];
|
||||
import { getPreviousAnswers, questionnaireRoutes, saveAnswersRoute } from "../../config/questionnaires.js";
|
||||
import { getPreviousAnswers, groupRoutes, saveAnswersRoute } from "../../config/questionnaires.js";
|
||||
const configTemplate = require("../../views/"+theme+"/config/"+lang+".js");
|
||||
|
||||
import { checkAnswerOuput, saveAnswer } from "./tools/answers.js";
|
||||
@ -25,19 +25,28 @@ const { noPreviousAnswer, previousAnswersLine, previousAnswersStats, previousAns
|
||||
const { serverError } = require("../../lang/"+lang+"/general");
|
||||
|
||||
// Principaux éléments du DOM manipulés :
|
||||
const myForm = document.getElementById("questionnaire");
|
||||
const divResponse = document.getElementById("response");
|
||||
const btnShow = document.getElementById("showQuestionnaire");
|
||||
const btnSubmit = document.getElementById("checkResponses");
|
||||
const divResponse = document.getElementById("response");
|
||||
const explanationsTitle = document.getElementById("explanationsTitle");
|
||||
const explanationsContent = document.getElementById("explanationsContent");
|
||||
const myForm = document.getElementById("group");
|
||||
|
||||
// Affiche le bouton de soumission + déclenche le chronomètre mesurant la durée de la réponse.
|
||||
let chronoBegin=0;
|
||||
const beginAnswer = () =>
|
||||
{
|
||||
chronoBegin=Date.now();
|
||||
btnSubmit.style.display="block";
|
||||
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview".
|
||||
}
|
||||
|
||||
let isConnected, user;
|
||||
const initialise = async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
btnShow.style.display="inline";// bouton caché si JS inactif, car JS nécessaire pour vérifier les réponses
|
||||
// Si JS activé, on affiche le bouton de soumission du formulaire :
|
||||
beginAnswer();
|
||||
isConnected=await checkSession(["user"]);// "user" car seuls les utilisateurs de base peuvent enregistrer leurs réponses aux quizs
|
||||
// Si l'utilisateur est connecté et a déjà répondu à ce quiz, on affiche ses précédentes réponses à la place du texte servant à expliquer le topo aux nouveaux
|
||||
if(isConnected)
|
||||
@ -57,40 +66,6 @@ const initialise = async () =>
|
||||
initialise();
|
||||
helloDev();
|
||||
|
||||
// Affichage du questionnaire quand l'utilisateur clique sur le bouton ou si l'id du formulaire est passée par l'url.
|
||||
// Déclenche en même temps le chronomètre mesurant la durée de la réponse aux questions.
|
||||
const showQuestionnaire = () =>
|
||||
{
|
||||
chronoBegin=Date.now();
|
||||
myForm.style.display="block";
|
||||
btnShow.style.display="none";
|
||||
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview".
|
||||
if(window.location.hash!=="")
|
||||
{
|
||||
window.location.hash="";// ! le "#" reste
|
||||
window.location.assign(here+"questionnaire");
|
||||
}
|
||||
else
|
||||
window.location.assign(here+"#questionnaire");
|
||||
}
|
||||
let chronoBegin=0;
|
||||
btnShow.addEventListener("click", function(e)
|
||||
{
|
||||
try
|
||||
{
|
||||
e.preventDefault();
|
||||
showQuestionnaire();
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
addElement(divResponse, "p", serverError, "", ["error"]);
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
// Lien passé par mail pour voir directement le quiz
|
||||
if(location.hash!="" && location.hash==="#questionnaire")
|
||||
showQuestionnaire();
|
||||
|
||||
// Traitement de l'envoi de la réponse de l'utilisateur :
|
||||
let answer = {};
|
||||
myForm.addEventListener("submit", function(e)
|
||||
@ -99,12 +74,12 @@ myForm.addEventListener("submit", function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
btnSubmit.style.display="none";// seulement un envoi à la fois, SVP :)
|
||||
divResponse.innerHTML="";// supprime les éventuels messages déjà affichés
|
||||
divResponse.innerHTML="";// supprime les éventuels messages déjà affichés.
|
||||
const userResponses=getDatasFromInputs(myForm);
|
||||
answer.duration=Math.round((Date.now()-chronoBegin)/1000);
|
||||
answer.nbQuestions=0;
|
||||
answer.nbCorrectAnswers=0;
|
||||
answer.QuestionnaireId=document.getElementById("questionnaireId").value;
|
||||
answer.GroupId=document.getElementById("groupId").value;
|
||||
// Les réponses sont regroupées par question, donc quand idQuestion change, on connaît le résultat pour la question précédente.
|
||||
// Pour qu'une réponse soit bonne, il faut cocher toutes les bonnes réponses (si QCM) à la question ET cocher aucune des mauvaises.
|
||||
let idChoice, idQuestion="", goodResponse=false;
|
||||
@ -113,32 +88,31 @@ myForm.addEventListener("submit", function(e)
|
||||
if(item.startsWith("isCorrect_response_"))// = Nouvelle réponse possible.
|
||||
{
|
||||
idChoice = item.substring(item.lastIndexOf("_") + 1);
|
||||
// si on change de queston
|
||||
if(userResponses["question_id_response_"+idChoice]!=idQuestion) // on commence à traiter une nouvelle question
|
||||
if(userResponses["question_id_response_"+idChoice] != idQuestion) // = on commence à traiter une nouvelle question.
|
||||
{
|
||||
idQuestion=userResponses["question_id_response_"+idChoice];
|
||||
answer.nbQuestions++;
|
||||
if(goodResponse) // résultat de la question précédente
|
||||
if(goodResponse) // = pas d'erreur à la question précédente
|
||||
answer.nbCorrectAnswers++;
|
||||
goodResponse=true;// réponse bonne jusqu'à la première erreur...
|
||||
goodResponse=true;// La réponse est considérée comme bonne, jusqu'à la première erreur...
|
||||
}
|
||||
if(userResponses[item]=="true")
|
||||
if(userResponses[item] == "true")
|
||||
{
|
||||
document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect");
|
||||
if(userResponses["response_"+idChoice]===undefined)// une bonne réponse n'a pas été sélectionnée
|
||||
if(userResponses["response_"+idChoice] === undefined)// = une bonne réponse n'a pas été sélectionnée
|
||||
goodResponse=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(userResponses["response_"+idChoice]==="on")// réponse cochée ne faisant pas partie des bonnes
|
||||
if(userResponses["response_"+idChoice] === "on")
|
||||
{
|
||||
goodResponse=false;
|
||||
goodResponse=false; // = une mauvaise réponse a été sélectionnée
|
||||
document.getElementById("response_"+idChoice).parentNode.classList.add("isNotCorrect");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// si j'ai bien répondu à la dernière question, il faut le compter ici, car je suis sorti de la boucle :
|
||||
// Si j'ai bien répondu à la dernière question, il faut le compter ici, car on est sorti de la boucle :
|
||||
if(goodResponse)
|
||||
answer.nbCorrectAnswers++;
|
||||
|
||||
@ -146,9 +120,9 @@ myForm.addEventListener("submit", function(e)
|
||||
let getOuput=checkAnswerOuput(answer);
|
||||
if(isConnected)
|
||||
{
|
||||
// Si l'utilisateur est connecté, on enregistre son résultat sur le serveur.
|
||||
// Si l'utilisateur est connecté, on passe son résultat au serveur pour le sauvegarder.
|
||||
const xhrSaveAnswer = new XMLHttpRequest();
|
||||
xhrSaveAnswer.open("POST", apiUrl+questionnaireRoutes+saveAnswersRoute);
|
||||
xhrSaveAnswer.open("POST", apiUrl+groupRoutes+saveAnswersRoute);
|
||||
xhrSaveAnswer.onreadystatechange = function()
|
||||
{
|
||||
if (this.readyState == XMLHttpRequest.DONE)
|
||||
@ -161,7 +135,7 @@ myForm.addEventListener("submit", function(e)
|
||||
}
|
||||
else
|
||||
getOuput+="<br>"+responseSavedError.replace("#URL", configTemplate.userHomePage);
|
||||
// on redirige vers le résultat
|
||||
// Puis on le redirige vers son résultat :
|
||||
window.location.hash="";
|
||||
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"...
|
||||
window.location.assign(here+"explanations");
|
||||
@ -169,31 +143,29 @@ myForm.addEventListener("submit", function(e)
|
||||
}
|
||||
xhrSaveAnswer.setRequestHeader("Authorization", "Bearer "+user.token);
|
||||
xhrSaveAnswer.setRequestHeader("Content-Type", "application/json");
|
||||
answer.timeDifference=getTimeDifference();// on en profite pour mettre les pendules à l'heure.
|
||||
answer.timeDifference=getTimeDifference();// On en profite pour mettre les pendules à l'heure.
|
||||
xhrSaveAnswer.send(JSON.stringify(answer));
|
||||
}
|
||||
else
|
||||
{ // si pas connecté, on enregistre le résultat côté client pour permettre de le retrouver au moment de la création du compte ou de la connexion.
|
||||
{ // Si internaute non connecté, on enregistre le résultat côté client pour permettre de le retrouver au moment de la création du compte ou de la connexion.
|
||||
if(saveAnswer(answer))
|
||||
{
|
||||
getOuput+="<br><br>"+wantToSaveResponses;
|
||||
addElement(divResponse, "p", getOuput, "", ["info"]);
|
||||
getOuput+="</p><p>"+wantToSaveResponses+"</p>";
|
||||
addElement(divResponse, "p", getOuput, "", ["success"]);
|
||||
document.querySelector(".subscribeBtns").style.display="block";
|
||||
}
|
||||
else // inutile de proposer de créer un compte si le stockage local ne fonctionne pas
|
||||
addElement(divResponse, "p", getOuput, "", ["info"]);
|
||||
// on redirige vers le résultat
|
||||
else // Mais inutile de proposer de créer un compte si le stockage local ne fonctionne pas
|
||||
addElement(divResponse, "p", getOuput, "", ["success"]);
|
||||
// Puis on le redirige vers son résultat :
|
||||
window.location.hash="";
|
||||
const here=window.location;// window.location à ajouter pour ne pas quitter la page en mode "preview"...
|
||||
window.location.assign(here+"response");
|
||||
}
|
||||
// + affichage des textes d'explications pour chaque question
|
||||
// + Affichage des textes d'explications pour chaque question
|
||||
const explanations=document.querySelectorAll(".help");
|
||||
for(let i in explanations)
|
||||
{
|
||||
if(explanations[i].style!=undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?)
|
||||
if(explanations[i].style !== undefined) // sinon, la console affiche une erreur "TypeError: explanations[i].style is undefined", bien que tout fonctionne (?)
|
||||
explanations[i].style.display="block";
|
||||
}
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
@ -202,12 +174,12 @@ myForm.addEventListener("submit", function(e)
|
||||
}
|
||||
})
|
||||
|
||||
// Fonction vérifiant les précédentes réponses de l'utilisateur
|
||||
// Utile si connecté lors du premier chargement de la page, puis après une nouvelle réponse
|
||||
// Fonction vérifiant les précédentes réponses de l'utilisateur.
|
||||
// Utile si connecté lors du premier chargement de la page, puis après une nouvelle réponse.
|
||||
const checkPreviousResponses = (user) =>
|
||||
{
|
||||
const xhrPreviousRes = new XMLHttpRequest();
|
||||
xhrPreviousRes.open("GET", apiUrl+questionnaireRoutes+getPreviousAnswers+user.id+"/"+document.getElementById("questionnaireId").value);
|
||||
xhrPreviousRes.open("GET", apiUrl+groupRoutes+getPreviousAnswers+user.id+"/"+document.getElementById("groupId").value);
|
||||
xhrPreviousRes.onreadystatechange = function()
|
||||
{
|
||||
if (this.readyState == XMLHttpRequest.DONE)
|
||||
@ -247,7 +219,6 @@ const checkPreviousResponses = (user) =>
|
||||
addElement(explanationsContent, "ul", noPreviousAnswer);
|
||||
// dans un cas comme dans l'autre, bouton pour revenir à l'accueil du compte
|
||||
addElement(explanationsContent, "p", "<a href=\"/"+configTemplate.userHomePage+"\" class=\"button cardboard\">"+configTemplate.userHomePageTxt+"</a>", "", ["btn"], "", false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,8 @@ const initialise = async () =>
|
||||
}
|
||||
xhrStats.setRequestHeader("Authorization", "Bearer "+user.token);
|
||||
xhrStats.send();
|
||||
|
||||
|
||||
/*
|
||||
// Par défaut, on affiche des derniers quizs proposés sans réponse :
|
||||
const xhrLastQuizs = new XMLHttpRequest();
|
||||
xhrLastQuizs.open("GET", apiUrl+questionnaireRoutes+getQuestionnairesWithoutAnswer+""+user.id+"/"+0+"/"+configTemplate.nbQuestionnairesUserHomePage+"/html");
|
||||
@ -113,7 +114,7 @@ const initialise = async () =>
|
||||
}
|
||||
}
|
||||
xhrLastQuizs.setRequestHeader("Authorization", "Bearer "+user.token);
|
||||
xhrLastQuizs.send();
|
||||
xhrLastQuizs.send();*/
|
||||
|
||||
// Traitement du lancement d'une recherche
|
||||
// La recherche peut être lancée via la bouton submit ou un lien de pagination
|
||||
|
@ -8,7 +8,7 @@ const txt = require("../../../lang/"+configFrontEnd.lang+"/answer");
|
||||
// Enregistrement côté client du dernier résultat à un quiz en attendant d'être connecté
|
||||
export const saveAnswer = (answer) =>
|
||||
{
|
||||
if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions) && !isEmpty(answer.QuestionnaireId))
|
||||
if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions) && (!isEmpty(answer.QuestionnaireId) || !isEmpty(answer.GroupId)))
|
||||
{
|
||||
saveLocaly("lastAnswer", answer);
|
||||
return true;
|
||||
@ -43,49 +43,4 @@ export const checkAnswerOuput = (answer) =>
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
export const checkSession = async (config) =>
|
||||
{
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
if(isEmpty(localStorage.getItem("user")))
|
||||
resolve(false);
|
||||
else
|
||||
{
|
||||
const user=JSON.parse(localStorage.getItem("user"));
|
||||
if(user.duration===undefined || user.duration < Date.now())
|
||||
{
|
||||
localStorage.removeItem("user");
|
||||
resolve(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", configFrontEnd.apiUrl+config.userRoutes+config.checkLoginRoute+user.token);
|
||||
xhr.onload = () =>
|
||||
{
|
||||
let response=JSON.parse(xhr.responseText);
|
||||
if (xhr.status === 200 && response.isValid && response.id != undefined)
|
||||
{
|
||||
if(response.id===user.id)
|
||||
resolve(true);
|
||||
else
|
||||
{
|
||||
localStorage.removeItem("user");
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
localStorage.removeItem("user");
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
xhr.onerror = () => reject(xhr.statusText);
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
});
|
||||
}*/
|
||||
}
|
@ -31,7 +31,7 @@ export const setSession = (userId, token, durationTS) =>
|
||||
saveLocaly("user", storageUser);
|
||||
}
|
||||
|
||||
// Vérifie qu'il y a des données locales concernant le résultat d'un quiz
|
||||
// Vérifie qu'il y a des données locales concernant le résultat d'un quiz ou d'un groupe de quizs
|
||||
// Et les ajoute aux données envoyées par les formulaires d'inscription/connexion si c'est le cas
|
||||
export const checkAnswerDatas = (datas) =>
|
||||
{
|
||||
@ -39,12 +39,15 @@ export const checkAnswerDatas = (datas) =>
|
||||
if(!isEmpty(lastAnswer))
|
||||
{
|
||||
const answer=JSON.parse(lastAnswer);
|
||||
if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.QuestionnaireId) && !isEmpty(answer.nbQuestions))
|
||||
if(!isEmpty(answer.duration) && !isEmpty(answer.nbCorrectAnswers) && !isEmpty(answer.nbQuestions) && (!isEmpty(answer.QuestionnaireId) || !isEmpty(answer.GroupId)))
|
||||
{
|
||||
datas.duration=answer.duration;
|
||||
datas.nbCorrectAnswers=answer.nbCorrectAnswers;
|
||||
datas.QuestionnaireId=answer.QuestionnaireId;
|
||||
datas.nbQuestions=answer.nbQuestions;
|
||||
if(!isEmpty(answer.QuestionnaireId))
|
||||
datas.QuestionnaireId=answer.QuestionnaireId;
|
||||
else
|
||||
datas.GroupId=answer.GroupId;
|
||||
}
|
||||
}
|
||||
return datas;
|
||||
|
@ -1,8 +1,8 @@
|
||||
module.exports =
|
||||
{
|
||||
checkResponsesOuputFail : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est certain, vous ferez mieux la prochaine fois !",
|
||||
checkResponsesOuputMedium : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. C'est pas mal du tout !",
|
||||
checkResponsesOuputSuccess : "Vous avez répondu en DURATION secondes et avez NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions. Bravo ! Rien ne vous échappe !",
|
||||
checkResponsesOuputFail : "Vous avez répondu en DURATION secondes et avez <u><b>NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions</b></u>. C'est certain, vous ferez mieux la prochaine fois !",
|
||||
checkResponsesOuputMedium : "Vous avez répondu en DURATION secondes et avez <u><b>NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions</b></u>. C'est pas mal du tout !",
|
||||
checkResponsesOuputSuccess : "Vous avez répondu en DURATION secondes et avez <u><b>NBCORRECTANSWERS bonne(s) réponse(s) sur NBQUESTIONS questions</b></u>. Bravo ! Rien ne vous échappe !",
|
||||
nbQuestionnaireWithoudAnswer: "Il y a #NB quizs qui vous ont été proposés et auxquels vous n'avez pas répondu. Voici les derniers :!",
|
||||
needIntegerNumberCorrectResponses : "Le nombre de réponses correctes doit être un nombre entier.",
|
||||
needIntegerNumberSecondesResponse : "La durée de la réponse doit être un nombre entier de secondes.",
|
||||
@ -22,5 +22,5 @@ module.exports =
|
||||
responseSavedError : "Cependant une erreur a été rencontrée durant l'enregistrement de votre résultat. <a href='/#URL'>Accèder à tous vos quizs</a>.",
|
||||
responseSavedMessage : "Votre résultat a été enregistré. <a href='/#URL'>Accèder à tous vos quizs</a>.",
|
||||
statsUser: "Vous avez enregistré NBANSWERS réponses à <b>NBQUESTIONNAIRES questionnaires différents</b> sur les NBTOTQUESTIONNAIRES proposés par le site.<br>En moyenne, vous avez mis AVGDURATION secondes à répondre et avez <b>correctement répondu à AVGCORRECTANSWERS % des questions</b>.",
|
||||
wantToSaveResponses: "Si vous le souhaitez, vous pouvez sauvegarder votre résultat <u>en créant votre compte ci-dessous</u>.<br><b>Cela vous permettra aussi de recevoir régulièrement de nouveaux quizs par e-mail</b>.",
|
||||
};
|
||||
wantToSaveResponses: "Si vous le souhaitez, vous pouvez <u><b>sauvegarder votre résultat</b></u> en créant votre compte ci-dessous. Cela vous permettra aussi de <u><b>recevoir régulièrement de nouvelles \"graines de culture\"</b></u> directement sur votre e-mail.",
|
||||
};
|
@ -2,7 +2,7 @@ module.exports =
|
||||
{
|
||||
btnSendResponse: "Testez vos réponses.",
|
||||
btnShareQuizTxt: "Partager ce quiz sur ",
|
||||
commonIntroTxt: "Ce quiz vous permet tester ce que vous avez retenu des textes proposés à la lecture. Au besoin, cliquez sur le bouton suivant pour les relire :",
|
||||
commonIntroTxt: "Ce quiz vous permet tester ce que vous avez retenu des textes proposés à la lecture. Au besoin, cliquez sur le bouton suivant pour les relire.",
|
||||
correctAnswerTxt: "Bonne réponse",
|
||||
groupsName: "Quiz",// nom d'un groupe pour l'affichage dans les vues
|
||||
haveBeenPublished: "#NB nouveaux groupes de quizs ont été publiés.",
|
||||
|
@ -1,16 +1,21 @@
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
|
||||
const auth = require("../middleware/authAdmin");
|
||||
const auth = require("../middleware/auth");
|
||||
const authAdmin = require("../middleware/authAdmin");
|
||||
|
||||
const answerCtrl = require("../controllers/answer");
|
||||
const groupCtrl = require("../controllers/group");
|
||||
|
||||
router.post("/search", auth, groupCtrl.searchGroups);
|
||||
router.post("/", auth, groupCtrl.create);
|
||||
router.get("/stats", auth, groupCtrl.getStatsGroups);
|
||||
router.put("/:id", auth, groupCtrl.modify);
|
||||
router.delete("/:id", auth, groupCtrl.delete);
|
||||
router.get("/get/:id", auth, groupCtrl.getOneById);
|
||||
router.post("/search", authAdmin, groupCtrl.searchGroups);
|
||||
router.post("/", authAdmin, groupCtrl.create);
|
||||
router.get("/stats", authAdmin, groupCtrl.getStatsGroups);
|
||||
router.put("/:id", authAdmin, groupCtrl.modify);
|
||||
router.delete("/:id", authAdmin, groupCtrl.delete);
|
||||
router.get("/get/:id", authAdmin, groupCtrl.getOneById);
|
||||
router.get("/preview/:id/:token", groupCtrl.showOneGroupById);// prévisualisation HTML, même si groupe "incomplet"
|
||||
|
||||
router.post("/answer/", auth, answerCtrl.createInGroup);
|
||||
router.get("/user/answers/:userId/:groupId", auth, answerCtrl.getAnswersByGroup);
|
||||
|
||||
module.exports = router;
|
@ -25,6 +25,6 @@ router.get("/preview/:id/:token", questionnaireCtrl.showOneQuestionnaireById);//
|
||||
router.post("/answer/", auth, answerCtrl.create);
|
||||
router.get("/user/anwswers/stats/:userId", auth, answerCtrl.getStatsByUser);
|
||||
router.get("/user/answers/:userId/:questionnaireId", auth, answerCtrl.getAnswersByQuestionnaire);
|
||||
router.get("/withoutanswer/user/:id/:begin/:nb/:output", auth, answerCtrl.getQuestionnairesWithouAnswerByUser);
|
||||
//router.get("/withoutanswer/user/:id/:begin/:nb/:output", auth, answerCtrl.getQuestionnairesWithouAnswerByUser);
|
||||
|
||||
module.exports = router;
|
@ -57,8 +57,8 @@ block content
|
||||
noscript
|
||||
div
|
||||
strong #{configTpl.noJSNotification}
|
||||
// à cacher si pas de JS !
|
||||
form(id="group" method="POST" class="needJS")
|
||||
|
||||
form(id="group" method="POST")
|
||||
h2 #{group.Group.title}
|
||||
div#response
|
||||
div(class="subscribeBtns")
|
||||
@ -70,7 +70,7 @@ block content
|
||||
for question in questionnaire.Questions
|
||||
p(id="question_"+question.Question.id) #{question.Question.text}
|
||||
if(question.Question.explanation)
|
||||
blockquote(class="help" id="help_"+question.Question.id cite=questionnaire.Links[0].url) #{txtexplanationBeforeTxt} #{question.Question.explanation}
|
||||
blockquote(class="help" id="help_"+question.Question.id cite="/"+configQuestionnaires.dirWebQuestionnaires+"/"+questionnaire.Questionnaire.slug+".html") #{txtexplanationBeforeTxt} #{question.Question.explanation}
|
||||
ul(class="checkbox_li")
|
||||
for response in question.Choices
|
||||
li(class="checkbox_li")
|
||||
@ -85,9 +85,10 @@ block content
|
||||
input(type="hidden" name="isCorrect_response_"+response.id id="isCorrect_response_"+response.id value=""+response.isCorrect)
|
||||
input(type="hidden" name="question_id_response_"+response.id id="question_id_response_"+response.id value=question.Question.id)
|
||||
input(name="groupId" id="groupId" value=group.Group.id type="hidden")
|
||||
// Bouton submit caché si pas de JS, car nécessaire au traitement de la réponse
|
||||
p
|
||||
span(class="input_wrapper")
|
||||
input(id="checkResponses" type="submit" value=txtGroups.btnSendResponse class="cardboard" title=txtGroups.btnSendResponse)
|
||||
input(id="checkResponses" type="submit" value=txtGroups.btnSendResponse class="cardboard needJS" title=txtGroups.btnSendResponse)
|
||||
|
||||
div#zerozozio
|
||||
a(href="http://sharetodiaspora.github.io/?url="+linkCanonical+"&title="+group.Group.title rel="nofollow noopener" title=txtGroups.btnShareQuizTxt+" diaspora* ("+txtGeneral.alertNewWindow+")" target="_blank")
|
||||
|
Loading…
Reference in New Issue
Block a user