145 lines
4.8 KiB
TypeScript
145 lines
4.8 KiB
TypeScript
import { Stat } from "@/interfaces/user";
|
|
import { capitalize, groupBy } from "lodash";
|
|
import { convertCamelCaseToReadable } from "@/utils/string";
|
|
import { UserSolution } from "@/interfaces/exam";
|
|
import { Module } from "@/interfaces";
|
|
import { MODULES } from "@/constants/ielts";
|
|
import moment from "moment";
|
|
|
|
export const timestampToMoment = (stat: Stat): moment.Moment => {
|
|
return moment.unix(stat.date > Math.pow(10, 11) ? stat.date / 1000 : stat.date);
|
|
};
|
|
|
|
export const totalExams = (stats: Stat[]): number => {
|
|
const moduleStats = formatModuleTotalStats(stats);
|
|
return moduleStats.reduce((previous, current) => previous + current.value, 0);
|
|
};
|
|
|
|
export const averageScore = (stats: Stat[]): number => {
|
|
const { correct, total } = stats.reduce(
|
|
(acc, current) => ({ correct: acc.correct + current.score.correct, total: acc.total + current.score.total }),
|
|
{ correct: 0, total: 0 },
|
|
);
|
|
return parseFloat(((correct / total) * 100).toFixed(2));
|
|
};
|
|
|
|
export const formatModuleTotalStats = (stats: Stat[]): { label: string; value: number }[] => {
|
|
const moduleSessions: { [key: string]: string[] } = {};
|
|
|
|
stats.forEach((stat) => {
|
|
if (stat.module in moduleSessions) {
|
|
if (!moduleSessions[stat.module].includes(stat.session)) {
|
|
moduleSessions[stat.module] = [...moduleSessions[stat.module], stat.session];
|
|
}
|
|
} else {
|
|
moduleSessions[stat.module] = [stat.session];
|
|
}
|
|
});
|
|
|
|
return ["reading", "listening", "writing", "speaking"].map((module) => ({
|
|
label: capitalize(module),
|
|
value: moduleSessions[module]?.length || 0,
|
|
}));
|
|
};
|
|
|
|
export const totalExamsByModule = (stats: Stat[], module: Module): number => {
|
|
const moduleSessions: { [key: string]: string[] } = {};
|
|
|
|
stats.forEach((stat) => {
|
|
if (stat.module in moduleSessions) {
|
|
if (!moduleSessions[stat.module].includes(stat.session)) {
|
|
moduleSessions[stat.module] = [...moduleSessions[stat.module], stat.session];
|
|
}
|
|
} else {
|
|
moduleSessions[stat.module] = [stat.session];
|
|
}
|
|
});
|
|
|
|
return moduleSessions[module]?.length || 0;
|
|
};
|
|
|
|
export const calculateModuleAverageScoreStats = (stats: Stat[]): { module: Module; value: number }[] => {
|
|
const moduleScores: { [key: string]: { correct: number; total: number } } = {};
|
|
|
|
stats.forEach((stat) => {
|
|
if (stat.module in moduleScores) {
|
|
moduleScores[stat.module] = {
|
|
correct: moduleScores[stat.module].correct + stat.score.correct,
|
|
total: moduleScores[stat.module].total + stat.score.total,
|
|
};
|
|
} else {
|
|
moduleScores[stat.module] = stat.score;
|
|
}
|
|
});
|
|
|
|
return MODULES.map((x) => {
|
|
const score = moduleScores[x as keyof typeof moduleScores];
|
|
|
|
return {
|
|
module: x,
|
|
value: score ? parseFloat(((score.correct / score.total) * 100).toFixed(2)) : 0,
|
|
};
|
|
});
|
|
};
|
|
|
|
export const formatModuleAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => {
|
|
return calculateModuleAverageScoreStats(stats).map((x) => ({ label: capitalize(x.module), value: x.value }));
|
|
};
|
|
|
|
export const formatExerciseTotalStats = (stats: Stat[]): { label: string; value: number }[] => {
|
|
const totalExercises = stats.map((stat) => ({
|
|
label: convertCamelCaseToReadable(stat.type),
|
|
value: stats.filter((x) => x.type === stat.type).length,
|
|
}));
|
|
|
|
return totalExercises.filter((ex, index) => totalExercises.findIndex((x) => x.label === ex.label) === index);
|
|
};
|
|
|
|
export const formatExerciseAverageScoreStats = (stats: Stat[]): { label: string; value: number }[] => {
|
|
const typeScores: { [key: string]: { correct: number; total: number } } = {};
|
|
|
|
stats.forEach((stat) => {
|
|
if (stat.type in typeScores) {
|
|
typeScores[stat.type] = {
|
|
correct: typeScores[stat.type].correct + stat.score.correct,
|
|
total: typeScores[stat.type].total + stat.score.total,
|
|
};
|
|
} else {
|
|
typeScores[stat.type] = stat.score;
|
|
}
|
|
});
|
|
|
|
return Object.keys(typeScores).map((x) => {
|
|
const { correct, total } = typeScores[x as keyof typeof typeScores];
|
|
|
|
return {
|
|
label: convertCamelCaseToReadable(x),
|
|
value: parseFloat(((correct / total) * 100).toFixed(2)),
|
|
};
|
|
});
|
|
};
|
|
|
|
export const getExamsBySession = (stats: Stat[], session: string) => {
|
|
const grouped = groupBySession(stats);
|
|
return grouped[session].map((exam) => exam.exam);
|
|
};
|
|
|
|
export const groupBySession = (stats: Stat[]) => groupBy(stats, "session");
|
|
export const groupByDate = (stats: Stat[]) => groupBy(stats, "date");
|
|
export const groupByExam = (stats: Stat[]) => groupBy(stats, "exam");
|
|
export const groupByModule = (stats: Stat[]) => groupBy(stats, "module");
|
|
export const groupByUser = (stats: Stat[]) => groupBy(stats, "user");
|
|
|
|
export const convertToUserSolutions = (stats: Stat[]): UserSolution[] => {
|
|
return stats.map((stat) => ({
|
|
exercise: stat.exercise,
|
|
exam: stat.exam,
|
|
score: stat.score,
|
|
solutions: stat.solutions,
|
|
type: stat.type,
|
|
module: stat.module,
|
|
shuffleMaps: stat.shuffleMaps,
|
|
isPractice: stat.isPractice
|
|
}));
|
|
};
|