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..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));
},
@@ -43,6 +44,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 +55,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/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 410c30cd..1624e5e0 100644
--- a/app/controllers/submissions_controller.rb
+++ b/app/controllers/submissions_controller.rb
@@ -197,7 +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
+ 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
@@ -284,14 +285,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
@@ -303,11 +306,22 @@ class SubmissionsController < ApplicationController
# to ensure responsiveness, we therefore open a thread here.
Thread.new {
tubesock.send_data JSON.dump(score_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'})
}
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
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
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/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 4f5b9dcb..05c7108e 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -331,6 +331,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
@@ -728,6 +730,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 f65b90f4..5d6ada43 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -331,6 +331,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
@@ -728,6 +730,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: