File

src/app/models/lrs/studentExerciseStatistics.ts

import { ExerciseStatistics } from "../gamification-lrs";
import { CabriActivity } from "../cabri-activity";
import { CabriDataService } from "src/app/services/cabri-data.service";
import { cloneDeep } from "lodash";
import { LmsService } from "src/app/services/lms.service";
import { Journey, JourneysByStatus, journeyStatus } from "../journey";
export enum ExerciseProgressionLength {
	number = 1
}

class StatisticsByCompetency {
	[category: string]: Array<object>;
}
export class AssignationStatistics {
	[journeyId: string]: {
		perCorrectAnswers?: number;
	};
}
export class StudentExerciseStatistics {
	statistics: ExerciseStatistics;
	exerciseStatistics: ExerciseStatistics;
	statisticsByCompetency;
	cabriService: CabriDataService;
	currentActivity: CabriActivity;
	selectedGrade: string;

	userId: string;
	constructor(statistics: ExerciseStatistics, userId?: string, cabriService?: CabriDataService, currentActivity?: CabriActivity) {
		this.statistics = statistics;
		this.userId = userId;
		if (cabriService) {
			this.cabriService = cabriService;
			this.selectedGrade = cabriService.exercices.selected;
		}
		if (currentActivity) {
			this.currentActivity = currentActivity;
			this.statisticsByCompetency = this.gatherStatisticsByActivity();
		}
	}

	/**
	 * Gather all statistics by competency and by activity (without taking care of the mode)
	 */
	private gatherStatisticsByActivity(): StatisticsByCompetency {
		this.exerciseStatistics = cloneDeep(this.statistics);

		/**
		 * Remove duplicate items and keep only statistics with at least one verb passed during the session
		 */
		const filterStatistics = (object,index,selfArray) => {
				if((index === selfArray.findIndex(t => {
					return t.exerciseId === object.exerciseId && t.progression !== null
				})) && object.passed > 0 || object.passedWithHelp > 0){
					return true
				}else{
					return false
				}
		}
		if (this.userId && this.exerciseStatistics[this.userId]) {
			// remove duplicates items with same exerciseId
			for (const userId in this.exerciseStatistics) {
				if (userId) {
					for (const activityId in this.exerciseStatistics[userId]) {
						if (activityId) {
							for (const category in this.exerciseStatistics[userId][activityId]) {
								if (category) {
									this.exerciseStatistics[userId][activityId][category] = this.exerciseStatistics[userId][activityId][
										category
									].filter((ex, index, self) => {
										return filterStatistics(ex, index, self)
									});
								}
							}
						}
					}
				}
			}

			const uniqueCategory = {};
			for (const categoryId in this.exerciseStatistics[this.userId][this.currentActivity.id]) {
				if (categoryId) {
					if (!uniqueCategory[categoryId]) {
						uniqueCategory[categoryId] = [];
					}
					const res = this.exerciseStatistics[this.userId][this.currentActivity.id][categoryId];
					uniqueCategory[categoryId] = uniqueCategory[categoryId].concat(res);
				}
			}

			/** Sort result per correct rate answer  and keep the best rate with removing other results */
			for (const categoryId in uniqueCategory) {
				if (categoryId) {
					uniqueCategory[categoryId] = uniqueCategory[categoryId].sort((a, b) => b.perGoodAnswers - a.perGoodAnswers);
					uniqueCategory[categoryId] = uniqueCategory[categoryId].filter((ex, index, self) => {
						return (
							index ===
							self.findIndex(t => {
								return t.exerciseId === ex.exerciseId;
							})
						);
					});
				}
			}

			return uniqueCategory;
		}
	}


	/**
	 * Count number of completed exercises
	 */
	get countCompletedExercises(): { [category: string]: { numberCompleted: number } } {
		const result = {};
		for (const categoryId in this.statisticsByCompetency) {
			if (categoryId) {
				const resultFinal = this.statisticsByCompetency[categoryId].filter(ex => {
					return ex.completed && this.selectedGrade === ex.grade;
				});

				if (resultFinal.length > 0) {
					result[categoryId] = { numberCompleted: resultFinal.length };
				} else {
					result[categoryId] = { numberCompleted: 0 };
				}
			}
		}
		this._addNotExistingCategory(result, { numberCompleted: 0 });
		return result;
	}

