Add user to testrun
* We want to identify a user that has triggered a testrun. Previously (in regular operation), only submission author who were regular users were able to start a testrun. Now, we want to prepare a future where submission authors are programming groups. Still, a testrun is triggered by an individual user and not a group. * Further, this commit fixes some missing foreign key constrains.
This commit is contained in:

committed by
Sebastian Serth

parent
e3603758ef
commit
be4f2b790d
@ -525,7 +525,7 @@ class ExercisesController < ApplicationController
|
||||
|
||||
def submit
|
||||
@submission = Submission.create(submission_params)
|
||||
@submission.calculate_score
|
||||
@submission.calculate_score(current_user)
|
||||
|
||||
if @submission.users.map {|user| lti_outcome_service?(@submission.exercise, user, @submission.study_group_id) }.any?
|
||||
transmit_lti_score
|
||||
|
@ -66,7 +66,7 @@ class RemoteEvaluationController < ApplicationController
|
||||
validation_token = remote_evaluation_params[:validation_token]
|
||||
if (remote_evaluation_mapping = RemoteEvaluationMapping.find_by(validation_token:))
|
||||
@submission = Submission.create(build_submission_params(cause, remote_evaluation_mapping))
|
||||
@submission.calculate_score
|
||||
@submission.calculate_score(remote_evaluation_mapping.user)
|
||||
else
|
||||
# TODO: better output
|
||||
# TODO: check token expired?
|
||||
|
@ -157,7 +157,7 @@ class RequestForCommentsController < ApplicationController
|
||||
# execute the tests here and wait until they finished.
|
||||
# As the same runner is used for the score and test run, no parallelization is possible
|
||||
# A run is triggered from the frontend and does not need to be handled here.
|
||||
@request_for_comment.submission.calculate_score
|
||||
@request_for_comment.submission.calculate_score(current_user)
|
||||
format.json { render :show, status: :created, location: @request_for_comment }
|
||||
else
|
||||
format.html { render :new }
|
||||
|
@ -249,7 +249,7 @@ class SubmissionsController < ApplicationController
|
||||
return true if disable_scoring
|
||||
|
||||
# The score is stored separately, we can forward it to the client immediately
|
||||
client_socket&.send_data(JSON.dump(@submission.calculate_score))
|
||||
client_socket&.send_data(JSON.dump(@submission.calculate_score(current_user)))
|
||||
# To enable hints when scoring a submission, uncomment the next line:
|
||||
# send_hints(client_socket, StructuredError.where(submission: @submission))
|
||||
rescue Runner::Error => e
|
||||
@ -284,7 +284,7 @@ class SubmissionsController < ApplicationController
|
||||
return true if @embed_options[:disable_run]
|
||||
|
||||
# The score is stored separately, we can forward it to the client immediately
|
||||
client_socket&.send_data(JSON.dump(@submission.test(@file)))
|
||||
client_socket&.send_data(JSON.dump(@submission.test(@file, current_user)))
|
||||
rescue Runner::Error => e
|
||||
extract_durations(e)
|
||||
send_and_store client_socket, {cmd: :status, status: :container_depleted}
|
||||
@ -396,6 +396,7 @@ class SubmissionsController < ApplicationController
|
||||
passed: @testrun[:passed],
|
||||
cause:,
|
||||
submission: @submission,
|
||||
user: current_user,
|
||||
exit_code: @testrun[:exit_code], # might be nil, e.g., when the run did not finish
|
||||
status: @testrun[:status] || :failed,
|
||||
output: @testrun[:output].presence, # TODO: Remove duplicated saving of the output after creating TestrunMessages
|
||||
|
@ -6,6 +6,7 @@ class ProgrammingGroup < ApplicationRecord
|
||||
has_many :programming_group_memberships, dependent: :destroy
|
||||
has_many :external_users, through: :programming_group_memberships, source_type: 'ExternalUser', source: :user
|
||||
has_many :internal_users, through: :programming_group_memberships, source_type: 'InternalUser', source: :user
|
||||
has_many :testruns, through: :submissions
|
||||
belongs_to :exercise
|
||||
|
||||
validate :group_size
|
||||
|
@ -141,7 +141,7 @@ class Submission < ApplicationRecord
|
||||
|
||||
# @raise [Runner::Error] if the score could not be calculated due to a failure with the runner.
|
||||
# See the specific type and message for more details.
|
||||
def calculate_score
|
||||
def calculate_score(requesting_user)
|
||||
file_scores = nil
|
||||
# If prepared_runner raises an error, no Testrun will be created.
|
||||
prepared_runner do |runner, waiting_duration|
|
||||
@ -153,7 +153,7 @@ class Submission < ApplicationRecord
|
||||
output = run_test_file file, runner, waiting_duration
|
||||
# If the previous execution failed and there is at least one more test, we request a new runner.
|
||||
runner, waiting_duration = swap_runner(runner) if output[:status] == :timeout && index < assessment_number
|
||||
score_file(output, file)
|
||||
score_file(output, file, requesting_user)
|
||||
end
|
||||
end
|
||||
# We sort the files again, so that the linter tests are displayed last.
|
||||
@ -178,10 +178,10 @@ class Submission < ApplicationRecord
|
||||
|
||||
# @raise [Runner::Error] if the file could not be tested due to a failure with the runner.
|
||||
# See the specific type and message for more details.
|
||||
def test(file)
|
||||
def test(file, requesting_user)
|
||||
prepared_runner do |runner, waiting_duration|
|
||||
output = run_test_file file, runner, waiting_duration
|
||||
score_file output, file
|
||||
score_file output, file, requesting_user
|
||||
rescue Runner::Error => e
|
||||
e.waiting_duration = waiting_duration
|
||||
raise
|
||||
@ -251,7 +251,7 @@ class Submission < ApplicationRecord
|
||||
}
|
||||
end
|
||||
|
||||
def score_file(output, file)
|
||||
def score_file(output, file, requesting_user)
|
||||
assessor = Assessor.new(execution_environment:)
|
||||
assessment = assessor.assess(output)
|
||||
passed = ((assessment[:passed] == assessment[:count]) and (assessment[:score]).positive?)
|
||||
@ -264,6 +264,7 @@ class Submission < ApplicationRecord
|
||||
end
|
||||
testrun = Testrun.create(
|
||||
submission: self,
|
||||
user: requesting_user,
|
||||
cause: 'assess', # Required to differ run and assess for RfC show
|
||||
file:, # Test file that was executed
|
||||
passed:,
|
||||
|
@ -17,6 +17,7 @@ class User < ApplicationRecord
|
||||
has_many :participations, through: :submissions, source: :exercise, as: :user
|
||||
has_many :user_proxy_exercise_exercises, as: :user
|
||||
has_many :user_exercise_interventions, as: :user
|
||||
has_many :testruns, as: :user
|
||||
has_many :interventions, through: :user_exercise_interventions
|
||||
has_many :remote_evaluation_mappings, as: :user
|
||||
has_one :codeharbor_link, dependent: :destroy
|
||||
|
@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RequireContributorOnSubmission < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
change_column_null :submissions, :contributor_id, false
|
||||
change_column_null :submissions, :contributor_type, false
|
||||
end
|
||||
end
|
@ -0,0 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class RequireSubmissionOnTestrun < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
change_column_null :testruns, :submission_id, false
|
||||
add_foreign_key :testruns, :submissions
|
||||
end
|
||||
end
|
22
db/migrate/20230710130039_add_user_to_testrun.rb
Normal file
22
db/migrate/20230710130039_add_user_to_testrun.rb
Normal file
@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddUserToTestrun < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_reference :testruns, :user, polymorphic: true, index: true
|
||||
|
||||
up_only do
|
||||
# Since we do not have programming groups, we can assume that the user who triggered a testrun is the same as the author of the corresponding submission.
|
||||
# For programming groups, this assumption is not valid (the author of a submission would the group, whereas an individual user would trigger the testrun).
|
||||
execute <<~SQL.squish
|
||||
UPDATE testruns
|
||||
SET user_id = submissions.contributor_id,
|
||||
user_type = submissions.contributor_type
|
||||
FROM submissions
|
||||
WHERE submissions.id = testruns.submission_id;
|
||||
SQL
|
||||
end
|
||||
|
||||
change_column_null :testruns, :user_id, false
|
||||
change_column_null :testruns, :user_type, false
|
||||
end
|
||||
end
|
10
db/schema.rb
10
db/schema.rb
@ -473,11 +473,11 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_084917) do
|
||||
create_table "submissions", id: :serial, force: :cascade do |t|
|
||||
t.integer "exercise_id"
|
||||
t.float "score"
|
||||
t.integer "contributor_id"
|
||||
t.integer "contributor_id", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "cause"
|
||||
t.string "contributor_type"
|
||||
t.string "contributor_type", null: false
|
||||
t.bigint "study_group_id"
|
||||
t.index ["contributor_id"], name: "index_submissions_on_contributor_id"
|
||||
t.index ["exercise_id"], name: "index_submissions_on_exercise_id"
|
||||
@ -528,7 +528,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_084917) do
|
||||
t.boolean "passed"
|
||||
t.text "output"
|
||||
t.integer "file_id"
|
||||
t.integer "submission_id"
|
||||
t.integer "submission_id", null: false
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.string "cause"
|
||||
@ -536,7 +536,10 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_084917) do
|
||||
t.interval "waiting_for_container_time"
|
||||
t.integer "exit_code", limit: 2, comment: "No exit code is available in case of a timeout"
|
||||
t.integer "status", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
|
||||
t.string "user_type", null: false
|
||||
t.bigint "user_id", null: false
|
||||
t.index ["submission_id"], name: "index_testruns_on_submission_id"
|
||||
t.index ["user_type", "user_id"], name: "index_testruns_on_user"
|
||||
t.check_constraint "exit_code >= 0 AND exit_code <= 255", name: "exit_code_constraint"
|
||||
end
|
||||
|
||||
@ -616,6 +619,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_08_19_084917) do
|
||||
add_foreign_key "testrun_execution_environments", "execution_environments"
|
||||
add_foreign_key "testrun_execution_environments", "testruns"
|
||||
add_foreign_key "testrun_messages", "testruns"
|
||||
add_foreign_key "testruns", "submissions"
|
||||
add_foreign_key "tips", "file_types"
|
||||
add_foreign_key "user_exercise_feedbacks", "submissions"
|
||||
end
|
||||
|
@ -134,12 +134,12 @@ describe Submission do
|
||||
allow(runner).to receive(:attach_to_execution).and_return(1.0)
|
||||
end
|
||||
|
||||
after { submission.calculate_score }
|
||||
after { submission.calculate_score(submission.contributor) }
|
||||
|
||||
it 'executes every teacher-defined test file' do
|
||||
allow(submission).to receive(:combine_file_scores)
|
||||
submission.collect_files.select(&:teacher_defined_assessment?).each do |file|
|
||||
expect(submission).to receive(:score_file).with(any_args, file)
|
||||
expect(submission).to receive(:score_file).with(any_args, file, submission.contributor)
|
||||
end
|
||||
end
|
||||
|
||||
|
Reference in New Issue
Block a user