2020-08-07 12:23:59 +02:00
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" ) ;
2020-10-22 17:47:05 +02:00
const groupCtrl = require ( "./group" ) ;
2020-08-07 12:23:59 +02:00
const questionnaireCtrl = require ( "./questionnaire" ) ;
2020-10-22 17:47:05 +02:00
const subscriptionCtrl = require ( "./subscription" ) ;
2020-08-07 12:23:59 +02:00
2020-10-22 17:47:05 +02:00
const txtAnswers = require ( "../lang/" + config . adminLang + "/answer" ) ;
2020-08-07 12:23:59 +02:00
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 ;
2020-10-22 17:47:05 +02:00
await saveAnswerToQuestionnaire ( req . body ) ;
2020-10-27 16:08:48 +01:00
// J'en profite pour remettre les pendules à l'heure ! (revoir pour appeler un contrôleur des users)
2020-10-22 17:47:05 +02:00
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 !
2020-08-07 12:23:59 +02:00
db [ "User" ] . update ( { timeDifference : req . body . timeDifference } , { where : { id : req . connectedUser . User . id } , limit : 1 } ) ;
2020-10-22 17:47:05 +02:00
res . status ( 201 ) . json ( { message : txtAnswers . responseSavedMessage } ) ;
2020-08-07 12:23:59 +02:00
next ( ) ;
}
catch ( e )
2020-10-22 17:47:05 +02:00
{
2020-08-07 12:23:59 +02:00
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 ) ;
}
}
2020-10-22 17:47:05 +02:00
// Retourne les réponses d'un utilisateur pour un groupe de questionnaires donné
exports . getAnswersByGroup = async ( req , res , next ) =>
2020-08-07 12:23:59 +02:00
{
try
{
2020-10-22 17:47:05 +02:00
const answers = await getUserAnswersByGroup ( req . params . userId , req . params . groupId ) ;
res . status ( 200 ) . json ( answers ) ;
next ( ) ;
2020-08-07 12:23:59 +02:00
}
catch ( e )
{
next ( e ) ;
}
}
2020-10-22 17:47:05 +02:00
// Retourne les statistiques de l'utilisateur
exports . getStatsByUser = async ( req , res , next ) =>
2020-08-07 12:23:59 +02:00
{
try
{
2020-10-22 17:47:05 +02:00
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 ) ;
2020-08-07 12:23:59 +02:00
}
catch ( e )
{
next ( e ) ;
}
}
// FONCTIONS UTILITAIRES
2020-10-27 16:08:48 +01:00
// Enregistre la réponse à un questionnaire autonome
2020-10-22 17:47:05 +02:00
const saveAnswerToQuestionnaire = async ( req ) =>
{
const db = require ( "../models/index" ) ;
const checkQuestionnaireAccess = await subscriptionCtrl . checkQuestionnaireAccess ( req . UserId , req . QuestionnaireId ) ;
2020-10-27 16:08:48 +01:00
if ( checkQuestionnaireAccess ) // Si l'utilisateur a déjà accès à ce questionnaire, j'enregistre juste sa réponse
2020-10-22 17:47:05 +02:00
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" ] } ) ;
2020-10-27 16:08:48 +01:00
// Si il a répondu à un quiz groupé, l'utilisateur est considéré comme ayant lu tous les éléments du groupe
2020-10-26 17:23:25 +01:00
const group = await groupCtrl . searchGroupById ( req . GroupId ) ;
2020-10-22 17:47:05 +02:00
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 ;
2020-08-07 12:23:59 +02:00
// Créer la liste des réponses d'un utilisateur
2020-10-27 16:08:48 +01:00
// !! à surveiller car fichier pouvant devenir gros ! mais utile pour future SVG côté client ?
2020-08-07 12:23:59 +02:00
const creaUserAnswersJson = async ( UserId ) =>
{
const db = require ( "../models/index" ) ;
2020-10-22 17:47:05 +02:00
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 } ) ;
2020-08-07 12:23:59 +02:00
if ( userAnswers )
{
2020-10-22 17:47:05 +02:00
await toolFile . createJSON ( config . dirCacheUsersAnswers , UserId , userAnswers ) ;
2020-08-07 12:23:59 +02:00
return userAnswers ;
}
else
return false ;
}
exports . creaUserAnswersJson = creaUserAnswersJson ;
2020-10-22 17:47:05 +02:00
// Retourne les réponses d'un utilisateurs à un questionnaire simple.
2020-08-07 12:23:59 +02:00
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 )
{
2020-10-22 17:47:05 +02:00
if ( userAnswers [ i ] . QuestionnaireId == QuestionnaireId ) // pas "===", car type de données pouvant être différents
2020-08-07 12:23:59 +02:00
answers . push ( userAnswers [ i ] ) ;
2020-10-27 16:08:48 +01:00
else if ( answers . length !== 0 ) // les réponses étant groupées par QuestionnaireId, je peux sortir de la boucle
2020-08-07 12:23:59 +02:00
break ;
}
return answers ;
}
2020-10-22 17:47:05 +02:00
// 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 ] ) ;
2020-10-27 16:08:48 +01:00
else if ( answers . length !== 0 ) // les réponses étant groupées par GroupId, je peux sortir de la boucle
2020-10-22 17:47:05 +02:00
break ;
}
2020-11-16 12:35:40 +01:00
return answers ;
2020-10-22 17:47:05 +02:00
}
// À combien de questionnaire l'utilisateur a-t-il répondu ? et quel est son résultat moyen ?
2020-08-07 12:23:59 +02:00
const creaUserStatsAnwsersJson = async ( UserId ) =>
{
const db = require ( "../models/index" ) ;
const getUserAnswers = await db [ "Answer" ] . findAll ( { where : { UserId : UserId } , attributes : [ "id" ] } ) ;
2020-10-22 17:47:05 +02:00
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 )
2020-08-07 12:23:59 +02:00
{
const stats =
{
nbAnswers : getUserAnswers . length ,
2020-10-22 17:47:05 +02:00
nbQuestionnaires : getUserQuestionnaires . length + getUserQuestionnairesGroup . length
2020-08-07 12:23:59 +02:00
}
2020-10-22 17:47:05 +02:00
if ( getUserStats && getUserAnswers . length !== 0 )
2020-08-07 12:23:59 +02:00
{
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 ;
2020-10-22 17:47:05 +02:00
// Retourne les données créées par la fonction précédente :
2020-08-07 12:23:59 +02:00
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 ;
}
2020-10-22 17:47:05 +02:00
// À combien de questionnaire les utilisateurs ont-ils répondu ces dernières 24H ? depuis le début ?
2020-08-07 12:23:59 +02:00
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 ;
2020-10-22 17:47:05 +02:00
/ *
// 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 ?
2020-08-07 12:23:59 +02:00
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 } ;
}
2020-10-22 17:47:05 +02:00
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 ) ;
}
}
* * /