diff --git a/HTML/Assets/Detectors/Lumilo/Critical_Struggle/critical_struggle.js b/HTML/Assets/Detectors/Lumilo/Critical_Struggle/critical_struggle.js
index c2ac14e..91e446c 100644
--- a/HTML/Assets/Detectors/Lumilo/Critical_Struggle/critical_struggle.js
+++ b/HTML/Assets/Detectors/Lumilo/Critical_Struggle/critical_struggle.js
@@ -22,7 +22,7 @@ var attemptWindow;
var skillLevelsAttempts;
//declare and/or initialize any other custom global variables for this detector here...
-var stepCounter;
+var stepCounter = {};
var help_model_output;
var help_variables = {"lastAction": "null",
"lastActionTime": "",
@@ -36,8 +36,8 @@ var timerId; var timerId2; var timerId3; var timerId4; var timerId5;
var windowSize = 7;
var threshold = 1;
var wheelSpinningAttemptThreshold = 10; //following Beck and Gong's wheel-spinning work
-var errorThreshold = 3; //currently arbitrary
-var newStepThreshold = 3; //currently arbitrary
+var errorThreshold = 2; //currently arbitrary
+var newStepThreshold = 1; //currently arbitrary
var familiarityThreshold = 0.4;
var senseOfWhatToDoThreshold = 0.6;
var hintIsHelpfulPlaceholder = true; //currently a dummy value (assumption that hint is always helpful...)
@@ -248,8 +248,10 @@ function updateHistory(e){
function updateSkillLevelsAttempts(e, rawSkills, currStepCount){
for (var skill in rawSkills) {
- if( (rawSkills[skill].name in skillLevelsAttempts) && (currStepCount==1) ){
- skillLevelsAttempts[rawSkills[skill].name][0] += 1;
+ if( rawSkills[skill].name in skillLevelsAttempts ){
+ if(currStepCount==1){
+ skillLevelsAttempts[rawSkills[skill].name][0] += 1;
+ }
skillLevelsAttempts[rawSkills[skill].name][1] = parseFloat(rawSkills[skill].pKnown);
}
else{
diff --git a/HTML/Assets/Detectors/Lumilo/System_Misuse/system_misuse.js b/HTML/Assets/Detectors/Lumilo/System_Misuse/system_misuse.js
index 14a7d1d..7900963 100644
--- a/HTML/Assets/Detectors/Lumilo/System_Misuse/system_misuse.js
+++ b/HTML/Assets/Detectors/Lumilo/System_Misuse/system_misuse.js
@@ -15,30 +15,238 @@ var detector_output = {name: variableName,
};
var mailer;
-//initialize any custom global variables for this detector here
-var prevStep = ""
-var windowSize = 10; //arbitrary: need to tune
-var gamingThreshold = 5; //arbitrary: need to tune
-var hintAbuseThreshold = 5; //arbitrary: need to tune
-var attemptWindow = Array.apply(null, Array(windowSize)).map(Number.prototype.valueOf,0);
+//declare any custom global variables that will be initialized
+//based on "remembered" values across problem boundaries, here
+// (initialize these at the bottom of this file, inside of self.onmessage)
+var attemptWindow;
+
+//declare and/or initialize any other custom global variables for this detector here...
+var stepCounter = {};
+var help_model_output;
+var help_variables = {"lastAction": "null",
+ "lastActionTime": "",
+ "seenAllHints": {},
+ "lastHintLength": "",
+ "lastSenseOfWhatToDo": false
+ };
+var timerId; var timerId2; var timerId3; var timerId4; var timerId5;
+//
+//[optional] single out TUNABLE PARAMETERS below
+var windowSize = 7; //arbitrary: need to tune
+var gamingThreshold = 1; //arbitrary: need to tune
+var hintAbuseThreshold = 1; //arbitrary: need to tune
+var errorThreshold = 2; //currently arbitrary
+var newStepThreshold = 1; //currently arbitrary
+var familiarityThreshold = 0.4;
+var senseOfWhatToDoThreshold = 0.6;
+var hintIsHelpfulPlaceholder = true; //currently a dummy value (assumption that hint is always helpful...)
+
+
+//
+//###############################
+//##### Help model ######
+//###############################
+//###############################
+//
+
+//non-controversial
+function lastActionIsHint(e){
+ if (help_variables.lastAction == "hint"){return true;}
+ else{return false;}
+}
+function lastActionIsError(e){
+ if (help_variables.lastAction == "error"){return true;}
+ else{return false;}
+}
+function seenAllHintLevels(e){
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "hint"){
+ if (e.data.tutor_data.selection in help_variables.seenAllHints){
+ return help_variables.seenAllHints[e.data.tutor_data.selection];
+ }
+ else{return false;}
+ }
+ else{
+ if (e.data.tool_data.selection in help_variables.seenAllHints){
+ return help_variables.seenAllHints[e.data.tool_data.selection];
+ }
+ else{return false;}
+ }
+}
+function isCorrect(e){
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "correct"){return true;}
+ else{return false;}
+}
+
+function secondsSinceLastAction(e){
+ var currTime = new Date();
+ diff = currTime.getTime() - help_variables.lastActionTime.getTime();
+ console.log("time elapsed: ", diff/1000)
+ return (diff / 1000);
+}
+
+//less controversial
+function isDeliberate(e){
+ var hintThreshold = (help_variables.lastHintLength/600)*60;
+ if (lastActionIsError(e)){
+ return (secondsSinceLastAction(e) > errorThreshold);
+ }
+ else if (lastActionIsHint(e)){
+ return (secondsSinceLastAction(e) > hintThreshold);
+ }
+ else{
+ return (secondsSinceLastAction(e) > newStepThreshold);
+ }
+}
+//more controversial...
+function isFamiliar(e){
+ var rawSkills = e.data.tutor_data.skills;
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ if (parseFloat(rawSkills[property].pKnown)<=familiarityThreshold){
+ return false;
+ }
+ }
+ }
+ return true;
+}
-function frequent_gaming(){
+function isLowSkillStep_All(e){
+ var rawSkills = e.data.tutor_data.skills;
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ if (parseFloat(rawSkills[property].pKnown)>=familiarityThreshold){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+function isLowSkillStep_Some(e){
+ var rawSkills = e.data.tutor_data.skills;
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ if (parseFloat(rawSkills[property].pKnown)<=familiarityThreshold){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+function hintIsHelpful(e){
+ return hintIsHelpfulPlaceholder;
+}
+function lastActionUnclearFix(e){
+ if (help_variables.lastSenseOfWhatToDo == false){return true;}
+ else{return false;}
+}
+function senseOfWhatToDo(e){
+ var sel = e.data.tutor_data.selection;
+ var rawSkills = e.data.tutor_data.skills;
+ for (var property in rawSkills) {
+ if (rawSkills.hasOwnProperty(property)) {
+ if (parseFloat(rawSkills[property].pKnown)<=senseOfWhatToDoThreshold){
+ return false;
+ }
+ }
+ }
+ return true;
}
-function cognitive_gaming_detector(){
+//evaluation of each step
+function evaluateAction(e){
+ var sel = e.data.tutor_data.selection;
+ var outcome = e.data.tutor_data.action_evaluation.toLowerCase();
+
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "hint"){
+ console.log("isHint")
+ if (isDeliberate(e)){
+ console.log("isDeliberate")
+ if (!seenAllHintLevels(e) &&
+ (!isFamiliar(e)
+ || (lastActionIsError(e) && lastActionUnclearFix(e))
+ || (lastActionIsHint(e) && !hintIsHelpful(e))) ){
+ return "preferred/ask hint";
+ }
+ else if ( (isFamiliar(e) && !senseOfWhatToDo(e) )
+ || (lastActionIsHint(e)) ){
+ return "acceptable/ask hint";
+ }
+ else{
+ return "not acceptable/hint abuse";
+ }
+
+ }
+ else{
+ console.log("not deliberate")
+ return "not acceptable/hint abuse";
+ }
+
+ }
+ else{
+ if (isDeliberate(e)){
+ if ( (isFamiliar(e) && (!(lastActionIsError(e) && lastActionUnclearFix(e))) )
+ || (lastActionIsHint(e) && hintIsHelpful(e))
+ ){
+ return "preferred/try step";
+ }
+ else if (seenAllHintLevels(e) &&
+ (!(lastActionIsError(e) && lastActionUnclearFix(e))) ){
+ return "preferred/try step";
+ }
+ else if (isCorrect(e)){
+ return "acceptable/try step";
+ }
+ else if (seenAllHintLevels(e)){
+ if (lastActionIsError(e) && lastActionUnclearFix(e)){
+ return "ask teacher for help/try step";
+ }
+ }
+ else{
+ return "not acceptable/hint avoidance";
+ }
+ }
+ else{
+ return "not acceptable/not deliberate";
+ }
+ }
}
+function updateHistory(e){
+ help_variables.lastActionTime = new Date();
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "hint"){
+ help_variables.lastAction = "hint";
+ help_variables.lastHintLength = e.data.tutor_data.tutor_advice.split(' ').length;
+ if (help_variables.seenAllHints[e.data.tutor_data.selection] != true){
+ help_variables.seenAllHints[e.data.tutor_data.selection] = (e.data.tutor_data.current_hint_number == e.data.tutor_data.total_hints_available);
+ }
+ }
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "incorrect"){
+ help_variables.lastAction = "error";
+ }
+ if (e.data.tutor_data.action_evaluation.toLowerCase() == "correct"){
+ help_variables.lastAction = "correct";
+ }
+
+ help_variables.lastSenseOfWhatToDo = senseOfWhatToDo(e);
+
+}
-function frequent_hint_abuse(){
+function is_gaming(e){
+ return false;
}
-function frequent_not_deliberate(){
+function is_abusing_hints(help_model_output){
+ return (help_model_output == "not acceptable/hint abuse");
+}
+function is_not_deliberate(help_model_output){
+ return (help_model_output == "not acceptable/not deliberate");
}
@@ -279,12 +487,25 @@ function receive_transaction( e ){
detector_output.step_id = e.data.tutor_data.step_id;
//custom processing (insert code here)
- //
- //
- //
- //
- //
+ if (help_variables.lastAction!="null"){
+ help_model_output = evaluateAction(e);
+ }
+ else{
+ help_model_output = "preferred"; //first action in whole tutor is set to "preferred" by default
+ }
+
+
+ var isGaming = is_gaming(e);
+ var isAbusingHints = is_abusing_hints(help_model_output);
+ var isNotDeliberate = is_not_deliberate(help_model_output);
+
+ attemptWindow.shift();
+ attemptWindow.push( (isGaming || isAbusingHints || isWheelSpinning) ? 1 : 0 );
+ var sumAskTeacherForHelp = attemptWindow.reduce(function(pv, cv) { return pv + cv; }, 0);
+ console.log(attemptWindow);
+
+ updateHistory(e);
}
@@ -295,11 +516,60 @@ function receive_transaction( e ){
detector_output.transaction_id = e.data.transaction_id;
//custom processing (insert code here)
- //
- //
- //
- //
- //
+ if (detector_output.value=="0, > 0 s" && (sumAskTeacherForHelp >= threshold)){
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.value = "1, > 25 s"
+ detector_output.time = new Date();
+
+ timerId = setTimeout(function() {
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.value = "1, > 45 s"
+ detector_output.time = new Date();
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output); },
+ 20000)
+ timerId2 = setTimeout(function() {
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.value = "1, > 1 min"
+ detector_output.time = new Date();
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output); },
+ 35000)
+ timerId3 = setTimeout(function() {
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.value = "1, > 2 min"
+ detector_output.time = new Date();
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output); },
+ 95000)
+ timerId4 = setTimeout(function() {
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.value = "1, > 5 min"
+ detector_output.time = new Date();
+ mailer.postMessage(detector_output);
+ postMessage(detector_output);
+ console.log("output_data = ", detector_output); },
+ 275000)
+
+ }
+ else if (detector_output.value!="0, > 0 s" && (sumAskTeacherForHelp >= threshold)){
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.time = new Date();
+ }
+ else{
+ detector_output.value = "0, > 0 s";
+ detector_output.history = JSON.stringify([attemptWindow]);
+ detector_output.time = new Date();
+
+ clearTimeout(timerId);
+ clearTimeout(timerId2);
+ clearTimeout(timerId3);
+ clearTimeout(timerId4);
+ }
+
mailer.postMessage(detector_output);
postMessage(detector_output);
@@ -324,20 +594,41 @@ self.onmessage = function ( e ) {
}
}
- //optional: Below, specify conditions under which a detector
+ //optional: In "detectorForget", specify conditions under which a detector
//should NOT remember their most recent value and history (using the variable "detectorForget").
//(e.g., setting the condition to "true" will mean that the detector
// will always be reset between problems... and setting the condition to "false"
// means that the detector will never be reset between problems)
//
+ detectorForget = false;
//
//
- detectorForget = false;
if (detectorForget){
detector_output.history = "";
detector_output.value = 0;
}
+
+
+ //optional: If any global variables are based on remembered values across problem boundaries,
+ // these initializations should be written here
+ //
+ //
+ if (detector_output.history == "" || detector_output.history == null){
+ //in the event that the detector history is empty,
+ //initialize variables to your desired 'default' values
+ //
+ attemptWindow = Array.apply(null, Array(windowSize)).map(Number.prototype.valueOf,0);
+ }
+ else{
+ //if the detector history is not empty, you can access it via:
+ // JSON.parse(detector_output.history);
+ //...and initialize your variables to your desired values, based on
+ //this history
+ //
+ var all_history = JSON.parse(detector_output.history);
+ attemptWindow = all_history[0];
+ }
break;
default:
break;