From e2f4991e5450891dc61f9b4d7e47f702a7035226 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 17:09:13 +0200 Subject: [PATCH 01/81] change collapsing style --- app/views/exercises/_file_form.html.slim | 2 +- app/views/exercises/show.html.slim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/exercises/_file_form.html.slim b/app/views/exercises/_file_form.html.slim index c737f068..5cdfdc98 100644 --- a/app/views/exercises/_file_form.html.slim +++ b/app/views/exercises/_file_form.html.slim @@ -4,7 +4,7 @@ li.panel.panel-default div.clearfix role="button" span = f.object.name a.pull-right data-toggle="collapse" data-parent="#files" href="#collapse#{id}" collapse - .panel-collapse.collapse.in id="collapse#{id}" role="tabpanel" + .panel-collapse.collapse id="collapse#{id}" role="tabpanel" .panel-body .clearfix = link_to(t('shared.destroy'), '#', class:'btn btn-warning btn-sm discard-file pull-right') .form-group diff --git a/app/views/exercises/show.html.slim b/app/views/exercises/show.html.slim index fc28272a..ceab96a7 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -29,7 +29,7 @@ ul.list-unstyled.panel-group#files div.clearfix role="button" span.panel-title = file.name_with_extension a.pull-right data-toggle="collapse" data-parent="#files" href="#collapse#{file.id}" collapse - .panel-collapse.collapse.in id="collapse#{file.id}" role="tabpanel" + .panel-collapse.collapse id="collapse#{file.id}" role="tabpanel" .panel-body - if policy(file).destroy? .clearfix = link_to(t('shared.destroy'), file, class:'btn btn-warning btn-sm pull-right', data: {confirm: t('shared.confirm_destroy')}, method: :delete) From a16eb4b15b9ae9846cb258c1ec775f888c0d829a Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 17:13:30 +0200 Subject: [PATCH 02/81] sorting files alphabetically --- app/views/exercises/_editor.html.slim | 4 ++-- app/views/exercises/show.html.slim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 42b12e42..3340d6ac 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,7 +1,7 @@ #editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - - @files.each do |file| + - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| = render('editor_frame', exercise: exercise, file: file) #autosave-label = t('exercises.editor.lastsaved') @@ -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/show.html.slim b/app/views/exercises/show.html.slim index ceab96a7..85fb09cb 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -23,7 +23,7 @@ h1 h2 = t('activerecord.attributes.exercise.files') ul.list-unstyled.panel-group#files - - @exercise.files.each do |file| + - @exercise.files.order('name').each do |file| li.panel.panel-default .panel-heading role="tab" id="heading" div.clearfix role="button" From 10e68915d82d8db4b39aaea790c3beea1848f905 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 17:34:30 +0200 Subject: [PATCH 03/81] add key exercise_deadline_passed --- config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/locales/de.yml b/config/locales/de.yml index e7d78943..30f4f7ef 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 Punkten 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 From e838f5a1b129c32ff94b73778abd84a4bcffec57 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 17:35:09 +0200 Subject: [PATCH 04/81] fix submit-button when not active --- app/views/exercises/implement.html.slim | 3 +++ 1 file changed, 3 insertions(+) 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}" From 5c9a9b8a476a8a1b7fe8d9d1b0e60acb6454d12b Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 17:53:21 +0200 Subject: [PATCH 05/81] add alert on submit-button click --- app/assets/javascripts/editor.js.erb | 8 ++++++++ app/views/exercises/_editor.html.slim | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index c69551bb..3d9bb5cb 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -465,6 +465,7 @@ $(function() { $('#start').on('click', showWorkspaceTab); //$('#submit').on('click', confirmSubmission); $('#submit').on('click', submitCode); + $('#submit_outdated').on('click', submitOutdated); }; var initializeWorkspaceButtons = function() { @@ -751,6 +752,13 @@ $(function() { }); }; + var submitOutdated = function(event) { + $.flash.danger({ + icon: ['fa', 'fa-clock-o'], + text: $('#editor').data('message-deadline') + }); + }; + var sendError = function(message, submission_id) { showSpinner($('#render')); var jqxhr = ajax({ diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 3340d6ac..39ef3bd1 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,4 +1,4 @@ -#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id +#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-deadline=t('exercises.editor.deadline_alert') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| From 3189f615353309148ee9a56637ae25e2b635dc0c Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:03:33 +0200 Subject: [PATCH 06/81] no alert on outdated button --- app/assets/javascripts/editor.js.erb | 8 -------- app/views/exercises/_editor.html.slim | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 3d9bb5cb..c69551bb 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -465,7 +465,6 @@ $(function() { $('#start').on('click', showWorkspaceTab); //$('#submit').on('click', confirmSubmission); $('#submit').on('click', submitCode); - $('#submit_outdated').on('click', submitOutdated); }; var initializeWorkspaceButtons = function() { @@ -752,13 +751,6 @@ $(function() { }); }; - var submitOutdated = function(event) { - $.flash.danger({ - icon: ['fa', 'fa-clock-o'], - text: $('#editor').data('message-deadline') - }); - }; - var sendError = function(message, submission_id) { showSpinner($('#render')); var jqxhr = ajax({ diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 39ef3bd1..b9c41863 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,4 +1,4 @@ -#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-deadline=t('exercises.editor.deadline_alert') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id +#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| From d54d1dd3e8b5b8d2b8f1119523f07d046ee85c02 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:22:17 +0200 Subject: [PATCH 07/81] Score given in percentage --- app/assets/javascripts/editor.js.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index c69551bb..b40b93ec 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -706,7 +706,8 @@ $(function() { var renderScore = function() { var score = $('#score').data('score'); var maxium_score = $('#score').data('maximum-score'); - $('.score').html((score || '?') + ' / ' + maxium_score); + var percentage_score = score / maxium_score * 100 + $('.score').html((percentage_score || 0) + ' % '); renderProgressBar(score, maxium_score); }; From d05ceac7a346bff9f7864a6491efbff6a5e3cab1 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:30:50 +0200 Subject: [PATCH 08/81] check if score is valid --- app/assets/javascripts/editor.js.erb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index b40b93ec..1d84361b 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -706,8 +706,13 @@ $(function() { var renderScore = function() { var score = $('#score').data('score'); var maxium_score = $('#score').data('maximum-score'); - var percentage_score = score / maxium_score * 100 - $('.score').html((percentage_score || 0) + ' % '); + if (score >= 0 && score <= maxium_score && maxium_score >0 ) { + var percentage_score = score / maxium_score * 100 + $('.score').html(percentage_score + '%'); + } + else { + $('.score').html( 0 + ' % '); + } renderProgressBar(score, maxium_score); }; From e5b3d33228599f6bf902a260b05eeed5d4404794 Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:34:16 +0200 Subject: [PATCH 09/81] delete spaces --- app/assets/javascripts/editor.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 1d84361b..0bb22853 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -711,7 +711,7 @@ $(function() { $('.score').html(percentage_score + '%'); } else { - $('.score').html( 0 + ' % '); + $('.score').html( 0 + '%'); } renderProgressBar(score, maxium_score); }; From a2e1b08730daa61476d0c4cd426446cfe9d83e4f Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:40:42 +0200 Subject: [PATCH 10/81] delete duplicate depleted --- app/views/exercises/_editor.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index b9c41863..3340d6ac 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,4 +1,4 @@ -#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id +#editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| From d26d859df1e532fd8519020a1dd08ed6a689c52b Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 18:45:14 +0200 Subject: [PATCH 11/81] resolve confilts --- app/views/exercises/_editor.html.slim | 2 +- app/views/exercises/_file_form.html.slim | 2 +- app/views/exercises/show.html.slim | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 3340d6ac..0ce5b73f 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,7 +1,7 @@ #editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| + - @files.each do |file| = render('editor_frame', exercise: exercise, file: file) #autosave-label = t('exercises.editor.lastsaved') diff --git a/app/views/exercises/_file_form.html.slim b/app/views/exercises/_file_form.html.slim index 5cdfdc98..c737f068 100644 --- a/app/views/exercises/_file_form.html.slim +++ b/app/views/exercises/_file_form.html.slim @@ -4,7 +4,7 @@ li.panel.panel-default div.clearfix role="button" span = f.object.name a.pull-right data-toggle="collapse" data-parent="#files" href="#collapse#{id}" collapse - .panel-collapse.collapse id="collapse#{id}" role="tabpanel" + .panel-collapse.collapse.in id="collapse#{id}" role="tabpanel" .panel-body .clearfix = link_to(t('shared.destroy'), '#', class:'btn btn-warning btn-sm discard-file pull-right') .form-group diff --git a/app/views/exercises/show.html.slim b/app/views/exercises/show.html.slim index 85fb09cb..fc28272a 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -23,13 +23,13 @@ h1 h2 = t('activerecord.attributes.exercise.files') ul.list-unstyled.panel-group#files - - @exercise.files.order('name').each do |file| + - @exercise.files.each do |file| li.panel.panel-default .panel-heading role="tab" id="heading" div.clearfix role="button" span.panel-title = file.name_with_extension a.pull-right data-toggle="collapse" data-parent="#files" href="#collapse#{file.id}" collapse - .panel-collapse.collapse id="collapse#{file.id}" role="tabpanel" + .panel-collapse.collapse.in id="collapse#{file.id}" role="tabpanel" .panel-body - if policy(file).destroy? .clearfix = link_to(t('shared.destroy'), file, class:'btn btn-warning btn-sm pull-right', data: {confirm: t('shared.confirm_destroy')}, method: :delete) From f5aa3cef3ce69f453fbf1fecd9c6ebe6599feb1c Mon Sep 17 00:00:00 2001 From: yqbk Date: Thu, 12 May 2016 19:03:17 +0200 Subject: [PATCH 12/81] Problem when no files solved --- app/views/exercises/_editor.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 3340d6ac..0ce5b73f 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -1,7 +1,7 @@ #editor.row data-exercise-id=exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id div class=(@exercise.hide_file_tree ? 'hidden col-sm-3' : 'col-sm-3') = render('editor_file_tree', files: @files) div id='frames' class=(@exercise.hide_file_tree ? 'col-sm-12' : 'col-sm-9') - - @files.sort{ |a,b| a[:group][:name] <=> b[:group][:name] }.each do |file| + - @files.each do |file| = render('editor_frame', exercise: exercise, file: file) #autosave-label = t('exercises.editor.lastsaved') From 34c9d791f43c83a4b75a7d9ecba28139b0560c29 Mon Sep 17 00:00:00 2001 From: John Geiger Date: Sun, 29 May 2016 17:30:49 +0200 Subject: [PATCH 13/81] Finishing up graphs. Formatting and adding "Runs" lines. --- Gemfile | 2 +- app/assets/javascripts/exercise_graphs.js | 65 ++--- app/assets/javascripts/working_time_graphs.js | 5 +- db/schema.rb | 225 ------------------ 4 files changed, 43 insertions(+), 254 deletions(-) delete mode 100644 db/schema.rb diff --git a/Gemfile b/Gemfile index 540b11b2..bf089d6c 100644 --- a/Gemfile +++ b/Gemfile @@ -37,7 +37,7 @@ gem 'tubesock' gem 'faye-websocket' gem 'nokogiri' gem 'd3-rails' -gem 'rest-client’ +gem 'rest-client' group :development do gem 'better_errors', platform: :ruby diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index 2d3588c6..f0a9f0fc 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -9,6 +9,7 @@ $(function() { submissionsScoreAndTimeAssess = [[0,0]]; submissionsScoreAndTimeSubmits = [[0,0]]; + submissionsScoreAndTimeRuns = []; var maximumValue = 0; var wtimes = $('#wtimes').data('working_times'); //.hidden#wtimes data-working_times=ActiveSupport::JSON.encode(working_times_until) @@ -18,43 +19,38 @@ $(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]); } } // 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 +78,9 @@ $(function() { function graph_assesses() { // MAKE THE GRAPH var width_ratio = .8; + if (width_ratio > 1000){ + width_ratio = 1000; + } var height_ratio = .7; // percent of height var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50 @@ -221,10 +220,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 +244,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 +254,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]) diff --git a/app/assets/javascripts/working_time_graphs.js b/app/assets/javascripts/working_time_graphs.js index 315d2c09..4b6db432 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 (width_ratio > 1000){ + width_ratio = 1000; + } var height_ratio = .7; // percent of height // currently sets as percentage of window width, however, unfortunately diff --git a/db/schema.rb b/db/schema.rb deleted file mode 100644 index bcf1a675..00000000 --- a/db/schema.rb +++ /dev/null @@ -1,225 +0,0 @@ -# encoding: UTF-8 -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20160510145341) do - - # These are extensions that must be enabled in order to support this database - enable_extension "plpgsql" - - create_table "code_harbor_links", force: true do |t| - t.string "oauth2token" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "user_id" - end - - add_index "code_harbor_links", ["user_id"], name: "index_code_harbor_links_on_user_id", using: :btree - - create_table "comments", force: true do |t| - t.integer "user_id" - t.integer "file_id" - t.string "user_type" - t.integer "row" - t.integer "column" - t.string "text" - t.datetime "created_at" - t.datetime "updated_at" - end - - add_index "comments", ["file_id"], name: "index_comments_on_file_id", using: :btree - add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree - - create_table "consumers", force: true do |t| - t.string "name" - t.datetime "created_at" - t.datetime "updated_at" - t.string "oauth_key" - t.string "oauth_secret" - end - - create_table "errors", force: true do |t| - t.integer "execution_environment_id" - t.text "message" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "submission_id" - end - - add_index "errors", ["submission_id"], name: "index_errors_on_submission_id", using: :btree - - create_table "execution_environments", force: true do |t| - t.string "docker_image" - t.string "name" - t.datetime "created_at" - t.datetime "updated_at" - t.string "run_command" - t.string "test_command" - t.string "testing_framework" - t.text "help" - t.string "exposed_ports" - t.integer "permitted_execution_time" - t.integer "user_id" - t.string "user_type" - t.integer "pool_size" - t.integer "file_type_id" - t.integer "memory_limit" - t.boolean "network_enabled" - end - - create_table "exercises", force: true do |t| - t.text "description" - t.integer "execution_environment_id" - t.string "title" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "user_id" - t.text "instructions" - t.boolean "public" - t.string "user_type" - t.string "token" - t.integer "team_id" - t.boolean "hide_file_tree" - t.boolean "allow_file_creation" - end - - add_index "exercises", ["execution_environment_id"], name: "test3", using: :btree - - create_table "external_users", force: true do |t| - t.integer "consumer_id" - t.string "email" - t.string "external_id" - t.string "name" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "file_types", force: true do |t| - t.string "editor_mode" - t.string "file_extension" - t.integer "indent_size" - t.string "name" - t.integer "user_id" - t.datetime "created_at" - t.datetime "updated_at" - t.boolean "executable" - t.boolean "renderable" - t.string "user_type" - t.boolean "binary" - end - - create_table "files", force: true do |t| - t.text "content" - t.integer "context_id" - t.string "context_type" - t.integer "file_id" - t.integer "file_type_id" - t.boolean "hidden" - t.string "name" - t.boolean "read_only" - t.datetime "created_at" - t.datetime "updated_at" - t.string "native_file" - t.string "role" - t.string "hashed_content" - t.string "feedback_message" - t.float "weight" - t.string "path" - end - - add_index "files", ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type", using: :btree - - create_table "hints", force: true do |t| - t.integer "execution_environment_id" - t.string "locale" - t.text "message" - t.string "name" - t.string "regular_expression" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "internal_users", force: true do |t| - t.integer "consumer_id" - t.string "email" - t.string "name" - t.string "role" - t.datetime "created_at" - t.datetime "updated_at" - t.string "crypted_password" - t.string "salt" - t.integer "failed_logins_count", default: 0 - t.datetime "lock_expires_at" - t.string "unlock_token" - t.string "remember_me_token" - t.datetime "remember_me_token_expires_at" - t.string "reset_password_token" - t.datetime "reset_password_token_expires_at" - t.datetime "reset_password_email_sent_at" - t.string "activation_state" - t.string "activation_token" - t.datetime "activation_token_expires_at" - end - - add_index "internal_users", ["activation_token"], name: "index_internal_users_on_activation_token", using: :btree - add_index "internal_users", ["email"], name: "index_internal_users_on_email", unique: true, using: :btree - add_index "internal_users", ["remember_me_token"], name: "index_internal_users_on_remember_me_token", using: :btree - add_index "internal_users", ["reset_password_token"], name: "index_internal_users_on_reset_password_token", using: :btree - - create_table "internal_users_teams", force: true do |t| - t.integer "internal_user_id" - t.integer "team_id" - end - - add_index "internal_users_teams", ["internal_user_id"], name: "index_internal_users_teams_on_internal_user_id", using: :btree - add_index "internal_users_teams", ["team_id"], name: "index_internal_users_teams_on_team_id", using: :btree - - create_table "request_for_comments", force: true do |t| - t.integer "user_id", null: false - t.integer "exercise_id", null: false - t.integer "file_id", null: false - t.datetime "requested_at" - t.datetime "created_at" - t.datetime "updated_at" - t.string "user_type" - t.text "question" - end - - create_table "submissions", force: true do |t| - t.integer "exercise_id" - t.float "score" - t.integer "user_id" - t.datetime "created_at" - t.datetime "updated_at" - t.string "cause" - t.string "user_type" - end - - add_index "submissions", ["exercise_id"], name: "test1", where: "((user_type)::text = 'ExternalUser'::text)", using: :btree - add_index "submissions", ["exercise_id"], name: "test2", using: :btree - - create_table "teams", force: true do |t| - t.string "name" - t.datetime "created_at" - t.datetime "updated_at" - end - - create_table "testruns", force: true do |t| - t.boolean "passed" - t.text "output" - t.integer "file_id" - t.integer "submission_id" - t.datetime "created_at" - t.datetime "updated_at" - end - -end From b6cd797772191fc170ee5c224add0829bb3ceda6 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Tue, 31 May 2016 11:17:11 +0200 Subject: [PATCH 14/81] Show displayname instead of username in request_for_comment --- app/views/request_for_comments/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/request_for_comments/show.html.erb b/app/views/request_for_comments/show.html.erb index ab53ebd0..e4dc7f06 100644 --- a/app/views/request_for_comments/show.html.erb +++ b/app/views/request_for_comments/show.html.erb @@ -12,7 +12,7 @@ limit 1").first['id'].to_i submission = Submission.find(submission_id) %> - <%= user %> | <%= @request_for_comment.requested_at %> + <%= user.displayname %> | <%= @request_for_comment.requested_at %>

