Add challenge feedback API
This commit is contained in:
@@ -60,6 +60,7 @@ CookieCode.ingredient("farine", 200, "g");
|
||||
CookieCode.materiel("saladier");
|
||||
CookieCode.constante("temperature", 180);
|
||||
CookieCode.preparation("pate");
|
||||
CookieCode.defi("Cookie avec boucle", { ingredientsMinimum: 3 });
|
||||
```
|
||||
|
||||
### Methodes d'une recette
|
||||
@@ -81,6 +82,7 @@ CookieCode.preparation("pate");
|
||||
.afficher()
|
||||
.toText()
|
||||
.toHTML()
|
||||
.analyser()
|
||||
```
|
||||
|
||||
## Exemple avec variables
|
||||
@@ -117,6 +119,62 @@ CookieCode.recette("Pate a cookies")
|
||||
.servir();
|
||||
```
|
||||
|
||||
## Feedback ludique
|
||||
|
||||
CookieCode peut analyser une recette et verifier si elle respecte un defi.
|
||||
|
||||
```js
|
||||
const recette = CookieCode.recette("Cookie")
|
||||
.prendre("farine", 200, "g")
|
||||
.prendre("sucre", 100, "g")
|
||||
.prendre("chocolat", 150, "g")
|
||||
.ajouter("farine")
|
||||
.ajouter("sucre")
|
||||
.ajouter("chocolat")
|
||||
.melanger()
|
||||
.repeter(2, function (recette) {
|
||||
recette.melanger();
|
||||
})
|
||||
.cuire(12, "minutes")
|
||||
.servir();
|
||||
|
||||
const defi = CookieCode.defi("Cookie avec boucle", {
|
||||
ingredientsMinimum: 3,
|
||||
actionsObligatoires: ["melanger", "cuire", "servir"],
|
||||
boucleObligatoire: true,
|
||||
terminerParServir: true,
|
||||
interdireErreurs: true
|
||||
});
|
||||
|
||||
const verification = CookieCode.verifier(recette, defi);
|
||||
console.log(verification.toText());
|
||||
```
|
||||
|
||||
Sortie possible :
|
||||
|
||||
```txt
|
||||
Defi : Cookie avec boucle
|
||||
Resultat : reussi
|
||||
✅ Tu as utilise 3 ingredient(s).
|
||||
✅ Action trouvee : melanger.
|
||||
✅ Action trouvee : cuire.
|
||||
✅ Action trouvee : servir.
|
||||
✅ Tu as utilise une boucle avec repeter.
|
||||
✅ Ta recette se termine par servir.
|
||||
✅ Aucune erreur logique detectee.
|
||||
```
|
||||
|
||||
Une recette peut aussi etre analysee directement :
|
||||
|
||||
```js
|
||||
const rapport = recette.analyser();
|
||||
|
||||
console.log(rapport.ingredients);
|
||||
console.log(rapport.actions);
|
||||
console.log(rapport.utiliseBoucle);
|
||||
console.log(rapport.erreurs);
|
||||
```
|
||||
|
||||
## Cartes pedagogiques conseillees
|
||||
|
||||
| Type de carte | Mots cles | Notion |
|
||||
|
||||
Vendored
+186
-1
@@ -381,6 +381,47 @@
|
||||
return lignes;
|
||||
};
|
||||
|
||||
Recette.prototype.analyser = function () {
|
||||
var actions = this.notions.fonctions.slice();
|
||||
var etapesAPlates = aplatirEtapes(this.etapes);
|
||||
var derniereAction =
|
||||
etapesAPlates.length > 0
|
||||
? actionDeTexte(etapesAPlates[etapesAPlates.length - 1].texte)
|
||||
: "";
|
||||
var erreurs = [];
|
||||
|
||||
if (this.etapes.length === 0) {
|
||||
erreurs.push("Ta recette est vide : ajoute au moins une etape.");
|
||||
}
|
||||
|
||||
if (this.etapes.length > 0 && derniereAction !== "servir") {
|
||||
erreurs.push("Ta recette ne se termine pas par servir.");
|
||||
}
|
||||
|
||||
etapesAPlates.forEach((etape, index) => {
|
||||
if (
|
||||
actionDeTexte(etape.texte) === "servir" &&
|
||||
index !== etapesAPlates.length - 1
|
||||
) {
|
||||
erreurs.push("Tu as servi avant la fin de la recette.");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
nom: this.nom,
|
||||
ingredients: this.notions.variables.slice(),
|
||||
variables: this.notions.variables.slice(),
|
||||
materiels: this.notions.constantes.slice(),
|
||||
constantes: this.notions.constantes.slice(),
|
||||
actions: actions,
|
||||
nombreEtapes: etapesAPlates.length,
|
||||
utiliseBoucle: this.notions.boucles,
|
||||
utiliseCondition: this.notions.conditions,
|
||||
termineParServir: derniereAction === "servir",
|
||||
erreurs: erreurs,
|
||||
};
|
||||
};
|
||||
|
||||
Recette.prototype._lignesNotions = function () {
|
||||
var lignes = [
|
||||
"",
|
||||
@@ -446,6 +487,148 @@
|
||||
return html;
|
||||
};
|
||||
|
||||
function aplatirEtapes(etapes) {
|
||||
var resultat = [];
|
||||
etapes.forEach((etape) => {
|
||||
resultat.push(etape);
|
||||
if (etape.enfants && etape.enfants.length > 0) {
|
||||
resultat = resultat.concat(aplatirEtapes(etape.enfants));
|
||||
}
|
||||
});
|
||||
return resultat;
|
||||
}
|
||||
|
||||
function actionDeTexte(texteEtape) {
|
||||
var index;
|
||||
var valeur = texte(texteEtape).toLowerCase();
|
||||
var actions = [
|
||||
"prendre",
|
||||
"utiliser",
|
||||
"ajouter",
|
||||
"retirer",
|
||||
"couper",
|
||||
"melanger",
|
||||
"verser",
|
||||
"chauffer",
|
||||
"cuire",
|
||||
"attendre",
|
||||
"repeter",
|
||||
"si",
|
||||
"servir",
|
||||
];
|
||||
|
||||
for (index = 0; index < actions.length; index += 1) {
|
||||
if (valeur.indexOf(actions[index]) === 0) {
|
||||
return actions[index];
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function defi(nom, regles) {
|
||||
if (!nom && nom !== 0) {
|
||||
throw new Error("CookieCode : un defi doit avoir un nom.");
|
||||
}
|
||||
|
||||
return {
|
||||
type: "defi",
|
||||
nom: texte(nom),
|
||||
regles: regles || {},
|
||||
};
|
||||
}
|
||||
|
||||
function verifier(recetteAVerifier, defiAVerifier) {
|
||||
var rapport = recetteAVerifier.analyser();
|
||||
var regles = defiAVerifier?.regles ? defiAVerifier.regles : {};
|
||||
var messages = [];
|
||||
var reussi = true;
|
||||
|
||||
function valider(condition, succes, echec) {
|
||||
if (condition) {
|
||||
messages.push("✅ " + succes);
|
||||
} else {
|
||||
reussi = false;
|
||||
messages.push("❌ " + echec);
|
||||
}
|
||||
}
|
||||
|
||||
if (regles.ingredientsMinimum !== undefined) {
|
||||
valider(
|
||||
rapport.ingredients.length >= regles.ingredientsMinimum,
|
||||
"Tu as utilise " + rapport.ingredients.length + " ingredient(s).",
|
||||
"Il faut au moins " + regles.ingredientsMinimum + " ingredient(s).",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.materielsMinimum !== undefined) {
|
||||
valider(
|
||||
rapport.materiels.length >= regles.materielsMinimum,
|
||||
"Tu as utilise " + rapport.materiels.length + " materiel(s).",
|
||||
"Il faut au moins " + regles.materielsMinimum + " materiel(s).",
|
||||
);
|
||||
}
|
||||
|
||||
(regles.actionsObligatoires || []).forEach((action) => {
|
||||
valider(
|
||||
rapport.actions.indexOf(action) !== -1,
|
||||
"Action trouvee : " + action + ".",
|
||||
"Action manquante : " + action + ".",
|
||||
);
|
||||
});
|
||||
|
||||
if (regles.boucleObligatoire) {
|
||||
valider(
|
||||
rapport.utiliseBoucle,
|
||||
"Tu as utilise une boucle avec repeter.",
|
||||
"Il manque une boucle : essaie avec .repeter().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.conditionObligatoire) {
|
||||
valider(
|
||||
rapport.utiliseCondition,
|
||||
"Tu as utilise une condition avec si.",
|
||||
"Il manque une condition : essaie avec .si().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.terminerParServir) {
|
||||
valider(
|
||||
rapport.termineParServir,
|
||||
"Ta recette se termine par servir.",
|
||||
"Ta recette doit se terminer par .servir().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.interdireErreurs) {
|
||||
valider(
|
||||
rapport.erreurs.length === 0,
|
||||
"Aucune erreur logique detectee.",
|
||||
rapport.erreurs.join(" ") || "Une erreur logique a ete detectee.",
|
||||
);
|
||||
}
|
||||
|
||||
if (messages.length === 0) {
|
||||
messages.push("ℹ️ Aucun critere de defi a verifier.");
|
||||
}
|
||||
|
||||
return {
|
||||
reussi: reussi,
|
||||
nom: defiAVerifier ? defiAVerifier.nom : "Defi",
|
||||
messages: messages,
|
||||
rapport: rapport,
|
||||
toText: function () {
|
||||
return [
|
||||
"Defi : " + this.nom,
|
||||
this.reussi ? "Resultat : reussi" : "Resultat : a ameliorer",
|
||||
]
|
||||
.concat(this.messages)
|
||||
.join("\n");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function etapesHTML(etapes) {
|
||||
var html = "<ol>";
|
||||
etapes.forEach((etape) => {
|
||||
@@ -470,7 +653,9 @@
|
||||
materiel: materiel,
|
||||
constante: constante,
|
||||
preparation: preparation,
|
||||
version: "0.1.0",
|
||||
defi: defi,
|
||||
verifier: verifier,
|
||||
version: "0.2.0",
|
||||
};
|
||||
|
||||
return CookieCode;
|
||||
|
||||
+11
-1
@@ -3,7 +3,7 @@ CookieCode.configurer({
|
||||
afficherNotions: true,
|
||||
});
|
||||
|
||||
CookieCode.recette("Cookie au chocolat")
|
||||
const recette = CookieCode.recette("Cookie au chocolat")
|
||||
.prendre("farine", 200, "g")
|
||||
.prendre("sucre", 100, "g")
|
||||
.prendre("chocolat", 150, "g")
|
||||
@@ -17,3 +17,13 @@ CookieCode.recette("Cookie au chocolat")
|
||||
})
|
||||
.cuire(12, "minutes")
|
||||
.servir();
|
||||
|
||||
const defi = CookieCode.defi("Cookie avec boucle", {
|
||||
ingredientsMinimum: 3,
|
||||
actionsObligatoires: ["melanger", "cuire", "servir"],
|
||||
boucleObligatoire: true,
|
||||
terminerParServir: true,
|
||||
interdireErreurs: true
|
||||
});
|
||||
|
||||
console.log(CookieCode.verifier(recette, defi).toText());
|
||||
|
||||
@@ -24,3 +24,15 @@ Feature: Apprendre l'algorithmique avec CookieCode
|
||||
Then la recette affiche l'etape conditionnelle
|
||||
And les actions du bloc conditionnel sont affichees
|
||||
And la lecture algorithmique indique que si est une condition
|
||||
|
||||
Scenario: Verifier un defi ludique
|
||||
Given un defi demande au moins trois ingredients, une boucle et une fin avec servir
|
||||
When l'apprenant verifie une recette qui respecte ces criteres
|
||||
Then CookieCode indique que le defi est reussi
|
||||
And CookieCode affiche des messages de validation positifs
|
||||
|
||||
Scenario: Aider a ameliorer une recette incomplete
|
||||
Given un defi demande au moins trois ingredients, une boucle et une fin avec servir
|
||||
When l'apprenant verifie une recette incomplete
|
||||
Then CookieCode indique que le resultat est a ameliorer
|
||||
And CookieCode explique les criteres manquants
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cookiecode",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "Microlibrairie educative pour apprendre l'algorithmique avec des recettes de cuisine.",
|
||||
"main": "dist/cookiecode.js",
|
||||
"scripts": {
|
||||
|
||||
+186
-1
@@ -381,6 +381,47 @@
|
||||
return lignes;
|
||||
};
|
||||
|
||||
Recette.prototype.analyser = function () {
|
||||
var actions = this.notions.fonctions.slice();
|
||||
var etapesAPlates = aplatirEtapes(this.etapes);
|
||||
var derniereAction =
|
||||
etapesAPlates.length > 0
|
||||
? actionDeTexte(etapesAPlates[etapesAPlates.length - 1].texte)
|
||||
: "";
|
||||
var erreurs = [];
|
||||
|
||||
if (this.etapes.length === 0) {
|
||||
erreurs.push("Ta recette est vide : ajoute au moins une etape.");
|
||||
}
|
||||
|
||||
if (this.etapes.length > 0 && derniereAction !== "servir") {
|
||||
erreurs.push("Ta recette ne se termine pas par servir.");
|
||||
}
|
||||
|
||||
etapesAPlates.forEach((etape, index) => {
|
||||
if (
|
||||
actionDeTexte(etape.texte) === "servir" &&
|
||||
index !== etapesAPlates.length - 1
|
||||
) {
|
||||
erreurs.push("Tu as servi avant la fin de la recette.");
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
nom: this.nom,
|
||||
ingredients: this.notions.variables.slice(),
|
||||
variables: this.notions.variables.slice(),
|
||||
materiels: this.notions.constantes.slice(),
|
||||
constantes: this.notions.constantes.slice(),
|
||||
actions: actions,
|
||||
nombreEtapes: etapesAPlates.length,
|
||||
utiliseBoucle: this.notions.boucles,
|
||||
utiliseCondition: this.notions.conditions,
|
||||
termineParServir: derniereAction === "servir",
|
||||
erreurs: erreurs,
|
||||
};
|
||||
};
|
||||
|
||||
Recette.prototype._lignesNotions = function () {
|
||||
var lignes = [
|
||||
"",
|
||||
@@ -446,6 +487,148 @@
|
||||
return html;
|
||||
};
|
||||
|
||||
function aplatirEtapes(etapes) {
|
||||
var resultat = [];
|
||||
etapes.forEach((etape) => {
|
||||
resultat.push(etape);
|
||||
if (etape.enfants && etape.enfants.length > 0) {
|
||||
resultat = resultat.concat(aplatirEtapes(etape.enfants));
|
||||
}
|
||||
});
|
||||
return resultat;
|
||||
}
|
||||
|
||||
function actionDeTexte(texteEtape) {
|
||||
var index;
|
||||
var valeur = texte(texteEtape).toLowerCase();
|
||||
var actions = [
|
||||
"prendre",
|
||||
"utiliser",
|
||||
"ajouter",
|
||||
"retirer",
|
||||
"couper",
|
||||
"melanger",
|
||||
"verser",
|
||||
"chauffer",
|
||||
"cuire",
|
||||
"attendre",
|
||||
"repeter",
|
||||
"si",
|
||||
"servir",
|
||||
];
|
||||
|
||||
for (index = 0; index < actions.length; index += 1) {
|
||||
if (valeur.indexOf(actions[index]) === 0) {
|
||||
return actions[index];
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function defi(nom, regles) {
|
||||
if (!nom && nom !== 0) {
|
||||
throw new Error("CookieCode : un defi doit avoir un nom.");
|
||||
}
|
||||
|
||||
return {
|
||||
type: "defi",
|
||||
nom: texte(nom),
|
||||
regles: regles || {},
|
||||
};
|
||||
}
|
||||
|
||||
function verifier(recetteAVerifier, defiAVerifier) {
|
||||
var rapport = recetteAVerifier.analyser();
|
||||
var regles = defiAVerifier?.regles ? defiAVerifier.regles : {};
|
||||
var messages = [];
|
||||
var reussi = true;
|
||||
|
||||
function valider(condition, succes, echec) {
|
||||
if (condition) {
|
||||
messages.push("✅ " + succes);
|
||||
} else {
|
||||
reussi = false;
|
||||
messages.push("❌ " + echec);
|
||||
}
|
||||
}
|
||||
|
||||
if (regles.ingredientsMinimum !== undefined) {
|
||||
valider(
|
||||
rapport.ingredients.length >= regles.ingredientsMinimum,
|
||||
"Tu as utilise " + rapport.ingredients.length + " ingredient(s).",
|
||||
"Il faut au moins " + regles.ingredientsMinimum + " ingredient(s).",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.materielsMinimum !== undefined) {
|
||||
valider(
|
||||
rapport.materiels.length >= regles.materielsMinimum,
|
||||
"Tu as utilise " + rapport.materiels.length + " materiel(s).",
|
||||
"Il faut au moins " + regles.materielsMinimum + " materiel(s).",
|
||||
);
|
||||
}
|
||||
|
||||
(regles.actionsObligatoires || []).forEach((action) => {
|
||||
valider(
|
||||
rapport.actions.indexOf(action) !== -1,
|
||||
"Action trouvee : " + action + ".",
|
||||
"Action manquante : " + action + ".",
|
||||
);
|
||||
});
|
||||
|
||||
if (regles.boucleObligatoire) {
|
||||
valider(
|
||||
rapport.utiliseBoucle,
|
||||
"Tu as utilise une boucle avec repeter.",
|
||||
"Il manque une boucle : essaie avec .repeter().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.conditionObligatoire) {
|
||||
valider(
|
||||
rapport.utiliseCondition,
|
||||
"Tu as utilise une condition avec si.",
|
||||
"Il manque une condition : essaie avec .si().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.terminerParServir) {
|
||||
valider(
|
||||
rapport.termineParServir,
|
||||
"Ta recette se termine par servir.",
|
||||
"Ta recette doit se terminer par .servir().",
|
||||
);
|
||||
}
|
||||
|
||||
if (regles.interdireErreurs) {
|
||||
valider(
|
||||
rapport.erreurs.length === 0,
|
||||
"Aucune erreur logique detectee.",
|
||||
rapport.erreurs.join(" ") || "Une erreur logique a ete detectee.",
|
||||
);
|
||||
}
|
||||
|
||||
if (messages.length === 0) {
|
||||
messages.push("ℹ️ Aucun critere de defi a verifier.");
|
||||
}
|
||||
|
||||
return {
|
||||
reussi: reussi,
|
||||
nom: defiAVerifier ? defiAVerifier.nom : "Defi",
|
||||
messages: messages,
|
||||
rapport: rapport,
|
||||
toText: function () {
|
||||
return [
|
||||
"Defi : " + this.nom,
|
||||
this.reussi ? "Resultat : reussi" : "Resultat : a ameliorer",
|
||||
]
|
||||
.concat(this.messages)
|
||||
.join("\n");
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function etapesHTML(etapes) {
|
||||
var html = "<ol>";
|
||||
etapes.forEach((etape) => {
|
||||
@@ -470,7 +653,9 @@
|
||||
materiel: materiel,
|
||||
constante: constante,
|
||||
preparation: preparation,
|
||||
version: "0.1.0",
|
||||
defi: defi,
|
||||
verifier: verifier,
|
||||
version: "0.2.0",
|
||||
};
|
||||
|
||||
return CookieCode;
|
||||
|
||||
+46
-2
@@ -1,7 +1,7 @@
|
||||
const assert = require("assert");
|
||||
const CookieCode = require("../dist/cookiecode.js");
|
||||
|
||||
CookieCode.configurer({ sortie: null, afficherNotions: true });
|
||||
CookieCode.configurer({ sortie: "test", afficherNotions: true });
|
||||
|
||||
const texteRecette = CookieCode.recette("Cookie au chocolat")
|
||||
.prendre("farine", 200, "g")
|
||||
@@ -48,6 +48,50 @@ assert.ok(!html.includes("<img src=x"));
|
||||
assert.ok(html.includes("<script>alert"));
|
||||
assert.ok(html.includes("<img src=x"));
|
||||
|
||||
assert.strictEqual(CookieCode.version, "0.1.0");
|
||||
const recetteDefi = CookieCode.recette("Cookie defi")
|
||||
.prendre("farine", 200, "g")
|
||||
.prendre("sucre", 100, "g")
|
||||
.prendre("chocolat", 150, "g")
|
||||
.ajouter("farine")
|
||||
.ajouter("sucre")
|
||||
.ajouter("chocolat")
|
||||
.melanger()
|
||||
.repeter(2, (recette) => {
|
||||
recette.melanger();
|
||||
})
|
||||
.cuire(12, "minutes")
|
||||
.servir();
|
||||
|
||||
const rapport = recetteDefi.analyser();
|
||||
assert.deepStrictEqual(rapport.ingredients, ["farine", "sucre", "chocolat"]);
|
||||
assert.ok(rapport.actions.includes("repeter"));
|
||||
assert.strictEqual(rapport.utiliseBoucle, true);
|
||||
assert.strictEqual(rapport.termineParServir, true);
|
||||
assert.deepStrictEqual(rapport.erreurs, []);
|
||||
|
||||
const defiCookie = CookieCode.defi("Cookie avec boucle", {
|
||||
ingredientsMinimum: 3,
|
||||
actionsObligatoires: ["melanger", "cuire", "servir"],
|
||||
boucleObligatoire: true,
|
||||
terminerParServir: true,
|
||||
interdireErreurs: true,
|
||||
});
|
||||
|
||||
const verification = CookieCode.verifier(recetteDefi, defiCookie);
|
||||
assert.strictEqual(verification.reussi, true);
|
||||
assert.match(verification.toText(), /Resultat : reussi/);
|
||||
assert.match(verification.toText(), /✅ Tu as utilise 3 ingredient\(s\)\./);
|
||||
|
||||
const recetteIncomplete = CookieCode.recette("Cookie incomplet")
|
||||
.prendre("farine", 200, "g")
|
||||
.melanger();
|
||||
|
||||
const verificationIncomplete = CookieCode.verifier(recetteIncomplete, defiCookie);
|
||||
assert.strictEqual(verificationIncomplete.reussi, false);
|
||||
assert.match(verificationIncomplete.toText(), /❌ Il faut au moins 3 ingredient\(s\)\./);
|
||||
assert.match(verificationIncomplete.toText(), /❌ Il manque une boucle : essaie avec \.repeter\(\)\./);
|
||||
assert.match(verificationIncomplete.toText(), /❌ Ta recette doit se terminer par \.servir\(\)\./);
|
||||
|
||||
assert.strictEqual(CookieCode.version, "0.2.0");
|
||||
|
||||
console.log("CookieCode tests: OK");
|
||||
|
||||
Reference in New Issue
Block a user