src/app/models/gamification-lrs.ts
import { CabriDataService } from "../services/cabri-data.service";
import { LrsService } from "../services/lrs.service";
import { NetworkService } from "../services/network.service";
import { AccountImplService } from "../services/account.Impl.service";
import { Status } from "./proposed-activity";
import { Journey, journeyStatus, JourneysByStatus } from "./journey";
import { LmsService } from "../services/lms.service";
import { NoCategoryStatus } from "./exercices";
import { environment } from "src/environments/environment";
import { format, parse } from "date-fns";
import { AppLanguage } from "./enums/enum-list";
import { AccountService } from "../services/account.service";
import { LocalStorageService, StorageKey } from "../services/local-storage-service";
export class ExerciseStatistics {
[studentId: string]: {
[activityId: string]: {
[category: string]: Array<{
exerciseId?: number;
failed?: number;
passed?: number;
perGoodAnswers?: number;
progression?: number;
passedWithHelp?: number;
grade?: string;
journeyId?: number;
completed?: boolean;
_id?: string;
story?: boolean;
}>;
};
};
}
export class LogBookStatistics {
exerciseid?: number;
gabarit: string | number;
mode: string;
failed?: number;
passed?: number;
perGoodAnswers?: number;
progression?: number;
parcours_id?: number;
activityName?: string;
numberGoodAnswer?: number;
exerciseName?: string;
journeyName?: string | null;
passedWithHelp?: number;
duration?: string;
timestamp: { day: number; dayOfWeek: string; month: string; year: number; time: string };
_id?: string;
}
export class StoredLrsStatements {
lrs: LrsService;
lmsService: LmsService;
cabriService: CabriDataService;
networkService: NetworkService;
accountService: AccountService;
public logBookStatistics: LogBookStatistics[] = new Array();
public localStorageService: LocalStorageService;
environment: any;
constructor(lrs, lmsService, cabriService, networkService, accountService, localStorageService) {
this.lrs = lrs;
this.lmsService = lmsService;
this.cabriService = cabriService;
this.networkService = networkService;
this.accountService = accountService;
this.localStorageService = localStorageService;
this.environment = environment;
}
/**
* Start Journeys
*/
async getStoredJourneys(forCurrentUser: boolean): Promise<any> {
return new Promise<any>(async resolve => {
let allJourneysByStatus: JourneysByStatus[] = new Array();
const allJourneysByProgressStatus: JourneysByStatus[] = new Array();
const statement = [
{
$match: {
timestamp: {
$gte: {
$dte: "2021-01-27T19:30:00.000Z"
}
}
}
},
{
$match: {
$and: [
{
"statement.verb.id": {
$nin: [
"https://xapi.mathia.education/verbs/initialized",
"https://xapi.mathia.education/verbs/finished"
]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/mode_jeu": {
$in: ["entraînement", "bilan"]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_etape": {
$nin: [null]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session": {
$nin: [null]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_session_id": {
$nin: [null]
}
},
{
"statement.actor.account.name": {
$in: forCurrentUser ? [this.accountService.team[0]?.id] : this.accountService.allStudents.map(s => s.id)
}
}
]
}
},
{
$project: {
search: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_session_id",
userId: "$statement.actor.account.name",
id_session: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session",
recommandation: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/recommandation",
remediation: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/remediation",
id_assignation: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/assignation_id",
id: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_id",
bilanAutoFinished: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/fin_automatique",
timestamp: "$statement.stored",
step: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_etape",
exerciseid: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id",
completed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/completed"] }, 1, 0] },
isCompetition: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/is_competition",
bilanCompleted: {
$cond: [
{
$and: [
{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/completed"] },
{ $eq: ["$statement.object.definition.type", "https://xapi.mathia.education/objectType/journey"] }
]
},
1,
0
]
},
timeToAnswer: "$metadata.https://learninglocker&46;net/result-duration.seconds",
question: "$statement.object.definition.description.en-US",
award: {
$switch: {
branches: [
{
case: { $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] },
then: "shooting"
},
{
case: { $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-with-help"] },
then: "normal"
},
{
case: { $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-on-retry"] },
then: "normal"
},
{ case: { $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed"] }, then: "moon" }
],
default: 0
}
},
passed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] }, 1, 0] },
skippedExerciseId: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/exercice_passe_id",
passedRetry: {
$cond: [
{
$or: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-with-help"]
},
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-on-retry"]
}
]
},
1,
0
]
}
}
},
{
$group: {
_id: "$search",
userId: { $first: "$userId" },
recommandation: { $first: "$recommandation" },
remediation: { $first: "$remediation" },
bilanAutoFinished: { $first: "$bilanAutoFinished" },
isCompetition: { $first: "$isCompetition" },
allAskedQuestions: {
$push: {
id_session: "$id_session",
step: "$step",
exerciseId: "$exerciseid",
completed: "$completed",
vTimer: "$timeToAnswer",
question: "$question",
award: "$award",
timestamp: "$timestamp",
bilanCompleted: "$bilanCompleted",
skippedExerciseId: "$skippedExerciseId",
passed: { $sum: { $add: ["$passed", "$passedRetry"] } }
}
},
timestamp: { $first: "$timestamp" },
assignationId: { $first: "$id_assignation" },
id: { $first: "$id" }
}
},
{
$sort: {
timestamp: -1
}
}
];
let allUserJourneys;
try {
if (this.lmsService.allJourneysLrsProgression && forCurrentUser && !this.lmsService.needRefreshStudentJourneysData) {
// change player
allUserJourneys = this.lmsService.allJourneysLrsProgression;
} else {
// first time no data / need update user statistics
allUserJourneys = await this.lrs.request(statement);
}
if (this.lmsService.needRefreshStudentJourneysData) {
// new activity is made so reload data
const currentUserJourneys = this.lmsService.allJourneysLrsProgression.filter(j => {
return Number(j.userId) === Number(this.accountService.team[0].id);
});
// get ony new items where the elements does not exist in global saved journey progression
const diffToSave = allUserJourneys.filter(item1 => {
return currentUserJourneys.findIndex(item2 => item2.timestamp === item1.timestamp) === -1;
});
// get first index in global array that that student items start
const firstElementIndex = this.lmsService.allJourneysLrsProgression.findIndex(globalJ => {
return globalJ.userId === this.accountService.team[0].id;
});
// add in global items the elements that does not yet exist in order to update array
this.lmsService.allJourneysLrsProgression.splice(firstElementIndex, 0, ...diffToSave);
}
this.lmsService.needRefreshStudentJourneysData = false;
this.localStorageService.set(StorageKey.journeysProgression, allUserJourneys);
} catch {
allUserJourneys = await this.localStorageService.get(StorageKey.journeysProgression);
} finally {
if (!forCurrentUser) {
resolve(allUserJourneys);
} else {
if (allUserJourneys) {
this.lmsService.storeLrsJourneysProgression = allUserJourneys;
if (forCurrentUser) {
// filter and keep only user's journey from all
allUserJourneys = allUserJourneys.filter(journey => {
return journey.userId === this.accountService.team[0].id;
});
}
if (allUserJourneys.length === 0) {
allJourneysByStatus = new Array();
const toDo = new Array();
let lastAdventureJourney: Array<any>;
this.lmsService.userJourneysByAssignation.forEach(data => {
toDo.push(Object.assign(data, { journeyStatus: 2 }));
});
if (toDo.length > 0 || this.lmsService.allJourneys.length > 0) {
const allStatuses = {
[this.lmsService.getStatusAsIndex(journeyStatus.toDo)]: { opened: false, items: toDo },
[this.lmsService.getStatusAsIndex(journeyStatus.allJourneys)]: {
opened: false,
items: this.lmsService.allJourneys
}
};
allJourneysByStatus.push(allStatuses);
if (toDo.length > 0) {
lastAdventureJourney = new Array();
// assignation journey offer first in adventure mode
const lastAdventureJourneyObj = {
[this.lmsService.getStatusAsIndex(journeyStatus.inProgress)]: {
opened: false,
items: new Array(toDo[0])
}
};
lastAdventureJourney.push(lastAdventureJourneyObj);
}
}
resolve({ journeysByStatus: allJourneysByStatus, recommandedJourneyByStatus: lastAdventureJourney });
return;
}
const recommandedJourneys = new Array();
const notRecommandedJourneys = new Array();
let lastMadeJourney: Journey;
allUserJourneys = allUserJourneys.map(journey => {
journey.allAskedQuestions = journey.allAskedQuestions.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
const resultUserJourneys: Journey = this.lmsService.userJourneysByAssignation.find(userJourney => {
return +userJourney.id === +journey.id;
});
const resultAllJourneys: Journey = this.lmsService.allJourneys.find(userJourney => {
return +userJourney.id === +journey.id;
});
/**
* TODO :
* Competition journey's:
*/
let result;
if (resultUserJourneys || resultAllJourneys) {
if (resultUserJourneys && resultUserJourneys.assignationId) {
result = resultUserJourneys;
} else {
result = resultAllJourneys;
}
}
// if (!result) {
// console.log("resultUserJourneys", resultUserJourneys)
// console.log("result", result)
// return;
// }
/** Ajouter les exercices d'un parcours dans le parcours */
if (result) {
if (result.exercises?.length > 0) {
// all statuses set to notDone
result.exercises.forEach((exercise, index) => {
result.exercises[index].status = Status.notDone;
});
}
journey.exercises = result.exercises;
journey.bilan = result.bilan;
journey.title = result.title;
journey.level = result.level;
journey.difficulty = result.difficulty;
journey.assignation = result.assignation;
journey.level = result.level;
journey = { ...result, ...journey };
// avoid circular dependancies when the object is copied
journey.lmsService = null;
journey.cabriService = null;
}
// Sur toutes les questions posées au cours d'un parcours garder celles
// qui ont été accomplie et les questions auxquelles l'élève a répondu
journey.allAskedQuestions = journey.allAskedQuestions.filter(question => {
return typeof question.award === "string" || question.completed === 1;
});
const proposedActivity = journey.allAskedQuestions.filter(question => {
return typeof question.award === "string";
});
const passed = proposedActivity.filter(question => {
return question.passed > 0;
});
journey.totalQuestions = proposedActivity.length;
journey.passed = passed.length;
// Calculer le taux des bonnes réponses dans un parcours
let correctAnswers;
if (journey.bilanAutoFinished) {
correctAnswers = 100;
} else {
correctAnswers = Math.floor((passed.length / proposedActivity.length) * 100);
}
journey.correctAnswers = correctAnswers;
if (journey.completed) {
journey.progression = 100;
} else {
journey.progression = proposedActivity.length;
}
if (!journey.remediation) {
if (!journey.recommandation) {
notRecommandedJourneys.push(journey);
} else {
recommandedJourneys.push(journey);
}
}
if (!lastMadeJourney) {
lastMadeJourney = JSON.parse(JSON.stringify(journey));
}
const j = new Journey(this.cabriService, this.lmsService, journey.id, journey.educationalLevel, null, journey.title, journey.difficulty, false, false);
for(const key in journey){
try{
j[key] = journey[key];
} catch(e){
console.error("key error", key);
}
}
j.lmsService = null;
j.cabriService = null;
// avoid circular dependency when we copy the full class
return j;
}) as Journey[];
// const response = allUserJourneys.filter(journey => {
// return journey.id == 127768;
// })
// console.log("response", response)
// Retourne un tableau associatif en clé l'identifiant de l'exercice
const notRecommanded = await this.studentJourneyInfos(allUserJourneys, allJourneysByStatus);
const resultRecommanded = await this.studentRecommandedJourneys(recommandedJourneys, allJourneysByProgressStatus);
if (notRecommanded.journeysByStatus && Array.isArray(notRecommanded.journeysByStatus)) {
// if assignation journey exist so force to do assignated journey in the adventure map
const firstAssignationJourneyToDo =
notRecommanded.journeysByStatus[0][this.lmsService.getStatusAsIndex(journeyStatus.toDo)]?.items[0];
if (firstAssignationJourneyToDo) {
const lastAdventureJourneyOffered =
resultRecommanded.journeysByStatus[0][this.lmsService.getStatusAsIndex(journeyStatus.inProgress)];
lastAdventureJourneyOffered.items[0] = firstAssignationJourneyToDo;
}
}
lastMadeJourney = this.changeLastJourneyExerciseStatus(lastMadeJourney);
let competitionTracesJourneys = allUserJourneys.filter(journey => journey.isCompetition);
competitionTracesJourneys = this.changeLastCompetitionJourneyExerciseStatus(competitionTracesJourneys);
const journeysProgression = {
journey: notRecommanded.journey,
journeysByStatus: notRecommanded.journeysByStatus,
recommandedJourney: resultRecommanded.journey,
recommandedJourneyByStatus: resultRecommanded.journeysByStatus,
lastMadeJourney,
competitionTracesJourneys
};
resolve(journeysProgression);
} else {
resolve(false);
}
}
}
});
}
/**
* Change exercises status for the first exercise
*/
changeLastJourneyExerciseStatus(lastMadeJourney: Journey) {
if (lastMadeJourney?.exercises) {
lastMadeJourney.idSession = lastMadeJourney._id;
const userJourneysExercise = lastMadeJourney.allAskedQuestions.filter(startedExercise => {
return lastMadeJourney.exercises.some(exercises => {
return startedExercise.completed === 1 && exercises.step === startedExercise.step;
});
});
if (userJourneysExercise.length > 0) {
if (userJourneysExercise.length >= lastMadeJourney.exercises.length) {
lastMadeJourney.completed = true;
} else {
lastMadeJourney.completed = false;
}
// change status to done when allAksedQuestions match with exercise id contained in journey exercises id
lastMadeJourney.exercises.forEach(exercises => {
const statusToChange = userJourneysExercise.find(startedExercise => {
return exercises.step === startedExercise.step;
});
if (statusToChange) {
exercises.status = Status.done;
exercises.skippedExerciseId = statusToChange.skippedExerciseId;
exercises.skip = exercises.skippedExerciseId ? true : false;
}
});
}
}
return lastMadeJourney;
}
/**
* Change exercises status for the first exercise
*/
changeLastCompetitionJourneyExerciseStatus(competitionJourneys: Journey[]) {
competitionJourneys.forEach(journey => {
if (journey?.exercises) {
journey.idSession = journey._id;
const userJourneysExercise = journey.allAskedQuestions.filter(startedExercise => {
return journey.exercises.some(exercises => {
return startedExercise.completed === 1 && exercises.step === startedExercise.step;
});
});
if (userJourneysExercise.length > 0) {
if (userJourneysExercise.length >= journey.exercises.length) {
journey.completed = true;
} else {
journey.completed = false;
}
// change status to done when allAksedQuestions match with exercise id contained in journey exercises id
journey.exercises.forEach(exercises => {
const statusToChange = userJourneysExercise.find(startedExercise => {
return exercises.step === startedExercise.step;
});
if (statusToChange) {
exercises.status = Status.done;
exercises.skippedExerciseId = statusToChange.skippedExerciseId;
exercises.skip = exercises.skippedExerciseId ? true : false;
}
});
}
}
});
return competitionJourneys;
}
async studentRecommandedJourneys(arr, journeysByStatus): Promise<{ journey: Journey; journeysByStatus: JourneysByStatus[] }> {
return new Promise<{ journey: Journey; journeysByStatus: JourneysByStatus[] }>(resolve => {
// Retourne un tableau associatif en clé l'identifiant de l'exercice
const newJourney: Journey = arr.reduce((res, curr) => {
if (res[curr.id]) {
res[curr.id].push({
idSession: curr._id,
...curr
});
} else {
Object.assign(res, {
[curr.id]: [
{
idSession: curr._id,
...curr
}
]
});
}
return res;
}, {});
const statusProgression = new Array();
const lastMadeJourneyByIds = {};
// tslint:disable-next-line: forin
for (const key in newJourney) {
// keep last statement in each array of journeys already filtered by date
if (newJourney.hasOwnProperty(key)) {
// Modifier le statut de l'exercice s'il a été accompli
newJourney[key].find(dataByJourney => {
if (dataByJourney.exercises) {
const userJourneysExercise = dataByJourney.allAskedQuestions.filter(startedExercise => {
return dataByJourney.exercises.some(exercises => {
return startedExercise.completed === 1 && exercises.step === startedExercise.step;
});
});
if (userJourneysExercise.length > 0) {
if (userJourneysExercise.length >= dataByJourney.exercises.length) {
dataByJourney.completed = true;
} else {
dataByJourney.completed = false;
}
// change status to done when allAksedQuestions match with exercise id contained in journey exercises id
const dataByJourneyExercises = JSON.parse(JSON.stringify(dataByJourney.exercises));
dataByJourneyExercises.forEach(exercises => {
const statusToChange = userJourneysExercise.find(startedExercise => {
return exercises.step === startedExercise.step;
});
if (statusToChange) {
exercises.status = Status.done;
exercises.skippedExerciseId = statusToChange.skippedExerciseId;
exercises.skip = exercises.skippedExerciseId ? true : false;
}
});
dataByJourney.exercises = dataByJourneyExercises;
}
}
});
if (!lastMadeJourneyByIds[key]) {
lastMadeJourneyByIds[key] = newJourney[key];
}
}
}
const allJourneysMade = new Array();
for (const key in lastMadeJourneyByIds) {
if (lastMadeJourneyByIds.hasOwnProperty(key) && lastMadeJourneyByIds[key][0]) {
// const items = Object.values(lastMadeJourneyByIds).sort((a: any, b: any) => a.timestamp.localeCompare(b.timestamp));
lastMadeJourneyByIds[key].forEach(cj => {
allJourneysMade.push(cj);
});
}
}
const orderAscByTimestamp = allJourneysMade.sort((a: any, b: any) => {
return a.timestamp.localeCompare(b.timestamp);
});
for (const key in lastMadeJourneyByIds) {
// keep last statement in each array of journeys already filtered by date
if (lastMadeJourneyByIds.hasOwnProperty(key)) {
if (lastMadeJourneyByIds[key][0]?.exercises) {
const inProgressJourney = lastMadeJourneyByIds[key][0].exercises.some(exercise => {
return exercise.status === Status.notDone;
});
// if one exercise of journey has been done starting from another index that 0 so pass status to done for previous exercises
if (inProgressJourney) {
// indexes that exercise has been done
const indexes = lastMadeJourneyByIds[key][0].exercises
.map((elm, idx) => {
if (elm.status === Status.done) {
return idx;
}
})
.filter(uNdefined => uNdefined !== undefined);
if (indexes.length > 0) {
// get last index of an exercise done
const maxOfIndex = Math.max(...indexes);
for (let i = 0; i <= maxOfIndex; i++) {
// change all exercises status to done until the last index of done
lastMadeJourneyByIds[key][0].exercises[i].status = Status.done;
}
}
}
}
}
}
if (orderAscByTimestamp[orderAscByTimestamp.length - 1]?.exercises) {
const allDone = orderAscByTimestamp[orderAscByTimestamp.length - 1].exercises.every(exercise => {
return exercise.status === Status.done;
});
if (!allDone) {
statusProgression.push(orderAscByTimestamp[orderAscByTimestamp.length - 1]);
}
}
const progressStatus: JourneysByStatus = {
[this.lmsService.getStatusAsIndex(journeyStatus.inProgress)]: { opened: false, items: statusProgression }
};
if (Object.keys(progressStatus).length > 0) {
journeysByStatus.push(progressStatus);
}
// En cours
resolve({ journey: newJourney, journeysByStatus });
});
}
async studentJourneyInfos(arr, journeysByStatus): Promise<{ journey: Journey; journeysByStatus: JourneysByStatus[] }> {
return new Promise<{ journey: Journey; journeysByStatus: JourneysByStatus[] }>(resolve => {
// Retourne un tableau associatif en clé l'identifiant de l'exercice
const newJourney: Journey = arr.reduce((res, curr) => {
if (res[curr.id]) {
res[curr.id].push({
idSession: curr._id,
...curr
});
} else {
Object.assign(res, {
[curr.id]: [
{
idSession: curr._id,
...curr
}
]
});
}
return res;
}, {});
const statusCompleted = new Array();
const statusProgression = new Array();
const statusToDo = new Array();
// tslint:disable-next-line: forin
for (const key in newJourney) {
if (newJourney.hasOwnProperty(key)) {
// Modifier le statut de l'exercice s'il a été accompli
newJourney[key].find(dataByJourney => {
if (dataByJourney.exercises) {
const userJourneysExercise = dataByJourney.allAskedQuestions.filter(startedExercise => {
return dataByJourney.exercises.some(exercises => {
return startedExercise.completed === 1 && exercises.step === startedExercise.step;
});
});
// keeping all completed journeys
if (userJourneysExercise.length > 0) {
const lastItem = userJourneysExercise[userJourneysExercise?.length - 1];
// change status to done when allAksedQuestions match with exercise id contained in journey exercises id
const dataByJourneyExercises = JSON.parse(JSON.stringify(dataByJourney.exercises));
const indexFound = dataByJourneyExercises.findIndex(ex => {
return ex.step === lastItem.step;
});
dataByJourney.completed = indexFound === dataByJourneyExercises.length - 1 ? true : false;
for (let i = 0; i <= indexFound; i++) {
dataByJourneyExercises[i].status = Status.done;
}
dataByJourneyExercises.forEach((exercises, index) => {
const statusToChange = userJourneysExercise.find(startedExercise => {
return exercises.step === startedExercise.step;
});
if (statusToChange) {
exercises.skippedExerciseId = statusToChange.skippedExerciseId;
exercises.skip = exercises.skippedExerciseId ? true : false;
}
});
dataByJourney.exercises = dataByJourneyExercises;
}
}
});
}
if (newJourney[key][0]) {
const tempLastJourneyMade = newJourney[key][0];
let exerciseEnumProgression;
try{
exerciseEnumProgression = JSON.parse(JSON.stringify(newJourney[key]));
} catch(err){
// debugger;
console.error(err);
}
exerciseEnumProgression = exerciseEnumProgression.sort((a, b) => b.correctAnswers - a.correctAnswers);
if (exerciseEnumProgression[0].bilan) {
// Mode diagnostique terminé
const completedBilan = exerciseEnumProgression.find(bilanJourney => {
return bilanJourney.allAskedQuestions.find(bilanQuestion => {
return bilanQuestion.bilanCompleted === 1;
});
});
if (completedBilan) {
const journey = this._transformNestyJourneyAsClass(
completedBilan,
tempLastJourneyMade,
this.lmsService.getStatusAsIndex(journeyStatus.completed)
);
statusCompleted.push(journey);
}
}
const completedFarthestJourney = exerciseEnumProgression.find(data => {
return data.completed;
});
tempLastJourneyMade.farthest = false;
newJourney[key] = new Array();
const forAllJourneys = this.lmsService.allJourneys.find(journey => {
return Number(journey.id) === Number(tempLastJourneyMade.id);
});
const forUserJourney = this.lmsService.userJourneysByAssignation.find(journey => {
return Number(journey.id) === Number(tempLastJourneyMade.id);
});
if (forUserJourney || forAllJourneys) {
if (tempLastJourneyMade && !tempLastJourneyMade.recommandation) {
if (!tempLastJourneyMade.completed && !tempLastJourneyMade.bilan) {
if (tempLastJourneyMade.allAskedQuestions.length > 0) {
const temp = JSON.parse(JSON.stringify(tempLastJourneyMade));
const journey = this._transformNestyJourneyAsClass(
temp,
tempLastJourneyMade,
this.lmsService.getStatusAsIndex(journeyStatus.inProgress)
);
statusProgression.push(journey);
}
} else if (tempLastJourneyMade.bilan) {
if (tempLastJourneyMade.allAskedQuestions.length > 0) {
const bilan = tempLastJourneyMade.allAskedQuestions.find(bilanQuestion => {
return bilanQuestion.bilanCompleted === 1;
});
if (!bilan) {
const temp = JSON.parse(JSON.stringify(tempLastJourneyMade));
const journey = this._transformNestyJourneyAsClass(
temp,
tempLastJourneyMade,
this.lmsService.getStatusAsIndex(journeyStatus.inProgress)
);
statusProgression.push(journey);
}
}
}
}
newJourney[key].push(tempLastJourneyMade);
if (completedFarthestJourney) {
// Parcours déjà terminé
const completedCopy = JSON.parse(JSON.stringify(completedFarthestJourney));
completedFarthestJourney.farthest = true;
if (!completedCopy.bilan) {
const journey = this._transformNestyJourneyAsClass(
completedCopy,
tempLastJourneyMade,
this.lmsService.getStatusAsIndex(journeyStatus.completed)
);
statusCompleted.push(journey);
}
newJourney[key].push(completedFarthestJourney);
} else {
// Parcours à faire
let needToDo = true;
if(exerciseEnumProgression[0].bilan){
// for bilan journeys can't determine if all ex were done
// due to the adaptative learning
const exist = statusCompleted.some(data => {
return data.id == exerciseEnumProgression[0].id;
});
if(exist){
needToDo = false;
}
}
if(needToDo){
const exerciseEnumCopy = JSON.parse(JSON.stringify(exerciseEnumProgression[0]));
const currentJourney = this.lmsService.userJourneysByAssignation.find(j => {
return j.id === exerciseEnumCopy.id;
});
if (currentJourney) {
const journey = this._transformNestyJourneyAsClass(
exerciseEnumCopy,
tempLastJourneyMade,
this.lmsService.getStatusAsIndex(journeyStatus.toDo)
);
statusToDo.push(journey);
}
}
newJourney[key].push(exerciseEnumProgression[0]);
}
}
}
}
// En cours
const inProcessing = this._addNewStatus(statusProgression, this.lmsService.getStatusAsIndex(journeyStatus.inProgress));
// A faire
const toDo = this._addNewStatus(statusToDo, this.lmsService.getStatusAsIndex(journeyStatus.toDo));
if (toDo?.value) {
// assignation journey atteibute assignationId for lrs journey
toDo.value.forEach(lrsAssignatedJ => {
const currAssignatedJourney = this.lmsService.userJourneysByAssignation.find(userAssignatedJ => {
return +userAssignatedJ.id === +lrsAssignatedJ.id;
});
if (currAssignatedJourney?.assignationId) {
lrsAssignatedJ.assignationId = currAssignatedJourney.assignationId;
}
});
}
// // Terminé
const completed = this._addNewStatus(statusCompleted, this.lmsService.getStatusAsIndex(journeyStatus.completed));
const listOfStatuses = statusProgression.concat(statusToDo, statusCompleted);
let toDoNotInLrs = new Array();
// A faire qui n'est pas stocké dans lrs
toDoNotInLrs = this._checkLrsNotIncludedJourney(this.lmsService.userJourneysByAssignation, listOfStatuses);
const allStatuses: JourneysByStatus = {
[this.lmsService.getStatusAsIndex(journeyStatus.inProgress)]: { opened: false, items: inProcessing.value },
[this.lmsService.getStatusAsIndex(journeyStatus.toDo)]: { opened: false, items: toDo.value.concat(toDoNotInLrs) },
[this.lmsService.getStatusAsIndex(journeyStatus.completed)]: { opened: false, items: completed.value },
[this.lmsService.getStatusAsIndex(journeyStatus.allJourneys)]: { opened: false, items: this.lmsService.allJourneys }
};
if (Object.keys(allStatuses).length > 0) {
journeysByStatus.push(allStatuses);
}
resolve({ journey: newJourney, journeysByStatus });
});
}
/**
* Add status to nesty journey considered as simple object and convert it to a real class with it's own functions
*/
private _transformNestyJourneyAsClass(journeyWithoutClass: Journey, journeyWithClass: Journey, status: string) {
// add status to nesty journey
// let nestyJourneyStatus = Object.assign(journeyWithoutClass, { journeyStatus: status });
journeyWithoutClass.journeyStatus = status;
// nesty journey as real class
let realJourneyAsClass = { ...journeyWithClass, ...journeyWithoutClass };
return this._recoverModelRequirements(realJourneyAsClass);
}
/**
* Add requirements in order to access to class functions
*/
private _recoverModelRequirements(journey: any) {
if (!journey.lmsService) {
journey.lmsService = this.lmsService;
}
if (!journey.cabriService) {
journey.cabriService = this.cabriService;
}
return journey;
}
private _checkLrsNotIncludedJourney(array, status) {
const notIncludedLrs = new Array();
array.forEach(data => {
const posIndex = status.findIndex(allJourney => {
return data.id === allJourney.id;
});
if (posIndex === -1) {
// All journeys
notIncludedLrs.push(data);
}
});
return notIncludedLrs;
}
private _addNewStatus(status, key: string) {
let statusItems;
let value = new Array();
if (status.length === 0) {
statusItems = {
key: value
};
} else {
value = status;
statusItems = {
key: value
};
}
return { key, value };
}
/**
* Start statistics by competencies
*/
getExercisesProgression(lang: string) {
return new Promise(async (resolve, reject) => {
let studentIds: Array<string>;
studentIds = this.accountService.allStudents.map(currStudent => {
return currStudent.id;
});
const statement = [
{
$match: {
timestamp: {
$gte: {
$dte: "2021-01-01T08:09:00.000Z"
}
}
}
},
{
$match: {
$and: [
{
"statement.verb.id": {
$nin: [
"https://xapi.mathia.education/verbs/initialized",
"https://xapi.mathia.education/verbs/finished"
]
}
},
// {
// "statement.context.extensions.https://xapi&46;mathia&46;education/extensions/kidaia": {
// $in: this.environment.kidaia ? [true] : [null, false]
// }
// },
{
"statement.actor.account.name": {
$in: studentIds
}
}
]
}
},
{
$project: {
search: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session",
userId: "$statement.actor.account.name",
story: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/story",
exerciseid: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id",
journeyId: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_id",
mode: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/mode_jeu",
completed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/completed"] }, 1, 0] },
perGoodAnswers: "$statement.context.extensions.https://xapi&46;mathia&46;education/pourcentage_bonnes_reponses",
failed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed"] }, 1, 0] },
passedWithHelp: {
$cond: [
{
$or: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-on-retry"]
},
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-with-help"]
}
]
},
1,
0
]
},
passed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] }, 1, 0] }
}
},
{
$group: {
_id: "$search",
userId: { $first: "$userId" },
perGoodAnswers: { $sum: "$perGoodAnswers" },
exerciseid: { $first: "$exerciseid" },
journeyId: { $first: "$journeyId" },
story: { $first: "$story" },
completed: { $sum: "$completed" },
mode: { $first: "$mode" },
passed: { $sum: "$passed" },
passedWithHelp: { $sum: "$passedWithHelp" },
failed: { $sum: "$failed" }
}
},
{
$sort: {
timestamp: -1
}
}
];
let allExStatistics;
try {
allExStatistics = (await this.lrs.request(statement)) as any;
// Directly storing LRS response traces which facilitates array management when an activity is completed
this.localStorageService.set(StorageKey.getExercisesProgression, allExStatistics);
} catch (err) {
allExStatistics = await this.localStorageService.get(StorageKey.getExercisesProgression);
} finally {
if (allExStatistics) {
allExStatistics.forEach(exercise => {
if (exercise.completed >= 1) {
exercise.progression = 100;
}
if (exercise.perGoodAnswers > 100) {
exercise.perGoodAnswers = 100;
}
if (exercise.mode === "défi" && exercise.perGoodAnswers <= 100) {
// défi eliminated directly so earned one moon by session
exercise.failed = 1;
}
});
const getStatistics = this._defineExerciseStatistics(allExStatistics, lang);
this.lmsService.exerciseStatistics = getStatistics;
this.lmsService.exerciseStatisticsLoaded.next(true);
resolve(getStatistics);
} else {
reject(true);
}
}
});
}
/**
* Return exercise id as key with progression as percentage with corresponding mode
*/
private _defineExerciseStatistics(arr, lang: string): ExerciseStatistics {
const statistics: ExerciseStatistics = {};
arr.forEach(exercise => {
if (!isNaN(exercise.progression)) {
const currentExercise = this.cabriService.exercices.getExercise(exercise.exerciseid);
if (currentExercise) {
let allExCategories = new Array();
if (currentExercise.categories.length > 0) {
if (lang === AppLanguage.EN) {
const result = this.cabriService.exercices.allCategories.filter(category => {
let found = false;
Object.entries(currentExercise.categoriesObject).forEach(([key, value]) => {
if (Number(key) === Number(category.term_id)) {
found = true;
}
});
return found;
});
if (result?.length > 0) {
result.forEach(category => {
if(category?.name){
allExCategories.push(category.name);
}
});
} else {
console.error("no any category no reason");
}
} else {
allExCategories = currentExercise.categories
}
} else {
allExCategories = new Array(NoCategoryStatus.label);
}
const activity = this.cabriService.activities.find(activity => {
return activity.id === currentExercise.gabarit;
});
if (activity !== undefined) {
if (exercise.exerciseid === 124 || exercise.exerciseid === 125 || exercise.exerciseid === 126) {
// decouverte des solides with no question so avoid to have 0 as percent
exercise.perGoodAnswers = 100;
}
allExCategories.forEach(cat => {
if (
statistics[exercise.userId] &&
statistics[exercise.userId][activity.id] &&
statistics[exercise.userId][activity.id][cat]
) {
statistics[exercise.userId][activity.id][cat].push({
progression: exercise.progression,
perGoodAnswers: Math.floor(exercise.perGoodAnswers),
exerciseId: exercise.exerciseid,
completed: exercise.completed >= 1 ? true : false,
_id: exercise._id,
passed: exercise.passed,
passedWithHelp: exercise.passedWithHelp,
failed: exercise.failed,
grade: currentExercise.classe,
journeyId: exercise.journeyId,
story: exercise.story
});
} else {
// first time define object item
if (!statistics[exercise.userId]) {
(statistics[exercise.userId] as any) = {
[activity.id]: {
[cat]: {}
}
};
} else if (!statistics[exercise.userId][activity.id]) {
(statistics[exercise.userId][activity.id] as any) = {
[cat]: {}
};
}
statistics[exercise.userId][activity.id][cat] = [
{
progression: exercise.progression,
perGoodAnswers: Math.floor(exercise.perGoodAnswers),
exerciseId: exercise.exerciseid,
_id: exercise._id,
completed: exercise.completed >= 1 ? true : false,
passed: exercise.passed,
passedWithHelp: exercise.passedWithHelp,
failed: exercise.failed,
grade: currentExercise.classe,
journeyId: exercise.journeyId,
story: exercise.story
}
];
}
});
}
}
}
});
// descending sort by progression
for (const userId in statistics) {
if (userId) {
for (const activityId in statistics[userId]) {
if (activityId) {
for (const category in statistics[userId][activityId]) {
if (category) {
statistics[userId][activityId][category] = statistics[userId][activityId][category].sort((a, b) => {
return b.perGoodAnswers - a.perGoodAnswers;
});
}
}
}
}
}
}
return statistics;
}
/**
* Start LogBook
*/
async nbU() {
return new Promise((resolve, reject) => {
const statement = [
{
$match: {
$and: [
{
"statement.verb.id": {
$in: ["https://xapi.mathia.education/verbs/initialized"]
}
}
]
}
},
{
$project: {
userid: "$statement.actor.account.name",
mydate: {
$dateToString: {
format: "%d-%m-%Y",
date: {
$toDate: "$statement.timestamp"
}
}
}
}
},
{
$group: {
_id: {
userid: "$userid",
mydate: "$mydate"
}
}
},
{
$group: {
_id: "$_id.mydate",
nbuser: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
date: "$_id",
nbuser: 1
}
},
{
$sort: {
date: 1
}
}
];
this.lrs
.request(statement)
.then(async (initilized: any) => {
console.log("Nombre élève", initilized);
resolve(this.logBookStatistics);
})
.catch(err => {
reject(true);
});
});
}
/**
* Start LogBook
//statistiques exercices terminés*/
async exercicescompleted() {
return new Promise((resolve, reject) => {
const statement = [
{
$match: {
$and: [
{
"statement.verb.id": {
$in: ["https://xapi.mathia.education/verbs/completed"]
}
}
]
}
},
{
$project: {
sessionid: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session",
mydate: {
$dateToString: {
format: "%d-%m-%Y",
date: {
$toDate: "$statement.timestamp"
}
}
}
}
},
{
$group: {
_id: {
sessionid: "$sessionid",
mydate: "$mydate"
}
}
},
{
$group: {
_id: "$_id.mydate",
nbsession: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
date: "$_id",
nbsession: 1
}
},
{
$sort: {
date: 1
}
}
];
this.lrs
.request(statement)
.then(async (initilized: any) => {
console.log("Exercices termine", initilized);
resolve(this.logBookStatistics);
})
.catch(err => {
reject(true);
});
});
}
//requete parcours terminé par un utilisateur
async parcourscompleted(cabriService: CabriDataService, journeys: Journey[]) {
return new Promise((resolve, reject) => {
const statement = [
{
$match: {
timestamp: {
$gte: {
$dte: "2021-01-01T08:09:00.000Z"
}
}
}
},
{
//on cherche à avoir le nombre de parcours terminé par un utilisateur
$match: {
$and: [
// {
// "statement.actor.account.name": {
// $in: ["1"]
// }
// },
{
"statement.verb.id": {
$in: ["https://xapi.mathia.education/verbs/completed"]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_session_id": {
$nin: [null]
}
}
// {
// "statement.context.extensions.https://xapi.mathia.education/extensions/codeclasse":{
// $nin: ["61103","60662"]
// }
// }
]
}
},
{
$project: {
parcours: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_session_id",
search: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id",
nomparcoursen: "$statement.object.definition.description.en-US"
//passed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] }, 1, 0] }
}
},
{
$group: {
_id: "$search",
idparcours: { $sum: "$parcours" },
nomparcours: { $first: "$nomparcoursen" }
//numberPassed: { $sum: "$passed" }
}
},
{
$sort: {
timestamp: -1
}
}
];
this.lrs
.request(statement)
.then(async (sessionExercises: Array<LogBookStatistics>) => {
console.info("parcours terminée", sessionExercises);
})
.catch(err => {
console.log("ERR2");
reject(true);
});
});
}
//requette nombre de classe
async nombredeclasse() {
return new Promise((resolve, reject) => {
const statement = [
{
//on cherche à avoir le nombre de code classe
$match: {
$and: [
{
"statement.verb.id": {
$in: ["https://xapi.mathia.education/verbs/initialized"]
}
}
]
}
},
{
$project: {
codeid: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/codeclasse",
mydate: {
$dateToString: {
format: "%d-%m-%Y",
date: {
$toDate: "statement.timestamp"
}
}
}
}
},
{
$group: {
_id: {
codeid: "$codeid",
mydate: "$mydate"
}
}
},
{
$group: {
_id: "$_id.mydate",
nbcodeid: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
date: "$_id",
nbcodeid: 1
}
},
{
$sort: {
date: 1
}
}
];
// ];
this.lrs
.request(statement)
.then(async (initilized: any) => {
console.log("Nombre de classe", initilized);
resolve(this.logBookStatistics);
})
.catch(err => {
reject(true);
});
});
}
/**
* Start LogBook
*/
async getLogBook(cabriService: CabriDataService, journeys: Journey[], lang: string) {
return new Promise((resolve, reject) => {
if (this.logBookStatistics.length > 0) {
resolve(this.logBookStatistics);
return;
}
const statement = [
{
$match: {
timestamp: {
$gte: {
$dte: "2021-01-01T08:09:00.000Z"
}
}
}
},
{
$match: {
$and: [
{
"statement.verb.id": {
$in: ["https://xapi.mathia.education/verbs/completed"]
}
},
{
"statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_activite": {
$nin: ["2"]
}
},
// {
// "statement.context.extensions.https://xapi&46;mathia&46;education/extensions/kidaia": {
// $in: [true]
// }
// },
{
"statement.actor.account.name": {
$in: [this.accountService.team[0].id]
}
}
]
}
},
{
$project: {
search: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session",
parcours_id: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_id",
exerciseid: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id",
parcours_session_id:
"$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/parcours_session_id",
mode: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/mode_jeu",
timestamp: "$statement.stored",
perGoodAnswers: "$statement.context.extensions.https://xapi&46;mathia&46;education/pourcentage_bonnes_reponses",
failed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed"] }, 1, 0] },
failedOnFirstAttempt: {
$cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed-on-first-attempt"] }, 1, 0]
},
passedWithHelp: {
$cond: [
{
$or: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-on-retry"]
},
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-with-help"]
}
]
},
1,
0
]
},
passed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] }, 1, 0] },
duration: "$metadata.https://learninglocker&46;net/result-duration.seconds"
}
},
{
$group: {
_id: "$search",
perGoodAnswers: { $first: "$perGoodAnswers" },
exerciseid: { $first: "$exerciseid" },
timestamp: { $first: "$timestamp" },
parcours_id: { $first: "$parcours_id" },
mode: { $first: "$mode" },
passed: { $sum: "$passed" },
passedWithHelp: { $sum: "$passedWithHelp" },
failed: { $sum: "$failed" },
failedOnFirstAttempt: { $sum: "$failedOnFirstAttempt" },
duration: { $first: "$duration" }
}
},
{
$sort: {
timestamp: -1
}
}
];
this.lrs
.request(statement)
.then(async (sessionExercises: Array<LogBookStatistics>) => {
sessionExercises.forEach(exercise => {
exercise.duration = this.secondsToHms(exercise.duration);
// let perGoodAnswer: number;
exercise.perGoodAnswers = Math.floor(exercise.perGoodAnswers);
const currentExercise = cabriService.exercices.getExercise(exercise.exerciseid);
let journeyName;
if (exercise.parcours_id) {
// get journay title
const currentJourney = journeys.find(currJourney => {
return currJourney.id === exercise.parcours_id;
});
if (currentJourney) {
journeyName = currentJourney.title;
}
}
if (currentExercise) {
const exerciseNameSplitted = currentExercise.name.split("-");
if (exerciseNameSplitted.length > 0) {
let activityName;
const activity = cabriService.activities.find(activitiy => {
return Number(activitiy.id) === Number(currentExercise.gabarit);
});
if (activity.name) {
// get activity name
activityName = activity.name;
}
let exerciseNameWithoutId;
if (exerciseNameSplitted[1]) {
exerciseNameWithoutId = exerciseNameSplitted[1].trim();
} else {
exerciseNameWithoutId = exerciseNameSplitted[0].trim();
}
if (lang === AppLanguage.FR) {
/** Convert lrs timestamp to local timezone to get locale hour */
// iso -> Date
const startTime = new Date(String(exercise.timestamp));
// date -> iso8601
exercise.timestamp = new Date(
startTime.getTime() - startTime.getTimezoneOffset() * 60000
).toISOString() as any;
}
exercise.timestamp = this._getDate(exercise.timestamp);
// statistic final
this.logBookStatistics.push({
...exercise,
exerciseName: exerciseNameWithoutId,
journeyName,
activityName,
perGoodAnswers: exercise.perGoodAnswers
});
}
}
});
this.logBookStatistics = this.logBookStatistics.reverse();
resolve(this.logBookStatistics);
})
.catch(err => {
reject(true);
});
});
}
secondsToHms(d) {
d = Number(d);
const h = Math.floor(d / 3600);
const m = Math.floor((d % 3600) / 60);
const s = Math.floor((d % 3600) % 60);
const hDisplay = h > 0 ? h + (h == 1 ? " " + $localize` heure ` + " " : " " + $localize` heures ` + " ") : "";
const mDisplay = m > 0 ? m + (m == 1 ? " " + $localize` minute ` + " " : " " + $localize` minutes ` + " ") : "";
const sDisplay = s > 0 ? s + (s == 1 ? " " + $localize` seconde` : " " + $localize` secondes`) : "";
let et = "";
if (mDisplay && sDisplay) {
et = " " + $localize`et ` + " ";
}
return hDisplay + mDisplay + et + sDisplay;
}
private _getDate(timestampIso8601): { day: number; dayOfWeek: string; month: string; year: number; time: string } {
const date = parse(timestampIso8601, "yyyy-MM-dd'T'HH:mm:ss'.'SSS'Z'", new Date());
return this._getCompleteDate(date);
}
private _getCompleteDate(dateJS: Date) {
const date = format(dateJS, "dd/MM/yyyy");
const time = format(dateJS, "HH:mm");
const dateSplitted = date.split("/");
const tabMois = new Array(
$localize`janvier`,
$localize`février`,
$localize`mars`,
$localize`avril`,
$localize`mai`,
$localize`juin`,
$localize`juillet`,
$localize`août`,
$localize`septembre`,
$localize`octobre`,
$localize`novembre`,
$localize`décembre`
);
const indexDayOfWeek = Number(format(dateJS, "i"));
const tabJour = new Array(
$localize`:Week day logbook text:Le lundi`,
$localize`:Week day logbook text:Le mardi`,
$localize`:Week day logbook text:Le mercredi`,
$localize`:Week day logbook text:Le jeudi`,
$localize`:Week day logbook text:Le vendredi`,
$localize`:Week day logbook text:Le samedi`,
$localize`:Week day logbook text:Le dimanche`
);
return {
month: tabMois[Number(dateSplitted[1]) - 1],
day: Number(dateSplitted[0]),
dayOfWeek: tabJour[indexDayOfWeek - 1],
year: Number(dateSplitted[2]),
time
};
}
/**
* END LogBook
*/
updatePerGoodAnswerStatements(http) {
const statement = [
{
$match: {
timestamp: {
$gte: {
$dte: "2021-01-27T19:30:00.000Z"
}
}
}
},
{
$match: {
$and: [
{
"statement.verb.id": {
$nin: ["https://xapi.mathia.education/verbs/finished", "https://xapi.mathia.education/verbs/initialized"]
}
}
]
}
},
{
$project: {
id: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/id_session",
failed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed"] }, 1, 0] },
modeJeu: "$statement.context.extensions.https://xapi&46;mathia&46;education/extensions/mode_jeu",
passedWithHelp: {
$cond: [
{
$or: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-on-retry"]
},
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed-with-help"]
}
]
},
1,
0
]
},
failedOnFirstAttempt: {
$cond: [
{
$and: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/failed-on-first-attempt"]
}
]
},
1,
0
]
},
passed: { $cond: [{ $eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/passed"] }, 1, 0] },
fieldExist: {
$cond: [
{
$gt: ["$statement.context.extensions.https://xapi&46;mathia&46;education/pourcentage_bonnes_reponses", null]
},
true,
false
]
},
isCompleted: {
$cond: [
{
$eq: ["$statement.verb.id", "https://xapi.mathia.education/verbs/completed"]
},
true,
false
]
}
}
},
{
$group: {
_id: "$id",
isCompleted: { $first: "$isCompleted" },
modeJeu: { $first: "$modeJeu" },
fieldExist: { $first: "$fieldExist" },
passed: { $sum: "$passed" },
failedOnFirstAttempt: { $sum: "$failedOnFirstAttempt" },
failed: { $sum: "$failed" },
passedWithHelp: { $sum: "$passedWithHelp" }
}
},
{
$sort: {
timestamp: -1
}
}
];
this.lrs
.request(statement)
.then(async (request: any) => {
const notExistingFieldRequest = [];
request.forEach(elem => {
if (elem.isCompleted && !elem.fieldExist) {
let perTotalGoodAnswer;
let perTotalWrongAnswer;
perTotalGoodAnswer =
((elem.passed + elem.passedWithHelp) / (elem.passed + elem.passedWithHelp + elem.failed)) * 100;
elem.perGoodAnswer = perTotalGoodAnswer;
if (elem.modeJeu === "défi") {
perTotalWrongAnswer =
(elem.failedOnFirstAttempt / (elem.passed + elem.failedOnFirstAttempt + elem.failed)) * 100;
} else {
perTotalWrongAnswer = (elem.failed / (elem.passed + elem.passedWithHelp + elem.failed)) * 100;
}
if (isNaN(perTotalWrongAnswer) || isNaN(perTotalGoodAnswer)) {
if (isNaN(perTotalWrongAnswer)) {
perTotalWrongAnswer = 0;
}
if (isNaN(perTotalGoodAnswer)) {
perTotalGoodAnswer = 0;
}
}
const finalElement = {
id: elem._id,
perTotalGoodAnswer,
perTotalWrongAnswer
};
notExistingFieldRequest.push(finalElement);
}
});
const items = notExistingFieldRequest.slice(0, 3);
// items[0].id = "16196066543626473oyl5pc";
// items[1].id = "16196185255886473kewvgq";
// items[2].id = "161961865769764737hnrh3";
http.post(`http://127.0.0.1:8000/search`, { statements: JSON.stringify(items) }).subscribe({
next: res => {
console.log("resultLrs", res);
},
error: err => {
console.log("error search", err);
}
});
})
.catch(err => {});
}
}