Files
codeocean/app/controllers/execution_environments_controller.rb
2021-05-14 22:03:06 +02:00

158 lines
4.9 KiB
Ruby

# frozen_string_literal: true
class ExecutionEnvironmentsController < ApplicationController
include CommonBehavior
before_action :set_docker_images, only: %i[create edit new update]
before_action :set_execution_environment, only: MEMBER_ACTIONS + %i[execute_command shell statistics]
before_action :set_testing_framework_adapters, only: %i[create edit new update]
def authorize!
authorize(@execution_environment || @execution_environments)
end
private :authorize!
def create
@execution_environment = ExecutionEnvironment.new(execution_environment_params)
authorize!
create_and_respond(object: @execution_environment)
end
def destroy
destroy_and_respond(object: @execution_environment)
end
def edit; end
def execute_command
@docker_client = DockerClient.new(execution_environment: @execution_environment)
render(json: @docker_client.execute_arbitrary_command(params[:command]))
end
def working_time_query
"
SELECT exercise_id, avg(working_time) as average_time, stddev_samp(extract('epoch' from working_time)) * interval '1 second' as stddev_time
FROM
(
SELECT user_id,
exercise_id,
sum(working_time_new) AS working_time
FROM
(SELECT user_id,
exercise_id,
CASE WHEN working_time >= #{StatisticsHelper::WORKING_TIME_DELTA_IN_SQL_INTERVAL} THEN '0' ELSE working_time END AS working_time_new
FROM
(SELECT user_id,
exercise_id,
id,
(created_at - lag(created_at) over (PARTITION BY user_id, exercise_id
ORDER BY created_at)) AS working_time
FROM submissions
WHERE exercise_id IN (SELECT ID FROM exercises WHERE execution_environment_id = #{@execution_environment.id})
GROUP BY exercise_id, user_id, id) AS foo) AS bar
GROUP BY user_id, exercise_id
) AS baz GROUP BY exercise_id;
"
end
def user_query
"
SELECT
id AS exercise_id,
COUNT(DISTINCT user_id) AS users,
AVG(score) AS average_score,
MAX(score) AS maximum_score,
stddev_samp(score) as stddev_score,
CASE
WHEN MAX(score)=0 THEN 0
ELSE 100 / MAX(score) * AVG(score)
END AS percent_correct,
SUM(submission_count) / COUNT(DISTINCT user_id) AS average_submission_count
FROM
(SELECT e.id,
s.user_id,
MAX(s.score) AS score,
COUNT(s.id) AS submission_count
FROM submissions s
JOIN exercises e ON e.id = s.exercise_id
WHERE e.execution_environment_id = #{@execution_environment.id}
GROUP BY e.id,
s.user_id) AS inner_query
GROUP BY id;
"
end
def statistics
working_time_statistics = {}
user_statistics = {}
ApplicationRecord.connection.execute(working_time_query).each do |tuple|
working_time_statistics[tuple['exercise_id'].to_i] = tuple
end
ApplicationRecord.connection.execute(user_query).each do |tuple|
user_statistics[tuple['exercise_id'].to_i] = tuple
end
render locals: {
working_time_statistics: working_time_statistics,
user_statistics: user_statistics,
}
end
def execution_environment_params
if params[:execution_environment].present?
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(
user_id: current_user.id, user_type: current_user.class.name
)
end
end
private :execution_environment_params
def index
@execution_environments = ExecutionEnvironment.all.includes(:user).order(:name).paginate(page: params[:page])
authorize!
end
def new
@execution_environment = ExecutionEnvironment.new
authorize!
end
def set_docker_images
DockerClient.check_availability!
@docker_images = DockerClient.image_tags.sort
rescue DockerClient::Error => e
@docker_images = []
flash[:warning] = e.message
Sentry.capture_exception(e)
end
private :set_docker_images
def set_execution_environment
@execution_environment = ExecutionEnvironment.find(params[:id])
authorize!
end
private :set_execution_environment
def set_testing_framework_adapters
Rails.application.eager_load!
@testing_framework_adapters = TestingFrameworkAdapter.descendants.sort_by(&:framework_name).map do |klass|
[klass.framework_name, klass.name]
end
end
private :set_testing_framework_adapters
def shell; end
def show
if @execution_environment.testing_framework?
@testing_framework_adapter = Kernel.const_get(@execution_environment.testing_framework)
end
end
def update
update_and_respond(object: @execution_environment, params: execution_environment_params)
end
end