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;