diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index c69551bb..1e64195e 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -704,9 +704,15 @@ $(function() { }; var renderScore = function() { - var score = $('#score').data('score'); - var maxium_score = $('#score').data('maximum-score'); - $('.score').html((score || '?') + ' / ' + maxium_score); + var score = parseFloat($('#score').data('score')); + var maxium_score = parseFloat($('#score').data('maximum-score')); + if (score >= 0 && score <= maxium_score && maxium_score >0 ) { + var percentage_score = (score / maxium_score * 100 ).toFixed(0); + $('.score').html(percentage_score + '%'); + } + else { + $('.score').html( 0 + '%'); + } renderProgressBar(score, maxium_score); }; diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index 2d3588c6..f5640788 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -9,6 +9,9 @@ $(function() { submissionsScoreAndTimeAssess = [[0,0]]; submissionsScoreAndTimeSubmits = [[0,0]]; + submissionsScoreAndTimeRuns = []; + submissionsSaves = []; + submissionsAutosaves = []; var maximumValue = 0; var wtimes = $('#wtimes').data('working_times'); //.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(working_times_until) @@ -18,43 +21,42 @@ $(function() { for (var i = 0;i 0) { + submissionArray[1] = workingTime; + } + if(submission.score>maximumValue){ + maximumValue = submission.score; + } if(submission.cause == "assess"){ - var workingTime = get_minutes(wtimes[i]); - var submissionArray = [submission.score, 0]; - - if (workingTime > 0) { - submissionArray[1] = workingTime; - } - - if(submission.score>maximumValue){ - maximumValue = submission.score; - } submissionsScoreAndTimeAssess.push(submissionArray); } else if(submission.cause == "submit"){ - var workingTime = get_minutes(wtimes[i]); - var submissionArray = [submission.score, 0]; - - if (workingTime > 0) { - submissionArray[1] = workingTime; - } - - if(submission.score>maximumValue){ - maximumValue = submission.score; - } submissionsScoreAndTimeSubmits.push(submissionArray); + } else if(submission.cause == "run"){ + submissionsScoreAndTimeRuns.push(submissionArray[1]); + } else if(submission.cause == "autosave"){ + submissionsAutosaves.push(submissionArray[1]); + } else if(submission.cause == "save"){ + submissionsSaves.push(submissionArray[1]); } } // console.log(submissionsScoreAndTimeAssess.length); // console.log(submissionsScoreAndTimeSubmits); + // console.log(submissionsScoreAndTimeRuns); function get_minutes (time_stamp) { try { hours = time_stamp.split(":")[0]; minutes = time_stamp.split(":")[1]; seconds = time_stamp.split(":")[2]; - - minutes = parseFloat(hours * 60) + parseInt(minutes); + seconds /= 60; + minutes = parseFloat(hours * 60) + parseInt(minutes) + seconds; if (minutes > 0){ return minutes; } else{ @@ -82,6 +84,9 @@ $(function() { function graph_assesses() { // MAKE THE GRAPH var width_ratio = .8; + if (getWidth()*width_ratio > 1000){ + width_ratio = 1000/getWidth(); + } var height_ratio = .7; // percent of height var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50 @@ -131,15 +136,45 @@ $(function() { .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + var largestSubmittedTimeStamp = submissions[submissions_length-1]; + var largestArrayForRange; - x.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) { - // console.log(d[1]); - return (d[1]); - })); - y.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) { + if(largestSubmittedTimeStamp.cause == "assess"){ + largestArrayForRange = submissionsScoreAndTimeAssess; + indexForTime = 1; + } else if(largestSubmittedTimeStamp.cause == "submit"){ + largestArrayForRange = submissionsScoreAndTimeSubmits; + indexForTime = 1; + } else if(largestSubmittedTimeStamp.cause == "run"){ + largestArrayForRange = submissionsScoreAndTimeRuns; + indexForTime = 0; + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true); + } else if(largestSubmittedTimeStamp.cause == "submit"){ + largestArrayForRange = submissionsScoreAndTimeSubmits; + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true); + } else if(largestSubmittedTimeStamp.cause == "run"){ + largestArrayForRange = submissionsScoreAndTimeRuns; + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1][1]]).clamp(true); + } else if(largestSubmittedTimeStamp.cause == "autosave"){ + largestArrayForRange = submissionsAutosaves; + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true); + } else if(largestSubmittedTimeStamp.cause == "save"){ + largestArrayForRange = submissionsSaves; + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true); + } + + // x.domain(d3.extent(largestArrayForRange, function (d) { + // // console.log(d[1]); + // return (d[indexForTime]); + // })); + + // take maximum value between assesses and submits + var yDomain = submissionsScoreAndTimeAssess.concat(submissionsScoreAndTimeSubmits); + y.domain(d3.extent(yDomain, function (d) { // console.log(d[0]); return (d[0]); })); + // y.domain([0,2]).clamp(true); svg.append("g") //x axis .attr("class", "x axis") @@ -221,10 +256,21 @@ $(function() { .attr("cx", function(d) { return x(d[1]); }) .attr("cy", function(d) { return y(d[0]); }); + for (var i = 0; i < submissionsScoreAndTimeRuns.length; i++) { + svg.append("line") + .attr("stroke", "red") + .attr("stroke-width", 1) + .attr("fill", "none")// end new + .attr("y1", y(0)) + .attr("y2", 0) + .attr("x1", x(submissionsScoreAndTimeRuns[i])) + .attr("x2", x(submissionsScoreAndTimeRuns[i])); + } var color_hash = { 0 : ["Submissions", "blue"], - 1 : ["Assesses", "orange"] - } + 1 : ["Assesses", "orange"], + 2 : ["Runs", "red"] + }; // add legend var legend = svg.append("g") @@ -234,7 +280,8 @@ $(function() { .attr("height", 100) .attr("width", 100); - var dataset = [submissionsScoreAndTimeSubmits,submissionsScoreAndTimeAssess]; + var dataset = [submissionsScoreAndTimeSubmits,submissionsScoreAndTimeAssess, submissionsScoreAndTimeRuns]; + var yOffset = -70; legend.selectAll('g').data(dataset) .enter() @@ -243,14 +290,14 @@ $(function() { var g = d3.select(this); g.append("rect") .attr("x", 20) - .attr("y", i*25 + 8) + .attr("y", i*25 + yOffset)// + 8 .attr("width", 10) .attr("height", 10) .style("fill", color_hash[String(i)][1]); g.append("text") .attr("x", 40) - .attr("y", i * 25 + 18) + .attr("y", i * 25 + yOffset + 10)// + 18 .attr("height",30) .attr("width",100) .style("fill", color_hash[String(i)][1]) @@ -273,7 +320,7 @@ $(function() { try{ graph_assesses(); } catch(err){ - // not enough data + alert("could not draw the graph"); } } diff --git a/app/assets/javascripts/turtle.js b/app/assets/javascripts/turtle.js index f2fa184b..ddbb27eb 100644 --- a/app/assets/javascripts/turtle.js +++ b/app/assets/javascripts/turtle.js @@ -41,8 +41,7 @@ Turtle.prototype.update = function () { var i, k, canvas, ctx, dx, dy, item, c, length; canvas = this.canvas[0]; ctx = canvas.getContext('2d'); - ctx.fillStyle = '#fff'; - ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.clearRect(0, 0, canvas.width, canvas.height); length = this.items.length; dx = canvas.width / 2; dy = canvas.height / 2; @@ -56,8 +55,8 @@ Turtle.prototype.update = function () { for (k = 2; k < c.length; k += 2) { ctx.lineTo(c[k] + dx, c[k + 1] + dy); } - if (this.fill) { - ctx.strokeStyle = this.fill; + if (item.fill) { + ctx.strokeStyle = item.fill; } ctx.stroke(); diff --git a/app/assets/javascripts/working_time_graphs.js b/app/assets/javascripts/working_time_graphs.js index 315d2c09..181b3799 100644 --- a/app/assets/javascripts/working_time_graphs.js +++ b/app/assets/javascripts/working_time_graphs.js @@ -4,7 +4,7 @@ $(function() { if ($.isController('exercises') && $('.graph-functions').isPresent()) { var working_times = $('#data').data('working-time'); - + function get_minutes (time_stamp){ try{ hours = time_stamp.split(":")[0]; @@ -67,6 +67,9 @@ $(function() { // DRAW THE LINE GRAPH ------------------------------------------------------------------------------ function draw_line_graph() { var width_ratio = .8; + if (getWidth()*width_ratio > 1000){ + width_ratio = 1000/getWidth(); + } var height_ratio = .7; // percent of height // currently sets as percentage of window width, however, unfortunately diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 42b12e42..0ce5b73f 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -38,4 +38,4 @@ = t('exercises.editor.test') = render('editor_button', data: {:'data-placement' => 'top', :'data-tooltip' => true}, icon: 'fa fa-trophy', id: 'assess', label: t('exercises.editor.score'), title: t('shared.tooltips.shortcut', shortcut: 'ALT + s')) -= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent') \ No newline at end of file += render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent') diff --git a/app/views/exercises/implement.html.slim b/app/views/exercises/implement.html.slim index 728492b6..0c5e109b 100644 --- a/app/views/exercises/implement.html.slim +++ b/app/views/exercises/implement.html.slim @@ -78,6 +78,9 @@ br - if session[:lti_parameters].try(:has_key?, 'lis_outcome_service_url') p.text-center = render('editor_button', classes: 'btn-lg btn-success', data: {:'data-url' => submit_exercise_path(@exercise)}, icon: 'fa fa-send', id: 'submit', label: t('exercises.editor.submit')) + - else + p.text-center = render('editor_button', classes: 'btn-lg btn-warning-outline', data: {:'data-placement' => 'bottom', :'data-tooltip' => true} , icon: 'fa fa-clock-o', id: 'submit_outdated', label: t('exercises.editor.exercise_deadline_passed'), title: t('exercises.editor.tooltips.exercise_deadline_passed')) + - if qa_url #questions-column #questions-holder data-url="#{qa_url}/qa/index/#{@exercise.id}/#{@user_id}" diff --git a/app/views/exercises/index.html.slim b/app/views/exercises/index.html.slim index 97847f1f..ae018e82 100644 --- a/app/views/exercises/index.html.slim +++ b/app/views/exercises/index.html.slim @@ -27,7 +27,7 @@ h1 = Exercise.model_name.human(count: 2) - @exercises.each do |exercise| tr data-id=exercise.id td = exercise.title - td = link_to_if(policy(exercise.author).show?, exercise.author, exercise.author) + td = link_to_if(exercise.author && policy(exercise.author).show?, exercise.author, exercise.author) td = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment) td = exercise.files.teacher_defined_tests.count td = exercise.maximum_score diff --git a/config/locales/de.yml b/config/locales/de.yml index e7d78943..ae7fb930 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -209,8 +209,10 @@ de: submit: Code zur Bewertung abgeben test: Testen timeout: 'Ausführung gestoppt. Ihr Code hat die erlaubte Ausführungszeit von %{permitted_execution_time} Sekunden überschritten.' + exercise_deadline_passed: 'Die Abgabefrist für diese Aufgabe ist bereits abgelaufen.' tooltips: save: Ihr Code wird automatisch gespeichert, wann immer Sie eine Datei herunterladen, ausführen oder testen. Explizites Speichern ist also selten notwendig. + exercise_deadline_passed: 'Die hier erzielten Punkte können nur bis zum Ablauf der Abgabefrist an die E-Learning-Plattform übertragen werden.' request_for_comments_sent: "Kommentaranfrage gesendet." editor_file_tree: file_root: Dateien diff --git a/config/locales/en.yml b/config/locales/en.yml index f2470fc8..6cc9dcaf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -209,8 +209,10 @@ en: submit: Submit Code For Assessment test: Test timeout: 'Execution stopped. Your code exceeded the permitted execution time of %{permitted_execution_time} seconds.' + exercise_deadline_passed: 'The deadline for this exercise has already passed' tooltips: save: Your code is automatically saved whenever you download, run, or test it. Therefore, explicitly saving is rarely necessary. + exercise_deadline_passed: 'The results for this exercise can only be submitted to the e-learning platform before the deadline has passed.' request_for_comments_sent: "Request for comments sent." editor_file_tree: file_root: Files diff --git a/lib/py_unit_adapter.rb b/lib/py_unit_adapter.rb index f7f98dae..1ff5cd98 100644 --- a/lib/py_unit_adapter.rb +++ b/lib/py_unit_adapter.rb @@ -1,6 +1,7 @@ class PyUnitAdapter < TestingFrameworkAdapter COUNT_REGEXP = /Ran (\d+) test/ - FAILURES_REGEXP = /FAILED \(failures=(\d+)\)/ + FAILURES_REGEXP = /FAILED \(.*failures=(\d+).*\)/ + ERRORS_REGEXP = /FAILED \(.*errors=(\d+).*\)/ ASSERTION_ERROR_REGEXP = /AssertionError:\s(.*)/ def self.framework_name @@ -9,9 +10,11 @@ class PyUnitAdapter < TestingFrameworkAdapter def parse_output(output) count = COUNT_REGEXP.match(output[:stderr]).captures.first.to_i - matches = FAILURES_REGEXP.match(output[:stderr]) - failed = matches ? matches.captures.try(:first).to_i : 0 - error_matches = ASSERTION_ERROR_REGEXP.match(output[:stderr]).try(:captures) || [] - {count: count, failed: failed, error_messages: error_matches} + failures_matches = FAILURES_REGEXP.match(output[:stderr]) + failed = failures_matches ? failures_matches.captures.try(:first).to_i : 0 + error_matches = ERRORS_REGEXP.match(output[:stderr]) + errors = error_matches ? error_matches.captures.try(:first).to_i : 0 + assertion_error_matches = ASSERTION_ERROR_REGEXP.match(output[:stderr]).try(:captures) || [] + {count: count, failed: failed + errors, error_messages: assertion_error_matches} end end