// MAVE functions for evaluating free-response answers var MAVE = function() { // PRIVATE VARS AND FUNCTIONS var current_question_id = null; var current_options = null; var current_submitted_answer = null; var current_submission_id = null; var current_submission_timestamp = null; var previous_answers = []; var default_options = { max_similarity_score: 0.6 , min_prototype_score: 0.075 , min_valid_score: 0.075 , min_valid_avg_score: 0.075 , max_question_text_score: 0.5 } function round(n) { n = Math.round(n*1000) / 1000; return n; } function get_option(key) { if (current_options != null && current_options[key] != null) { return current_options[key]; } else { return default_options[key]; } } // questions can specify a "_validity_criterion" and/or "_plagiarism_criterion" // of 1-5. Translate these into options here. // note that these will override any explicit value of min_valid_score, etc // that might also be sent in (so don't use both in a question) function register_questions_criteria() { var v = current_options.validity_criterion; if (v == 1) { current_options.min_prototype_score = 0.025; current_options.min_valid_score = 0.025; current_options.min_valid_avg_score = 0.025; } else if (v == 2) { current_options.min_prototype_score = 0.050; current_options.min_valid_score = 0.050; current_options.min_valid_avg_score = 0.050; } else if (v == 3) { // these are the defaults set above } else if (v == 4) { current_options.min_prototype_score = 0.100; current_options.min_valid_score = 0.100; current_options.min_valid_avg_score = 0.100; } else if (v == 5) { current_options.min_prototype_score = 0.125; current_options.min_valid_score = 0.125; current_options.min_valid_avg_score = 0.125; } var p = current_options.plagiarism_criterion; if (p == 1) { current_options.max_similarity_score = 0.8; } else if (p == 2) { current_options.max_similarity_score = 0.7; } else if (p == 3) { // this is the default set above } else if (p == 4) { current_options.max_similarity_score = 0.5; } else if (p == 5) { current_options.max_similarity_score = 0.4; } } // return true if the answer is close to another answer already given // false means it's *not* a plagiarism function plagiarism_check(vd) { // item is a plagiarism if... // maxSimilarityScore (calculated across all answers either verified or submitted as valid) is > max_similarity_score previous_answers.push(current_submitted_answer); return false; } // return true if the answer should be considered "valid" -- an honest attempt // to answer the question function valid_check(vd) { // to be valid... if ( // similarity with prototype answer must be at least x // OR max similarity with some other valid answer must be at least x // OR average similarity with other valid answers must be at least x //(vd.prototypeScore > get_option("min_prototype_score") // similarity score for prototype // || vd.maxValidScore > get_option("min_valid_score") // max sim score for all valid answers vd.validScore > get_option("min_valid_avg_score") // avg sim score for all valids ) // AND average similarity with valid answers must be > avg similarity with invalids //&& vd.validScore > vd.invalidScore // AND it can't be too similar to the question //&& vd.questionTextScore < get_option("max_question_text_score") { return true; } else { return false } } // PUBLIC VARS AND FUNCTIONS return { public_var: 10, // possible options: // callback_fn (see validate_answer_success; this should really always be included) // options shown in default_options above // validity_criterion (1-5 -- see above) // plagiarism_criterion (1-5 -- see above) validate_answer: function(question_id, submitted_answer, options) { current_question_id = question_id; current_options = options; register_questions_criteria(); current_submitted_answer = submitted_answer; // limit length of submitted_answer? submitted_answer = submitted_answer.substr(0,1000); //var dataString = "&questionID=" + current_question_id + "&submittedAnswer=" + escape(submitted_answer); // The only two pieces of information we need to validate the answer // are the mave question id and the answer text. Create JS object // to hold those. var submission = { questionID: current_question_id, answerText: submitted_answer } // Turn submission object into JSON string var dataString = JSON.stringify(submission); Standard_Dialog.wait("Validating answer..."); // Send the submission to the validator and await answer $.ajax({ type: 'POST', url: 'https://responsevalidator.azurewebsites.net/api/Submissions', contentType: "application/json; charset=utf-8", data: dataString, success: MAVE.validate_answer_success, error: MAVE.validate_answer_error, timeout:10000, dataType: "json" }); }, validate_answer_success: function(result) { var vd = result; // close the "waiting" dialog Standard_Dialog.close(); // If there was an error... if (vd == null) { // log error result var s = "validate_answer error:\n"; for (var i in result) { s += i + ": " + result[i] + "\n"; } safe_log(s) // for now, in case of an error we assume that the answer is OK current_options.callback_fn(true); return; } // else webservice executed successfully var s = "* validate_answer success:\n" + "submission: " + current_submitted_answer + "\n" + "max valid score: " + round(vd.maxValidScore) + "\n" + "avg valid score: " + round(vd.validScore) + "\n" + "score against prototype answer: " + round(vd.prototypeScore) + "\n" + "avg invalid score: " + round(vd.invalidScore) + "\n" + "score against question text: " + round(vd.questionTextScore) + "\n" + "max similarity score (plagiarism detection): " + round(vd.maxSimilarityScore) + "\n" ; safe_log(s); // get current_submission_id and current_submission_timestamp current_submission_id = result.ID; current_submission_timestamp = result.timestamp; // If answer is too close to a previously-submitted one, tell them if (plagiarism_check(vd) == true) { html = "
The answer you entered is similar or identical to an answer that was entered previously. Please try again.
"; Standard_Dialog.open(html, {title:"Try Again", close: MAVE.invalid_revise, buttons: [{text:"OK", click: MAVE.invalid_revise_and_close}]}); // Else if answer is valid, call callback indicating that submission is valid } else if (valid_check(vd)) { // first call the webservice to "save" it MAVE.submit_answer(false) current_options.callback_fn(true); // otherwise show a modal dialog to make the student sweat... } else { html = "Did you make an honest attempt to answer the question? MAVE (what's this?) doesn't think your response is valid, but this may be because you didn't know the answer—or MAVE may have simply made a mistake.
" + " " + "