<% if @request_for_comment.question and not @request_for_comment.question == '' %> From 8158d60d307d348095f16933b91ba769938aaa61 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 1 Jun 2016 11:30:42 +0200 Subject: [PATCH 15/81] also write "parsable json" to the frontend console if it does not have a valid command set (solves the issue when just printing an array in java, it is not shown anywhere). --- app/controllers/submissions_controller.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 1fec0b1b..4f9d6a5b 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -174,8 +174,14 @@ class SubmissionsController < ApplicationController def parse_message(message, output_stream, socket, recursive = true) begin parsed = JSON.parse(message) - socket.send_data message - Rails.logger.info('parse_message sent: ' + message) + if(parsed.class == Hash && parsed.key?('cmd')) + socket.send_data message + Rails.logger.info('parse_message sent: ' + message) + else + parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message} + socket.send_data JSON.dump(parsed) + Rails.logger.info('parse_message sent: ' + JSON.dump(parsed)) + end rescue JSON::ParserError => e # Check wether the message contains multiple lines, if true try to parse each line if ((recursive == true) && (message.include? "\n")) From 2ff04cf59df90dd0c3432268b8de784a6ee5d50c Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 2 Jun 2016 15:27:36 +0200 Subject: [PATCH 16/81] fixed scaling of graphs --- app/assets/javascripts/exercise_graphs.js | 4 ++-- app/assets/javascripts/working_time_graphs.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index f0a9f0fc..8b11f668 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -78,8 +78,8 @@ $(function() { function graph_assesses() { // MAKE THE GRAPH var width_ratio = .8; - if (width_ratio > 1000){ - width_ratio = 1000; + if (getWidth()*width_ratio > 1000){ + width_ratio = 1000/getWidth(); } var height_ratio = .7; // percent of height diff --git a/app/assets/javascripts/working_time_graphs.js b/app/assets/javascripts/working_time_graphs.js index 4b6db432..181b3799 100644 --- a/app/assets/javascripts/working_time_graphs.js +++ b/app/assets/javascripts/working_time_graphs.js @@ -67,8 +67,8 @@ $(function() { // DRAW THE LINE GRAPH ------------------------------------------------------------------------------ function draw_line_graph() { var width_ratio = .8; - if (width_ratio > 1000){ - width_ratio = 1000; + if (getWidth()*width_ratio > 1000){ + width_ratio = 1000/getWidth(); } var height_ratio = .7; // percent of height From 5ba980b31612c092afa8ac98d7470e57705a7636 Mon Sep 17 00:00:00 2001 From: John Geiger Date: Thu, 2 Jun 2016 15:30:18 +0200 Subject: [PATCH 17/81] Fix x-axis domain problem --- app/assets/javascripts/exercise_graphs.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index 8b11f668..efde486d 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -130,8 +130,17 @@ $(function() { .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + var largestSubmittedTimeStamp = submissions[submissions_length-1]; + var largestArrayForRange; + if(largestSubmittedTimeStamp.cause == "assess"){ + largestArrayForRange = submissionsScoreAndTimeAssess; + } else if(largestSubmittedTimeStamp.cause == "submit"){ + largestArrayForRange = submissionsScoreAndTimeSubmits; + } else if(largestSubmittedTimeStamp.cause == "run"){ + largestArrayForRange = submissionsScoreAndTimeRuns; + } - x.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) { + x.domain(d3.extent(largestArrayForRange, function (d) { // console.log(d[1]); return (d[1]); })); From 3c8bd90e2b03fec1d904ccefb3e0ce89346f13e2 Mon Sep 17 00:00:00 2001 From: John Geiger Date: Thu, 2 Jun 2016 15:40:18 +0200 Subject: [PATCH 18/81] fix indexing for x axis --- app/assets/javascripts/exercise_graphs.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index efde486d..8e6263bf 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -131,18 +131,23 @@ $(function() { .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var largestSubmittedTimeStamp = submissions[submissions_length-1]; + var indexForTime; var largestArrayForRange; if(largestSubmittedTimeStamp.cause == "assess"){ - largestArrayForRange = submissionsScoreAndTimeAssess; + largestArrayForRange = submissionsScoreAndTimeAssess[1]; + indexForTime = 1; } else if(largestSubmittedTimeStamp.cause == "submit"){ - largestArrayForRange = submissionsScoreAndTimeSubmits; + largestArrayForRange = submissionsScoreAndTimeSubmits[1]; + indexForTime = 1; } else if(largestSubmittedTimeStamp.cause == "run"){ - largestArrayForRange = submissionsScoreAndTimeRuns; + largestArrayForRange = submissionsScoreAndTimeRuns[0]; + indexForTime = 0; + } x.domain(d3.extent(largestArrayForRange, function (d) { // console.log(d[1]); - return (d[1]); + return (d[indexForTime]); })); y.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) { // console.log(d[0]); From cd005483ab4814be176f53791f326853c0ee224e Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 2 Jun 2016 16:34:13 +0200 Subject: [PATCH 19/81] removed incorrect array accesses. --- app/assets/javascripts/exercise_graphs.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index 8e6263bf..8e8e15db 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -134,13 +134,13 @@ $(function() { var indexForTime; var largestArrayForRange; if(largestSubmittedTimeStamp.cause == "assess"){ - largestArrayForRange = submissionsScoreAndTimeAssess[1]; + largestArrayForRange = submissionsScoreAndTimeAssess; indexForTime = 1; } else if(largestSubmittedTimeStamp.cause == "submit"){ - largestArrayForRange = submissionsScoreAndTimeSubmits[1]; + largestArrayForRange = submissionsScoreAndTimeSubmits; indexForTime = 1; } else if(largestSubmittedTimeStamp.cause == "run"){ - largestArrayForRange = submissionsScoreAndTimeRuns[0]; + largestArrayForRange = submissionsScoreAndTimeRuns; indexForTime = 0; } From c8b21dd7ea446987d985c9936773812aecf7a6fb Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 3 Jun 2016 15:26:24 +0200 Subject: [PATCH 20/81] first steps for new mails on comments. --- app/mailers/user_mailer.rb | 5 +++++ app/views/user_mailer/got_new_comment.slim | 1 + 2 files changed, 6 insertions(+) create mode 100644 app/views/user_mailer/got_new_comment.slim diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 4b3c71f4..8cb5eed1 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -11,4 +11,9 @@ class UserMailer < ActionMailer::Base @reset_password_url = reset_password_internal_user_url(user, token: user.reset_password_token) mail(subject: t('mailers.user_mailer.reset_password.subject'), to: user.email) end + + def got_new_comment(comment, user, commenting_user) + @commenting_user = commenting_user + mail(subject: t('mailers.user_mailer.got_new_comment.subject'), to: user.email) + end end diff --git a/app/views/user_mailer/got_new_comment.slim b/app/views/user_mailer/got_new_comment.slim new file mode 100644 index 00000000..3b8afa5b --- /dev/null +++ b/app/views/user_mailer/got_new_comment.slim @@ -0,0 +1 @@ +== t('mailers.user_mailer.activation_needed.body', link: link_to(@activation_url, @activation_url)) From a75227c8f7ffcaab447ca5c4b69aaae887a6eafe Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 8 Jun 2016 11:49:34 +0200 Subject: [PATCH 21/81] Don't call author policy if author is missing --- app/views/exercises/index.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 52ba35d0e4d20f4a41ad7d64a7efde1dcdee770c Mon Sep 17 00:00:00 2001 From: John Geiger Date: Wed, 8 Jun 2016 13:55:09 +0200 Subject: [PATCH 22/81] fixed graph sizing --- app/assets/javascripts/exercise_graphs.js | 38 ++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index 8e8e15db..f5640788 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -10,6 +10,8 @@ $(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) @@ -38,6 +40,10 @@ $(function() { 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); @@ -131,8 +137,8 @@ $(function() { .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var largestSubmittedTimeStamp = submissions[submissions_length-1]; - var indexForTime; var largestArrayForRange; + if(largestSubmittedTimeStamp.cause == "assess"){ largestArrayForRange = submissionsScoreAndTimeAssess; indexForTime = 1; @@ -142,17 +148,33 @@ $(function() { } 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]); - })); - y.domain(d3.extent(submissionsScoreAndTimeAssess, function (d) { + // 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") @@ -298,7 +320,7 @@ $(function() { try{ graph_assesses(); } catch(err){ - // not enough data + alert("could not draw the graph"); } } From 9e5297c1de6b0bc08b9e1d1f4507f69c5ac693a9 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 8 Jun 2016 14:05:13 +0200 Subject: [PATCH 23/81] re-add schema.rb --- db/schema.rb | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 db/schema.rb diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 00000000..f519dfe1 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,220 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20160512131539) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "code_harbor_links", force: true do |t| + t.string "oauth2token" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "user_id" + end + + add_index "code_harbor_links", ["user_id"], name: "index_code_harbor_links_on_user_id", using: :btree + + create_table "comments", force: true do |t| + t.integer "user_id" + t.integer "file_id" + t.string "user_type" + t.integer "row" + t.integer "column" + t.text "text" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "comments", ["file_id"], name: "index_comments_on_file_id", using: :btree + add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree + + create_table "consumers", force: true do |t| + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + t.string "oauth_key" + t.string "oauth_secret" + end + + create_table "errors", force: true do |t| + t.integer "execution_environment_id" + t.text "message" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "submission_id" + end + + add_index "errors", ["submission_id"], name: "index_errors_on_submission_id", using: :btree + + create_table "execution_environments", force: true do |t| + t.string "docker_image" + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + t.string "run_command" + t.string "test_command" + t.string "testing_framework" + t.text "help" + t.string "exposed_ports" + t.integer "permitted_execution_time" + t.integer "user_id" + t.string "user_type" + t.integer "pool_size" + t.integer "file_type_id" + t.integer "memory_limit" + t.boolean "network_enabled" + end + + create_table "exercises", force: true do |t| + t.text "description" + t.integer "execution_environment_id" + t.string "title" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "user_id" + t.text "instructions" + t.boolean "public" + t.string "user_type" + t.string "token" + t.integer "team_id" + t.boolean "hide_file_tree" + t.boolean "allow_file_creation" + end + + create_table "external_users", force: true do |t| + t.integer "consumer_id" + t.string "email" + t.string "external_id" + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "file_types", force: true do |t| + t.string "editor_mode" + t.string "file_extension" + t.integer "indent_size" + t.string "name" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + t.boolean "executable" + t.boolean "renderable" + t.string "user_type" + t.boolean "binary" + end + + create_table "files", force: true do |t| + t.text "content" + t.integer "context_id" + t.string "context_type" + t.integer "file_id" + t.integer "file_type_id" + t.boolean "hidden" + t.string "name" + t.boolean "read_only" + t.datetime "created_at" + t.datetime "updated_at" + t.string "native_file" + t.string "role" + t.string "hashed_content" + t.string "feedback_message" + t.float "weight" + t.string "path" + end + + add_index "files", ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type", using: :btree + + create_table "hints", force: true do |t| + t.integer "execution_environment_id" + t.string "locale" + t.text "message" + t.string "name" + t.string "regular_expression" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "internal_users", force: true do |t| + t.integer "consumer_id" + t.string "email" + t.string "name" + t.string "role" + t.datetime "created_at" + t.datetime "updated_at" + t.string "crypted_password" + t.string "salt" + t.integer "failed_logins_count", default: 0 + t.datetime "lock_expires_at" + t.string "unlock_token" + t.string "remember_me_token" + t.datetime "remember_me_token_expires_at" + t.string "reset_password_token" + t.datetime "reset_password_token_expires_at" + t.datetime "reset_password_email_sent_at" + t.string "activation_state" + t.string "activation_token" + t.datetime "activation_token_expires_at" + end + + add_index "internal_users", ["activation_token"], name: "index_internal_users_on_activation_token", using: :btree + add_index "internal_users", ["email"], name: "index_internal_users_on_email", unique: true, using: :btree + add_index "internal_users", ["remember_me_token"], name: "index_internal_users_on_remember_me_token", using: :btree + add_index "internal_users", ["reset_password_token"], name: "index_internal_users_on_reset_password_token", using: :btree + + create_table "internal_users_teams", force: true do |t| + t.integer "internal_user_id" + t.integer "team_id" + end + + add_index "internal_users_teams", ["internal_user_id"], name: "index_internal_users_teams_on_internal_user_id", using: :btree + add_index "internal_users_teams", ["team_id"], name: "index_internal_users_teams_on_team_id", using: :btree + + create_table "request_for_comments", force: true do |t| + t.integer "user_id", null: false + t.integer "exercise_id", null: false + t.integer "file_id", null: false + t.datetime "requested_at" + t.datetime "created_at" + t.datetime "updated_at" + t.string "user_type" + t.text "question" + end + + create_table "submissions", force: true do |t| + t.integer "exercise_id" + t.float "score" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + t.string "cause" + t.string "user_type" + end + + create_table "teams", force: true do |t| + t.string "name" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "testruns", force: true do |t| + t.boolean "passed" + t.text "output" + t.integer "file_id" + t.integer "submission_id" + t.datetime "created_at" + t.datetime "updated_at" + end + +end \ No newline at end of file From 81fe1f1f14a5c2e00ce45968c2e4c4f920d92cc2 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 8 Jun 2016 14:07:34 +0200 Subject: [PATCH 24/81] fix end-line of schema.rb --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index f519dfe1..104a1469 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -217,4 +217,4 @@ ActiveRecord::Schema.define(version: 20160512131539) do t.datetime "updated_at" end -end \ No newline at end of file +end From 1cd879bcb6560ed43f35a366281f40042d6c1370 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 9 Jun 2016 22:38:19 +0200 Subject: [PATCH 25/81] Scaffold file templates --- .../javascripts/file_templates.js.coffee | 3 + .../stylesheets/file_templates.css.scss | 3 + app/controllers/file_templates_controller.rb | 86 +++++++++++++++++++ app/models/file_template.rb | 10 +++ app/models/file_type.rb | 1 + app/policies/file_template_policy.rb | 7 ++ app/views/file_templates/_form.html.slim | 12 +++ app/views/file_templates/edit.html.slim | 3 + app/views/file_templates/index.html.slim | 20 +++++ app/views/file_templates/new.html.slim | 3 + app/views/file_templates/show.html.slim | 7 ++ config/locales/de.yml | 4 + config/locales/en.yml | 4 + config/routes.rb | 1 + .../20160609185708_create_file_templates.rb | 10 +++ db/schema.rb | 10 ++- 16 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/file_templates.js.coffee create mode 100644 app/assets/stylesheets/file_templates.css.scss create mode 100644 app/controllers/file_templates_controller.rb create mode 100644 app/models/file_template.rb create mode 100644 app/policies/file_template_policy.rb create mode 100644 app/views/file_templates/_form.html.slim create mode 100644 app/views/file_templates/edit.html.slim create mode 100644 app/views/file_templates/index.html.slim create mode 100644 app/views/file_templates/new.html.slim create mode 100644 app/views/file_templates/show.html.slim create mode 100644 db/migrate/20160609185708_create_file_templates.rb diff --git a/app/assets/javascripts/file_templates.js.coffee b/app/assets/javascripts/file_templates.js.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/app/assets/javascripts/file_templates.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/file_templates.css.scss b/app/assets/stylesheets/file_templates.css.scss new file mode 100644 index 00000000..bf8e27e8 --- /dev/null +++ b/app/assets/stylesheets/file_templates.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the FileTemplates controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/file_templates_controller.rb b/app/controllers/file_templates_controller.rb new file mode 100644 index 00000000..45636d61 --- /dev/null +++ b/app/controllers/file_templates_controller.rb @@ -0,0 +1,86 @@ +class FileTemplatesController < ApplicationController + before_action :set_file_template, only: [:show, :edit, :update, :destroy] + + def authorize! + authorize(@file_template || @file_templates) + end + private :authorize! + + # GET /file_templates + # GET /file_templates.json + def index + @file_templates = FileTemplate.all.order(:file_type_id).paginate(page: params[:page]) + authorize! + end + + # GET /file_templates/1 + # GET /file_templates/1.json + def show + authorize! + end + + # GET /file_templates/new + def new + @file_template = FileTemplate.new + authorize! + end + + # GET /file_templates/1/edit + def edit + authorize! + end + + # POST /file_templates + # POST /file_templates.json + def create + @file_template = FileTemplate.new(file_template_params) + authorize! + + respond_to do |format| + if @file_template.save + format.html { redirect_to @file_template, notice: 'File template was successfully created.' } + format.json { render :show, status: :created, location: @file_template } + else + format.html { render :new } + format.json { render json: @file_template.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /file_templates/1 + # PATCH/PUT /file_templates/1.json + def update + authorize! + respond_to do |format| + if @file_template.update(file_template_params) + format.html { redirect_to @file_template, notice: 'File template was successfully updated.' } + format.json { render :show, status: :ok, location: @file_template } + else + format.html { render :edit } + format.json { render json: @file_template.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /file_templates/1 + # DELETE /file_templates/1.json + def destroy + authorize! + @file_template.destroy + respond_to do |format| + format.html { redirect_to file_templates_url, notice: 'File template was successfully destroyed.' } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_file_template + @file_template = FileTemplate.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def file_template_params + params[:file_template].permit(:name, :file_type_id, :content) + end +end diff --git a/app/models/file_template.rb b/app/models/file_template.rb new file mode 100644 index 00000000..ef068e13 --- /dev/null +++ b/app/models/file_template.rb @@ -0,0 +1,10 @@ +class FileTemplate < ActiveRecord::Base + + belongs_to :file_type + + + def to_s + name + end + +end diff --git a/app/models/file_type.rb b/app/models/file_type.rb index 53bf18dc..d3b519d5 100644 --- a/app/models/file_type.rb +++ b/app/models/file_type.rb @@ -12,6 +12,7 @@ class FileType < ActiveRecord::Base has_many :execution_environments has_many :files + has_many :file_templates validates :binary, boolean_presence: true validates :editor_mode, presence: true, unless: :binary? diff --git a/app/policies/file_template_policy.rb b/app/policies/file_template_policy.rb new file mode 100644 index 00000000..b2f780a8 --- /dev/null +++ b/app/policies/file_template_policy.rb @@ -0,0 +1,7 @@ +class FileTemplatePolicy < AdminOnlyPolicy + + def show? + everyone + end + +end diff --git a/app/views/file_templates/_form.html.slim b/app/views/file_templates/_form.html.slim new file mode 100644 index 00000000..1a5c34bc --- /dev/null +++ b/app/views/file_templates/_form.html.slim @@ -0,0 +1,12 @@ += form_for(@file_template) do |f| + = render('shared/form_errors', object: @file_template) + .form-group + = f.label(:name) + = f.text_field(:name, class: 'form-control', required: true) + .form-group + = f.label(:file_type_id) + = f.collection_select(:file_type_id, FileType.all.order(:name), :id, :name, {}, class: 'form-control') + .form-group + = f.label(:content) + = f.text_area(:content, class: 'form-control') + .actions = render('shared/submit_button', f: f, object: @file_template) diff --git a/app/views/file_templates/edit.html.slim b/app/views/file_templates/edit.html.slim new file mode 100644 index 00000000..c198271f --- /dev/null +++ b/app/views/file_templates/edit.html.slim @@ -0,0 +1,3 @@ +h1 = @file_template + += render('form') diff --git a/app/views/file_templates/index.html.slim b/app/views/file_templates/index.html.slim new file mode 100644 index 00000000..3022ea53 --- /dev/null +++ b/app/views/file_templates/index.html.slim @@ -0,0 +1,20 @@ +h1 = FileTemplate.model_name.human(count: 2) + +.table-responsive + table.table + thead + tr + th = t('activerecord.attributes.file_template.name') + th = t('activerecord.attributes.file_template.file_type') + th colspan=3 = t('shared.actions') + tbody + - @file_templates.each do |file_template| + tr + td = file_template.name + td = link_to(file_template.file_type, file_type_path(file_template.file_type)) + td = link_to(t('shared.show'), file_template) + td = link_to(t('shared.edit'), edit_file_template_path(file_template)) + td = link_to(t('shared.destroy'), file_template, data: {confirm: t('shared.confirm_destroy')}, method: :delete) + += render('shared/pagination', collection: @file_templates) +p = render('shared/new_button', model: FileTemplate) diff --git a/app/views/file_templates/new.html.slim b/app/views/file_templates/new.html.slim new file mode 100644 index 00000000..bf434860 --- /dev/null +++ b/app/views/file_templates/new.html.slim @@ -0,0 +1,3 @@ +h1 = t('shared.new_model', model: FileTemplate.model_name.human) + += render('form') diff --git a/app/views/file_templates/show.html.slim b/app/views/file_templates/show.html.slim new file mode 100644 index 00000000..19f0d28f --- /dev/null +++ b/app/views/file_templates/show.html.slim @@ -0,0 +1,7 @@ +h1 + = @file_template + = render('shared/edit_button', object: @file_template) + += row(label: 'file_template.name', value: @file_template.name) += row(label: 'file_template.file_type', value: link_to(@file_template.file_type, file_type_path(@file_template.file_type))) += row(label: 'file_template.content', value: @file_template.content) diff --git a/config/locales/de.yml b/config/locales/de.yml index e7d78943..4a98e0a0 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -93,6 +93,10 @@ de: team: internal_user_ids: Mitglieder name: Name + file_template: + name: "Name" + file_type: "Dateityp" + content: "Code" models: code_harbor_link: one: CodeHarbor-Link diff --git a/config/locales/en.yml b/config/locales/en.yml index f2470fc8..de0f3aa4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -93,6 +93,10 @@ en: team: internal_user_ids: Members name: Name + file_template: + name: "Name" + file_type: "File Type" + content: "Content" models: code_harbor_link: one: CodeHarbor Link diff --git a/config/routes.rb b/config/routes.rb index 72ccb489..7965f0a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do + resources :file_templates resources :code_harbor_links resources :request_for_comments get '/my_request_for_comments', as: 'my_request_for_comments', to: 'request_for_comments#get_my_comment_requests' diff --git a/db/migrate/20160609185708_create_file_templates.rb b/db/migrate/20160609185708_create_file_templates.rb new file mode 100644 index 00000000..43cde0d0 --- /dev/null +++ b/db/migrate/20160609185708_create_file_templates.rb @@ -0,0 +1,10 @@ +class CreateFileTemplates < ActiveRecord::Migration + def change + create_table :file_templates do |t| + t.string :name + t.text :content + t.belongs_to :file_type + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 104a1469..4f7fc833 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160512131539) do +ActiveRecord::Schema.define(version: 20160609185708) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -101,6 +101,14 @@ ActiveRecord::Schema.define(version: 20160512131539) do t.datetime "updated_at" end + create_table "file_templates", force: true do |t| + t.string "name" + t.text "content" + t.integer "file_type_id" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "file_types", force: true do |t| t.string "editor_mode" t.string "file_extension" From 1e4ab7cabe2ea052d4c1251a7c6c7549559b4057 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 10 Jun 2016 11:54:11 +0200 Subject: [PATCH 26/81] py_unit_adapter now also counts errors, not just failures --- lib/py_unit_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/py_unit_adapter.rb b/lib/py_unit_adapter.rb index f7f98dae..23b86ae9 100644 --- a/lib/py_unit_adapter.rb +++ b/lib/py_unit_adapter.rb @@ -1,6 +1,6 @@ class PyUnitAdapter < TestingFrameworkAdapter COUNT_REGEXP = /Ran (\d+) test/ - FAILURES_REGEXP = /FAILED \(failures=(\d+)\)/ + FAILURES_REGEXP = /FAILED \((failures)|(errors)=(\d+)\)/ ASSERTION_ERROR_REGEXP = /AssertionError:\s(.*)/ def self.framework_name @@ -10,7 +10,7 @@ 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 + failed = matches ? matches.captures.try(:third).to_i : 0 error_matches = ASSERTION_ERROR_REGEXP.match(output[:stderr]).try(:captures) || [] {count: count, failed: failed, error_messages: error_matches} end From 4f8feb38e10c7e3591a6f7c60436c45dbd67e29d Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Fri, 10 Jun 2016 13:41:38 +0200 Subject: [PATCH 27/81] Use file template to generate new file content --- app/controllers/code_ocean/files_controller.rb | 3 +++ app/controllers/concerns/file_parameters.rb | 2 +- app/views/code_ocean/files/_form.html.slim | 3 +++ config/locales/de.yml | 1 + config/locales/en.yml | 1 + db/migrate/20160610111602_add_file_template_to_file.rb | 5 +++++ db/schema.rb | 3 ++- 7 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20160610111602_add_file_template_to_file.rb diff --git a/app/controllers/code_ocean/files_controller.rb b/app/controllers/code_ocean/files_controller.rb index 74c1932f..e38607c2 100644 --- a/app/controllers/code_ocean/files_controller.rb +++ b/app/controllers/code_ocean/files_controller.rb @@ -10,6 +10,9 @@ module CodeOcean def create @file = CodeOcean::File.new(file_params) + if @file.file_template_id + @file.content = FileTemplate.find(@file.file_template_id).content + end authorize! create_and_respond(object: @file, path: proc { implement_exercise_path(@file.context.exercise, tab: 2) }) end diff --git a/app/controllers/concerns/file_parameters.rb b/app/controllers/concerns/file_parameters.rb index e61e719e..295b66c3 100644 --- a/app/controllers/concerns/file_parameters.rb +++ b/app/controllers/concerns/file_parameters.rb @@ -1,6 +1,6 @@ module FileParameters def file_attributes - %w(content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight) + %w(content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight file_template_id) end private :file_attributes end diff --git a/app/views/code_ocean/files/_form.html.slim b/app/views/code_ocean/files/_form.html.slim index 07dd3355..ee74f4b8 100644 --- a/app/views/code_ocean/files/_form.html.slim +++ b/app/views/code_ocean/files/_form.html.slim @@ -8,5 +8,8 @@ .form-group = f.label(:file_type_id, t('activerecord.attributes.file.file_type_id')) = f.collection_select(:file_type_id, FileType.where(binary: false).order(:name), :id, :name, {selected: @exercise.execution_environment.file_type.try(:id)}, class: 'form-control') + .form-group + = f.label(:file_template_id, t('activerecord.attributes.file.file_template_id')) + = f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {}, class: 'form-control') = f.hidden_field(:context_id) .actions = render('shared/submit_button', f: f, object: CodeOcean::File.new) diff --git a/config/locales/de.yml b/config/locales/de.yml index 4a98e0a0..9a48ee4f 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -54,6 +54,7 @@ de: read_only: Schreibgeschützt role: Rolle weight: Punktzahl + file_template_id: "Dateivorlage" file_type: binary: Binär editor_mode: Editor-Modus diff --git a/config/locales/en.yml b/config/locales/en.yml index de0f3aa4..49fc8bbb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -54,6 +54,7 @@ en: read_only: Read-only role: Role weight: Score + file_template_id: "File Template" file_type: binary: Binary editor_mode: Editor Mode diff --git a/db/migrate/20160610111602_add_file_template_to_file.rb b/db/migrate/20160610111602_add_file_template_to_file.rb new file mode 100644 index 00000000..a595e90b --- /dev/null +++ b/db/migrate/20160610111602_add_file_template_to_file.rb @@ -0,0 +1,5 @@ +class AddFileTemplateToFile < ActiveRecord::Migration + def change + add_reference :files, :file_template + end +end diff --git a/db/schema.rb b/db/schema.rb index 4f7fc833..bed23c98 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160609185708) do +ActiveRecord::Schema.define(version: 20160610111602) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -140,6 +140,7 @@ ActiveRecord::Schema.define(version: 20160609185708) do t.string "feedback_message" t.float "weight" t.string "path" + t.integer "file_template_id" end add_index "files", ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type", using: :btree From c10b07690ad29138dd7be79bc2fdf21836b90b48 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Fri, 10 Jun 2016 13:46:13 +0200 Subject: [PATCH 28/81] Make file template optional --- app/views/code_ocean/files/_form.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/code_ocean/files/_form.html.slim b/app/views/code_ocean/files/_form.html.slim index ee74f4b8..4b44647d 100644 --- a/app/views/code_ocean/files/_form.html.slim +++ b/app/views/code_ocean/files/_form.html.slim @@ -10,6 +10,6 @@ = f.collection_select(:file_type_id, FileType.where(binary: false).order(:name), :id, :name, {selected: @exercise.execution_environment.file_type.try(:id)}, class: 'form-control') .form-group = f.label(:file_template_id, t('activerecord.attributes.file.file_template_id')) - = f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {}, class: 'form-control') + = f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {:include_blank => true}, class: 'form-control') = f.hidden_field(:context_id) .actions = render('shared/submit_button', f: f, object: CodeOcean::File.new) From 4d2676fea75a724faed1ef7a2beaf0d04d22c4c2 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Fri, 10 Jun 2016 17:48:04 +0200 Subject: [PATCH 29/81] Only show file templates which are available for the selected file type --- app/assets/javascripts/exercises.js | 20 ++++++++++++++++++++ app/controllers/file_templates_controller.rb | 8 ++++++++ app/policies/file_template_policy.rb | 4 ++++ app/views/code_ocean/files/_form.html.slim | 1 + config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ config/routes.rb | 6 +++++- 7 files changed, 42 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/exercises.js b/app/assets/javascripts/exercises.js index 1f861ef6..0e3805db 100644 --- a/app/assets/javascripts/exercises.js +++ b/app/assets/javascripts/exercises.js @@ -148,6 +148,22 @@ $(function() { }); }; + var updateFileTemplates = function(fileType) { + var jqxhr = $.ajax({ + url: '/file_templates/by_file_type/' + fileType + '.json', + dataType: 'json' + }); + jqxhr.done(function(response) { + var noTemplateLabel = $('#noTemplateLabel').data('text'); + var options = ""; + for (var i = 0; i < response.length; i++) { + options += "" + } + $("#code_ocean_file_file_template_id").find('option').remove().end().append($(options)); + }); + jqxhr.fail(ajaxError); + } + if ($.isController('exercises')) { if ($('table').isPresent()) { enableBatchUpdate(); @@ -162,6 +178,10 @@ $(function() { inferFileAttributes(); observeFileRoleChanges(); overrideTextareaTabBehavior(); + } else if ($('#files.jstree').isPresent()) { + var fileTypeSelect = $('#code_ocean_file_file_type_id'); + fileTypeSelect.on("change", function() {updateFileTemplates(fileTypeSelect.val())}); + updateFileTemplates(fileTypeSelect.val()); } toggleCodeHeight(); if (window.hljs) { diff --git a/app/controllers/file_templates_controller.rb b/app/controllers/file_templates_controller.rb index 45636d61..a6039500 100644 --- a/app/controllers/file_templates_controller.rb +++ b/app/controllers/file_templates_controller.rb @@ -6,6 +6,14 @@ class FileTemplatesController < ApplicationController end private :authorize! + def by_file_type + @file_templates = FileTemplate.where(:file_type_id => params[:file_type_id]) + authorize! + respond_to do |format| + format.json { render :show, status: :ok, json: @file_templates.to_json } + end + end + # GET /file_templates # GET /file_templates.json def index diff --git a/app/policies/file_template_policy.rb b/app/policies/file_template_policy.rb index b2f780a8..92ced442 100644 --- a/app/policies/file_template_policy.rb +++ b/app/policies/file_template_policy.rb @@ -4,4 +4,8 @@ class FileTemplatePolicy < AdminOnlyPolicy everyone end + def by_file_type? + everyone + end + end diff --git a/app/views/code_ocean/files/_form.html.slim b/app/views/code_ocean/files/_form.html.slim index 4b44647d..46c5b2c2 100644 --- a/app/views/code_ocean/files/_form.html.slim +++ b/app/views/code_ocean/files/_form.html.slim @@ -12,4 +12,5 @@ = f.label(:file_template_id, t('activerecord.attributes.file.file_template_id')) = f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {:include_blank => true}, class: 'form-control') = f.hidden_field(:context_id) + .hidden#noTemplateLabel data-text=t('file_template.no_template_label') .actions = render('shared/submit_button', f: f, object: CodeOcean::File.new) diff --git a/config/locales/de.yml b/config/locales/de.yml index 9a48ee4f..c0aa4b13 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -432,3 +432,5 @@ de: next_label: 'Nächste Seite →' page_gap: '…' previous_label: '← Vorherige Seite' + file_template: + no_template_label: "Leere Datei" diff --git a/config/locales/en.yml b/config/locales/en.yml index 49fc8bbb..f03e42fb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -432,3 +432,5 @@ en: next_label: 'Next Page →' page_gap: '…' previous_label: '← Previous Page' + file_template: + no_template_label: "Empty File" diff --git a/config/routes.rb b/config/routes.rb index 7965f0a3..afd8c8ce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,11 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do - resources :file_templates + resources :file_templates do + collection do + get 'by_file_type/:file_type_id', as: :by_file_type, to: :by_file_type + end + end resources :code_harbor_links resources :request_for_comments get '/my_request_for_comments', as: 'my_request_for_comments', to: 'request_for_comments#get_my_comment_requests' From 8d030e42e9621391c4c9aa4a7de32eb731cdba27 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Fri, 10 Jun 2016 18:08:57 +0200 Subject: [PATCH 30/81] Allow templates to include the file name as a macro --- app/controllers/code_ocean/files_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/code_ocean/files_controller.rb b/app/controllers/code_ocean/files_controller.rb index e38607c2..55d8b369 100644 --- a/app/controllers/code_ocean/files_controller.rb +++ b/app/controllers/code_ocean/files_controller.rb @@ -11,7 +11,9 @@ module CodeOcean def create @file = CodeOcean::File.new(file_params) if @file.file_template_id - @file.content = FileTemplate.find(@file.file_template_id).content + content = FileTemplate.find(@file.file_template_id).content + content.sub! '{{file_name}}', @file.name + @file.content = content end authorize! create_and_respond(object: @file, path: proc { implement_exercise_path(@file.context.exercise, tab: 2) }) From 7ef401f75aa97c9dd4fc86ec7deb87151891d187 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Fri, 10 Jun 2016 18:15:37 +0200 Subject: [PATCH 31/81] Add navigation item for file templates --- app/views/application/_navigation.html.slim | 2 +- config/locales/de.yml | 3 +++ config/locales/en.yml | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/views/application/_navigation.html.slim b/app/views/application/_navigation.html.slim index 4ab39e30..0f859844 100644 --- a/app/views/application/_navigation.html.slim +++ b/app/views/application/_navigation.html.slim @@ -8,7 +8,7 @@ - if current_user.admin? li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path) li.divider - - models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, InternalUser, Submission, Team].sort_by { |model| model.model_name.human(count: 2) } + - models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser, Submission, Team].sort_by { |model| model.model_name.human(count: 2) } - models.each do |model| - if policy(model).index? li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path")) diff --git a/config/locales/de.yml b/config/locales/de.yml index c0aa4b13..c0013777 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -120,6 +120,9 @@ de: file: one: Datei other: Dateien + file_template: + one: Dateivorlage + other: Dateivorlagen file_type: one: Dateityp other: Dateitypen diff --git a/config/locales/en.yml b/config/locales/en.yml index f03e42fb..1d597bf8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -120,6 +120,9 @@ en: file: one: File other: Files + file_template: + one: File Template + other: File Templates file_type: one: File Type other: File Types From 74b33492cb58a25c2efca4fff1f4a9c52c14dd4c Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Tue, 14 Jun 2016 10:42:15 +0200 Subject: [PATCH 32/81] fixed python failure regex. --- lib/py_unit_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/py_unit_adapter.rb b/lib/py_unit_adapter.rb index 23b86ae9..11b8cb09 100644 --- a/lib/py_unit_adapter.rb +++ b/lib/py_unit_adapter.rb @@ -1,6 +1,6 @@ class PyUnitAdapter < TestingFrameworkAdapter COUNT_REGEXP = /Ran (\d+) test/ - FAILURES_REGEXP = /FAILED \((failures)|(errors)=(\d+)\)/ + FAILURES_REGEXP = /FAILED \((failures|errors)=(\d+)\)/ ASSERTION_ERROR_REGEXP = /AssertionError:\s(.*)/ def self.framework_name @@ -10,7 +10,7 @@ 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(:third).to_i : 0 + failed = matches ? matches.captures.try(:second).to_i : 0 error_matches = ASSERTION_ERROR_REGEXP.match(output[:stderr]).try(:captures) || [] {count: count, failed: failed, error_messages: error_matches} end From f2e2c232e48b42e5c3e7c70299c1e41466ddfc35 Mon Sep 17 00:00:00 2001 From: yqbk Date: Tue, 14 Jun 2016 17:01:50 +0200 Subject: [PATCH 33/81] round result to fixed 2 digits --- app/assets/javascripts/editor.js.erb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 0bb22853..10123521 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -707,7 +707,8 @@ $(function() { var score = $('#score').data('score'); var maxium_score = $('#score').data('maximum-score'); if (score >= 0 && score <= maxium_score && maxium_score >0 ) { - var percentage_score = score / maxium_score * 100 + var rounded_number = (score / maxium_score ).toFixed(2); + var percentage_score = rounded_number * 100 $('.score').html(percentage_score + '%'); } else { From d5b78c29da39c15df6a61e7de1da83cf2b3f1e63 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Tue, 14 Jun 2016 17:19:52 +0200 Subject: [PATCH 34/81] Fixed rounding issue caused by JavaScript and the conversion to String. --- app/assets/javascripts/editor.js.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 10123521..50217b1f 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -707,8 +707,7 @@ $(function() { var score = $('#score').data('score'); var maxium_score = $('#score').data('maximum-score'); if (score >= 0 && score <= maxium_score && maxium_score >0 ) { - var rounded_number = (score / maxium_score ).toFixed(2); - var percentage_score = rounded_number * 100 + var percentage_score = (score / maxium_score * 100 ).toFixed(2); $('.score').html(percentage_score + '%'); } else { From a1ead9658e22f413eadd719a1c251bfbb49c9bce Mon Sep 17 00:00:00 2001 From: tstaubitz Date: Tue, 14 Jun 2016 17:27:15 +0200 Subject: [PATCH 35/81] Update de.yml --- config/locales/de.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 30f4f7ef..ae7fb930 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -212,7 +212,7 @@ de: 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 Punkten können nur bis zum Ablauf der Abgabefrist an die E-Learning-Plattform übertragen werden.' + 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 From 8ddf1e158e02410874fe4354ae6eb3067f2cdc2e Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Tue, 14 Jun 2016 17:53:54 +0200 Subject: [PATCH 36/81] Fixed background and pen coloring on client side (javascript). See: https://github.com/openHPI/codeocean/issues/59 Kudos to waywwaard. --- app/assets/javascripts/turtle.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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(); From 894f8abda233fd00dd0efe6bcb2b2cd704592f63 Mon Sep 17 00:00:00 2001 From: yqbk Date: Wed, 15 Jun 2016 12:03:34 +0200 Subject: [PATCH 37/81] round result to fixed 0 digits in percentage output --- app/assets/javascripts/editor.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 50217b1f..f000a23a 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -707,7 +707,7 @@ $(function() { var score = $('#score').data('score'); var maxium_score = $('#score').data('maximum-score'); if (score >= 0 && score <= maxium_score && maxium_score >0 ) { - var percentage_score = (score / maxium_score * 100 ).toFixed(2); + var percentage_score = (score / maxium_score * 100 ).toFixed(0); $('.score').html(percentage_score + '%'); } else { From fbb83e3057ddb4fe01c77badb8d637bc8df4e618 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 16 Jun 2016 11:38:16 +0200 Subject: [PATCH 38/81] fix comparison problem caused by javascript comparisons based on Strings. ("1.00" <= "1.0" --> false) --- app/assets/javascripts/editor.js.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index f000a23a..1e64195e 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -704,8 +704,8 @@ $(function() { }; var renderScore = function() { - var score = $('#score').data('score'); - var maxium_score = $('#score').data('maximum-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 + '%'); From 9e644563e3eae71b48b990323c3d9fd545628cf9 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 16 Jun 2016 11:39:13 +0200 Subject: [PATCH 39/81] adapt regex for py_unit_adapter once more... hopefully we got all cases now. --- lib/py_unit_adapter.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/py_unit_adapter.rb b/lib/py_unit_adapter.rb index 11b8cb09..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|errors)=(\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(:second).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 From ec26a095f692e2102e8a87ea85d36e1655582099 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 14:48:57 +0200 Subject: [PATCH 40/81] First working version of mails on comments. --- app/controllers/comments_controller.rb | 14 +++++++++++--- app/mailers/user_mailer.rb | 10 +++++++--- app/views/request_for_comments/show.html.erb | 5 +++-- app/views/user_mailer/got_new_comment.slim | 2 +- config/locales/de.yml | 7 +++++++ config/locales/en.yml | 10 ++++++++++ 6 files changed, 39 insertions(+), 9 deletions(-) diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index fd6840ff..fe7f454c 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -46,7 +46,11 @@ class CommentsController < ApplicationController # POST /comments # POST /comments.json def create - @comment = Comment.new(comment_params) + @comment = Comment.new(comment_params_without_request_id) + + if comment_params[:request_id] + UserMailer.got_new_comment(@comment, RequestForComment.find(comment_params[:request_id]), current_user) + end respond_to do |format| if @comment.save @@ -64,7 +68,7 @@ class CommentsController < ApplicationController # PATCH/PUT /comments/1.json def update respond_to do |format| - if @comment.update(comment_params) + if @comment.update(comment_params_without_request_id) format.html { head :no_content, notice: 'Comment was successfully updated.' } format.json { render :show, status: :ok, location: @comment } else @@ -101,10 +105,14 @@ class CommentsController < ApplicationController @comment = Comment.find(params[:id]) end + def comment_params_without_request_id + comment_params.except :request_id + end + # Never trust parameters from the scary internet, only allow the white list through. def comment_params #params.require(:comment).permit(:user_id, :file_id, :row, :column, :text) # fuer production mode, damit böse menschen keine falsche user_id uebergeben: - params.require(:comment).permit(:file_id, :row, :column, :text).merge(user_id: current_user.id, user_type: current_user.class.name) + params.require(:comment).permit(:file_id, :row, :column, :text, :request_id).merge(user_id: current_user.id, user_type: current_user.class.name) end end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 8cb5eed1..e1773d48 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -12,8 +12,12 @@ class UserMailer < ActionMailer::Base mail(subject: t('mailers.user_mailer.reset_password.subject'), to: user.email) end - def got_new_comment(comment, user, commenting_user) - @commenting_user = commenting_user - mail(subject: t('mailers.user_mailer.got_new_comment.subject'), to: user.email) + def got_new_comment(comment, request_for_comment, commenting_user) + # todo: check whether we can take the last known locale of the receiver? + @receiver_displayname = request_for_comment.user.displayname + @commenting_user_displayname = commenting_user.displayname + @comment_text = comment.text + @rfc_link = request_for_comment_url(request_for_comment) + mail(subject: t('mailers.user_mailer.got_new_comment.subject', commenting_user_displayname: @commenting_user_displayname), to: request_for_comment.user.email).deliver end end diff --git a/app/views/request_for_comments/show.html.erb b/app/views/request_for_comments/show.html.erb index e4dc7f06..1d6bb54c 100644 --- a/app/views/request_for_comments/show.html.erb +++ b/app/views/request_for_comments/show.html.erb @@ -1,5 +1,5 @@
-

