Merge branch 'master' into user_exercise_feedback_backend
# Conflicts: # app/views/application/_navigation.html.slim
This commit is contained in:
18
app/assets/javascripts/error_templates.js
Normal file
18
app/assets/javascripts/error_templates.js
Normal file
@@ -0,0 +1,18 @@
|
||||
$(function() {
|
||||
if ($.isController('error_templates')) {
|
||||
$('#add-attribute').find('button').on('click', function () {
|
||||
$.ajax(location + '/attribute.json', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
_method: 'PUT',
|
||||
dataType: 'json',
|
||||
error_template_attribute_id: $('#add-attribute').find('select').val()
|
||||
}
|
||||
}).success(function () {
|
||||
location.reload();
|
||||
}).error(function (error) {
|
||||
$.flash.danger({text: error.statusText});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
9
app/assets/stylesheets/error_templates.scss
Normal file
9
app/assets/stylesheets/error_templates.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
#add-attribute {
|
||||
display: flex;
|
||||
max-width: 400px;
|
||||
margin-top: 30px;
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
@@ -9,6 +9,14 @@ module SubmissionScoring
|
||||
assessment = assessor.assess(output)
|
||||
passed = ((assessment[:passed] == assessment[:count]) and (assessment[:score] > 0))
|
||||
testrun_output = passed ? nil : 'message: ' + output[:message].to_s + "\n stdout: " + output[:stdout].to_s + "\n stderr: " + output[:stderr].to_s
|
||||
if !testrun_output.blank?
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
Testrun.new(submission: submission, cause: 'assess', file: file, passed: passed, output: testrun_output).save
|
||||
output.merge!(assessment)
|
||||
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output[:score]), weight: file.weight)
|
||||
|
86
app/controllers/error_template_attributes_controller.rb
Normal file
86
app/controllers/error_template_attributes_controller.rb
Normal file
@@ -0,0 +1,86 @@
|
||||
class ErrorTemplateAttributesController < ApplicationController
|
||||
before_action :set_error_template_attribute, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
def authorize!
|
||||
authorize(@error_template_attributes || @error_template_attribute)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
# GET /error_template_attributes
|
||||
# GET /error_template_attributes.json
|
||||
def index
|
||||
@error_template_attributes = ErrorTemplateAttribute.all.order('important DESC', :key, :id).paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_template_attributes/1
|
||||
# GET /error_template_attributes/1.json
|
||||
def show
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_template_attributes/new
|
||||
def new
|
||||
@error_template_attribute = ErrorTemplateAttribute.new
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_template_attributes/1/edit
|
||||
def edit
|
||||
authorize!
|
||||
end
|
||||
|
||||
# POST /error_template_attributes
|
||||
# POST /error_template_attributes.json
|
||||
def create
|
||||
@error_template_attribute = ErrorTemplateAttribute.new(error_template_attribute_params)
|
||||
authorize!
|
||||
|
||||
respond_to do |format|
|
||||
if @error_template_attribute.save
|
||||
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully created.' }
|
||||
format.json { render :show, status: :created, location: @error_template_attribute }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /error_template_attributes/1
|
||||
# PATCH/PUT /error_template_attributes/1.json
|
||||
def update
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
if @error_template_attribute.update(error_template_attribute_params)
|
||||
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @error_template_attribute }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /error_template_attributes/1
|
||||
# DELETE /error_template_attributes/1.json
|
||||
def destroy
|
||||
authorize!
|
||||
@error_template_attribute.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to error_template_attributes_url, notice: 'Error template attribute was successfully destroyed.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template_attribute
|
||||
@error_template_attribute = ErrorTemplateAttribute.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_attribute_params
|
||||
params[:error_template_attribute].permit(:key, :description, :regex, :important)
|
||||
end
|
||||
end
|
104
app/controllers/error_templates_controller.rb
Normal file
104
app/controllers/error_templates_controller.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
class ErrorTemplatesController < ApplicationController
|
||||
before_action :set_error_template, only: [:show, :edit, :update, :destroy, :add_attribute, :remove_attribute]
|
||||
|
||||
def authorize!
|
||||
authorize(@error_templates || @error_template)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
# GET /error_templates
|
||||
# GET /error_templates.json
|
||||
def index
|
||||
@error_templates = ErrorTemplate.all.order(:execution_environment_id, :name).paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_templates/1
|
||||
# GET /error_templates/1.json
|
||||
def show
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_templates/new
|
||||
def new
|
||||
@error_template = ErrorTemplate.new
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /error_templates/1/edit
|
||||
def edit
|
||||
authorize!
|
||||
end
|
||||
|
||||
# POST /error_templates
|
||||
# POST /error_templates.json
|
||||
def create
|
||||
@error_template = ErrorTemplate.new(error_template_params)
|
||||
authorize!
|
||||
|
||||
respond_to do |format|
|
||||
if @error_template.save
|
||||
format.html { redirect_to @error_template, notice: 'Error template was successfully created.' }
|
||||
format.json { render :show, status: :created, location: @error_template }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.json { render json: @error_template.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /error_templates/1
|
||||
# PATCH/PUT /error_templates/1.json
|
||||
def update
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
if @error_template.update(error_template_params)
|
||||
format.html { redirect_to @error_template, notice: 'Error template was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @error_template }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.json { render json: @error_template.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /error_templates/1
|
||||
# DELETE /error_templates/1.json
|
||||
def destroy
|
||||
authorize!
|
||||
@error_template.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to error_templates_url, notice: 'Error template was successfully destroyed.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
def add_attribute
|
||||
authorize!
|
||||
@error_template.error_template_attributes << ErrorTemplateAttribute.find(params['error_template_attribute_id'])
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @error_template }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
def remove_attribute
|
||||
authorize!
|
||||
@error_template.error_template_attributes.delete(ErrorTemplateAttribute.find(params['error_template_attribute_id']))
|
||||
respond_to do |format|
|
||||
format.html { redirect_to @error_template }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_error_template
|
||||
@error_template = ErrorTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_params
|
||||
params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint)
|
||||
end
|
||||
end
|
@@ -6,7 +6,7 @@ class SubmissionsController < ApplicationController
|
||||
include SubmissionScoring
|
||||
include Tubesock::Hijack
|
||||
|
||||
before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :show, :statistics, :stop, :test]
|
||||
before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :extract_errors, :show, :statistics, :stop, :test]
|
||||
before_action :set_docker_client, only: [:run, :test]
|
||||
before_action :set_files, only: [:download, :download_file, :render_file, :show]
|
||||
before_action :set_file, only: [:download_file, :render_file]
|
||||
@@ -191,6 +191,9 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def kill_socket(tubesock)
|
||||
# search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb)
|
||||
extract_errors
|
||||
|
||||
# save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb)
|
||||
save_run_output
|
||||
|
||||
@@ -199,6 +202,17 @@ class SubmissionsController < ApplicationController
|
||||
tubesock.close
|
||||
end
|
||||
|
||||
def extract_errors
|
||||
if !@message_buffer.blank?
|
||||
@submission.exercise.execution_environment.error_templates.each do |template|
|
||||
pattern = Regexp.new(template.signature).freeze
|
||||
if pattern.match(@message_buffer)
|
||||
StructuredError.create_from_template(template, @message_buffer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_message(message, tubesock, container)
|
||||
@run_output ||= ""
|
||||
# Handle special commands first
|
||||
|
2
app/helpers/error_template_attributes_helper.rb
Normal file
2
app/helpers/error_template_attributes_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module ErrorTemplateAttributesHelper
|
||||
end
|
2
app/helpers/error_templates_helper.rb
Normal file
2
app/helpers/error_templates_helper.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
module ErrorTemplatesHelper
|
||||
end
|
8
app/models/error_template.rb
Normal file
8
app/models/error_template.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class ErrorTemplate < ActiveRecord::Base
|
||||
belongs_to :execution_environment
|
||||
has_and_belongs_to_many :error_template_attributes
|
||||
|
||||
def to_s
|
||||
"#{id} [#{name}]"
|
||||
end
|
||||
end
|
7
app/models/error_template_attribute.rb
Normal file
7
app/models/error_template_attribute.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class ErrorTemplateAttribute < ActiveRecord::Base
|
||||
has_and_belongs_to_many :error_template
|
||||
|
||||
def to_s
|
||||
"#{id} [#{key}]"
|
||||
end
|
||||
end
|
@@ -11,6 +11,7 @@ class ExecutionEnvironment < ActiveRecord::Base
|
||||
has_many :exercises
|
||||
belongs_to :file_type
|
||||
has_many :hints
|
||||
has_many :error_templates
|
||||
|
||||
scope :with_exercises, -> { where('id IN (SELECT execution_environment_id FROM exercises)') }
|
||||
|
||||
|
@@ -36,8 +36,8 @@ class ProxyExercise < ActiveRecord::Base
|
||||
Rails.logger.debug("retrieved assigned exercise for user #{user.id}: Exercise #{assigned_user_proxy_exercise.exercise}" )
|
||||
assigned_user_proxy_exercise.exercise
|
||||
else
|
||||
Rails.logger.debug("find new matching exercise for user #{user.id}" )
|
||||
matching_exercise =
|
||||
Rails.logger.debug("find new matching exercise for user #{user.id}" )
|
||||
begin
|
||||
find_matching_exercise(user)
|
||||
rescue => e #fallback
|
||||
@@ -72,7 +72,7 @@ class ProxyExercise < ActiveRecord::Base
|
||||
|
||||
# find exercises
|
||||
potential_recommended_exercises = []
|
||||
exercises.where("expected_difficulty > 1").each do |ex|
|
||||
exercises.where("expected_difficulty >= 1").each do |ex|
|
||||
## find exercises which have only tags the user has already seen
|
||||
if (ex.tags - tags_user_has_seen).empty?
|
||||
potential_recommended_exercises << ex
|
||||
@@ -85,8 +85,7 @@ class ProxyExercise < ActiveRecord::Base
|
||||
@reason[:reason] = "easiest exercise in pool. empty potential exercises"
|
||||
select_easiest_exercise(exercises)
|
||||
else
|
||||
recommended_exercise = select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises)
|
||||
recommended_exercise
|
||||
select_best_matching_exercise(user, exercises_user_has_accessed, potential_recommended_exercises)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -238,4 +237,4 @@ class ProxyExercise < ActiveRecord::Base
|
||||
exercises.order(:expected_difficulty).first
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
12
app/models/structured_error.rb
Normal file
12
app/models/structured_error.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class StructuredError < ActiveRecord::Base
|
||||
belongs_to :error_template
|
||||
belongs_to :file, class_name: 'CodeOcean::File'
|
||||
|
||||
def self.create_from_template(template, message_buffer)
|
||||
instance = self.create(error_template: template)
|
||||
template.error_template_attributes.each do |attribute|
|
||||
StructuredErrorAttribute.create_from_template(attribute, instance, message_buffer)
|
||||
end
|
||||
instance
|
||||
end
|
||||
end
|
17
app/models/structured_error_attribute.rb
Normal file
17
app/models/structured_error_attribute.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
class StructuredErrorAttribute < ActiveRecord::Base
|
||||
belongs_to :structured_error
|
||||
belongs_to :error_template_attribute
|
||||
|
||||
def self.create_from_template(attribute, structured_error, message_buffer)
|
||||
match = false
|
||||
value = nil
|
||||
result = message_buffer.match(attribute.regex)
|
||||
if result != nil
|
||||
match = true
|
||||
if result.captures.size > 0
|
||||
value = result.captures[0]
|
||||
end
|
||||
end
|
||||
self.create(structured_error: structured_error, error_template_attribute: attribute, value: value, match: match)
|
||||
end
|
||||
end
|
3
app/policies/error_template_attribute_policy.rb
Normal file
3
app/policies/error_template_attribute_policy.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class ErrorTemplateAttributePolicy < AdminOnlyPolicy
|
||||
|
||||
end
|
9
app/policies/error_template_policy.rb
Normal file
9
app/policies/error_template_policy.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class ErrorTemplatePolicy < AdminOnlyPolicy
|
||||
def add_attribute?
|
||||
admin?
|
||||
end
|
||||
|
||||
def remove_attribute?
|
||||
admin?
|
||||
end
|
||||
end
|
@@ -8,7 +8,8 @@
|
||||
- if current_user.admin?
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
|
||||
li.divider
|
||||
- models = [ExecutionEnvironment, Exercise, ExerciseCollection, ProxyExercise, Tag, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser, UserExerciseFeedback].sort_by { |model| model.model_name.human(count: 2) }
|
||||
- models = [ExecutionEnvironment, Exercise, ExerciseCollection, ProxyExercise, Tag, Consumer, CodeHarborLink, UserExerciseFeedback,
|
||||
ErrorTemplate, ErrorTemplateAttribute, ExternalUser, FileType, FileTemplate, InternalUser].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"))
|
||||
|
16
app/views/error_template_attributes/_form.html.slim
Normal file
16
app/views/error_template_attributes/_form.html.slim
Normal file
@@ -0,0 +1,16 @@
|
||||
= form_for(@error_template_attribute) do |f|
|
||||
= render('shared/form_errors', object: @error_template_attribute)
|
||||
.form-group
|
||||
= f.label(:key)
|
||||
= f.text_field(:key, class: 'form-control', required: true)
|
||||
.form-group
|
||||
= f.label(:description)
|
||||
= f.text_field(:description, class: 'form-control')
|
||||
.form-group
|
||||
= f.label(:regex)
|
||||
= f.text_field(:regex, class: 'form-control', required: true)
|
||||
.help-block == t('error_templates.hints.signature')
|
||||
.form-group
|
||||
= f.check_box(:important)
|
||||
= t('activerecord.attributes.error_template_attribute.important')
|
||||
.actions = render('shared/submit_button', f: f, object: @error_template_attribute)
|
3
app/views/error_template_attributes/edit.html.slim
Normal file
3
app/views/error_template_attributes/edit.html.slim
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 = @error_template_attribute
|
||||
|
||||
= render('form')
|
28
app/views/error_template_attributes/index.html.slim
Normal file
28
app/views/error_template_attributes/index.html.slim
Normal file
@@ -0,0 +1,28 @@
|
||||
h1 = ErrorTemplateAttribute.model_name.human(count: 2)
|
||||
|
||||
.table-responsive
|
||||
table.sortable.table
|
||||
thead
|
||||
tr
|
||||
th
|
||||
th = t('activerecord.attributes.error_template_attribute.key')
|
||||
th = t('activerecord.attributes.error_template_attribute.description')
|
||||
th = t('activerecord.attributes.error_template_attribute.regex')
|
||||
th colspan=5 = t('shared.actions')
|
||||
tbody
|
||||
- @error_template_attributes.each do |error_template_attribute|
|
||||
tr
|
||||
td
|
||||
- if error_template_attribute.important
|
||||
span class="fa fa-star" aria-hidden="true"
|
||||
- else
|
||||
span class="fa fa-star-o" aria-hidden="true"
|
||||
td = error_template_attribute.key
|
||||
td = error_template_attribute.description
|
||||
td = error_template_attribute.regex
|
||||
td = link_to(t('shared.show'), error_template_attribute)
|
||||
td = link_to(t('shared.edit'), edit_error_template_attribute_path(error_template_attribute))
|
||||
td = link_to(t('shared.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
|
||||
|
||||
= render('shared/pagination', collection: @error_template_attributes)
|
||||
p = render('shared/new_button', model: ErrorTemplateAttribute)
|
3
app/views/error_template_attributes/new.html.slim
Normal file
3
app/views/error_template_attributes/new.html.slim
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 = t('shared.new_model', model: ErrorTemplateAttribute.model_name.human)
|
||||
|
||||
= render('form')
|
8
app/views/error_template_attributes/show.html.slim
Normal file
8
app/views/error_template_attributes/show.html.slim
Normal file
@@ -0,0 +1,8 @@
|
||||
h1
|
||||
= @error_template_attribute
|
||||
= render('shared/edit_button', object: @error_template_attribute)
|
||||
|
||||
- [:key, :description, :regex, :important].each do |attribute|
|
||||
= row(label: "error_template_attribute.#{attribute}", value: @error_template_attribute.send(attribute))
|
||||
|
||||
// todo: used by
|
19
app/views/error_templates/_form.html.slim
Normal file
19
app/views/error_templates/_form.html.slim
Normal file
@@ -0,0 +1,19 @@
|
||||
= form_for(@error_template) do |f|
|
||||
= render('shared/form_errors', object: @error_template)
|
||||
.form-group
|
||||
= f.label(:name)
|
||||
= f.text_field(:name, class: 'form-control', required: true)
|
||||
.form-group
|
||||
= f.label(:execution_environment_id)
|
||||
= f.collection_select(:execution_environment_id, ExecutionEnvironment.all.order(:name), :id, :name, {include_blank: false}, class: 'form-control')
|
||||
.form-group
|
||||
= f.label(:signature)
|
||||
= f.text_field(:signature, class: 'form-control')
|
||||
.help-block == t('error_templates.hints.signature')
|
||||
.form-group
|
||||
= f.label(:description)
|
||||
= f.text_field(:description, class: 'form-control')
|
||||
.form-group
|
||||
= f.label(:hint)
|
||||
= f.text_field(:hint, class: 'form-control')
|
||||
.actions = render('shared/submit_button', f: f, object: @error_template)
|
3
app/views/error_templates/edit.html.slim
Normal file
3
app/views/error_templates/edit.html.slim
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 = @error_template
|
||||
|
||||
= render('form')
|
22
app/views/error_templates/index.html.slim
Normal file
22
app/views/error_templates/index.html.slim
Normal file
@@ -0,0 +1,22 @@
|
||||
h1 = ErrorTemplate.model_name.human(count: 2)
|
||||
|
||||
.table-responsive
|
||||
table.sortable.table
|
||||
thead
|
||||
tr
|
||||
th = t('activerecord.attributes.error_template.name')
|
||||
th = t('activerecord.attributes.error_template.description')
|
||||
th = t('activerecord.attributes.exercise.execution_environment')
|
||||
th colspan=3 = t('shared.actions')
|
||||
tbody
|
||||
- @error_templates.each do |error_template|
|
||||
tr
|
||||
td = error_template.name
|
||||
td = error_template.description
|
||||
td = link_to(error_template.execution_environment)
|
||||
td = link_to(t('shared.show'), error_template)
|
||||
td = link_to(t('shared.edit'), edit_error_template_path(error_template))
|
||||
td = link_to(t('shared.destroy'), error_template, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
|
||||
|
||||
= render('shared/pagination', collection: @error_templates)
|
||||
p = render('shared/new_button', model: ErrorTemplate)
|
3
app/views/error_templates/new.html.slim
Normal file
3
app/views/error_templates/new.html.slim
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 = t('shared.new_model', model: ErrorTemplate.model_name.human)
|
||||
|
||||
= render('form')
|
40
app/views/error_templates/show.html.slim
Normal file
40
app/views/error_templates/show.html.slim
Normal file
@@ -0,0 +1,40 @@
|
||||
h1
|
||||
= @error_template
|
||||
= render('shared/edit_button', object: @error_template)
|
||||
|
||||
= row(label: 'error_template.name', value: @error_template.name)
|
||||
= row(label: 'exercise.execution_environment', value: link_to(@error_template.execution_environment))
|
||||
- [:signature, :description, :hint].each do |attribute|
|
||||
= row(label: "error_template.#{attribute}", value: @error_template.send(attribute))
|
||||
|
||||
h3
|
||||
= t 'error_templates.attributes'
|
||||
|
||||
.table-responsive
|
||||
table.sortable.table
|
||||
thead
|
||||
tr
|
||||
th
|
||||
th = t('activerecord.attributes.error_template_attribute.key')
|
||||
th = t('activerecord.attributes.error_template_attribute.description')
|
||||
th = t('activerecord.attributes.error_template_attribute.regex')
|
||||
th colspan=3 = t('shared.actions')
|
||||
tbody
|
||||
- @error_template.error_template_attributes.order('important DESC', :key).each do |attribute|
|
||||
tr
|
||||
td
|
||||
- if attribute.important
|
||||
span class="fa fa-star" aria-hidden="true"
|
||||
- else
|
||||
span class="fa fa-star-o" aria-hidden="true"
|
||||
td = attribute.key
|
||||
td = attribute.description
|
||||
td = attribute.regex
|
||||
td = link_to(t('shared.show'), attribute)
|
||||
td = link_to(t('shared.destroy'), attribute_error_template_url(:error_template_attribute_id => attribute.id), :method => :delete)
|
||||
|
||||
#add-attribute
|
||||
= collection_select({}, :error_template_attribute_id,
|
||||
ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key),
|
||||
:id, :key, {include_blank: false}, class: '')
|
||||
button.btn.btn-default = t('error_templates.add_attribute')
|
@@ -3,7 +3,7 @@
|
||||
- consumer_id = @current_user.respond_to?(:external_id) ? @current_user.consumer_id : '' #'tests' #(@current_user.uuid.present? ? @current_user.uuid : '')
|
||||
- show_break_interventions = @show_break_interventions || "false"
|
||||
- show_rfc_interventions = @show_rfc_interventions || "false"
|
||||
#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 data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path data-intervention-save-url=intervention_exercise_path data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path
|
||||
#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 data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise)
|
||||
div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files)
|
||||
div id='output_sidebar' class='output-col-collapsed' = render('exercises/editor_output', external_user_id: external_user_id, consumer_id: consumer_id )
|
||||
div id='frames' class='editor-col'
|
||||
@@ -24,4 +24,4 @@
|
||||
|
||||
|
||||
= render('shared/modal', id: 'comment-modal', title: t('exercises.implement.comment.request'), template: 'exercises/_request_comment_dialogcontent')
|
||||
= render('shared/modal', id: 'break-intervention-modal', title: t('exercises.implement.break_intervention.title'), template: 'interventions/_break_intervention_modal')
|
||||
= render('shared/modal', id: 'break-intervention-modal', title: t('exercises.implement.break_intervention.title'), template: 'interventions/_break_intervention_modal')
|
||||
|
Reference in New Issue
Block a user