	/**
	 * Get history of all story statistics / keeping the best session result based on exercise
	 */
	getStoryStatistics(): { [idExercice: string]: { shooting: number; star: number; moon: number; averageGoodAnswer: number } } {
		const statisticsBestResultByExercise: {
			[idExercice: string]: { shooting: number; star: number; moon: number; averageGoodAnswer: number };
		} = {};
		for (const userId in this.statistics) {
			if (userId === this.userId) {
				for (const activityId in this.statistics[userId]) {
					if (activityId) {
						for (const exerciseInfo in this.statistics[userId][activityId]) {
							if (exerciseInfo) {
								this.statistics[userId][activityId][exerciseInfo].forEach(result => {
									if (result?.story) {
										if (!statisticsBestResultByExercise[result.exerciseId]) {
											statisticsBestResultByExercise[result.exerciseId] = {
												shooting: result.passed,
												star: result.passedWithHelp,
												moon: result.failed,
												averageGoodAnswer: result.perGoodAnswers
											};
										} else {
											if (
												result.perGoodAnswers > statisticsBestResultByExercise[result.exerciseId].averageGoodAnswer
											) {
												statisticsBestResultByExercise[result.exerciseId] = {
													shooting: result.passed,
													star: result.passedWithHelp,
													moon: result.failed,
													averageGoodAnswer: result.perGoodAnswers
												};
											}
										}
									}
								});
							}
						}
					}
				}
			}
		}

		return statisticsBestResultByExercise;
	}

	/**
	 * Get history of all story statistics / keeping the best session result based on exercise
	 */
	getAssignationStatistics(): { [idExercice: string]: { shooting: number; star: number; moon: number; averageGoodAnswer: number } } {
		const statisticsBestResultByExercise: {
			[idExercice: string]: { shooting: number; star: number; moon: number; averageGoodAnswer: number };
		} = {};
		for (const userId in this.statistics) {
			if (userId) {
				for (const activityId in this.statistics[userId]) {
					if (activityId) {
						for (const exerciseInfo in this.statistics[userId][activityId]) {
							if (exerciseInfo) {
								this.statistics[userId][activityId][exerciseInfo].forEach(result => {
									if (result?.story) {
										if (!statisticsBestResultByExercise[result.exerciseId]) {
											statisticsBestResultByExercise[result.exerciseId] = {
												shooting: result.passed,
												star: result.passedWithHelp,
												moon: result.failed,
												averageGoodAnswer: result.perGoodAnswers
											};
										} else {
											if (
												result.perGoodAnswers > statisticsBestResultByExercise[result.exerciseId].averageGoodAnswer
											) {
												statisticsBestResultByExercise[result.exerciseId] = {
													shooting: result.passed,
													star: result.passedWithHelp,
													moon: result.failed,
													averageGoodAnswer: result.perGoodAnswers
												};
											}
										}
									}
								});
							}
						}
					}
				}
			}
		}

		return statisticsBestResultByExercise;
	}

	/**
	 * Get history of all assignation journey statistics / keeping the best session result based on exercise
	 */
	getTotalAssignationStatistics(allJourneysByStatuses: JourneysByStatus[], completedJourneysKeyName: string) {
		let completedJourneys: Journey[];
		const journeyStatistics: AssignationStatistics = {};
		if (allJourneysByStatuses?.length > 0) {
			for (const currentJourneyByStatus in allJourneysByStatuses[0]) {
				if (currentJourneyByStatus === completedJourneysKeyName) {
					if (allJourneysByStatuses[0][currentJourneyByStatus].items?.length > 0) {
						completedJourneys = allJourneysByStatuses[0][currentJourneyByStatus].items;
					}
				}
			}
		}
		if (completedJourneys) {
			completedJourneys.forEach(currentJourney => {
				if(!journeyStatistics[currentJourney.id] || journeyStatistics[currentJourney.id].perCorrectAnswers < currentJourney.correctAnswers){
					journeyStatistics[currentJourney.id] = { perCorrectAnswers: currentJourney.correctAnswers };
				}
			});
			return journeyStatistics;
		}
	}

