350 lines
13 KiB
JavaScript
350 lines
13 KiB
JavaScript
// Fonctions externes :
|
|
import { addElement } from "./dom.js";
|
|
import { dateFormat, isEmpty, replaceAll } from "../../../tools/main";
|
|
import { saveLocaly, getStore } from "./clientstorage.js";
|
|
import { getDatasFromInputs } from "./forms.js";
|
|
|
|
// Textes :
|
|
import { availableLangs } from "../../../config/instance.js";
|
|
const lang=availableLangs[0];
|
|
const { localDBNeedDatas, localDBNeedQuizId, noPreviousResults, previousResultsLine, previousResultsTitle, previousResultsStats, userAnswersFail, userAnswersMedium, userAnswersSuccess }=require("../../../lang/"+lang+"/answer");
|
|
|
|
// Vérification des réponses de l'utilisateur au quiz
|
|
export const checkUserAnswers = (myForm) =>
|
|
{
|
|
// 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 avoir coché toutes les bonnes réponses (si QCM) à la question ET n'avoir coché aucune des mauvaises.
|
|
const userResponses=getDatasFromInputs(myForm);
|
|
let idChoice, idQuestion="", goodResponse=false, answer={ nbCorrectAnswers:0, nbQuestions:0 };
|
|
for(let item in userResponses)
|
|
{
|
|
if(item.startsWith("isCorrect_response_")) // = Nouvelle réponse possible.
|
|
{
|
|
idChoice=item.substring(item.lastIndexOf("_")+1);
|
|
// Si on change de 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
|
|
answer.nbCorrectAnswers++;
|
|
goodResponse=true;// Réponse bonne jusqu'à la première erreur...
|
|
}
|
|
if(userResponses[item] == "true")
|
|
{
|
|
document.getElementById("response_"+idChoice).parentNode.classList.add("isCorrect");
|
|
if(userResponses["response_"+idChoice] === undefined) // Une des bonnes réponses 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 :(
|
|
{
|
|
goodResponse=false;
|
|
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 :
|
|
if(goodResponse)
|
|
answer.nbCorrectAnswers++;
|
|
return answer;
|
|
}
|
|
|
|
// Retourne un texte suivant le nombre de bonnes réponses
|
|
export const getResultOutput = (result) =>
|
|
{
|
|
if(!isEmpty(result.duration) && !isEmpty(result.nbCorrectAnswers) && !isEmpty(result.nbQuestions))
|
|
{
|
|
const ratio=result.nbCorrectAnswers/result.nbQuestions;
|
|
const mapObj=
|
|
{
|
|
DURATION: result.duration,
|
|
NBCORRECTANSWERS: result.nbCorrectAnswers,
|
|
NBQUESTIONS: result.nbQuestions
|
|
}
|
|
let output="";
|
|
if(ratio < 0.4)
|
|
output=replaceAll(userAnswersFail, mapObj);
|
|
else if(ratio < 0.8)
|
|
output=replaceAll(userAnswersMedium, mapObj);
|
|
else
|
|
output=replaceAll(userAnswersSuccess, mapObj);
|
|
return output;
|
|
}
|
|
else
|
|
return "";
|
|
}
|
|
|
|
const checkIfResultIsComplete = (result) =>
|
|
{
|
|
if(!isEmpty(result.duration) && !isEmpty(result.nbCorrectAnswers) && !isEmpty(result.nbQuestions) && (!isEmpty(result.QuestionnaireId) || !isEmpty(result.GroupId)))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// Enregistrement temporaire du dernier résultat à un quiz en attendant de savoir si l'utilisateur souhaite une sauvegarde durable :
|
|
export const saveResultTemp = (result) =>
|
|
{
|
|
if(checkIfResultIsComplete(result))
|
|
{
|
|
saveLocaly("lastResult", result); // écrasera l'éventuel résultat précédent.
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
console.error(localDBNeedDatas);
|
|
console.log(result);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Se connecte à la base de données de sauvegarde des résultats
|
|
// Et la créé si c'est la première connexion (ou mise à jour de version)
|
|
export const resultsOpenDb = (dbName, dbVersion) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
let req=indexedDB.open(dbName, dbVersion);
|
|
req.onupgradeneeded= (e) =>
|
|
{
|
|
// Création du stockage des quizs auxquels l'utilisateur a déjà répondu :
|
|
let store=e.currentTarget.result.createObjectStore("userQuizs", { keyPath: "id", autoIncrement: true });
|
|
store.createIndex("url", "url", { unique: true });
|
|
store.createIndex("QuestionnaireId", "id", { unique: true }); // = simple quiz
|
|
store.createIndex("GroupId", "id", { unique: true }); // = quiz après lecture d'un ou +sieurs articles.
|
|
store.createIndex("title", "title", { unique: false });
|
|
// Création du stockage des résultats :
|
|
store=e.currentTarget.result.createObjectStore("userResults", { keyPath: "id", autoIncrement: true });
|
|
store.createIndex("QuestionnaireId", "QuestionnaireId", { unique: false });
|
|
store.createIndex("GroupId", "GroupId", { unique: false });
|
|
store.createIndex("duration", "duration", { unique: true });
|
|
store.createIndex("nbCorrectAnswers", "nbCorrectAnswers", { unique: false });
|
|
store.createIndex("nbQuestions", "nbQuestions", { unique: false });
|
|
store.createIndex("date", "date", { unique: false }); // bien que doublons peu probables...
|
|
/// Revoir comment gérér les mises à jour de version de la BD sans tout casser...
|
|
};
|
|
req.onsuccess= (e) =>
|
|
{
|
|
resolve(req.result); // On retourne la base de données
|
|
};
|
|
req.onerror= (e) =>
|
|
{
|
|
console.error(e); // dans cette fonction, peut simplement planter parce que navigation privée...
|
|
resolve(undefined);
|
|
};
|
|
})
|
|
}
|
|
|
|
// Fonction cherchant les éventuels résultats déjà enregistrés pour un quiz :
|
|
export const checkPreviousResultsForId= (db, QuestionnaireId=0, GroupId=0) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
const resultsStore=getStore(db, "userResults", "readonly");
|
|
let myIndex, getResults;
|
|
if(QuestionnaireId != 0)
|
|
{
|
|
myIndex=resultsStore.index("QuestionnaireId");
|
|
getResults=myIndex.openCursor(QuestionnaireId);
|
|
}
|
|
else if(GroupId != 0)
|
|
{
|
|
myIndex=resultsStore.index("GroupId");
|
|
getResults=myIndex.openCursor(GroupId);
|
|
}
|
|
else
|
|
reject(localDBNeedQuizId);
|
|
|
|
const answers=[];
|
|
getResults.onsuccess = (e) =>
|
|
{
|
|
const cursor=e.target.result;
|
|
if (cursor)
|
|
{
|
|
answers.push(cursor.value);
|
|
cursor.continue();
|
|
}
|
|
else // = on a parcouru toutes les données
|
|
resolve(answers);
|
|
};
|
|
getResults.onerror= (e) =>
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
};
|
|
})
|
|
}
|
|
|
|
// Fonction affichant les précédents résultats connus pour le quiz encours :
|
|
export const showPreviousResults = async (userDB, QuestionnaireId, GroupId) =>
|
|
{
|
|
if(isEmpty(userDB) || (isEmpty(QuestionnaireId) && isEmpty(GroupId)))
|
|
return false;
|
|
|
|
// Recherche dans la base de données :
|
|
const previousResults=await checkPreviousResultsForId(userDB, QuestionnaireId, GroupId);
|
|
if(previousResults === undefined) // Peut être un tableau vide si ancun résultat enregistré, mais pas undefined.
|
|
console.error(localDBGetPreviousResultsFail);
|
|
else
|
|
{
|
|
const explanationsContent=document.getElementById("explanationsContent");
|
|
const explanationsTitle=document.getElementById("explanationsTitle");
|
|
// Les précédents résultats sont classés par ordre d'enregistrement et sont donc à inverser :
|
|
previousResults.reverse();
|
|
const nbPrevious=previousResults.length;
|
|
let previousResultsContent="";
|
|
addElement(explanationsTitle, "span", previousResultsTitle);
|
|
if(nbPrevious !== 0)
|
|
{
|
|
let totNbQuestions=0, totNbCorrectAnswers=0, totDuration=0, mapLineContent;
|
|
for(const i in previousResults)
|
|
{
|
|
totNbQuestions+=previousResults[i].nbQuestions; // ! on ne peut se baser sur la version actuelle du quiz, car le nombre de questions peut évolué.
|
|
totNbCorrectAnswers+=previousResults[i].nbCorrectAnswers;
|
|
totDuration+=previousResults[i].duration;
|
|
mapLineContent=
|
|
{
|
|
DATEANSWER: dateFormat(previousResults[i].date, lang),
|
|
NBCORRECTANSWERS: previousResults[i].nbCorrectAnswers,
|
|
NBQUESTIONS: previousResults[i].nbQuestions,
|
|
AVGDURATION: previousResults[i].duration
|
|
};
|
|
previousResultsContent+="<li>"+replaceAll(previousResultsLine, mapLineContent)+"</li>";
|
|
}
|
|
mapLineContent=
|
|
{
|
|
AVGDURATION: Math.round(totDuration/nbPrevious),
|
|
AVGCORRECTANSWERS: Math.round(totNbCorrectAnswers/totNbQuestions*100)
|
|
};
|
|
previousResultsContent="<h5>"+replaceAll(previousResultsStats, mapLineContent)+"</h5>"+previousResultsContent;
|
|
addElement(explanationsContent, "ul", previousResultsContent);
|
|
}
|
|
else
|
|
addElement(explanationsContent, "ul", noPreviousResults);
|
|
/// Revoir : ajouter un lien vers la page listant les quizs auxquels l'utilisateur a répondu
|
|
/// addElement(explanationsContent, "p", "<a href=\"/"+configTemplate.userHomePage+"\" class=\"button cardboard\">"+configTemplate.userHomePageTxt+"</a>", "", ["btn"], "", false);
|
|
}
|
|
}
|
|
|
|
// Recherche de tous les résultats déjà enregistrés
|
|
export const checkAllPreviousResults = (db) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
const resultsStore=getStore(db, "userResults", "readonly");
|
|
const getResults=resultsStore.getAll();
|
|
getResults.onsuccess = (e) =>
|
|
{
|
|
resolve(e.target.result);
|
|
};
|
|
getResults.onerror = (e) =>
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
};
|
|
})
|
|
}
|
|
|
|
// Tentative d'enregistrement d'un résultat :
|
|
export const saveResult = (db, result) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
if(checkIfResultIsComplete(result))
|
|
{
|
|
const resultsStore=getStore(db, "userResults", "readwrite");
|
|
let req;
|
|
try
|
|
{
|
|
result.date=new Date();
|
|
req=resultsStore.add(result);
|
|
}
|
|
catch (e)
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
}
|
|
req.onsuccess= (e) =>
|
|
{
|
|
resolve(true);
|
|
};
|
|
req.onerror= (e) =>
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
console.error(localDBNeedDatas);
|
|
reject(localDBNeedDatas);
|
|
}
|
|
})
|
|
}
|
|
|
|
// Fonction retournant tous les quizs enregistrés pour cette personne
|
|
export const getAllQuizs = (db) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
const quizsStore=getStore(db, "userQuizs", "readonly");
|
|
const getquizs=quizsStore.getAll();
|
|
getquizs.onsuccess = (e) =>
|
|
{
|
|
resolve(e.target.result);
|
|
};
|
|
getquizs.onerror= (e) =>
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
};
|
|
})
|
|
}
|
|
|
|
// Ajout le quiz à la base de donnée de l'utilisateur, s'il n'y existe pas déjà
|
|
export const saveNewQuiz = async (userDB, quizInfos) =>
|
|
{
|
|
const getUserQuizs=await getAllQuizs(userDB);
|
|
const checkQuizExist=getUserQuizs.find(quiz => quiz.url == quizInfos.url);
|
|
if(checkQuizExist === undefined)
|
|
await saveQuiz(userDB, quizInfos);
|
|
}
|
|
|
|
// Enregistre le quiz parmi ceux auxquels l'internaute a répondu, s'il n'y est pas déjà.
|
|
export const saveQuiz = (db, quiz) =>
|
|
{
|
|
return new Promise( (resolve, reject) =>
|
|
{
|
|
if(!isEmpty(quiz.url) && !isEmpty(quiz.title) && (!isEmpty(quiz.QuestionnaireId) || !isEmpty(quiz.GroupId)))
|
|
{
|
|
const quizsStore=getStore(db, "userQuizs", "readwrite");
|
|
let req;
|
|
try
|
|
{
|
|
req=quizsStore.add(quiz);
|
|
}
|
|
catch (e)
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
}
|
|
req.onsuccess= (e) =>
|
|
{
|
|
resolve(true);
|
|
};
|
|
req.onerror= (e) =>
|
|
{
|
|
console.error(e);
|
|
reject(e);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
console.error(localDBNeedDatas);
|
|
reject(localDBNeedDatas);
|
|
}
|
|
})
|
|
} |