Merge branch 'linter_toggle'
# Conflicts: # config/locales/de.yml
This commit is contained in:
@ -94,7 +94,9 @@ var CodeOceanEditor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getCardClass: function (result) {
|
getCardClass: function (result) {
|
||||||
if (result.stderr && !result.score) {
|
if (result.file_role === 'teacher_defined_linter') {
|
||||||
|
return 'card bg-info text-white'
|
||||||
|
} else if (result.stderr && !result.score) {
|
||||||
return 'card bg-danger text-white';
|
return 'card bg-danger text-white';
|
||||||
} else if (result.score < 1) {
|
} else if (result.score < 1) {
|
||||||
return 'card bg-warning text-white';
|
return 'card bg-warning text-white';
|
||||||
@ -428,7 +430,7 @@ var CodeOceanEditor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
isActiveFileTestable: function () {
|
isActiveFileTestable: function () {
|
||||||
return this.isActiveFileExecutable() && ['teacher_defined_test', 'user_defined_test'].includes(this.active_frame.data('role'));
|
return this.isActiveFileExecutable() && ['teacher_defined_test', 'user_defined_test', 'teacher_defined_linter'].includes(this.active_frame.data('role'));
|
||||||
},
|
},
|
||||||
|
|
||||||
isBrowserSupported: function () {
|
isBrowserSupported: function () {
|
||||||
@ -443,8 +445,13 @@ var CodeOceanEditor = {
|
|||||||
card.find('.card-title .number').text(index + 1);
|
card.find('.card-title .number').text(index + 1);
|
||||||
card.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
|
card.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
|
||||||
card.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
|
card.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
|
||||||
card.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
|
if (result.weight !== 0) {
|
||||||
card.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
|
card.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
|
||||||
|
card.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
|
||||||
|
} else {
|
||||||
|
// Hide score row if no score could be achieved
|
||||||
|
card.find('.attribute-row.row').eq(1).addClass('d-none');
|
||||||
|
}
|
||||||
card.find('.row .col-sm-9').eq(2).html(result.message);
|
card.find('.row .col-sm-9').eq(2).html(result.message);
|
||||||
|
|
||||||
// Add error message from code to card
|
// Add error message from code to card
|
||||||
|
@ -48,7 +48,12 @@ CodeOceanEditorEvaluation = {
|
|||||||
|
|
||||||
printScoringResult: function (result, index) {
|
printScoringResult: function (result, index) {
|
||||||
$('#results').show();
|
$('#results').show();
|
||||||
var card = $('#dummies').children().first().clone();
|
let card;
|
||||||
|
if (result.file_role === 'teacher_defined_linter') {
|
||||||
|
card = $('#linter-dummies').children().first().clone();
|
||||||
|
} else {
|
||||||
|
card = $('#test-dummies').children().first().clone();
|
||||||
|
}
|
||||||
if (card.isPresent()) {
|
if (card.isPresent()) {
|
||||||
// the card won't be present if @embed_options[:hide_test_results] == true
|
// the card won't be present if @embed_options[:hide_test_results] == true
|
||||||
this.populateCard(card, result, index);
|
this.populateCard(card, result, index);
|
||||||
@ -58,7 +63,7 @@ CodeOceanEditorEvaluation = {
|
|||||||
|
|
||||||
printScoringResults: function (response) {
|
printScoringResults: function (response) {
|
||||||
$('#results ul').first().html('');
|
$('#results ul').first().html('');
|
||||||
$('.test-count .number').html(response.length);
|
$('.test-count .number').html(response.filter(function(x) { return x.file_role === 'teacher_defined_test'; }).length);
|
||||||
this.clearOutput();
|
this.clearOutput();
|
||||||
|
|
||||||
_.each(response, function (result, index) {
|
_.each(response, function (result, index) {
|
||||||
|
@ -280,7 +280,7 @@ $(document).on('turbolinks:load', function () {
|
|||||||
|
|
||||||
var observeFileRoleChanges = function () {
|
var observeFileRoleChanges = function () {
|
||||||
$(document).on('change', 'select[name$="[role]"]', function () {
|
$(document).on('change', 'select[name$="[role]"]', function () {
|
||||||
var is_test_file = $(this).val() === 'teacher_defined_test';
|
var is_test_file = $(this).val() === 'teacher_defined_test' || $(this).val() === 'teacher_defined_linter';
|
||||||
var parent = $(this).parents('.card');
|
var parent = $(this).parents('.card');
|
||||||
var fields = parent.find('.test-related-fields');
|
var fields = parent.find('.test-related-fields');
|
||||||
if (is_test_file) {
|
if (is_test_file) {
|
||||||
|
@ -3,7 +3,7 @@ require 'concurrent/future'
|
|||||||
module SubmissionScoring
|
module SubmissionScoring
|
||||||
def collect_test_results(submission)
|
def collect_test_results(submission)
|
||||||
# Mnemosyne.trace 'custom.codeocean.collect_test_results', meta: { submission: submission.id } do
|
# Mnemosyne.trace 'custom.codeocean.collect_test_results', meta: { submission: submission.id } do
|
||||||
submission.collect_files.select(&:teacher_defined_test?).map do |file|
|
submission.collect_files.select(&:teacher_defined_assessment?).map do |file|
|
||||||
future = Concurrent::Future.execute do
|
future = Concurrent::Future.execute do
|
||||||
# Mnemosyne.trace 'custom.codeocean.collect_test_results_block', meta: { file: file.id, submission: submission.id } do
|
# Mnemosyne.trace 'custom.codeocean.collect_test_results_block', meta: { file: file.id, submission: submission.id } do
|
||||||
assessor = Assessor.new(execution_environment: submission.execution_environment)
|
assessor = Assessor.new(execution_environment: submission.execution_environment)
|
||||||
@ -29,7 +29,7 @@ module SubmissionScoring
|
|||||||
waiting_for_container_time: output[:waiting_for_container_time]
|
waiting_for_container_time: output[:waiting_for_container_time]
|
||||||
).save
|
).save
|
||||||
output.merge!(assessment)
|
output.merge!(assessment)
|
||||||
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output[:score]), weight: file.weight)
|
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output), weight: file.weight)
|
||||||
# end
|
# end
|
||||||
end
|
end
|
||||||
future.value
|
future.value
|
||||||
@ -45,9 +45,15 @@ module SubmissionScoring
|
|||||||
|
|
||||||
private :execute_test_file
|
private :execute_test_file
|
||||||
|
|
||||||
def feedback_message(file, score)
|
def feedback_message(file, output)
|
||||||
set_locale
|
set_locale
|
||||||
score == Assessor::MAXIMUM_SCORE ? I18n.t('exercises.implement.default_feedback') : render_markdown(file.feedback_message)
|
if output[:score] == Assessor::MAXIMUM_SCORE && output[:file_role] == 'teacher_defined_test'
|
||||||
|
I18n.t('exercises.implement.default_test_feedback')
|
||||||
|
elsif output[:score] == Assessor::MAXIMUM_SCORE && output[:file_role] == 'teacher_defined_linter'
|
||||||
|
I18n.t('exercises.implement.default_linter_feedback')
|
||||||
|
else
|
||||||
|
render_markdown(file.feedback_message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def score_submission(submission)
|
def score_submission(submission)
|
||||||
|
@ -19,11 +19,11 @@ module CodeOcean
|
|||||||
include DefaultValues
|
include DefaultValues
|
||||||
|
|
||||||
DEFAULT_WEIGHT = 1.0
|
DEFAULT_WEIGHT = 1.0
|
||||||
ROLES = %w(main_file reference_implementation regular_file executable_file teacher_defined_test user_defined_file user_defined_test)
|
ROLES = %w[main_file reference_implementation regular_file executable_file teacher_defined_test user_defined_file user_defined_test teacher_defined_linter].freeze
|
||||||
TEACHER_DEFINED_ROLES = ROLES - %w(user_defined_file)
|
TEACHER_DEFINED_ROLES = ROLES - %w[user_defined_file]
|
||||||
|
|
||||||
after_initialize :set_default_values
|
after_initialize :set_default_values
|
||||||
before_validation :clear_weight, unless: :teacher_defined_test?
|
before_validation :clear_weight, unless: :teacher_defined_assessment?
|
||||||
before_validation :hash_content, if: :content_present?
|
before_validation :hash_content, if: :content_present?
|
||||||
before_validation :set_ancestor_values, if: :incomplete_descendent?
|
before_validation :set_ancestor_values, if: :incomplete_descendent?
|
||||||
|
|
||||||
@ -45,19 +45,20 @@ module CodeOcean
|
|||||||
ROLES.each do |role|
|
ROLES.each do |role|
|
||||||
scope :"#{role}s", -> { where(role: role) }
|
scope :"#{role}s", -> { where(role: role) }
|
||||||
end
|
end
|
||||||
|
scope :teacher_defined_assessments, -> { where(role: %w[teacher_defined_test teacher_defined_linter]) }
|
||||||
|
|
||||||
default_scope { order(name: :asc) }
|
default_scope { order(name: :asc) }
|
||||||
|
|
||||||
validates :feedback_message, if: :teacher_defined_test?, presence: true
|
validates :feedback_message, if: :teacher_defined_assessment?, presence: true
|
||||||
validates :feedback_message, absence: true, unless: :teacher_defined_test?
|
validates :feedback_message, absence: true, unless: :teacher_defined_assessment?
|
||||||
validates :file_type_id, presence: true
|
validates :file_type_id, presence: true
|
||||||
validates :hashed_content, if: :content_present?, presence: true
|
validates :hashed_content, if: :content_present?, presence: true
|
||||||
validates :hidden, boolean_presence: true
|
validates :hidden, boolean_presence: true
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
validates :read_only, boolean_presence: true
|
validates :read_only, boolean_presence: true
|
||||||
validates :role, inclusion: {in: ROLES}
|
validates :role, inclusion: {in: ROLES}
|
||||||
validates :weight, if: :teacher_defined_test?, numericality: true, presence: true
|
validates :weight, if: :teacher_defined_assessment?, numericality: true, presence: true
|
||||||
validates :weight, absence: true, unless: :teacher_defined_test?
|
validates :weight, absence: true, unless: :teacher_defined_assessment?
|
||||||
validates :file, presence: true if :context.is_a?(Submission)
|
validates :file, presence: true if :context.is_a?(Submission)
|
||||||
|
|
||||||
validates_with FileNameValidator, fields: [:name, :path, :file_type_id]
|
validates_with FileNameValidator, fields: [:name, :path, :file_type_id]
|
||||||
@ -75,6 +76,10 @@ module CodeOcean
|
|||||||
end
|
end
|
||||||
private :clear_weight
|
private :clear_weight
|
||||||
|
|
||||||
|
def teacher_defined_assessment?
|
||||||
|
teacher_defined_test? || teacher_defined_linter?
|
||||||
|
end
|
||||||
|
|
||||||
def content_present?
|
def content_present?
|
||||||
content? || native_file?
|
content? || native_file?
|
||||||
end
|
end
|
||||||
@ -111,7 +116,7 @@ module CodeOcean
|
|||||||
|
|
||||||
def set_default_values
|
def set_default_values
|
||||||
set_default_values_if_present(content: '', hidden: false, read_only: false)
|
set_default_values_if_present(content: '', hidden: false, read_only: false)
|
||||||
set_default_values_if_present(weight: DEFAULT_WEIGHT) if teacher_defined_test?
|
set_default_values_if_present(weight: DEFAULT_WEIGHT) if teacher_defined_assessment?
|
||||||
end
|
end
|
||||||
private :set_default_values
|
private :set_default_values
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ class Exercise < ApplicationRecord
|
|||||||
FROM files
|
FROM files
|
||||||
WHERE context_type = 'Exercise'
|
WHERE context_type = 'Exercise'
|
||||||
AND context_id = #{id}
|
AND context_id = #{id}
|
||||||
AND role = 'teacher_defined_test'
|
AND role IN ('teacher_defined_test', 'teacher_defined_linter')
|
||||||
GROUP BY context_id),
|
GROUP BY context_id),
|
||||||
-- filter for rows containing max points
|
-- filter for rows containing max points
|
||||||
time_max_score AS
|
time_max_score AS
|
||||||
@ -394,7 +394,7 @@ class Exercise < ApplicationRecord
|
|||||||
WHERE exercise_id = #{id} AND user_id = #{user.id} AND user_type = '#{user_type}'
|
WHERE exercise_id = #{id} AND user_id = #{user.id} AND user_type = '#{user_type}'
|
||||||
GROUP BY user_id, id, exercise_id),
|
GROUP BY user_id, id, exercise_id),
|
||||||
MAX_POINTS AS
|
MAX_POINTS AS
|
||||||
(SELECT context_id AS ex_id, sum(weight) AS max_points FROM files WHERE context_type = 'Exercise' AND context_id = #{id} AND role = 'teacher_defined_test' GROUP BY context_id),
|
(SELECT context_id AS ex_id, sum(weight) AS max_points FROM files WHERE context_type = 'Exercise' AND context_id = #{id} AND role IN ('teacher_defined_test', 'teacher_defined_linter') GROUP BY context_id),
|
||||||
|
|
||||||
-- filter for rows containing max points
|
-- filter for rows containing max points
|
||||||
TIME_MAX_SCORE AS
|
TIME_MAX_SCORE AS
|
||||||
@ -508,7 +508,7 @@ class Exercise < ApplicationRecord
|
|||||||
0
|
0
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
files.teacher_defined_tests.sum(:weight)
|
files.teacher_defined_assessments.sum(:weight)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ module ProformaService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def tests
|
def tests
|
||||||
@exercise.files.filter { |file| file.role == 'teacher_defined_test' }.map do |file|
|
@exercise.files.filter { |file| file.role == 'teacher_defined_test' || file.role == 'teacher_defined_linter' }.map do |file|
|
||||||
Proforma::Test.new(
|
Proforma::Test.new(
|
||||||
id: file.id,
|
id: file.id,
|
||||||
title: file.name,
|
title: file.name,
|
||||||
@ -78,7 +78,7 @@ module ProformaService
|
|||||||
|
|
||||||
def task_files
|
def task_files
|
||||||
@exercise.files
|
@exercise.files
|
||||||
.filter { |file| !file.role.in? %w[reference_implementation teacher_defined_test] }.map do |file|
|
.filter { |file| !file.role.in? %w[reference_implementation teacher_defined_test teacher_defined_linter] }.map do |file|
|
||||||
task_file(file)
|
task_file(file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -12,16 +12,25 @@ div.h-100 id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-bottom
|
|||||||
p.test-count == t('exercises.implement.test_count', count: 0)
|
p.test-count == t('exercises.implement.test_count', count: 0)
|
||||||
- unless @embed_options[:hide_test_results]
|
- unless @embed_options[:hide_test_results]
|
||||||
ul.list-unstyled
|
ul.list-unstyled
|
||||||
ul#dummies.d-none.list-unstyled
|
ul#test-dummies.d-none.list-unstyled
|
||||||
li.card.mt-2
|
li.card.mt-2
|
||||||
.card-header.py-2
|
.card-header.py-2
|
||||||
h5.card-title.m-0 == t('exercises.implement.file', filename: '', number: 0)
|
h5.card-title.m-0 == t('exercises.implement.test_file', filename: '', number: 0)
|
||||||
.card-body.bg-white.text-dark
|
.card-body.bg-white.text-dark
|
||||||
= row(label: 'exercises.implement.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
= row(label: 'exercises.implement.passed_tests', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
= row(label: 'exercises.implement.feedback')
|
= row(label: 'exercises.implement.feedback')
|
||||||
= row(label: 'exercises.implement.error_messages')
|
= row(label: 'exercises.implement.error_messages')
|
||||||
/= row(label: 'exercises.implement.output', value: link_to(t('shared.show'), '#'))
|
/= row(label: 'exercises.implement.output', value: link_to(t('shared.show'), '#'))
|
||||||
|
ul#linter-dummies.d-none.list-unstyled
|
||||||
|
li.card.mt-2
|
||||||
|
.card-header.py-2
|
||||||
|
h5.card-title.m-0 == t('exercises.implement.linter_file', filename: '', number: 0)
|
||||||
|
.card-body.bg-white.text-dark
|
||||||
|
= row(label: 'exercises.implement.code_rating', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
|
= row(label: 'activerecord.attributes.submission.score', value: t('shared.out_of', maximum_value: 0, value: 0).html_safe)
|
||||||
|
= row(label: 'exercises.implement.feedback')
|
||||||
|
= row(label: 'exercises.implement.messages')
|
||||||
#score data-maximum-score=@exercise.maximum_score data-score=@exercise.final_submission(@current_user).try(:score)
|
#score data-maximum-score=@exercise.maximum_score data-score=@exercise.final_submission(@current_user).try(:score)
|
||||||
h4
|
h4
|
||||||
span == "#{t('activerecord.attributes.submission.score')}: "
|
span == "#{t('activerecord.attributes.submission.score')}: "
|
||||||
|
@ -37,12 +37,12 @@ li.card.mt-2
|
|||||||
label.form-check-label
|
label.form-check-label
|
||||||
= f.check_box(:read_only, class: 'form-check-input')
|
= f.check_box(:read_only, class: 'form-check-input')
|
||||||
= t('activerecord.attributes.file.read_only')
|
= t('activerecord.attributes.file.read_only')
|
||||||
.test-related-fields style="display: #{f.object.teacher_defined_test? ? 'initial' : 'none'};"
|
.test-related-fields style="display: #{f.object.teacher_defined_assessment? ? 'initial' : 'none'};"
|
||||||
.form-group
|
.form-group
|
||||||
= f.label(:name, t('activerecord.attributes.file.feedback_message'))
|
= f.label(:name, t('activerecord.attributes.file.feedback_message'))
|
||||||
= f.text_area(:feedback_message, class: 'form-control', maxlength: 255)
|
= f.text_area(:feedback_message, class: 'form-control', maxlength: 255)
|
||||||
.help-block.form-text = t('.hints.feedback_message')
|
.help-block.form-text = t('.hints.feedback_message')
|
||||||
.form-group
|
.form-group
|
||||||
= f.label(:role, t('activerecord.attributes.file.weight'))
|
= f.label(:role, t('activerecord.attributes.file.weight'))
|
||||||
= f.number_field(:weight, class: 'form-control', min: 1, step: 'any')
|
= f.number_field(:weight, class: 'form-control', min: 0, step: 'any')
|
||||||
= render('code_field', attribute: :content, form: f, label: t('activerecord.attributes.file.content'))
|
= render('code_field', attribute: :content, form: f, label: t('activerecord.attributes.file.content'))
|
||||||
|
@ -30,7 +30,7 @@ h1 = Exercise.model_name.human(count: 2)
|
|||||||
tr data-id=exercise.id
|
tr data-id=exercise.id
|
||||||
td.p-1.pt-2 = link_to_if(policy(exercise).show?, exercise.title, exercise, 'data-turbolinks' => "false")
|
td.p-1.pt-2 = link_to_if(policy(exercise).show?, exercise.title, exercise, 'data-turbolinks' => "false")
|
||||||
td.p-1.pt-2 = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
|
td.p-1.pt-2 = link_to_if(exercise.execution_environment && policy(exercise.execution_environment).show?, exercise.execution_environment, exercise.execution_environment)
|
||||||
td.p-1.pt-2 = exercise.files.teacher_defined_tests.count
|
td.p-1.pt-2 = exercise.files.teacher_defined_assessments.count
|
||||||
td.p-1.pt-2 = exercise.maximum_score
|
td.p-1.pt-2 = exercise.maximum_score
|
||||||
td.p-1.pt-2 = exercise.exercise_tags.count
|
td.p-1.pt-2 = exercise.exercise_tags.count
|
||||||
td.p-1.pt-2 = exercise.expected_difficulty
|
td.p-1.pt-2 = exercise.expected_difficulty
|
||||||
|
@ -53,7 +53,9 @@
|
|||||||
- rescue Timeout::Error
|
- rescue Timeout::Error
|
||||||
pre= output or t('request_for_comments.no_output')
|
pre= output or t('request_for_comments.no_output')
|
||||||
|
|
||||||
- assess_runs = testruns.select {|run| run.cause == 'assess'}
|
- assess_runs = testruns.select {|run| run.cause == 'assess' }
|
||||||
|
- unless @current_user.admin?
|
||||||
|
- assess_runs = assess_runs.select {|run| run.file.teacher_defined_test? }
|
||||||
- if assess_runs.size > 0
|
- if assess_runs.size > 0
|
||||||
h5.mt-4= t('request_for_comments.test_results')
|
h5.mt-4= t('request_for_comments.test_results')
|
||||||
.testrun-assess-results
|
.testrun-assess-results
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
= row(label: 'file.role', value: file.role? ? t("files.roles.#{file.role}") : '')
|
= row(label: 'file.role', value: file.role? ? t("files.roles.#{file.role}") : '')
|
||||||
= row(label: 'file.hidden', value: file.hidden)
|
= row(label: 'file.hidden', value: file.hidden)
|
||||||
= row(label: 'file.read_only', value: file.read_only)
|
= row(label: 'file.read_only', value: file.read_only)
|
||||||
- if file.teacher_defined_test?
|
- if file.teacher_defined_assessment?
|
||||||
= row(label: 'file.feedback_message', value: render_markdown(file.feedback_message), class: 'm-0')
|
= row(label: 'file.feedback_message', value: render_markdown(file.feedback_message), class: 'm-0')
|
||||||
= row(label: 'file.weight', value: file.weight)
|
= row(label: 'file.weight', value: file.weight)
|
||||||
= row(label: 'file.content', value: file.native_file? ? link_to_if(policy(file).show?, file.native_file.file.filename, file.native_file.url) : code_tag(file.content))
|
= row(label: 'file.content', value: file.native_file? ? link_to_if(policy(file).show?, file.native_file.file.filename, file.native_file.url) : code_tag(file.content))
|
||||||
|
@ -381,16 +381,20 @@ de:
|
|||||||
alert:
|
alert:
|
||||||
text: 'Ihr Browser unterstützt nicht alle Funktionalitäten, die %{application_name} benötigt. Bitte nutzen Sie einen modernen Browser, um %{application_name} zu besuchen.'
|
text: 'Ihr Browser unterstützt nicht alle Funktionalitäten, die %{application_name} benötigt. Bitte nutzen Sie einen modernen Browser, um %{application_name} zu besuchen.'
|
||||||
title: Ihr Browser wird nicht unterstützt!
|
title: Ihr Browser wird nicht unterstützt!
|
||||||
default_feedback: Sehr gut. Alle Tests waren erfolgreich.
|
default_test_feedback: Sehr gut. Alle Tests waren erfolgreich.
|
||||||
|
default_linter_feedback: Sehr gut. Der Linter hat nichts mehr zu beanstanden.
|
||||||
error_messages: Fehlermeldungen
|
error_messages: Fehlermeldungen
|
||||||
|
messages: Meldungen
|
||||||
feedback: Feedback
|
feedback: Feedback
|
||||||
file: 'Test-Datei <span class="number">%{number}</span> (<span class="filename">%{filename}</span>)'
|
test_file: 'Test-Datei <span class="number">%{number}</span> (<span class="filename">%{filename}</span>)'
|
||||||
|
linter_file: 'Linter-Feedback (ohne Punkte)<span class="number d-none">%{number}</span><span class="filename d-none">%{filename}</span>'
|
||||||
hint: Hinweis
|
hint: Hinweis
|
||||||
no_files: Die Aufgabe umfasst noch keine sichtbaren Dateien.
|
no_files: Die Aufgabe umfasst noch keine sichtbaren Dateien.
|
||||||
no_output: Die letzte Code-Ausführung terminierte am %{timestamp} ohne Ausgabe.
|
no_output: Die letzte Code-Ausführung terminierte am %{timestamp} ohne Ausgabe.
|
||||||
no_output_yet: Bisher existiert noch keine Ausgabe.
|
no_output_yet: Bisher existiert noch keine Ausgabe.
|
||||||
output: Programm-Ausgabe
|
output: Programm-Ausgabe
|
||||||
passed_tests: Erfolgreiche Tests
|
passed_tests: Erfolgreiche Tests
|
||||||
|
code_rating: Code-Stil
|
||||||
progress: Fortschritt
|
progress: Fortschritt
|
||||||
results: Ergebnisse
|
results: Ergebnisse
|
||||||
start: Mit dem Programmieren beginnen
|
start: Mit dem Programmieren beginnen
|
||||||
@ -500,6 +504,7 @@ de:
|
|||||||
teacher_defined_test: Test als Bewertungsgrundlage
|
teacher_defined_test: Test als Bewertungsgrundlage
|
||||||
user_defined_file: Benutzerdefinierte Datei
|
user_defined_file: Benutzerdefinierte Datei
|
||||||
user_defined_test: Benutzerdefinierter Test
|
user_defined_test: Benutzerdefinierter Test
|
||||||
|
teacher_defined_linter: Linter als Bewertungsgrundlage
|
||||||
error:
|
error:
|
||||||
filename: "Die Datei konnte nicht gespeichert werden, da eine Datei mit dem Namen '%{name}' bereits existiert."
|
filename: "Die Datei konnte nicht gespeichert werden, da eine Datei mit dem Namen '%{name}' bereits existiert."
|
||||||
hints:
|
hints:
|
||||||
|
@ -381,16 +381,20 @@ en:
|
|||||||
alert:
|
alert:
|
||||||
text: 'Your browser does not support features required for using %{application_name}. Please access %{application_name} using a modern browser.'
|
text: 'Your browser does not support features required for using %{application_name}. Please access %{application_name} using a modern browser.'
|
||||||
title: Your browser is not supported!
|
title: Your browser is not supported!
|
||||||
default_feedback: Well done. All tests have been passed.
|
default_test_feedback: Well done. All tests have been passed.
|
||||||
|
default_linter_feedback: Well done. The linter is completly satisfied.
|
||||||
error_messages: Error Messages
|
error_messages: Error Messages
|
||||||
|
messages: Messages
|
||||||
feedback: Feedback
|
feedback: Feedback
|
||||||
file: 'Test File <span class="number">%{number}</span> (<span class="filename">%{filename}</span>)'
|
test_file: 'Test File <span class="number">%{number}</span> (<span class="filename">%{filename}</span>)'
|
||||||
|
linter_file: 'Linter Feedback (not graded)<span class="number d-none">%{number}</span><span class="filename d-none">%{filename}</span>'
|
||||||
hint: Hint
|
hint: Hint
|
||||||
no_files: The exercise does not comprise visible files yet.
|
no_files: The exercise does not comprise visible files yet.
|
||||||
no_output: The last code run finished on %{timestamp} without any output.
|
no_output: The last code run finished on %{timestamp} without any output.
|
||||||
no_output_yet: There is no output yet.
|
no_output_yet: There is no output yet.
|
||||||
output: Program Output
|
output: Program Output
|
||||||
passed_tests: Passed Tests
|
passed_tests: Passed Tests
|
||||||
|
code_rating: Code Rating
|
||||||
progress: Progress
|
progress: Progress
|
||||||
results: Results
|
results: Results
|
||||||
start: Start Coding
|
start: Start Coding
|
||||||
@ -500,6 +504,7 @@ en:
|
|||||||
teacher_defined_test: Test for Assessment
|
teacher_defined_test: Test for Assessment
|
||||||
user_defined_file: User-defined File
|
user_defined_file: User-defined File
|
||||||
user_defined_test: User-defined Test
|
user_defined_test: User-defined Test
|
||||||
|
teacher_defined_linter: Linter for Assessment
|
||||||
error:
|
error:
|
||||||
filename: "The file could not be saved, because another file with the name '%{name}' already exists."
|
filename: "The file could not be saved, because another file with the name '%{name}' already exists."
|
||||||
hints:
|
hints:
|
||||||
|
@ -375,10 +375,13 @@ class DockerClient
|
|||||||
"""
|
"""
|
||||||
Stick to existing Docker API with exec command.
|
Stick to existing Docker API with exec command.
|
||||||
"""
|
"""
|
||||||
filepath = submission.collect_files.find { |f| f.name_with_extension == filename }.filepath
|
file = submission.collect_files.find { |f| f.name_with_extension == filename }
|
||||||
|
filepath = file.filepath
|
||||||
command = submission.execution_environment.test_command % command_substitutions(filepath)
|
command = submission.execution_environment.test_command % command_substitutions(filepath)
|
||||||
create_workspace_files = proc { create_workspace_files(container, submission) }
|
create_workspace_files = proc { create_workspace_files(container, submission) }
|
||||||
execute_command(command, create_workspace_files, block)
|
test_result = execute_command(command, create_workspace_files, block)
|
||||||
|
test_result.merge!(file_role: file.role)
|
||||||
|
test_result
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_image_by_tag(tag)
|
def self.find_image_by_tag(tag)
|
||||||
|
@ -5,9 +5,10 @@ class PyUnitAndPyLintAdapter < TestingFrameworkAdapter
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_output(output)
|
def parse_output(output)
|
||||||
PyLintAdapter.new.parse_output(output)
|
if output[:file_role] == 'teacher_defined_linter'
|
||||||
rescue NoMethodError
|
PyLintAdapter.new.parse_output(output)
|
||||||
# The regex for PyLint failed and did not return any matches
|
else
|
||||||
PyUnitAdapter.new.parse_output(output)
|
PyUnitAdapter.new.parse_output(output)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -13,7 +13,7 @@ describe SubmissionScoring do
|
|||||||
after(:each) { controller.send(:collect_test_results, @submission) }
|
after(:each) { controller.send(:collect_test_results, @submission) }
|
||||||
|
|
||||||
it 'executes every teacher-defined test file' do
|
it 'executes every teacher-defined test file' do
|
||||||
@submission.collect_files.select(&:teacher_defined_test?).each do |file|
|
@submission.collect_files.select(&:teacher_defined_assessment?).each do |file|
|
||||||
expect(controller).to receive(:execute_test_file).with(file, @submission).and_return({})
|
expect(controller).to receive(:execute_test_file).with(file, @submission).and_return({})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -241,7 +241,7 @@ describe SubmissionsController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #test' do
|
describe 'GET #test' do
|
||||||
let(:filename) { submission.collect_files.detect(&:teacher_defined_test?).name_with_extension }
|
let(:filename) { submission.collect_files.detect(&:teacher_defined_assessment?).name_with_extension }
|
||||||
let(:output) { {} }
|
let(:output) { {} }
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
|
@ -263,7 +263,7 @@ describe DockerClient, docker: true do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#execute_test_command' do
|
describe '#execute_test_command' do
|
||||||
let(:filename) { submission.exercise.files.detect { |file| file.role == 'teacher_defined_test' }.name_with_extension }
|
let(:filename) { submission.exercise.files.detect { |file| file.role == 'teacher_defined_test' || file.role == 'teacher_defined_linter' }.name_with_extension }
|
||||||
after(:each) { docker_client.send(:execute_test_command, submission, filename) }
|
after(:each) { docker_client.send(:execute_test_command, submission, filename) }
|
||||||
|
|
||||||
it 'takes a container from the pool' do
|
it 'takes a container from the pool' do
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe ProformaService::ConvertTaskToExercise do
|
describe ProformaService::ConvertTaskToExercise do
|
||||||
|
# ToDo: Add teacher_defined_linter for tests
|
||||||
|
|
||||||
describe '.new' do
|
describe '.new' do
|
||||||
subject(:convert_to_exercise_service) { described_class.new(task: task, user: user, exercise: exercise) }
|
subject(:convert_to_exercise_service) { described_class.new(task: task, user: user, exercise: exercise) }
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user