	/**
	 * Better rate of correct answers
	 */
	get averageRateCorrectAnswers(): { [category: string]: number } {
		const result = {};
		for (const categoryId in this.statisticsByCompetency) {
			if (categoryId) {
				let allCompletedExercises;
				allCompletedExercises = this.statisticsByCompetency[categoryId].filter(ex => {
					return ex.completed && this.selectedGrade === ex.grade;
				});
				if (allCompletedExercises.length > 0) {
					let totalResult = 0;
					for (let i = 0; i < allCompletedExercises.length; i++) {
						totalResult += allCompletedExercises[i].perGoodAnswers;
						if (i === allCompletedExercises.length - 1) {
							totalResult = totalResult / allCompletedExercises.length;
							result[categoryId] = Math.floor(totalResult);
						}
					}
				}
			}
		}

		this._addNotExistingCategory(result, 0);
		return result;
	}

	/**
	 * Get each exercise better rate of correct answer
	 */
	get exercisePerGoodAnswer(): { [exerciseId: string]: number } {
		const result = {};
		for (const categoryId in this.statisticsByCompetency) {
			if (categoryId) {
				this.statisticsByCompetency[categoryId].forEach(ex => {
					if (!ex.completed) {
						return;
					}
					if (!result[ex.exerciseId]) {
						result[ex.exerciseId] = {};
						result[ex.exerciseId].result = "";
						result[ex.exerciseId].progressedUntil = "";
						result[ex.exerciseId].decimalValue = "";
						result[ex.exerciseId].status = "";
					}
					result[ex.exerciseId].result = ex.perGoodAnswers;
					const progressed = ExerciseProgressionLength.number * (ex.perGoodAnswers / 100);
					result[ex.exerciseId].progressedUntil = Math.ceil(progressed);
					result[ex.exerciseId].decimalValue = progressed % 1;
				});
			}
		}
		return result;
	}

	/**
	 * Get total number of awards by activity(shootings/normal/moons)
	 */
	get totalNumberAwardsByActivity(): { [category: string]: { totalShootings: number; totalNormals: number; totalMoons: number } } {
		const result = {};
		for (const categoryId in this.statisticsByCompetency) {
			if (categoryId) {
				let totalShootings = 0;
				let totalNormals = 0;
				let totalMoons = 0;
				const level = this.statisticsByCompetency[categoryId].filter(ex => {
					return ex.grade === this.selectedGrade;
				});
				if (level?.length > 0) {
					level.forEach(ex => {
						totalShootings += ex.passed;
						totalNormals += ex.passedWithHelp;
						totalMoons += ex.failed;
						result[categoryId] = { totalShootings, totalNormals, totalMoons };
					});
				}
			}
		}

		this._addNotExistingCategory(result, { totalShootings: 0, totalNormals: 0, totalMoons: 0 });
		return result;
	}

	/**
	 * Get total number of awards(shootings/normal/moons) for Kidaia.
	 */
	get totalNumberAwards(): { shooting: number } {
		let shooting = 0;
		let result = { shooting };

		try {
			if (!this.userId) {
				throw new Error("User id not defined");
			}

			if (!this.statistics[this.userId]) {
				// throw new Error(`User id ${this.userId} has no any normal or shooting stars`);
			}
			for (const activityId in this.statistics[this.userId]) {
				if (activityId) {
					for (const categoryId in this.statistics[this.userId][activityId]) {
						if (categoryId) {
							this.statistics[this.userId][activityId][categoryId].forEach(ex => {
								shooting += ex.passed + ex.passedWithHelp;
								result = { shooting };
							});
						}
					}
				}
			}
		} catch (err) {
			console.warn("student statistics error", err);
		}

		return result;
	}

	/**
	 * Add the category with initial value if not any data has been found
	 */
	private _addNotExistingCategory(result: { [category: string]: number }, defaultResult) {
		for (const categoryName in this.cabriService.categories[this.cabriService.exercices.selected]) {
			if (!result.hasOwnProperty(categoryName)) {
				result[categoryName] = defaultResult;
			}
		}
	}
}

results matching ""

    No results matching ""