From aaf6587a1a5945802ab8fd471798c9ab20193340 Mon Sep 17 00:00:00 2001 From: flysaiah Date: Thu, 7 Apr 2016 23:20:11 -0500 Subject: [PATCH 01/21] Modified timed checking for existing components to allow for ignorable timed components --- runestone/assess/js/timed.js | 78 ++++++++++---------- runestone/assess/js/timedfitb.js | 9 ++- runestone/assess/js/timedmc.js | 18 ++++- runestone/clickableArea/js/timedclickable.js | 9 ++- runestone/dragndrop/js/timeddnd.js | 10 ++- 5 files changed, 80 insertions(+), 44 deletions(-) diff --git a/runestone/assess/js/timed.js b/runestone/assess/js/timed.js index 12148f2f7..62245f2f8 100644 --- a/runestone/assess/js/timed.js +++ b/runestone/assess/js/timed.js @@ -90,10 +90,10 @@ Timed.prototype.renderTimedAssess = function () { this.renderNavControls(); this.renderSubmitButton(); this.renderFeedbackContainer(); - + // Replace intermediate HTML with rendered HTML $(this.origElem).replaceWith(this.assessDiv); - + // check if already taken and if so show results this.tookTimedExam(); if (this.taken) { @@ -177,7 +177,7 @@ Timed.prototype.renderNavControls = function () { this.navDiv.appendChild(this.pagNavList); this.break = document.createElement("br"); this.navDiv.appendChild(this.break); - + // render the question number jump buttons this.qNumList = document.createElement("ul"); $(this.qNumList).attr("id", "pageNums"); @@ -191,18 +191,18 @@ Timed.prototype.renderNavControls = function () { $(tmpLi).addClass("active"); } tmpLi.appendChild(tmpA); - this.qNumList.appendChild(tmpLi); + this.qNumList.appendChild(tmpLi); } this.navDiv.appendChild(this.qNumList); - this.navBtnListeners(); - + this.navBtnListeners(); + $(function(){ var tenSet = $("ul#pageNums li"); for (var i = 0; i < tenSet.length; i += 10) { tenSet.slice(i, i + 10).wrapAll(""); } }); - + }; Timed.prototype.navBtnListeners = function() { @@ -210,18 +210,18 @@ Timed.prototype.navBtnListeners = function() { this.pagNavList.addEventListener("click", function (event) { if ($("div#timed_Test form input[name='group1']").is(":checked")) { $("ul#pageNums > ul > li:eq(" + this.currentQuestionIndex +")").addClass("answered"); - } + } var target = $(event.target).text(); if (target.match(/Next/)) { if ($(this.rightContainer).hasClass("disabled")) { - return; - } + return; + } this.currentQuestionIndex++; - } + } else if (target.match(/Prev/)) { if ($(this.leftContainer).hasClass("disabled")) { return; - } + } this.currentQuestionIndex--; } this.renderTimedQuestion(); @@ -241,7 +241,7 @@ Timed.prototype.navBtnListeners = function() { } for (var i = 0; i < this.qNumList.childNodes.length; i++) { for (var j = 0; j < this.qNumList.childNodes[i].childNodes.length; j++) { - $(this.qNumList.childNodes[i].childNodes[j]).removeClass("active"); + $(this.qNumList.childNodes[i].childNodes[j]).removeClass("active"); } } var target = $(event.target).text(); @@ -250,7 +250,7 @@ Timed.prototype.navBtnListeners = function() { this.renderTimedQuestion(); this.ensureButtonSafety(); }.bind(this), false); - + }; Timed.prototype.renderSubmitButton = function () { @@ -538,19 +538,21 @@ Timed.prototype.checkScore = function () { this.skippedStr = ""; this.incorrectStr = ""; // Gets the score of each problem + for (var i = 0; i < this.renderedQuestionArray.length; i++) { var correct = this.renderedQuestionArray[i].checkCorrectTimed(); - if (correct) { - this.score++; - this.correctStr = this.correctStr + (i + 1) + ", "; - + if (correct == "T") { + this.score++; + this.correctStr = this.correctStr + (i + 1) + ", "; + + } else if (correct == "F") { + this.incorrect++; + this.incorrectStr = this.incorrectStr + (i + 1) + ", "; } else if (correct === null) { this.skipped++; - this.skippedStr = this.skippedStr + (i + 1) + ", "; - + this.skippedStr = this.skippedStr + (i + 1) + ", "; } else { - this.incorrect++; - this.incorrectStr = this.incorrectStr + (i + 1) + ", "; + // ignored question; just do nothing } } // remove extra comma and space at end if any @@ -610,27 +612,27 @@ Timed.prototype.restoreFromStorage = function () { }; Timed.prototype.displayScore = function () { - + if (this.showResults) - { - // If we have the list of + { + // If we have the list of if (this.correctStr.length > 0 || this.incorrectStr.length > 0 || this.skippedStr.length > 0) { var scoreString = "Num Correct: " + this.score + ". Questions: " + this.correctStr + "
" + "Num Wrong: " + this.incorrect + ". Questions: " + this.incorrectStr + "
" + "Num Skipped: " + this.skipped + ". Questions: " + this.skippedStr + "
"; - var numQuestions = this.renderedQuestionArray.length; + var numQuestions = this.score + this.incorrect + this.skipped; var percentCorrect = (this.score / numQuestions) * 100; scoreString += "Percent Correct: " + percentCorrect + "%"; $(this.scoreDiv).html(scoreString); this.scoreDiv.style.display = "block"; } - else + else { var scoreString = "Num Correct: " + this.score + "
" + "Num Wrong: " + this.incorrect + "
" + "Num Skipped: " + this.skipped + "
"; - var numQuestions = this.renderedQuestionArray.length; + var numQuestions = this.score + this.incorrect + this.skipped var percentCorrect = (this.score / numQuestions) * 100; scoreString += "Percent Correct: " + percentCorrect + "%"; $(this.scoreDiv).html(scoreString); @@ -639,7 +641,7 @@ Timed.prototype.displayScore = function () { } this.highlightNumberedList(); }; - + Timed.prototype.highlightNumberedList = function () { var correctCount = this.correctStr; var incorrectCount = this.incorrectStr; @@ -648,24 +650,24 @@ Timed.prototype.highlightNumberedList = function () { correctCount = correctCount.replace(/ /g,'').split(','); incorrectCount = incorrectCount.replace(/ /g,'').split(','); skippedCount = skippedCount.replace(/ /g,'').split(','); - + $(function () { // This code is wrapped in a function so that it executes only after DOM has loaded var numberedBtns = $("ul#pageNums > ul > li"); if (numberedBtns.hasClass("answered")) { - numberedBtns.removeClass("answered"); - } + numberedBtns.removeClass("answered"); + } for (var i = 0; i < correctCount.length; i++) { - var test = parseInt(correctCount[i])-1; - numberedBtns.eq(parseInt(correctCount[i])-1).addClass("correctCount"); + var test = parseInt(correctCount[i])-1; + numberedBtns.eq(parseInt(correctCount[i])-1).addClass("correctCount"); } for (var j = 0; j < incorrectCount.length; j++) { - numberedBtns.eq(parseInt(incorrectCount[j])-1).addClass("incorrectCount"); + numberedBtns.eq(parseInt(incorrectCount[j])-1).addClass("incorrectCount"); } for (var k = 0; k < skippedCount.length; k++) { - numberedBtns.eq(parseInt(skippedCount[k])-1).addClass("skippedCount"); - } + numberedBtns.eq(parseInt(skippedCount[k])-1).addClass("skippedCount"); + } }); -}; +}; /*======================================================= diff --git a/runestone/assess/js/timedfitb.js b/runestone/assess/js/timedfitb.js index 2ed5b04c5..555ff4e1b 100644 --- a/runestone/assess/js/timedfitb.js +++ b/runestone/assess/js/timedfitb.js @@ -35,7 +35,14 @@ TimedFITB.prototype.renderTimedIcon = function (component) { TimedFITB.prototype.checkCorrectTimed = function () { // Returns if the question was correct. Used for timed assessment grading. - return this.correct; + switch (this.correct) { + case true: + return "T"; + case false: + return "F"; + default: + return null; + } }; TimedFITB.prototype.hideFeedback = function () { diff --git a/runestone/assess/js/timedmc.js b/runestone/assess/js/timedmc.js index 8a1064c4b..e37f5aa9e 100644 --- a/runestone/assess/js/timedmc.js +++ b/runestone/assess/js/timedmc.js @@ -112,11 +112,25 @@ TimedMC.prototype.checkCorrectTimedMCMA = function () { } else if (this.givenArray.length !== 0) { this.correct = false; } - return this.correct; + switch (this.correct) { + case true: + return "T"; + case false: + return "F"; + default: + return null; + } }; TimedMC.prototype.checkCorrectTimedMCMF = function () { - return this.correct; + switch (this.correct) { + case true: + return "T"; + case false: + return "F"; + default: + return null; + } }; TimedMC.prototype.checkCorrectTimed = function () { diff --git a/runestone/clickableArea/js/timedclickable.js b/runestone/clickableArea/js/timedclickable.js index 286cac4d5..aae7ac769 100644 --- a/runestone/clickableArea/js/timedclickable.js +++ b/runestone/clickableArea/js/timedclickable.js @@ -36,7 +36,14 @@ TimedClickableArea.prototype.checkCorrectTimed = function () { if (this.correctNum === 0 && this.incorrectNum === 0) { this.correct = null; } - return this.correct; + switch (this.correct) { + case true: + return "T"; + case false: + return "F"; + default: + return null; + } }; TimedClickableArea.prototype.hideFeedback = function () { diff --git a/runestone/dragndrop/js/timeddnd.js b/runestone/dragndrop/js/timeddnd.js index c3cdfca2d..49ab4959b 100644 --- a/runestone/dragndrop/js/timeddnd.js +++ b/runestone/dragndrop/js/timeddnd.js @@ -36,8 +36,14 @@ TimedDragNDrop.prototype.checkCorrectTimed = function () { if (this.unansweredNum === this.dragPairArray.length) { this.correct = null; } - - return this.correct; + switch (this.correct) { + case true: + return "T"; + case false: + return "F"; + default: + return null; + } }; TimedDragNDrop.prototype.hideFeedback = function () { From 4852acebb291a7f75bd617d67a5ccf2c252ec713 Mon Sep 17 00:00:00 2001 From: flysaiah Date: Fri, 8 Apr 2016 15:30:34 -0500 Subject: [PATCH 02/21] Removed unnecessary SA code that was causing bugs --- runestone/shortanswer/shortanswer.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/runestone/shortanswer/shortanswer.py b/runestone/shortanswer/shortanswer.py index 4b98b22f6..c902f5f63 100755 --- a/runestone/shortanswer/shortanswer.py +++ b/runestone/shortanswer/shortanswer.py @@ -41,13 +41,8 @@ def __init__(self, options): def visit_journal_node(self, node): div_id = node.journalnode_components['divid'] - back, subchapter = os.path.split(os.path.splitext(node.source.lower())[0]) - back, chapter = os.path.split(back) - content = '' components = dict(node.journalnode_components) - components.update({'divid': div_id, - 'subchapter': subchapter, - 'chapter': chapter}) + components.update({'divid': div_id}) res = TEXT % components self.body.append(res) From 7f4372fb42ed0feafb0b93dd6b22c332780d55b2 Mon Sep 17 00:00:00 2001 From: flysaiah Date: Fri, 8 Apr 2016 15:53:43 -0500 Subject: [PATCH 03/21] Fixed small syntax bug --- runestone/assess/js/timedfitb.js | 4 ++-- runestone/assess/js/timedmc.js | 4 ++-- runestone/clickableArea/js/timedclickable.js | 4 ++-- runestone/dragndrop/js/timeddnd.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runestone/assess/js/timedfitb.js b/runestone/assess/js/timedfitb.js index 555ff4e1b..44f1ff7e8 100644 --- a/runestone/assess/js/timedfitb.js +++ b/runestone/assess/js/timedfitb.js @@ -36,9 +36,9 @@ TimedFITB.prototype.renderTimedIcon = function (component) { TimedFITB.prototype.checkCorrectTimed = function () { // Returns if the question was correct. Used for timed assessment grading. switch (this.correct) { - case true: + case (true): return "T"; - case false: + case (false): return "F"; default: return null; diff --git a/runestone/assess/js/timedmc.js b/runestone/assess/js/timedmc.js index e37f5aa9e..877f31f70 100644 --- a/runestone/assess/js/timedmc.js +++ b/runestone/assess/js/timedmc.js @@ -124,9 +124,9 @@ TimedMC.prototype.checkCorrectTimedMCMA = function () { TimedMC.prototype.checkCorrectTimedMCMF = function () { switch (this.correct) { - case true: + case (true): return "T"; - case false: + case (false): return "F"; default: return null; diff --git a/runestone/clickableArea/js/timedclickable.js b/runestone/clickableArea/js/timedclickable.js index aae7ac769..526706d37 100644 --- a/runestone/clickableArea/js/timedclickable.js +++ b/runestone/clickableArea/js/timedclickable.js @@ -37,9 +37,9 @@ TimedClickableArea.prototype.checkCorrectTimed = function () { this.correct = null; } switch (this.correct) { - case true: + case (true): return "T"; - case false: + case (false): return "F"; default: return null; diff --git a/runestone/dragndrop/js/timeddnd.js b/runestone/dragndrop/js/timeddnd.js index 49ab4959b..7083a541f 100644 --- a/runestone/dragndrop/js/timeddnd.js +++ b/runestone/dragndrop/js/timeddnd.js @@ -37,9 +37,9 @@ TimedDragNDrop.prototype.checkCorrectTimed = function () { this.correct = null; } switch (this.correct) { - case true: + case (true): return "T"; - case false: + case (false): return "F"; default: return null; From 662c03d6da3cb707b1fe289ac419f95f7232e218 Mon Sep 17 00:00:00 2001 From: flysaiah Date: Fri, 8 Apr 2016 16:09:39 -0500 Subject: [PATCH 04/21] Added support for ignorable components in Timed --- runestone/assess/js/timed.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runestone/assess/js/timed.js b/runestone/assess/js/timed.js index 62245f2f8..269c81e51 100644 --- a/runestone/assess/js/timed.js +++ b/runestone/assess/js/timed.js @@ -311,6 +311,8 @@ Timed.prototype.createRenderedQuestionArray = function () { this.renderedQuestionArray.push(new TimedDragNDrop(opts)); } else if ($(tmpChild).is("[data-component=clickablearea]")) { this.renderedQuestionArray.push(new TimedClickableArea(opts)); + } else if ($(tmpChild).is("[data-component=shortanswer]")) { + this.renderedQuestionArray.push(new TimedShortAnswer(opts)); } } if (this.random) { @@ -520,7 +522,10 @@ Timed.prototype.finishAssessment = function () { Timed.prototype.submitTimedProblems = function (logFlag) { for (var i = 0; i < this.renderedQuestionArray.length; i++) { - this.renderedQuestionArray[i].processTimedSubmission(logFlag); + // Only try to check the answer if it's something automatically graded + if (this.renderedQuestionArray[i].ignoredTimedElement === undefined) { + this.renderedQuestionArray[i].processTimedSubmission(logFlag); + } } if (!this.showFeedback) { this.hideTimedFeedback(); From f791016cb672d26ad4567e9bb07203c9f70bf07e Mon Sep 17 00:00:00 2001 From: flysaiah Date: Sat, 9 Apr 2016 22:21:02 -0500 Subject: [PATCH 05/21] Added support for timed shortanswer questions --- runestone/assess/js/timed.js | 5 +- runestone/shortanswer/js/shortanswer.js | 4 +- runestone/shortanswer/js/timed_shortanswer.js | 48 +++++++++++++++++++ runestone/shortanswer/shortanswer.py | 3 +- 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 runestone/shortanswer/js/timed_shortanswer.js diff --git a/runestone/assess/js/timed.js b/runestone/assess/js/timed.js index 269c81e51..52077b3e9 100644 --- a/runestone/assess/js/timed.js +++ b/runestone/assess/js/timed.js @@ -522,10 +522,7 @@ Timed.prototype.finishAssessment = function () { Timed.prototype.submitTimedProblems = function (logFlag) { for (var i = 0; i < this.renderedQuestionArray.length; i++) { - // Only try to check the answer if it's something automatically graded - if (this.renderedQuestionArray[i].ignoredTimedElement === undefined) { - this.renderedQuestionArray[i].processTimedSubmission(logFlag); - } + this.renderedQuestionArray[i].processTimedSubmission(logFlag); } if (!this.showFeedback) { this.hideTimedFeedback(); diff --git a/runestone/shortanswer/js/shortanswer.js b/runestone/shortanswer/js/shortanswer.js index cbc452241..936462025 100644 --- a/runestone/shortanswer/js/shortanswer.js +++ b/runestone/shortanswer/js/shortanswer.js @@ -179,7 +179,9 @@ ShortAnswer.prototype.loadJournal = function () { =================================*/ $(document).ready(function () { $("[data-component=shortanswer]").each(function (index) { - saList[this.id] = new ShortAnswer({"orig": this, 'useRunestoneServices': eBookConfig.useRunestoneServices}); + if ($(this.parentNode).data("component") !== "timedAssessment") { // If this element exists within a timed component, don't render it here + saList[this.id] = new ShortAnswer({"orig": this, 'useRunestoneServices': eBookConfig.useRunestoneServices}); + } }); }); diff --git a/runestone/shortanswer/js/timed_shortanswer.js b/runestone/shortanswer/js/timed_shortanswer.js new file mode 100644 index 000000000..5a3fffdb9 --- /dev/null +++ b/runestone/shortanswer/js/timed_shortanswer.js @@ -0,0 +1,48 @@ +function TimedShortAnswer (opts) { + if (opts) { + this.timedInit(opts); + } +} +TimedShortAnswer.prototype = new ShortAnswer(); + +TimedShortAnswer.prototype.timedInit = function (opts) { + this.init(opts); + this.ignoredTimedElement = true; + this.renderTimedIcon(this.containerDiv); + this.hideButtons(); +}; + + +TimedShortAnswer.prototype.hideButtons = function () { + $(this.submitButton).hide(); + +}; + +TimedShortAnswer.prototype.renderTimedIcon = function (component) { + // renders the clock icon on timed components. The component parameter + // is the element that the icon should be appended to. + var timeIconDiv = document.createElement("div"); + var timeIcon = document.createElement("img"); + $(timeIcon).attr({ + "src": "../_static/clock.png", + "style": "width:15px;height:15px" + }); + timeIconDiv.className = "timeTip"; + timeIconDiv.title = ""; + timeIconDiv.appendChild(timeIcon); + $(component).prepend(timeIconDiv); +}; + +TimedShortAnswer.prototype.checkCorrectTimed = function () { + // Returns if the question was correct. Used for timed assessment grading. + return "I"; // we ignore this in the grading +}; + +TimedShortAnswer.prototype.hideFeedback = function () { + $(this.feedbackDiv).hide(); +}; + +TimedShortAnswer.prototype.processTimedSubmission = function () { + this.submitJournal(); + this.jTextArea.disabled = true; +}; diff --git a/runestone/shortanswer/shortanswer.py b/runestone/shortanswer/shortanswer.py index c902f5f63..817b049e9 100755 --- a/runestone/shortanswer/shortanswer.py +++ b/runestone/shortanswer/shortanswer.py @@ -24,10 +24,9 @@ def setup(app): app.add_directive('shortanswer', JournalDirective) - app.add_node(JournalNode, html=(visit_journal_node, depart_journal_node)) - app.add_javascript('shortanswer.js') + app.add_javascript('timed_shortanswer.js') TEXT = """ From d9f11fcf90f065e97369b3865ff820985bae140c Mon Sep 17 00:00:00 2001 From: flysaiah Date: Sun, 10 Apr 2016 13:27:24 -0500 Subject: [PATCH 06/21] Don't need ignoredTimed property anymore --- runestone/shortanswer/js/timed_shortanswer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/runestone/shortanswer/js/timed_shortanswer.js b/runestone/shortanswer/js/timed_shortanswer.js index 5a3fffdb9..d00f9e591 100644 --- a/runestone/shortanswer/js/timed_shortanswer.js +++ b/runestone/shortanswer/js/timed_shortanswer.js @@ -7,7 +7,6 @@ TimedShortAnswer.prototype = new ShortAnswer(); TimedShortAnswer.prototype.timedInit = function (opts) { this.init(opts); - this.ignoredTimedElement = true; this.renderTimedIcon(this.containerDiv); this.hideButtons(); }; @@ -34,7 +33,6 @@ TimedShortAnswer.prototype.renderTimedIcon = function (component) { }; TimedShortAnswer.prototype.checkCorrectTimed = function () { - // Returns if the question was correct. Used for timed assessment grading. return "I"; // we ignore this in the grading }; From 05a2f4b57f038fab826b3dd8953480efd2a7b1cb Mon Sep 17 00:00:00 2001 From: flysaiah Date: Sun, 10 Apr 2016 13:38:45 -0500 Subject: [PATCH 07/21] Updated comments --- runestone/assess/js/timedfitb.js | 3 ++- runestone/assess/js/timedmc.js | 6 ++++-- runestone/clickableArea/js/timedclickable.js | 5 +++-- runestone/dragndrop/js/timeddnd.js | 1 + runestone/shortanswer/js/timed_shortanswer.js | 1 + 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/runestone/assess/js/timedfitb.js b/runestone/assess/js/timedfitb.js index 44f1ff7e8..664fa4826 100644 --- a/runestone/assess/js/timedfitb.js +++ b/runestone/assess/js/timedfitb.js @@ -34,7 +34,7 @@ TimedFITB.prototype.renderTimedIcon = function (component) { }; TimedFITB.prototype.checkCorrectTimed = function () { - // Returns if the question was correct. Used for timed assessment grading. + // Returns if the question was correct, incorrect, or skipped (return null in the last case) switch (this.correct) { case (true): return "T"; @@ -53,6 +53,7 @@ TimedFITB.prototype.hideFeedback = function () { }; TimedFITB.prototype.processTimedSubmission = function () { + // Disable input, then evaluate component for (var i = 0; i < this.blankArray.length; i++) { this.blankArray[i].disabled = true; } diff --git a/runestone/assess/js/timedmc.js b/runestone/assess/js/timedmc.js index 877f31f70..43b406d38 100644 --- a/runestone/assess/js/timedmc.js +++ b/runestone/assess/js/timedmc.js @@ -113,9 +113,9 @@ TimedMC.prototype.checkCorrectTimedMCMA = function () { this.correct = false; } switch (this.correct) { - case true: + case (true): return "T"; - case false: + case (false): return "F"; default: return null; @@ -123,6 +123,7 @@ TimedMC.prototype.checkCorrectTimedMCMA = function () { }; TimedMC.prototype.checkCorrectTimedMCMF = function () { + // Returns if the question was correct, incorrect, or skipped (return null in the last case) switch (this.correct) { case (true): return "T"; @@ -148,6 +149,7 @@ TimedMC.prototype.hideFeedback = function () { }; TimedMC.prototype.processTimedSubmission = function (logFlag) { + // Disable input, then evaluate component for (var i = 0; i < this.optionArray.length; i++) { this.optionArray[i]["input"].disabled = true; } diff --git a/runestone/clickableArea/js/timedclickable.js b/runestone/clickableArea/js/timedclickable.js index 526706d37..c652b4324 100644 --- a/runestone/clickableArea/js/timedclickable.js +++ b/runestone/clickableArea/js/timedclickable.js @@ -32,7 +32,7 @@ TimedClickableArea.prototype.renderTimedIcon = function (component) { }; TimedClickableArea.prototype.checkCorrectTimed = function () { - // Returns if the question was correct. Used for timed assessment grading. + // Returns if the question was correct, incorrect, or skipped (return null in the last case) if (this.correctNum === 0 && this.incorrectNum === 0) { this.correct = null; } @@ -51,11 +51,12 @@ TimedClickableArea.prototype.hideFeedback = function () { }; TimedClickableArea.prototype.processTimedSubmission = function () { + // Disable input, then evaluate component for (var i = 0; i < this.clickableArray.length; i++) { $(this.clickableArray[i]).css("cursor", "initial"); this.clickableArray[i].onclick = function () { return; - } + }; } this.clickableEval(); }; diff --git a/runestone/dragndrop/js/timeddnd.js b/runestone/dragndrop/js/timeddnd.js index 7083a541f..4ea0de2ff 100644 --- a/runestone/dragndrop/js/timeddnd.js +++ b/runestone/dragndrop/js/timeddnd.js @@ -51,6 +51,7 @@ TimedDragNDrop.prototype.hideFeedback = function () { }; TimedDragNDrop.prototype.processTimedSubmission = function () { + // Disable input & evaluate component $(this.resetButton).hide(); this.dragEval(); for (var i = 0; i < this.dragPairArray.length; i++) { // No more dragging diff --git a/runestone/shortanswer/js/timed_shortanswer.js b/runestone/shortanswer/js/timed_shortanswer.js index d00f9e591..aa1484de4 100644 --- a/runestone/shortanswer/js/timed_shortanswer.js +++ b/runestone/shortanswer/js/timed_shortanswer.js @@ -41,6 +41,7 @@ TimedShortAnswer.prototype.hideFeedback = function () { }; TimedShortAnswer.prototype.processTimedSubmission = function () { + // Disable input & evaluate component this.submitJournal(); this.jTextArea.disabled = true; }; From 63a866aee19428c3aa751b29d38180096a61876c Mon Sep 17 00:00:00 2001 From: flysaiah Date: Sun, 10 Apr 2016 14:08:40 -0500 Subject: [PATCH 08/21] Fixed grading bug in FITB --- runestone/assess/js/fitb.js | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/runestone/assess/js/fitb.js b/runestone/assess/js/fitb.js index 41640494a..b69a1149d 100644 --- a/runestone/assess/js/fitb.js +++ b/runestone/assess/js/fitb.js @@ -11,26 +11,6 @@ === 6/4/15 === ==========================================*/ -/*======================================= -=== Global functions === -=== (used by more than one component) === -=======================================*/ - -var feedBack = function (elem, correct, feedbackText) { // Displays feedback on page--miscellaneous function that can be used by multple objects - // elem is the Element in which to put the feedback - if (correct) { - $(elem).html("You are Correct!"); - $(elem).attr("class", "alert alert-success"); - } else { - if (feedbackText === null) { - feedbackText = ""; - } - $(elem).html("Incorrect. " + feedbackText); - $(elem).attr("class", "alert alert-danger"); - } -}; - - /*================================================== == Begin code for the Fill In The Blank component == ==================================================*/ @@ -247,7 +227,7 @@ FITB.prototype.enableCompareButton = function () { FITB.prototype.checkFITBStorage = function () { this.isCorrectArray = []; this.displayFeed = []; - // Starts chain of functions which ends with feedBack() displaying feedback to user + // Starts chain of functions which ends with displaying feedback to user this.evaluateAnswers(); this.renderFITBFeedback(); var answerInfo = "answer:" + this.given_arr + ":" + (this.correct ? "correct" : "no"); @@ -278,12 +258,25 @@ FITB.prototype.evaluateAnswers = function () { } if ($.inArray("", this.isCorrectArray) < 0 && $.inArray(false, this.isCorrectArray) < 0) { this.correct = true; - } else if ($.inArray(false, this.isCorrectArray) >= 0 && $.inArray("", this.isCorrectArray) < 0) { + } else if (this.isCompletelyBlank()) { + this.correct = null; + } else { this.correct = false; } + console.log(this.correct); localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", this.given_arr.join(";")); }; +FITB.prototype.isCompletelyBlank = function () { + // Returns true if the user didn't fill in any of the blanks, else false + for (var i = 0; i < this.isCorrectArray.length; i++) { + if (this.isCorrectArray[i] !== "") { + return false; + } + } + return true; +}; + FITB.prototype.populateDisplayFeed = function (index, given) { var fbl = this.feedbackArray[index]; for (var j = 0; j < fbl.length; j++) { From de51664691eeeb7b50241614ca75505172e1ee6f Mon Sep 17 00:00:00 2001 From: flysaiah Date: Thu, 28 Apr 2016 23:22:43 -0500 Subject: [PATCH 09/21] Added support for timed parsons questions --- runestone/parsons/css/parsons.css | 4 ++ runestone/parsons/js/parsons_setup.js | 20 ++++----- runestone/parsons/js/timedparsons.js | 61 +++++++++++++++++++++++++++ runestone/parsons/parsons.py | 3 +- 4 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 runestone/parsons/js/timedparsons.js diff --git a/runestone/parsons/css/parsons.css b/runestone/parsons/css/parsons.css index 7dcb5fef2..837dbd67f 100644 --- a/runestone/parsons/css/parsons.css +++ b/runestone/parsons/css/parsons.css @@ -135,3 +135,7 @@ li.correctPosition, .testcase.pass { margin-right: auto; text-align:center; } + +.parsons-disabled { + pointer-events: none; +} diff --git a/runestone/parsons/js/parsons_setup.js b/runestone/parsons/js/parsons_setup.js index be2146a79..9be0effea 100644 --- a/runestone/parsons/js/parsons_setup.js +++ b/runestone/parsons/js/parsons_setup.js @@ -93,28 +93,28 @@ Parsons.prototype.formatCode = function () { == Creating/appending new HTML tags == ====================================*/ Parsons.prototype.createParsonsView = function () { // Create DOM elements - this.containingDiv = document.createElement("div"); - $(this.containingDiv).addClass("parsons alert alert-warning"); - this.containingDiv.id = "parsons-" + this.counterId; + this.containerDiv = document.createElement("div"); + $(this.containerDiv).addClass("parsons alert alert-warning"); + this.containerDiv.id = "parsons-" + this.counterId; this.parsTextDiv = document.createElement("div"); $(this.parsTextDiv).addClass("parsons-text"); this.parsTextDiv.innerHTML = this.question.innerHTML; - this.containingDiv.appendChild(this.parsTextDiv); + this.containerDiv.appendChild(this.parsTextDiv); this.leftClearDiv = document.createElement("div"); this.leftClearDiv.style["clear"] = "left"; - this.containingDiv.appendChild(this.leftClearDiv); + this.containerDiv.appendChild(this.leftClearDiv); this.origDiv = document.createElement("div"); this.origDiv.id = "parsons-orig-" + this.counterId; this.origDiv.style["display"] = "none"; this.origDiv.innerHTML = this.fmtCode; - this.containingDiv.appendChild(this.origDiv); + this.containerDiv.appendChild(this.origDiv); this.sortContainerDiv = document.createElement("div"); $(this.sortContainerDiv).addClass("sortable-code-container"); - this.containingDiv.appendChild(this.sortContainerDiv); + this.containerDiv.appendChild(this.sortContainerDiv); this.sortTrashDiv = document.createElement("div"); this.sortTrashDiv.id = "parsons-sortableTrash-" + this.counterId; @@ -132,7 +132,7 @@ Parsons.prototype.createParsonsView = function () { // Create DOM elemen this.parsonsControlDiv = document.createElement("div"); $(this.parsonsControlDiv).addClass("parsons-controls"); - this.containingDiv.appendChild(this.parsonsControlDiv); + this.containerDiv.appendChild(this.parsonsControlDiv); this.checkButt = document.createElement("button"); $(this.checkButt).attr("class", "btn btn-success"); @@ -153,7 +153,7 @@ Parsons.prototype.createParsonsView = function () { // Create DOM elemen this.parsonsControlDiv.appendChild(this.messageDiv); $(this.messageDiv).hide(); - $(this.origElem).replaceWith(this.containingDiv); + $(this.origElem).replaceWith(this.containerDiv); this.createParsonsWidget(); }; @@ -270,7 +270,7 @@ Parsons.prototype.tryLocalStorage = function () { $(document).ready(function () { $pjQ("[data-component=parsons]").each(function (index) { if ($(this.parentNode).data("component") != "timedAssessment") { - prsList[this.id] = new Parsons({"orig": this}); + prsList[this.id] = new Parsons({"orig": this, "useRunestoneServices": eBookConfig.useRunestoneServices}); } }); diff --git a/runestone/parsons/js/timedparsons.js b/runestone/parsons/js/timedparsons.js new file mode 100644 index 000000000..86a55dd51 --- /dev/null +++ b/runestone/parsons/js/timedparsons.js @@ -0,0 +1,61 @@ +function TimedParsons (opts) { + if (opts) { + this.timedInit(opts); + } +} + +TimedParsons.prototype = new Parsons(); + +TimedParsons.prototype.timedInit = function (opts) { + this.init(opts); + this.renderTimedIcon(this.containerDiv); + this.hideButtons(); +}; + + +TimedParsons.prototype.hideButtons = function () { + $(this.checkButt).hide(); +}; + +TimedParsons.prototype.renderTimedIcon = function (component) { + // renders the clock icon on timed components. The component parameter + // is the element that the icon should be appended to. + var timeIconDiv = document.createElement("div"); + var timeIcon = document.createElement("img"); + $(timeIcon).attr({ + "src": "../_static/clock.png", + "style": "width:15px;height:15px" + }); + timeIconDiv.className = "timeTip"; + timeIconDiv.title = ""; + timeIconDiv.appendChild(timeIcon); + $(component).prepend(timeIconDiv); +}; + +TimedParsons.prototype.checkCorrectTimed = function () { + return this.correct ? "T" : "F"; +}; + +TimedParsons.prototype.hideFeedback = function () { + $(this.messageDiv).hide(); +}; + +TimedParsons.prototype.processTimedSubmission = function () { + // Disable input & evaluate component + var hash = this.pwidget.getHash("#ul-parsons-sortableCode-" + this.counterId); + localStorage.setItem(this.divid, hash); + hash = this.pwidget.getHash("#ul-parsons-sortableTrash-" + this.counterId); + localStorage.setItem(this.divid + "-trash", hash); + this.pwidget.getFeedback(); + + // Gross way to check if it's correct or not, but it's better than modifying the 3rd party parsons code to include a "correct" variable + if ($(this.messageDiv).hasClass("alert-success")) { + this.correct = true; + } else { + this.correct = false; + } + + this.resetButt.disabled = true; + $(this.sortContainerDiv).addClass("parsons-disabled"); + +}; diff --git a/runestone/parsons/parsons.py b/runestone/parsons/parsons.py index 00c7dd887..36e3fecd3 100644 --- a/runestone/parsons/parsons.py +++ b/runestone/parsons/parsons.py @@ -37,6 +37,7 @@ def setup(app): app.add_javascript('parsons_setup.js') app.add_javascript('parsons.js') app.add_javascript('parsons-noconflict.js') + app.add_javascript('timedparsons.js') class ParsonsProblem(Assessment): required_arguments = 1 @@ -88,7 +89,7 @@ def findmax(alist): self.options['qnumber'] = self.getNumber() self.options['instructions'] = "" self.options['code'] = self.content - + if 'maxdist' not in self.options: self.options['maxdist'] = '3' if '-----' in self.content: From df5282e47216ae748dea4a0ab3e9f71b56b9dcee Mon Sep 17 00:00:00 2001 From: flysaiah Date: Thu, 28 Apr 2016 23:27:08 -0500 Subject: [PATCH 10/21] Removed console.logs --- runestone/assess/js/fitb.js | 1 - runestone/assess/js/timed.js | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/runestone/assess/js/fitb.js b/runestone/assess/js/fitb.js index b69a1149d..86ae5bcae 100644 --- a/runestone/assess/js/fitb.js +++ b/runestone/assess/js/fitb.js @@ -263,7 +263,6 @@ FITB.prototype.evaluateAnswers = function () { } else { this.correct = false; } - console.log(this.correct); localStorage.setItem(eBookConfig.email + ":" + this.divid + "-given", this.given_arr.join(";")); }; diff --git a/runestone/assess/js/timed.js b/runestone/assess/js/timed.js index 52077b3e9..a072a97c0 100644 --- a/runestone/assess/js/timed.js +++ b/runestone/assess/js/timed.js @@ -313,6 +313,8 @@ Timed.prototype.createRenderedQuestionArray = function () { this.renderedQuestionArray.push(new TimedClickableArea(opts)); } else if ($(tmpChild).is("[data-component=shortanswer]")) { this.renderedQuestionArray.push(new TimedShortAnswer(opts)); + } else if ($(tmpChild).is("[data-component=parsons]")) { + this.renderedQuestionArray.push(new TimedParsons(opts)); } } if (this.random) { @@ -335,6 +337,8 @@ Timed.prototype.randomizeRQA = function () { }; Timed.prototype.renderTimedQuestion = function () { + console.log(this.renderedQuestionArray); + console.log(this.currentQuestionIndex); $(this.switchDiv).replaceWith(this.renderedQuestionArray[this.currentQuestionIndex].containerDiv); this.switchDiv = this.renderedQuestionArray[this.currentQuestionIndex].containerDiv; }; From 9a894075f656d8d753d9cbde16a7075d1bcf8b0d Mon Sep 17 00:00:00 2001 From: flysaiah Date: Thu, 28 Apr 2016 23:56:11 -0500 Subject: [PATCH 11/21] First version of timed activecode --- runestone/activecode/activecode.py | 1 + runestone/activecode/css/activecode.css | 6 ++- runestone/activecode/js/activecode.js | 28 ++++++------ runestone/activecode/js/timed_activecode.js | 49 +++++++++++++++++++++ runestone/assess/js/timed.js | 4 +- 5 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 runestone/activecode/js/timed_activecode.js diff --git a/runestone/activecode/activecode.py b/runestone/activecode/activecode.py index 987bbbf77..42b38c10d 100644 --- a/runestone/activecode/activecode.py +++ b/runestone/activecode/activecode.py @@ -47,6 +47,7 @@ def setup(app): app.add_javascript('skulpt.min.js') app.add_javascript('skulpt-stdlib.js') app.add_javascript('clike.js') + app.add_javascript('timed_activecode.js') app.add_node(ActivcodeNode, html=(visit_ac_node, depart_ac_node)) diff --git a/runestone/activecode/css/activecode.css b/runestone/activecode/css/activecode.css index 7fbb6a54e..84b419e38 100644 --- a/runestone/activecode/css/activecode.css +++ b/runestone/activecode/css/activecode.css @@ -94,4 +94,8 @@ .full_width ol { max-width: 100% !important; -} \ No newline at end of file +} + +.ac-disabled { + pointer-events: none; +} diff --git a/runestone/activecode/js/activecode.js b/runestone/activecode/js/activecode.js index 3c8391260..7c0812881 100755 --- a/runestone/activecode/js/activecode.js +++ b/runestone/activecode/js/activecode.js @@ -64,23 +64,23 @@ ActiveCode.prototype.init = function(opts) { }; ActiveCode.prototype.createEditor = function (index) { - var newdiv = document.createElement('div'); + this.containerDiv = document.createElement('div'); var linkdiv = document.createElement('div') linkdiv.id = this.divid.replace(/_/g,'-').toLowerCase(); // :ref: changes _ to - so add this as a target - $(newdiv).addClass("ac_section alert alert-warning"); + $(this.containerDiv).addClass("ac_section alert alert-warning"); var codeDiv = document.createElement("div"); $(codeDiv).addClass("ac_code_div col-md-12"); this.codeDiv = codeDiv; - newdiv.id = this.divid; - newdiv.lang = this.language; - this.outerDiv = newdiv; + this.containerDiv.id = this.divid; + this.containerDiv.lang = this.language; + this.outerDiv = this.containerDiv; - $(this.origElem).replaceWith(newdiv); + $(this.origElem).replaceWith(this.containerDiv); if (linkdiv.id !== this.divid) { // Don't want the 'extra' target if they match. - newdiv.appendChild(linkdiv); + this.containerDiv.appendChild(linkdiv); } - newdiv.appendChild(codeDiv); - var editor = CodeMirror(codeDiv, {value: this.code, lineNumbers: true, mode: newdiv.lang}); + this.containerDiv.appendChild(codeDiv); + var editor = CodeMirror(codeDiv, {value: this.code, lineNumbers: true, mode: this.containerDiv.lang}); // Make the editor resizable $(editor.getWrapperElement()).resizable({ @@ -727,7 +727,7 @@ JSActiveCode.prototype.runProg = function() { if (!str) str=""; _this.output.innerHTML += _this.outputfun(str)+"
"; }; - + $(this.eContainer).remove(); $(this.output).text(''); $(this.codeDiv).switchClass("col-md-12","col-md-6",{duration:500,queue:false}); @@ -997,8 +997,8 @@ AudioTour.prototype.tour = function (divid, audio_type, bcount) { // str+=""; - - var dir = "http://media.interactivepython.org/" + eBookConfig.basecourse + "/audio/" + + var dir = "http://media.interactivepython.org/" + eBookConfig.basecourse + "/audio/" //var dir = "../_static/audio/" str += "