From 81a079a25664565c3fc324ffa21cfeaae0dab0ae Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 8 Mar 2018 14:57:26 +0100 Subject: [PATCH 1/6] Allow to use simple templates in hints to embed captured error values --- app/models/structured_error.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/structured_error.rb b/app/models/structured_error.rb index c1f6669c..851b3fb8 100644 --- a/app/models/structured_error.rb +++ b/app/models/structured_error.rb @@ -3,11 +3,21 @@ class StructuredError < ActiveRecord::Base belongs_to :submission belongs_to :file, class_name: 'CodeOcean::File' + has_many :structured_error_attributes + def self.create_from_template(template, message_buffer, submission) instance = self.create(error_template: template, submission: submission) - template.error_template_attributes.each do |attribute| + template.error_template_attributes.each do | attribute | StructuredErrorAttribute.create_from_template(attribute, instance, message_buffer) end instance end + + def hint + content = error_template.hint + structured_error_attributes.each do | attribute | + content.sub! "{{#{attribute.error_template_attribute.key}}}", attribute.value if attribute.match + end + content + end end From d510639d724579f108074292341c8bba836f9176 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 8 Mar 2018 15:07:05 +0100 Subject: [PATCH 2/6] Add help text to new/edit actions --- app/views/error_templates/_form.html.slim | 1 + config/locales/de.yml | 1 + config/locales/en.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/app/views/error_templates/_form.html.slim b/app/views/error_templates/_form.html.slim index f9363155..d9716ce3 100644 --- a/app/views/error_templates/_form.html.slim +++ b/app/views/error_templates/_form.html.slim @@ -16,4 +16,5 @@ .form-group = f.label(:hint) = f.text_field(:hint, class: 'form-control') + .help-block == t('error_templates.hints.hint_templates') .actions = render('shared/submit_button', f: f, object: @error_template) diff --git a/config/locales/de.yml b/config/locales/de.yml index ce03d90f..4eac60b8 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -666,6 +666,7 @@ de: error_templates: hints: signature: "Ein regulärer Ausdruck in Ruby-Syntax und ohne führende und schließende \"/\"" + hint_templates: 'Attributnamen in {{doppelten geschweiften Klammern}} werden zur Laufzeit durch die jeweiligen Attributwerte ersetzt. Beispiel: "Der Fehler ist in Zeile {{Line}}." --(StructuredError: {Line: 4})--> "Der Fehler ist in Zeile 4."' attributes: "Attribute" add_attribute: "Attribut hinzufügen" comments: diff --git a/config/locales/en.yml b/config/locales/en.yml index be92de5b..999a644c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -666,6 +666,7 @@ en: error_templates: hints: signature: "A regular expression in Ruby syntax without leading and trailing \"/\"" + hint_templates: 'Attribute names in {{double curly braces}} are replaced by the corresponding attribute value at runtime, e.g. "The error occurs in line {{Line}}." --(StructuredError: {Line: 4})--> "The error occurs in line 4."' attributes: "Attributes" add_attribute: "Add attribute" comments: From a228541dd339fa27403009347ff90fbc6f993b74 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 8 Mar 2018 15:25:39 +0100 Subject: [PATCH 3/6] Send hints to the frontend via websocket connection --- app/controllers/submissions_controller.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 410c30cd..009e93b7 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -197,7 +197,9 @@ class SubmissionsController < ApplicationController def kill_socket(tubesock) # search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb) - extract_errors + extract_errors.each do | error | + tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint}) + end # save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb) save_run_output @@ -284,14 +286,16 @@ class SubmissionsController < ApplicationController end def extract_errors + results = [] unless @raw_output.blank? @submission.exercise.execution_environment.error_templates.each do |template| pattern = Regexp.new(template.signature).freeze if pattern.match(@raw_output) - StructuredError.create_from_template(template, @raw_output, @submission) + results << StructuredError.create_from_template(template, @raw_output, @submission) end end end + results end def score From ccdcc43431060f7f9d25229764787d5c063a425e Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Thu, 8 Mar 2018 16:20:07 +0100 Subject: [PATCH 4/6] Display hints in editor frontend --- app/assets/javascripts/editor/editor.js.erb | 27 ++++++++++++++++++- .../javascripts/editor/execution.js.erb | 3 ++- app/assets/stylesheets/editor.css.scss | 21 +++++++++++++++ app/controllers/submissions_controller.rb | 2 +- app/views/exercises/_editor_output.html.slim | 5 +++- config/locales/de.yml | 2 ++ config/locales/en.yml | 2 ++ 7 files changed, 58 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb index f6ff6887..e16d37a7 100644 --- a/app/assets/javascripts/editor/editor.js.erb +++ b/app/assets/javascripts/editor/editor.js.erb @@ -476,6 +476,7 @@ configureEditors: function () { this.clearOutput(); $('#hint').fadeOut(); $('#flowrHint').fadeOut(); + this.clearHints(); this.showOutputBar(); }, @@ -512,6 +513,30 @@ configureEditors: function () { } }, + clearHints: function() { + var container = $('#error-hints'); + container.find('ul.body > li.hint').remove(); + container.fadeOut(); + }, + + showHint: function(message) { + var template = function(description, hint) { + return '\ +
  • \ +
    \ + ' + description + '\ +
    \ +
    \ + ' + hint + '\ +
    \ +
  • \ + ' + }; + var container = $('#error-hints'); + container.find('ul.body').append(template(message.description, message.hint)); + container.fadeIn(); + }, + showContainerDepletedMessage: function() { $.flash.danger({ icon: ['fa', 'fa-clock-o'], @@ -692,4 +717,4 @@ configureEditors: function () { // create autosave when the editor is opened the first time this.autosave(); } -}; \ No newline at end of file +}; diff --git a/app/assets/javascripts/editor/execution.js.erb b/app/assets/javascripts/editor/execution.js.erb index 37c53cb0..0d2015e4 100644 --- a/app/assets/javascripts/editor/execution.js.erb +++ b/app/assets/javascripts/editor/execution.js.erb @@ -43,6 +43,7 @@ CodeOceanEditorWebsocket = { this.websocket.on('exit', this.handleExitCommand.bind(this)); this.websocket.on('timeout', this.showTimeoutMessage.bind(this)); this.websocket.on('status', this.showStatus.bind(this)); + this.websocket.on('hint', this.showHint.bind(this)); }, handleExitCommand: function() { @@ -53,4 +54,4 @@ CodeOceanEditorWebsocket = { this.cleanUpTurtle(); this.cleanUpUI(); } -}; \ No newline at end of file +}; diff --git a/app/assets/stylesheets/editor.css.scss b/app/assets/stylesheets/editor.css.scss index a38157b4..d98868eb 100644 --- a/app/assets/stylesheets/editor.css.scss +++ b/app/assets/stylesheets/editor.css.scss @@ -193,3 +193,24 @@ button i.fa-spin { .enforce-bottom-margin { margin-bottom: 5px !important; } + +#error-hints { + display: none; + background-color: #FAFAFA; + + .heading { + font-weight: bold; + font-size: larger; + } + + ul.body { + + li.hint { + .description { + font-style: italic; + } + + .hint {} + } + } +} diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 009e93b7..4f1bb49f 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -198,7 +198,7 @@ class SubmissionsController < ApplicationController def kill_socket(tubesock) # search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb) extract_errors.each do | error | - tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint}) + tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint, description: error.error_template.description}) end # save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb) diff --git a/app/views/exercises/_editor_output.html.slim b/app/views/exercises/_editor_output.html.slim index 4b255d5a..36401d30 100644 --- a/app/views/exercises/_editor_output.html.slim +++ b/app/views/exercises/_editor_output.html.slim @@ -47,9 +47,12 @@ div id='output_sidebar_uncollapsed' class='hidden col-sm-12 enforce-bottom-margi input#prompt-input.form-control type='text' span.input-group-btn button#prompt-submit.btn.btn-primary type="button" = t('exercises.editor.send') + #error-hints + .heading = t('exercises.implement.error_hints.heading') + ul.body #output pre = t('exercises.implement.no_output_yet') - if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] #flowrHint.panel.panel-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab' .panel-heading = 'Gain more insights here' - .panel-body \ No newline at end of file + .panel-body diff --git a/config/locales/de.yml b/config/locales/de.yml index 4eac60b8..fcf5f632 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -327,6 +327,8 @@ de: break_intervention: title: "Pause" text: "Uns ist aufgefallen, dass du schon lange an dieser Aufgabe arbeitest. Möchtest du vielleicht später weiter machen um erstmal auf neue Gedanken zu kommen?" + error_hints: + heading: "Hinweise" index: clone: Duplizieren implement: Implementieren diff --git a/config/locales/en.yml b/config/locales/en.yml index 999a644c..90544a34 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -327,6 +327,8 @@ en: break_intervention: title: "Break" text: "We recognized that you are already working quite a while on this exercise. We would like to encourage you to take a break and come back later." + error_hints: + heading: "Hints" index: clone: Duplicate implement: Implement From 841a885711e91721872118ac203620381ea5a9a1 Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Tue, 13 Mar 2018 14:52:40 +0100 Subject: [PATCH 5/6] Send hints when scoring --- app/assets/javascripts/editor/execution.js.erb | 1 + app/controllers/concerns/submission_scoring.rb | 2 +- app/controllers/submissions_controller.rb | 15 ++++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/editor/execution.js.erb b/app/assets/javascripts/editor/execution.js.erb index 0d2015e4..5d50e69d 100644 --- a/app/assets/javascripts/editor/execution.js.erb +++ b/app/assets/javascripts/editor/execution.js.erb @@ -30,6 +30,7 @@ CodeOceanEditorWebsocket = { initializeSocketForScoring: function(url) { this.initializeSocket(url); this.websocket.on('default',this.handleScoringResponse.bind(this)); + this.websocket.on('hint', this.showHint.bind(this)); this.websocket.on('exit', this.handleExitCommand.bind(this)); }, diff --git a/app/controllers/concerns/submission_scoring.rb b/app/controllers/concerns/submission_scoring.rb index 5b3c0ce7..06bba11c 100644 --- a/app/controllers/concerns/submission_scoring.rb +++ b/app/controllers/concerns/submission_scoring.rb @@ -13,7 +13,7 @@ module SubmissionScoring submission.exercise.execution_environment.error_templates.each do |template| pattern = Regexp.new(template.signature).freeze if pattern.match(testrun_output) - StructuredError.create_from_template(template, testrun_output) + StructuredError.create_from_template(template, testrun_output, submission) end end end diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 4f1bb49f..9032bda9 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -197,9 +197,8 @@ class SubmissionsController < ApplicationController def kill_socket(tubesock) # search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb) - extract_errors.each do | error | - tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint, description: error.error_template.description}) - end + errors = extract_errors + send_hints(tubesock, errors) # save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb) save_run_output @@ -307,11 +306,21 @@ class SubmissionsController < ApplicationController # to ensure responsiveness, we therefore open a thread here. Thread.new { tubesock.send_data JSON.dump(score_submission(@submission)) + + send_hints(tubesock, StructuredError.where(submission: @submission)) + tubesock.send_data JSON.dump({'cmd' => 'exit'}) } end end + def send_hints(tubesock, errors) + errors = errors.to_a.uniq { |e| e.hint} + errors.each do | error | + tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint, description: error.error_template.description}) + end + end + def set_docker_client @docker_client = DockerClient.new(execution_environment: @submission.execution_environment) end From e583fe16a415e6fcae648e030deef3200719ba5f Mon Sep 17 00:00:00 2001 From: Maximilian Grundke Date: Wed, 14 Mar 2018 14:29:44 +0100 Subject: [PATCH 6/6] Disable hints when scoring --- app/controllers/submissions_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 9032bda9..1624e5e0 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -307,7 +307,8 @@ class SubmissionsController < ApplicationController Thread.new { tubesock.send_data JSON.dump(score_submission(@submission)) - send_hints(tubesock, StructuredError.where(submission: @submission)) + # To enable hints when scoring a submission, uncomment the next line: + #send_hints(tubesock, StructuredError.where(submission: @submission)) tubesock.send_data JSON.dump({'cmd' => 'exit'}) }