<%= Exercise.find(@request_for_comment.exercise_id) %>

+

<%= Exercise.find(@request_for_comment.exercise_id) %>

<% user = @request_for_comment.user @@ -101,7 +101,8 @@ do not put a carriage return in the line below. it will be present in the presen file_id: file_id, row: row, column: 0, - text: commenttext + text: commenttext, + request_id: $('h4').data('rfc-id') } }, dataType: 'json', diff --git a/app/views/user_mailer/got_new_comment.slim b/app/views/user_mailer/got_new_comment.slim index 3b8afa5b..656e245d 100644 --- a/app/views/user_mailer/got_new_comment.slim +++ b/app/views/user_mailer/got_new_comment.slim @@ -1 +1 @@ -== t('mailers.user_mailer.activation_needed.body', link: link_to(@activation_url, @activation_url)) +== t('mailers.user_mailer.got_new_comment.body', receiver_displayname: @receiver_displayname, link: link_to(@rfc_link, @rfc_link), commenting_user_displayname: @commenting_user_displayname, comment_text: @comment_text) diff --git a/config/locales/de.yml b/config/locales/de.yml index ae7fb930..a3b580b5 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -325,6 +325,13 @@ de: activation_needed: body: 'Bitte besuchen Sie %{link} und wählen Sie ein Passwort, um Ihre Registrierung abzuschließen.' subject: Bitte schließen Sie Ihre Registrierung ab. + got_new_comment: + body: | + Hallo %{receiver_displayname}, + + es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean. + Sie finden ihn hier: %{link} + subject: Sie haben einen neuen Kommentar von %{commenting_user_displayname} auf CodeOcean erhalten. reset_password: body: 'Bitte besuchen Sie %{link}, sofern Sie Ihr Passwort zurücksetzen wollen.' subject: Anweisungen zum Zurücksetzen Ihres Passworts diff --git a/config/locales/en.yml b/config/locales/en.yml index 6cc9dcaf..1162971d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -325,6 +325,16 @@ en: activation_needed: body: 'Please visit %{link} and set up a password in order to complete your registration.' subject: Please complete your registration. + got_new_comment: + body: | + Dear %{receiver_displayname}, + + you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean. + You can find it here: %{link} + + Best regards, + your Teaching Team + subject: 'You received a new comment on CodeOcean from %{commenting_user_displayname}.' reset_password: body: 'Please visit %{link} if you want to reset your password.' subject: Password reset instructions From 003e89b23043645cf0ec01e4473722a8f18f8805 Mon Sep 17 00:00:00 2001 From: John Geiger Date: Fri, 17 Jun 2016 15:03:07 +0200 Subject: [PATCH 41/81] x axis stuff --- app/assets/javascripts/exercise_graphs.js | 33 +++++++---------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index f5640788..f98509b3 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -141,20 +141,13 @@ $(function() { 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); + x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true); } else if(largestSubmittedTimeStamp.cause == "autosave"){ largestArrayForRange = submissionsAutosaves; x.domain([0,largestArrayForRange[largestArrayForRange.length - 1]]).clamp(true); @@ -162,12 +155,6 @@ $(function() { 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) { @@ -212,15 +199,15 @@ $(function() { .style('font-size', 20) .style('text-decoration', 'underline'); - // - // svg.append("path") - // //.datum() - // .attr("class", "line") - // .attr('id', 'myPath')// new - // .attr("stroke", "black") - // .attr("stroke-width", 5) - // .attr("fill", "none")// end new - // .attr("d", line(submissionsScoreAndTimeAssess));//--- + + svg.append("path") + //.datum() + .attr("class", "line") + .attr('id', 'myPath')// new + .attr("stroke", "black") + .attr("stroke-width", 5) + .attr("fill", "none")// end new + .attr("d", line(submissionsScoreAndTimeAssess));//--- svg.append("path") .datum(submissionsScoreAndTimeAssess) From 5840b626922ba4e990a2f6ff9b11f501594966ad Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 15:04:17 +0200 Subject: [PATCH 42/81] 2nd part of the fix. --- app/assets/javascripts/exercise_graphs.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/exercise_graphs.js b/app/assets/javascripts/exercise_graphs.js index f98509b3..5f521b39 100644 --- a/app/assets/javascripts/exercise_graphs.js +++ b/app/assets/javascripts/exercise_graphs.js @@ -226,20 +226,17 @@ $(function() { .attr("cx", function(d) { return x(d[1]); }) .attr("cy", function(d) { return y(d[0]); }); - - svg.append("path") - .datum(submissionsScoreAndTimeSubmits) - .attr("class", "line2") - .attr('id', 'myPath')// new - .attr("stroke", "blue") - .attr("stroke-width", 5) - .attr("fill", "none")// end new - .attr("d", line);//--- + if (submissionsScoreAndTimeSubmits.length > 0){ + // get rid of the 0 element at the beginning + submissionsScoreAndTimeSubmits.shift(); + } svg.selectAll("dot") // Add dots to submits .data(submissionsScoreAndTimeSubmits) .enter().append("circle") - .attr("r", 3.5) + .attr("r", 6) + .attr("stroke", "black") + .attr("fill", "blue") .attr("cx", function(d) { return x(d[1]); }) .attr("cy", function(d) { return y(d[0]); }); From 96de763b8314c0fb6a194fc985cb6191c651dbe0 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 16:42:07 +0200 Subject: [PATCH 43/81] Handle scoring presentation to client via websockets --- app/assets/javascripts/editor.js.erb | 19 ++++++++++--------- app/controllers/submissions_controller.rb | 6 +++++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index 1e64195e..f9555e92 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -184,8 +184,8 @@ $(function() { (streamed ? evaluateCodeWithStreamedResponse : evaluateCodeWithoutStreamedResponse)(url, callback); }; - var evaluateCodeWithStreamedResponse = function(url, callback) { - initWebsocketConnection(url); + var evaluateCodeWithStreamedResponse = function(url, onmessageFunction) { + initWebsocketConnection(url, onmessageFunction); // TODO only init turtle when required initTurtle(); @@ -306,9 +306,10 @@ $(function() { } }; - var handleScoringResponse = function(response) { - printScoringResults(response); - var score = _.reduce(response, function(sum, result) { + var handleScoringResponse = function(websocket_event) { + results = JSON.parse(websocket_event.data); + printScoringResults(results); + var score = _.reduce(results, function(sum, result) { return sum + result.score * result.weight; }, 0).toFixed(2); $('#score').data('score', score); @@ -743,7 +744,7 @@ $(function() { showSpinner($('#run')); toggleButtonStates(); var url = response.run_url.replace(FILENAME_URL_PLACEHOLDER, active_file.filename); - evaluateCode(url, true, printChunk); + evaluateCode(url, true, function(evt) { parseCanvasMessage(evt.data, true); }); }); } }; @@ -778,7 +779,7 @@ $(function() { createSubmission(this, null, function(response) { showSpinner($('#assess')); var url = response.score_url; - evaluateCode(url, false, handleScoringResponse); + evaluateCode(url, true, handleScoringResponse); }); }; @@ -974,14 +975,14 @@ $(function() { $('#test').toggle(isActiveFileTestable()); }; - var initWebsocketConnection = function(url) { + var initWebsocketConnection = function(url, onmessageFunction) { //TODO: get the protocol from config file dependent on environment. (dev: ws, prod: wss) //causes: Puma::HttpParserError: Invalid HTTP format, parsing fails. //TODO: make sure that this gets cached. websocket = new WebSocket('<%= DockerClient.config['ws_client_protocol'] %>' + window.location.hostname + ':' + window.location.port + url); websocket.onopen = function(evt) { resetOutputTab(); }; // todo show some kind of indicator for established connection websocket.onclose = function(evt) { /* expected at some point */ }; - websocket.onmessage = function(evt) { parseCanvasMessage(evt.data, true); }; + websocket.onmessage = onmessageFunction; websocket.onerror = function(evt) { showWebsocketError(); }; websocket.flush = function() { this.send('\n'); } }; diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 4f9d6a5b..c656ea87 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -214,7 +214,11 @@ class SubmissionsController < ApplicationController end def score - render(json: score_submission(@submission)) + hijack do |tubesock| + Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive? + # tubesock is the socket to the client + tubesock.send_data JSON.dump(score_submission(@submission)) + end end def set_docker_client From 7c1be5594a72d210997fb2757ef3448712cbbe55 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 17:23:47 +0200 Subject: [PATCH 44/81] also handle testcommand via websocket --- app/assets/javascripts/editor.js.erb | 11 ++++++----- app/controllers/submissions_controller.rb | 10 ++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index f9555e92..dc79ccff 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -350,13 +350,14 @@ $(function() { flowrOutputBuffer = ''; }; - var handleTestResponse = function(response) { + var handleTestResponse = function(websocket_event) { + result = JSON.parse(websocket_event.data); clearOutput(); - printOutput(response[0], false, 0); + printOutput(result, false, 0); if (qa_api) { - qa_api.executeCommand('syncOutput', [response]); + qa_api.executeCommand('syncOutput', [result]); } - showStatus(response[0]); + showStatus(result); showTab(1); }; @@ -956,7 +957,7 @@ $(function() { createSubmission(this, null, function(response) { showSpinner($('#test')); var url = response.test_url.replace(FILENAME_URL_PLACEHOLDER, active_file.filename); - evaluateCode(url, false, handleTestResponse); + evaluateCode(url, true, handleTestResponse); }); } }; diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index c656ea87..335e4b0c 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -270,8 +270,14 @@ class SubmissionsController < ApplicationController private :store_error def test - output = @docker_client.execute_test_command(@submission, params[:filename]) - render(json: [output]) + hijack do |tubesock| + Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive? + + output = @docker_client.execute_test_command(@submission, params[:filename]) + + # tubesock is the socket to the client + tubesock.send_data JSON.dump(output) + end end def with_server_sent_events From d29cf9cf6134691cdeba974499d4979b46521848 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 19:22:25 +0200 Subject: [PATCH 45/81] Also support run output for QaApi --- app/assets/javascripts/editor.js.erb | 15 +++++++++++++++ lib/docker_client.rb | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index dc79ccff..f9ab5b94 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -37,6 +37,7 @@ $(function() { var ENTER_KEY_CODE = 13; var flowrOutputBuffer = ""; + var QaApiOutputBuffer = {'stdout': '', 'stderr': ''}; var flowrResultHtml = '

' var ajax = function(options) { @@ -317,6 +318,14 @@ $(function() { showTab(2); }; + var handleQaApiOutput = function() { + if (qa_api) { + qa_api.executeCommand('syncOutput', [[QaApiOutputBuffer]]); + // reset the object + } + QaApiOutputBuffer = {'stdout': '', 'stderr': ''}; + } + // activate flowr only for half of the audience var isFlowrEnabled = true;//parseInt($('#editor').data('user-id'))%2 == 0; var handleStderrOutputForFlowr = function() { @@ -586,10 +595,15 @@ $(function() { } else if (output.stderr) { element.addClass('text-warning').append(output.stderr); + flowrOutputBuffer += output.stderr; + QaApiOutputBuffer.stderr += output.stderr; } else if (output.stdout) { //if (output_mode_is_streaming){ element.addClass('text-success').append(output.stdout); flowrOutputBuffer += output.stdout; + QaApiOutputBuffer.stdout += output.stdout; + + //}else{ // element.addClass('text-success'); // element.data('content_buffer' , element.data('content_buffer') + output.stdout); @@ -1037,6 +1051,7 @@ $(function() { break; case 'exit': killWebsocketAndContainer(); + handleQaApiOutput(); handleStderrOutputForFlowr(); break; case 'timeout': diff --git a/lib/docker_client.rb b/lib/docker_client.rb index 80986377..f993c37c 100644 --- a/lib/docker_client.rb +++ b/lib/docker_client.rb @@ -200,7 +200,7 @@ class DockerClient execute_command(command, nil, block) end - #only used by server sent events (deprecated?) + #only used by score def execute_command(command, before_execution_block, output_consuming_block) #tries ||= 0 @container = DockerContainerPool.get_container(@execution_environment) From c21bf3f3d3aeb4a2d4130886e644e7e69d071249 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Fri, 17 Jun 2016 19:24:04 +0200 Subject: [PATCH 46/81] Adapt Modernizr requires.. We no longer need eventsource --- app/assets/javascripts/editor.js.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb index f9ab5b94..c00d7350 100644 --- a/app/assets/javascripts/editor.js.erb +++ b/app/assets/javascripts/editor.js.erb @@ -538,8 +538,8 @@ $(function() { }; var isBrowserSupported = function() { - // eventsource tests for server send events (used for scoring), websockets is used for run - return Modernizr.eventsource && Modernizr.websockets; + // websockets is used for run, score and test + return Modernizr.websockets; }; var populatePanel = function(panel, result, index) { From 918da5270dddb74fd9b985ef7f18547f4b8c5d5c Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 22 Jun 2016 13:06:35 +0200 Subject: [PATCH 47/81] added id for h4 holding the rfc-id updated locales. --- app/views/request_for_comments/show.html.erb | 4 ++-- config/locales/de.yml | 2 ++ config/locales/en.yml | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/request_for_comments/show.html.erb b/app/views/request_for_comments/show.html.erb index 1d6bb54c..3f353e32 100644 --- a/app/views/request_for_comments/show.html.erb +++ b/app/views/request_for_comments/show.html.erb @@ -1,5 +1,5 @@
-

<%= Exercise.find(@request_for_comment.exercise_id) %>

+

<%= Exercise.find(@request_for_comment.exercise_id) %>

<% user = @request_for_comment.user @@ -102,7 +102,7 @@ do not put a carriage return in the line below. it will be present in the presen row: row, column: 0, text: commenttext, - request_id: $('h4').data('rfc-id') + request_id: $('h4#exercise_caption').data('rfc-id') } }, dataType: 'json', diff --git a/config/locales/de.yml b/config/locales/de.yml index a3b580b5..a4d69694 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -331,6 +331,8 @@ de: es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean. Sie finden ihn hier: %{link} + + Diese Mail wurde automatisch von CodeOcean verschickt. subject: Sie haben einen neuen Kommentar von %{commenting_user_displayname} auf CodeOcean erhalten. reset_password: body: 'Bitte besuchen Sie %{link}, sofern Sie Ihr Passwort zurücksetzen wollen.' diff --git a/config/locales/en.yml b/config/locales/en.yml index 1162971d..28344446 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -332,8 +332,7 @@ en: you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean. You can find it here: %{link} - Best regards, - your Teaching Team + This mail was automatically sent by CodeOcean. subject: 'You received a new comment on CodeOcean from %{commenting_user_displayname}.' reset_password: body: 'Please visit %{link} if you want to reset your password.' From 83cf3b23212fcf78f3953a9dbb3697cc5dbac6c6 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 22 Jun 2016 14:16:23 +0200 Subject: [PATCH 48/81] added line breaks (
tags) in the email body. --- config/locales/de.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index a4d69694..32ab47ee 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -327,12 +327,12 @@ de: subject: Bitte schließen Sie Ihre Registrierung ab. got_new_comment: body: | - Hallo %{receiver_displayname}, - + Hallo %{receiver_displayname},
+
es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean. - Sie finden ihn hier: %{link} - - Diese Mail wurde automatisch von CodeOcean verschickt. + Sie finden ihn hier: %{link}
+
+ Diese Mail wurde automatisch von CodeOcean verschickt.
subject: Sie haben einen neuen Kommentar von %{commenting_user_displayname} auf CodeOcean erhalten. reset_password: body: 'Bitte besuchen Sie %{link}, sofern Sie Ihr Passwort zurücksetzen wollen.' From e0eac2036114a75726bb7242dc044cdcec295cff Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Wed, 22 Jun 2016 14:21:17 +0200 Subject: [PATCH 49/81] added some more line breaks to the mail bodys of mails on new comments. --- config/locales/de.yml | 2 +- config/locales/en.yml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 32ab47ee..7f0afc00 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -329,7 +329,7 @@ de: body: | Hallo %{receiver_displayname},

- es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean. + es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean.
Sie finden ihn hier: %{link}

Diese Mail wurde automatisch von CodeOcean verschickt.
diff --git a/config/locales/en.yml b/config/locales/en.yml index 28344446..d622ea88 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -327,11 +327,11 @@ en: subject: Please complete your registration. got_new_comment: body: | - Dear %{receiver_displayname}, - - you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean. - You can find it here: %{link} - + Dear %{receiver_displayname},
+
+ you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean.
+ You can find it here: %{link}
+
This mail was automatically sent by CodeOcean. subject: 'You received a new comment on CodeOcean from %{commenting_user_displayname}.' reset_password: From b7ce38996108409242e8335ca769736adcb76d51 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 23 Jun 2016 10:47:44 +0200 Subject: [PATCH 50/81] Adapted locales so that german as well as english texts are now sent for every new comment. --- config/locales/de.yml | 12 ++++++++++++ config/locales/en.yml | 14 +++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 7f0afc00..d227e7b7 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -327,12 +327,24 @@ de: subject: Bitte schließen Sie Ihre Registrierung ab. got_new_comment: body: | + English version below
+ _________________________
+
Hallo %{receiver_displayname},

es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean.
Sie finden ihn hier: %{link}

Diese Mail wurde automatisch von CodeOcean verschickt.
+
+ _________________________
+
+ Dear %{receiver_displayname},
+
+ you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean.
+ You can find it here: %{link}
+
+ This mail was automatically sent by CodeOcean.
subject: Sie haben einen neuen Kommentar von %{commenting_user_displayname} auf CodeOcean erhalten. reset_password: body: 'Bitte besuchen Sie %{link}, sofern Sie Ihr Passwort zurücksetzen wollen.' diff --git a/config/locales/en.yml b/config/locales/en.yml index d622ea88..684a350d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -327,12 +327,24 @@ en: subject: Please complete your registration. got_new_comment: body: | + English version below
+ _________________________
+
+ Hallo %{receiver_displayname},
+
+ es gibt einen neuen Kommentar von %{commenting_user_displayname} zu Ihrer Kommentaranfrage auf CodeOcean.
+ Sie finden ihn hier: %{link}
+
+ Diese Mail wurde automatisch von CodeOcean verschickt.
+
+ _________________________
+
Dear %{receiver_displayname},

you received a new comment from %{commenting_user_displayname} to your request for comments on CodeOcean.
You can find it here: %{link}

- This mail was automatically sent by CodeOcean. + This mail was automatically sent by CodeOcean.
subject: 'You received a new comment on CodeOcean from %{commenting_user_displayname}.' reset_password: body: 'Please visit %{link} if you want to reset your password.' From bd6d4c4d71987ecee9dd7d7237975399a1e27378 Mon Sep 17 00:00:00 2001 From: Ralf Teusner Date: Thu, 23 Jun 2016 14:23:28 +0200 Subject: [PATCH 51/81] Syntax Highlighting for the Request for Comment View --- app/views/request_for_comments/show.html.erb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/request_for_comments/show.html.erb b/app/views/request_for_comments/show.html.erb index 3f353e32..a1ffb5ff 100644 --- a/app/views/request_for_comments/show.html.erb +++ b/app/views/request_for_comments/show.html.erb @@ -25,22 +25,32 @@ <% submission.files.each do |file| %> <%= (file.path or "") + "/" + file.name + file.file_type.file_extension %> -

<%= file.content %> +
<%= file.content %>
<% end %> <%= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.dialogtitle'), template: 'exercises/_comment_dialogcontent') %>