import { addWeeks, format, parseISO } from "date-fns";
import { first, Subscription } from "rxjs";
import { AppUtils } from "../app-utils";
import { SolidesPage } from "../page/solides/solides.page";
import { CabriDataService } from "../services/cabri-data.service";
import { GlobalService } from "../services/global.service";
import { LmsService } from "../services/lms.service";
import { AnswerStatus } from "./activity-answer";
import { CabriIntegrationSolides } from "./cabri-integration-solides";
import { LrsUtils } from "./lrs/lrsUtils";
import { ScenarioSolides } from "./scenario-solides";
import { AwardsType } from "./enums/awards";
declare var window: {
store: any;
document: any;
innerWidth: any;
innerHeight: any;
outerWidth;
outerHeight;
dispatchEvent: any;
CabriSceneBuilder: any;
SceneUpdater: any;
Hud: any;
Globals: any;
params: any;
webGLCleanup: any;
URL: any;
};
export enum ExerciseType {
name = 0,
recognize = 1,
describe = 2,
pattern = 3
}
export enum SolideType {
CP = "1, 2, 4",
CE1 = "1, 2, 4, 3, 5",
CE2 = "1, 2, 4, 3, 5, 6"
}
export enum SolideIds {
cube = 1,
pave = 2,
cone = 3,
boule = 4,
pyramide = 5,
cylindre = 6
}
export enum SolideSubjects {
faceNombre = "faceNombre",
faceForme = "faceForme",
arete = "arete",
sommet = "sommet"
}
export enum ResultType {
success,
successOnRetry,
fail,
failOnFirstAttempt
}
export class ExercicesSolides {
public consigne;
public previousConsigne;
public qcm: any;
public reponseNumérique = false;
randomSolidTypeIndex: number;
// public exercise;
public solideTypes: Map<string, Array<number>>;
allowedSolids: any;
allowedSolidRepresentations = [];
allowedSolidPatterns = [];
/** Id du solide affiché à la question précédente. */
lastSolidId: number;
/** Id du solide à afficher. */
solidId: any;
solidRepresentationId: any;
private grade;
lastSolidRepresentationId: any;
exoType: any;
goodResult;
public answerSubscription: Subscription;
allowedSubjects: any;
subject: any;
lastSolidPatternId: any;
patternId: any;
remediationRunning: any;
saveDefaultInputMethod: any;
lastSubject: any;
lastExoType: any;
spacedRepetitonDate: any;
recognizeMode: string;
consignSolidId: any;
constructor(
public cabri: CabriIntegrationSolides,
public page: SolidesPage,
public scenario: ScenarioSolides,
public cabriService: CabriDataService,
public lmsService: LmsService,
public globalService: GlobalService
) {
const solidsByClass = [
{ grade: "CP", solids: [1, 2, 4] },
{ grade: "CE1", solids: [1, 2, 4, 3, 5] },
{ grade: "CE2", solids: [1, 2, 4, 3, 5, 6] }
];
this.solideTypes = new Map(solidsByClass.map(item => [item.grade, item.solids]));
}
// 1 Manipulation libre
// 2 Manipulation libre
// 3 Manipulation libre
// 1 Reconnaître les solides simples parmi des objets du quotidien
// 2 Reconnaître les solides usuels parmi des objets du quotidien
// 1 Nommer les solides simples parmi des objets du quotidien
// 2 Nommer les solides usuels parmi des objets du quotidien
// 3 Nommer les solides usuels parmi des objets du quotidien
// 1 Décrire un cube/pavé
// 2 Décrire un cube/pavé/pyramide
// 3 Décrire un cube/pavé/pyramide/solide quelconque
// 2 Patrons du cube
// 3 Patrons du cube/pavé/pyramide
updateVariables() {
this.debug(this.cabriService.getCurrentExercice());
this.lastExoType = this.exoType;
// security for variables JSON decoding
if (typeof this.cabriService.getCurrentExercice().variables === "string") {
this.cabriService.getCurrentExercice().variables = JSON.parse(this.cabriService.getCurrentExercice().variables);
}
this.grade = this.cabriService.getCurrentExercice().classe;
this.exoType = this.cabriService.getCurrentExercice().variables.type;
this.debug(this.grade);
this.debug(this.exoType);
// default values for testing
// this.grade = "CE2";
// const randomExo = Math.floor(Math.random() * 4);
// this.exoType = randomExo; // ExerciseType.describe;
// this.exoType = ExerciseType.describe;
// clean qcm choices.
this.qcm = new Array();
// clean representationId
this.solidRepresentationId = null;
// ExerciseType.name;
// ExerciseType.recognize;
// ExerciseType.describe;
// ExerciseType.pattern;
// ExerciseType.discovery;
switch (this.exoType) {
case ExerciseType.name:
this.debug("ExerciseType.name");
// choisir le solide (random)
this.solidId = this.chooseRandomSolid();
// choisir sa representation (cube de base, dés etc)
this.solidRepresentationId = this.chooseRandomSolidRepresentation();
this.debug("this.representation = " + this.page.SOLIDES[this.solidId].values[this.solidRepresentationId]);
// ecrire consigne "quel est ce solide?"
this.consigne = $localize`Quel est ce solide ?`;
// set goodResult
this.goodResult = this.page.SOLIDES[this.solidId].name;
this.debug("this.goodResult = " + this.goodResult);
// QCM pour numpad
this.qcm = [
{
text: this.page.SOLIDES[this.solidId].qcmValue,
value: this.page.SOLIDES[this.solidId].name
}
];
this.addQCMFalseChoice();
AppUtils.shuffleArray(this.qcm);
break;
case ExerciseType.recognize:
// choisir le solide (random)
this.solidId = this.chooseRandomSolid();
// choisir mode OUI/NON ou QCM
if (Math.random() > 0.5) {
// QCM
this.recognizeMode = "qcm";
this.consigne = $localize`Quel est ce solide ?`;
this.goodResult = this.page.SOLIDES[this.solidId].name;
this.qcm = [
{
text: this.page.SOLIDES[this.solidId].qcmValue,
value: this.page.SOLIDES[this.solidId].name
}
];
// choisir les intrus
this.addQCMFalseChoice();
AppUtils.shuffleArray(this.qcm);
} else {
// oui/non
this.recognizeMode = "ouinon";
this.qcm = [
{
text: $localize`OUI`,
value: $localize`:participantAnswereYes:oui`
},
{
text: $localize`NON`,
value: $localize`:participantAnswereNo:non`
}
];
const solids = this.solideTypes.get(this.grade);
const solidId2 = this.extractRandomElementFromArray(solids);
this.consignSolidId = solidId2;
if (solidId2 === this.solidId) {
this.goodResult = $localize`:participantAnswereYes:oui`;
} else {
this.goodResult = $localize`:participantAnswereNo:non`;
}
const solidName = String(this.page.SOLIDES[solidId2].qcmValue).toLowerCase();
this.consigne = $localize`Ce solide est-il ${solidName}:aSolidName: ?`;
}
// lequel de ces solides est un (solidType)
break;
case ExerciseType.describe:
// choisir face (nombre ou forme) arretes sommets
this.subject = this.chooseRandomSubject();
// for testing
// this.subject = SolideSubjects.faceNombre;
// choisir le solide (random)
this.solidId = this.chooseRandomSolid();
// choisir sa representation (cube de base, dés etc)
this.solidRepresentationId = this.chooseRandomSolidRepresentation();
// question:
switch (this.subject) {
case SolideSubjects.faceForme:
this.goodResult = this.page.SOLIDES[this.solidId].facetShape;
this.consigne = $localize`Quelle forme ont les faces de ce solide ?`;
this.qcm = [
{
text: this.page.SOLIDES[this.solidId].facetShape
}
];
this.addQCMFalseChoice();
AppUtils.shuffleArray(this.qcm);
break;
case SolideSubjects.faceNombre:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbFacets;
this.consigne = $localize`Combien de faces à ce solide ?`;
this.createQCMNumberChoice();
break;
case SolideSubjects.arete:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbEdges;
this.consigne = $localize`Combien d’arêtes à ce solide ?`;
this.createQCMNumberChoice();
break;
case SolideSubjects.sommet:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbVertices;
this.consigne = $localize`Combien de sommets à ce solide ?`;
this.createQCMNumberChoice();
break;
}
break;
case ExerciseType.pattern:
// choisir le solide (random)
this.solidId = this.chooseRandomSolid();
// choisir le patron
this.patternId = this.chooseRandomSolidPattern();
// question:
this.consigne = $localize`De quel solide est-ce le patron ?`;
// set goodResult
this.goodResult = this.page.SOLIDES[this.solidId].name;
// build qcm
this.qcm = [
{
text: this.page.SOLIDES[this.solidId].qcmValue,
value: this.page.SOLIDES[this.solidId].name
}
];
this.addQCMFalseChoice();
AppUtils.shuffleArray(this.qcm);
// quel patron represente un cube? (multi patrons)
break;
default:
break;
}
}
addQCMFalseChoice(nb = 4) {
let allowedSolids = new Array();
switch (this.exoType) {
case ExerciseType.name:
case ExerciseType.recognize:
// choose allowed solids : all
this.solideTypes.get(this.grade).forEach(sId => {
if (sId !== this.solidId) {
allowedSolids.push(sId);
}
});
// build qcm values : solide name
while (allowedSolids.length > 0 && this.qcm.length < nb) {
const qcmChoice = this.extractRandomElementFromArray(allowedSolids);
allowedSolids = allowedSolids.filter(item => item !== qcmChoice);
this.qcm.push({
text: this.page.SOLIDES[qcmChoice].qcmValue,
value: this.page.SOLIDES[qcmChoice].name
});
}
break;
case ExerciseType.describe:
allowedSolids = new Array();
// choose allowed solids : hasFacets
this.solideTypes.get(this.grade).forEach(sId => {
if (sId !== this.solidId && this.page.SOLIDES[sId].hasFacets) {
allowedSolids.push(sId);
}
});
// build qcm values : facet shape
while (allowedSolids.length > 0 && this.qcm.length < nb) {
const qcmChoice = this.extractRandomElementFromArray(allowedSolids);
allowedSolids = allowedSolids.filter(item => item !== qcmChoice);
this.qcm.push({
text: this.page.SOLIDES[qcmChoice].facetShape
});
}
break;
case ExerciseType.pattern:
allowedSolids = new Array();
// choose allowed solids : hasPatron
this.solideTypes.get(this.grade).forEach(sId => {
if (sId !== this.solidId && this.page.SOLIDES[sId].hasPatron) {
allowedSolids.push(sId);
}
});
// build qcm values : solide name
while (allowedSolids.length > 0 && this.qcm.length < nb) {
const qcmChoice = this.extractRandomElementFromArray(allowedSolids);
allowedSolids = allowedSolids.filter(item => item !== qcmChoice);
this.qcm.push({
text: this.page.SOLIDES[qcmChoice].qcmValue,
value: this.page.SOLIDES[qcmChoice].name
});
}
break;
default:
break;
}
}
createQCMNumberChoice() {
this.qcm = [{ text: 5 }, { text: 6 }, { text: 8 }, { text: 12 }];
AppUtils.shuffleArray(this.qcm);
}
runExercice() {
switch (this.exoType) {
case ExerciseType.name:
this.debug("ExerciseType.name");
this.runExerciceNaming();
break;
case ExerciseType.recognize:
this.debug("ExerciseType.recognize");
this.runExerciceRecognize();
break;
case ExerciseType.describe:
this.debug("ExerciseType.describe");
this.runExerciceDescribe();
break;
case ExerciseType.pattern:
this.debug("ExerciseType.pattern");
this.runExercicePattern();
break;
default:
break;
}
}
chooseRandomSolid() {
if (
!this.allowedSolids ||
this.allowedSolids.length === 0 ||
(this.exoType === ExerciseType.describe && this.subject !== this.lastSubject) ||
this.exoType !== this.lastExoType
) {
switch (this.exoType) {
case ExerciseType.describe:
this.allowedSolids = new Array();
switch (this.subject) {
case SolideSubjects.faceForme:
case SolideSubjects.faceNombre:
this.solideTypes.get(this.grade).forEach(sId => {
if (this.page.SOLIDES[sId].hasFacets) {
this.allowedSolids.push(sId);
}
});
break;
case SolideSubjects.arete:
this.solideTypes.get(this.grade).forEach(sId => {
if (this.page.SOLIDES[sId].hasEdges) {
this.allowedSolids.push(sId);
}
});
break;
case SolideSubjects.sommet:
this.solideTypes.get(this.grade).forEach(sId => {
if (this.page.SOLIDES[sId].hasVertices) {
this.allowedSolids.push(sId);
}
});
break;
}
break;
case ExerciseType.pattern:
this.allowedSolids = new Array();
this.solideTypes.get(this.grade).forEach(sId => {
if (this.page.SOLIDES[sId].hasPatron) {
this.allowedSolids.push(Number(sId));
}
});
break;
default:
this.allowedSolids = [...this.solideTypes.get(this.grade)];
}
}
const solidId = this.extractRandomElementFromArray(this.allowedSolids);
this.allowedSolids = this.allowedSolids.filter(item => item !== solidId);
this.debug("solidId = " + solidId);
if (this.lastSolidId !== solidId) {
return solidId;
} else {
return this.chooseRandomSolid();
}
}
chooseRandomSubject() {
if (!this.allowedSubjects || this.allowedSubjects.length === 0) {
this.allowedSubjects = new Array(
SolideSubjects.faceForme,
SolideSubjects.faceNombre,
SolideSubjects.arete,
SolideSubjects.sommet
);
}
const subject = this.extractRandomElementFromArray(this.allowedSubjects);
this.allowedSubjects = this.allowedSubjects.filter(item => item !== subject);
if (this.lastSubject !== subject) {
this.lastSubject = subject;
return subject;
} else {
return this.chooseRandomSubject();
}
}
/**
* Choose one representation for the current solid
* @returns id of the choosen solid representation
*/
chooseRandomSolidRepresentation() {
if (!this.allowedSolidRepresentations[this.solidId] || this.allowedSolidRepresentations[this.solidId].length === 0) {
this.allowedSolidRepresentations[this.solidId] = new Array();
for (const representationId in this.page.SOLIDES[this.solidId].values) {
if (this.page.SOLIDES[this.solidId].values.hasOwnProperty(representationId)) {
this.allowedSolidRepresentations[this.solidId].push(Number(representationId));
}
}
}
this.debug(this.allowedSolidRepresentations[this.solidId]);
const solidRepresentationId = this.extractRandomElementFromArray(this.allowedSolidRepresentations[this.solidId]);
this.allowedSolidRepresentations[this.solidId] = this.allowedSolidRepresentations[this.solidId].filter(
item => item !== solidRepresentationId
);
if (this.lastSolidRepresentationId !== solidRepresentationId) {
this.lastSolidRepresentationId = solidRepresentationId;
return solidRepresentationId;
} else {
return this.chooseRandomSolidRepresentation();
}
}
/**
* Choose one pattern for the current solid
* @returns id of the choosen solid representation
*/
chooseRandomSolidPattern() {
if (!this.allowedSolidPatterns[this.solidId] || this.allowedSolidPatterns[this.solidId].length === 0) {
this.allowedSolidPatterns[this.solidId] = new Array();
for (let i = 1; i <= this.page.PATRON[this.solidId].max; i++) {
this.allowedSolidPatterns[this.solidId].push(i);
}
}
const solidPatternId = this.extractRandomElementFromArray(this.allowedSolidPatterns[this.solidId]);
this.allowedSolidPatterns[this.solidId] = this.allowedSolidPatterns[this.solidId].filter(item => item !== solidPatternId);
if (this.lastSolidPatternId !== solidPatternId) {
this.lastSolidPatternId = solidPatternId;
return solidPatternId;
} else {
return this.chooseRandomSolidPattern();
}
}
async displaySolid() {
await this.page.startLoader();
if (this.page.cabri.patron || this.page.currentSolid !== this.solidId) {
await this.page.changeSolidType(this.solidId);
await this.page.cabri.getCabriMeshAsync(this.solidId + "-1");
await AppUtils.timeOut(100);
if (this.globalService.isIos) {
await AppUtils.timeOut(2000);
}
}
if (this.page.currentRepresentation !== this.solidRepresentationId) {
await this.page.changeRepresentation(null, this.solidRepresentationId);
await this.page.cabri.getCabriMeshAsync(this.solidId + "-" + this.solidRepresentationId);
}
await this.page.stopLoader();
}
async displayPattern() {
this.page.startLoader();
await this.page.changeSolidType(this.solidId);
await this.page.cabri.getCabriMeshAsync(this.solidId + "-1");
await AppUtils.timeOut(100);
await this.page.patron(this.patternId);
this.page.stopLoader();
}
extractRandomElementFromArray(array) {
const randomIndex = Math.floor(Math.random() * array.length);
const randomNumber = array[randomIndex];
return randomNumber;
}
// nommer le solide affiché
public async runExerciceNaming() {
this.page.checkIfSonicMode();
// afficher le solide
if (!this.page.failed) {
await this.displaySolid();
}
// poser la question
await this.scenario.readConsigne(this.consigne);
// attendre la réponse
this.answerSubscription = this.page.waitUserAnswer(this.cabriService.inputMethod).subscribe(async answer => {
const responseTimeInMs = Date.now() - LrsUtils.currentUserResponseTime;
const responseTimeInSeconds = Math.floor(responseTimeInMs / 1000);
LrsUtils.responseTime = responseTimeInSeconds;
// traiter la réponse
this.debug("participantAnswer = " + answer);
if (answer === this.goodResult) {
// si juste, passer à la question suivante
this.page.audioService.playStarSound();
this.answerSubscription.unsubscribe();
if (this.page.failed) {
this.manageLRS(ResultType.successOnRetry);
await this.scenario.goodResponse(AnswerStatus.VALID2ND, responseTimeInSeconds, AwardsType.normal);
} else {
this.manageLRS(ResultType.success);
await this.scenario.goodResponse(AnswerStatus.VALID1ST, responseTimeInSeconds, AwardsType.shooting);
}
this.page.failed = false;
this.page.newQuestion();
} else {
this.page.audioService.playAwardMoonSound();
await this.page.timeOut(500);
if (!this.page.failed) {
// si faux, lancer remédiation
await this.scenario.badAnswer(AnswerStatus.ERROR1ST, responseTimeInSeconds);
this.manageLRS(ResultType.failOnFirstAttempt);
this.page.failed = true;
this.runRemediation()
.pipe(first())
.subscribe(async e => {
this.remediationRunning = false;
this.page.waitUserAnswer(this.cabriService.inputMethod);
});
} else {
this.page.failed = false;
this.answerSubscription.unsubscribe();
this.manageLRS(ResultType.fail);
await this.scenario.readConsigne($localize`Oups ! La bonne réponse était : ${this.goodResult}:goodResult:`);
this.page.newQuestion();
}
}
});
}
// nommer le solide affiché
public async runExerciceRecognize() {
this.page.checkIfSonicMode();
// afficher le solide
if (!this.page.failed) {
await this.displaySolid();
}
// poser la question
await this.scenario.readConsigne(this.consigne);
// attendre la réponse
this.answerSubscription = this.page.waitUserAnswer(this.cabriService.inputMethod).subscribe(async answer => {
const responseTimeInMs = Date.now() - LrsUtils.currentUserResponseTime;
const responseTimeInSeconds = Math.floor(responseTimeInMs / 1000);
LrsUtils.responseTime = responseTimeInSeconds;
// traiter la réponse
this.debug("participantAnswer = ", answer);
if (answer === this.goodResult) {
this.page.audioService.playStarSound();
// si juste, passer à la question suivante
this.answerSubscription.unsubscribe();
if (this.page.failed) {
this.manageLRS(ResultType.successOnRetry);
await this.scenario.goodResponse(AnswerStatus.VALID2ND, responseTimeInSeconds, AwardsType.normal);
} else {
this.manageLRS(ResultType.success);
await this.scenario.goodResponse(AnswerStatus.VALID1ST, responseTimeInSeconds, AwardsType.shooting);
}
if (this.goodResult === $localize`:participantAnswereNo:non` && !this.page.sonicAward) {
const solidName = String(this.page.SOLIDES[this.solidId].qcmValue).toLowerCase();
await this.scenario.readConsigne($localize`Ce solide est ${solidName}:aSolidName:.`);
}
this.page.failed = false;
this.page.newQuestion();
} else {
this.page.audioService.playAwardMoonSound();
await this.page.timeOut(500);
if (!this.page.failed) {
// si faux, lancer remédiation
await this.scenario.badAnswer(AnswerStatus.ERROR1ST, responseTimeInSeconds);
this.manageLRS(ResultType.failOnFirstAttempt);
this.page.failed = true;
this.runRemediation()
.pipe(first())
.subscribe(async e => {
this.remediationRunning = false;
this.page.waitUserAnswer(this.cabriService.inputMethod);
});
} else {
this.page.failed = false;
this.answerSubscription.unsubscribe();
this.manageLRS(ResultType.fail);
if (
this.goodResult === $localize`:participantAnswereYes:oui` ||
this.goodResult === $localize`:participantAnswereNo:non`
) {
await this.scenario.readConsigne($localize`Oups ! La bonne réponse était : ${this.goodResult}:goodResult:`);
const solidName = String(this.page.SOLIDES[this.solidId].qcmValue).toLowerCase();
await this.scenario.readConsigne($localize`Ce solide est ${solidName}:aSolidName:`);
} else {
const solidName = String(this.page.SOLIDES[this.solidId].qcmValue).toLowerCase();
await this.scenario.readConsigne($localize`Oups ! Ce solide était ${solidName}:aSolidName:`);
}
this.page.newQuestion();
}
}
});
}
async runExerciceDescribe() {
this.page.checkIfSonicMode();
// si difficulté === 1 ne pas afficher le solide
// afficher le solide
if (!this.page.failed) {
await this.displaySolid();
}
// poser la question
await this.scenario.readConsigne(this.consigne);
// attendre la réponse
this.answerSubscription = this.page.waitUserAnswer(this.cabriService.inputMethod).subscribe(async answer => {
const responseTimeInMs = Date.now() - LrsUtils.currentUserResponseTime;
const responseTimeInSeconds = Math.floor(responseTimeInMs / 1000);
LrsUtils.responseTime = responseTimeInSeconds;
// traiter la réponse
this.debug("participantAnswer = " + answer);
this.debug("goodResult = " + this.goodResult);
if (String(answer) === String(this.goodResult)) {
this.page.audioService.playStarSound();
// si juste, passer à la question suivante
this.answerSubscription.unsubscribe();
if (this.page.failed) {
this.manageLRS(ResultType.successOnRetry);
await this.scenario.goodResponse(AnswerStatus.VALID2ND, responseTimeInSeconds, AwardsType.normal);
} else {
this.manageLRS(ResultType.success);
await this.scenario.goodResponse(AnswerStatus.VALID1ST, responseTimeInSeconds, AwardsType.shooting);
}
this.page.failed = false;
this.page.newQuestion();
} else {
this.page.audioService.playAwardMoonSound();
await this.page.timeOut(500);
if (!this.page.failed) {
// si faux, lancer remédiation
await this.scenario.badAnswer(AnswerStatus.ERROR1ST, responseTimeInSeconds);
this.manageLRS(ResultType.failOnFirstAttempt);
this.page.failed = true;
this.runRemediation()
.pipe(first())
.subscribe(async e => {
if (this.saveDefaultInputMethod) {
// auto switch to numpad for SolideSubjects.faceForme, so revert
this.cabriService.inputMethod = this.saveDefaultInputMethod;
this.saveDefaultInputMethod = null;
}
this.remediationRunning = false;
await this.endRemediationDescribe();
await this.scenario.readConsigne(this.consigne);
this.page.remediationCountingMode = false;
this.page.waitUserAnswer(this.cabriService.inputMethod);
});
} else {
this.page.failed = false;
this.answerSubscription.unsubscribe();
this.manageLRS(ResultType.fail);
await this.scenario.readConsigne($localize`Oups ! La bonne réponse était : ${this.goodResult}:goodResult:`);
this.page.newQuestion();
}
}
});
}
async endRemediationDescribe() {
switch (this.subject) {
case SolideSubjects.faceForme:
this.goodResult = this.page.SOLIDES[this.solidId].facetShape;
this.consigne = $localize`Alors ? Quelle forme ont les faces de ce solide ?`;
this.qcm = [
{
text: this.page.SOLIDES[this.solidId].facetShape
}
];
this.addQCMFalseChoice();
AppUtils.shuffleArray(this.qcm);
break;
case SolideSubjects.faceNombre:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbFacets;
this.consigne = $localize`Alors ? Combien de faces à ce solide ?`;
this.createQCMNumberChoice();
break;
case SolideSubjects.arete:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbEdges;
this.consigne = $localize`Alors ? Combien d’arêtes à ce solide ?`;
this.createQCMNumberChoice();
break;
case SolideSubjects.sommet:
this.goodResult = this.page.SOLIDES_DATA[this.solidId + "-1"].nbVertices;
this.consigne = $localize`Alors ? Combien de sommets à ce solide ?`;
this.createQCMNumberChoice();
break;
}
}
async runExercicePattern() {
this.page.checkIfSonicMode();
// si difficulté === 1 ne pas afficher le solide
// afficher le solide
if (!this.page.failed) {
await this.displayPattern();
this.page.cabri.camera.beta = Math.PI / 6;
this.page.cabri.camera.radius = 72;
this.page.store.getters.cps.setPageOrientation(this.cabri.camera.alpha, this.cabri.camera.beta);
}
// poser la question
await this.scenario.readConsigne(this.consigne);
// attendre la réponse
this.answerSubscription = this.page.waitUserAnswer(this.cabriService.inputMethod).subscribe(async answer => {
const responseTimeInMs = Date.now() - LrsUtils.currentUserResponseTime;
const responseTimeInSeconds = Math.floor(responseTimeInMs / 1000);
LrsUtils.responseTime = responseTimeInSeconds;
// traiter la réponse
this.debug("participantAnswer = " + answer);
this.debug("goodResult = " + this.goodResult);
if (String(answer) === String(this.goodResult)) {
// si juste, passer à la question suivante
this.page.audioService.playStarSound();
this.answerSubscription.unsubscribe();
if (this.page.failed) {
this.manageLRS(ResultType.successOnRetry);
await this.scenario.goodResponse(AnswerStatus.VALID2ND, responseTimeInSeconds, AwardsType.normal);
} else {
this.manageLRS(ResultType.success);
await this.scenario.goodResponse(AnswerStatus.VALID1ST, responseTimeInSeconds, AwardsType.shooting);
}
this.page.failed = false;
this.page.newQuestion();
} else {
this.page.audioService.playAwardMoonSound();
await this.page.timeOut(500);
if (!this.page.failed) {
// si faux la première fois, lancer remédiation
this.manageLRS(ResultType.failOnFirstAttempt);
await this.scenario.badAnswer(AnswerStatus.ERROR1ST, responseTimeInSeconds);
this.page.failed = true;
this.runRemediation()
.pipe(first())
.subscribe(async e => {
this.remediationRunning = false;
this.consigne = $localize`Alors ? De quel solide est-ce le patron ?`;
await this.scenario.readConsigne(this.consigne);
this.page.waitUserAnswer(this.cabriService.inputMethod);
});
} else {
this.page.failed = false;
this.answerSubscription.unsubscribe();
this.manageLRS(ResultType.fail);
await this.scenario.readConsigne($localize`Oups ! La bonne réponse était : ${this.goodResult}:goodResult:`);
this.page.newQuestion();
}
}
});
}
runRemediation() {
this.remediationRunning = true;
switch (this.exoType) {
case ExerciseType.name:
this.runRemediationName();
break;
case ExerciseType.recognize:
this.runRemediationRecognize();
break;
case ExerciseType.describe:
this.runRemediationDescribe();
break;
case ExerciseType.pattern:
this.runRemediationPattern();
break;
}
return this.page.remediationEndSubscription;
}
async runRemediationDescribe() {
if (this.page.currentRepresentation !== 1) {
await this.page.changeRepresentation(null, 1);
}
switch (this.subject) {
case SolideSubjects.faceForme:
await this.scenario.readConsigne($localize`Regarde bien et réessaye`);
this.saveDefaultInputMethod = this.cabriService.inputMethod;
this.cabriService.inputMethod = "numpad";
this.page.remediationEndSubscription.next();
break;
case SolideSubjects.arete:
this.page.remediationCountingMode = true;
this.scenario.readConsigne(
$localize`Tu peux zoomer en utilisant le curseur en bas à droite`,
$localize`Clique sur toutes les arêtes pour les compter`
);
this.page.markEdges(null, true);
break;
case SolideSubjects.faceNombre:
this.page.remediationCountingMode = true;
this.scenario.readConsigne(
$localize`Tu peux zoomer en utilisant le curseur en bas à droite`,
$localize`Clique sur toutes les faces pour les compter`
);
this.page.markFacets(null, true);
break;
case SolideSubjects.sommet:
this.page.remediationCountingMode = true;
this.scenario.readConsigne(
$localize`Tu peux zoomer en utilisant le curseur en bas à droite`,
$localize`Clique sur tous les sommets pour les compter`
);
this.page.markVertices(null, true);
break;
}
}
async runRemediationName() {
await this.scenario.descriptionSolid();
await this.scenario.readConsigne($localize`Qui suis-je ?`);
this.page.remediationEndSubscription.next();
}
async runRemediationRecognize() {
await this.scenario.descriptionSolid();
let consigne: string;
if (this.recognizeMode === "qcm") {
consigne = $localize`Alors ? Qui suis-je ?`;
} else {
const solidName = String(this.page.SOLIDES[this.consignSolidId].qcmValue).toLowerCase();
consigne = $localize`Alors ? Suis-je ${solidName}:aSolidName: ?`;
}
await this.scenario.readConsigne(consigne);
this.page.remediationEndSubscription.next();
}
async runRemediationPattern() {
this.scenario.introRemediationPattern();
}
// getRandomIndexWithoutRepetition(numberArray, oldIndex): Promise<number> {
// return new Promise((resolve, reject) => {
// const newIndex = AppUtils.getRandomIntInclusive(1, 7);
// newIndex !== oldIndex ? resolve(newIndex) : resolve(this.getRandomIndexWithoutRepetition(min, max, oldIndex));
// });
// }
// without repetition / without filtering array
chooseRandomIndexInArray(array, oldIndex) {
const randomIndex = Math.floor(Math.random() * array.length);
const randomNumber = array[randomIndex];
if (randomNumber !== oldIndex) {
return randomNumber;
} else {
return this.chooseRandomIndexInArray(array, oldIndex);
}
}
manageLRS(result: ResultType) {
switch (result) {
case ResultType.success:
this.page.manageLrsOperation("passed");
this.page.updatePlayerAndTeamStarboards(AwardsType.shooting);
break;
case ResultType.successOnRetry:
this.page.manageLrsOperation("passed-with-help");
this.page.updatePlayerAndTeamStarboards(AwardsType.normal);
break;
case ResultType.fail:
this.page.manageLrsOperation("failed");
this.page.updatePlayerAndTeamStarboards(AwardsType.moon);
break;
case ResultType.failOnFirstAttempt:
this.page.manageLrsOperation("failed-on-first-attempt");
break;
}
if (result !== ResultType.failOnFirstAttempt) {
this.page.showHideAward();
}
}
getCurrentSolidName() {
return this.page.SOLIDES[this.solidId].values[this.solidRepresentationId ? this.solidRepresentationId : 1];
}
async updateExercice(firstTime = true) {
return new Promise<void>((resolve, reject) => {
// console.error("update exercice");
const previousConsigne = this.cabri.getCurrentOperation();
this.updateVariables();
// force numpad mode for certain exercises
if (
this.cabriService.currentExercice.responseMode &&
this.cabriService.currentExercice.responseMode === "numpad" &&
this.cabriService.holoMode === "0"
) {
this.cabriService.currentActivity.setParamValue("input-method", "numpad");
}
this.page.inputMethodsAllowed();
// update time for spaced repetition
const date = new Date();
const seconds = (date.getTime() - this.spacedRepetitonDate.getTime()) / 1000;
if (this.foundQuestionValid(seconds, previousConsigne)) {
this.lastSolidId = this.solidId;
this.spacedRepetitonDate = new Date();
resolve();
} else {
setTimeout(async () => {
await this.updateExercice(false);
resolve();
}, 30);
}
});
}
foundQuestionValid(seconds, previousConsigne) {
if (this.lmsService.currentUserJourney) {
// mode parcours
if (!this.spacedStudentRepetition() && seconds <= 3) {
return false;
} else {
if (seconds >= 3 && this.lmsService.currentUserJourney.allAskedQuestions) {
this.lmsService.currentUserJourney.allAskedQuestions = new Array();
// ne pas reposer la question précédente
if (String(this.cabri.getCurrentOperation()) === String(previousConsigne)) {
return false;
}
}
}
} else {
// mode exercice
const posInd = this.page.indexAlreadyAskedQuestion(this.cabri.getCurrentOperation(), previousConsigne);
if (posInd > -1 && seconds <= 3) {
return false;
} else if (seconds >= 3 && this.page.questionAskedDuringSession) {
this.page.questionAskedDuringSession = new Array();
// ne pas reposer la question précédente
if (String(this.consigne) === String(previousConsigne)) {
return false;
}
}
}
return true;
}
/**
* @returns true if the current question could be asked
*/
spacedStudentRepetition() {
let spacedRepetition = true;
const countCorrectAnswers = this.page.checkQuestionAskedDuringSession(this.cabri.getCurrentOperation());
if (countCorrectAnswers !== 0) {
const questionAwards = this.lmsService.currentUserJourney.allAskedQuestions.filter(questionAward => {
return (
questionAward.question &&
(questionAward.question.trim() === this.cabri.getCurrentOperation().trim() ||
this.cabri.getCurrentOperation().trim() === questionAward.question.trim().split("").reverse().join("")) &&
typeof questionAward.award === "string"
);
});
const lastTimePassedQuestion = questionAwards[questionAwards.length - 1];
if (!lastTimePassedQuestion) {
spacedRepetition = true;
} else {
const lastAnswerDate = lastTimePassedQuestion.timestamp;
const weekCount = Math.pow(2, countCorrectAnswers - 1);
const recurrenceWeek: string = format(addWeeks(parseISO(lastAnswerDate), weekCount), "T");
spacedRepetition = Number(format(new Date(), "T")) > Number(recurrenceWeek);
}
return spacedRepetition;
} else {
// question never been asked
return true;
}
}
private debug(message, on = true) {
if (on) {
console.error(message);
}
}
}