Apply automatic rubocop fixes
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsController < ApplicationController
|
||||
include ActionController::Live
|
||||
include CommonBehavior
|
||||
@ -6,15 +8,16 @@ class SubmissionsController < ApplicationController
|
||||
include SubmissionScoring
|
||||
include Tubesock::Hijack
|
||||
|
||||
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, :run]
|
||||
before_action :set_file, only: [:download_file, :render_file, :run]
|
||||
before_action :set_mime_type, only: [:download_file, :render_file]
|
||||
skip_before_action :verify_authenticity_token, only: [:download_file, :render_file]
|
||||
before_action :set_submission,
|
||||
only: %i[download download_file render_file run score extract_errors show statistics stop test]
|
||||
before_action :set_docker_client, only: %i[run test]
|
||||
before_action :set_files, only: %i[download download_file render_file show run]
|
||||
before_action :set_file, only: %i[download_file render_file run]
|
||||
before_action :set_mime_type, only: %i[download_file render_file]
|
||||
skip_before_action :verify_authenticity_token, only: %i[download_file render_file]
|
||||
|
||||
def max_run_output_buffer_size
|
||||
if(@submission.cause == 'requestComments')
|
||||
if @submission.cause == 'requestComments'
|
||||
5000
|
||||
else
|
||||
500
|
||||
@ -34,30 +37,30 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def command_substitutions(filename)
|
||||
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename, module_name: File.basename(filename, File.extname(filename)).underscore}
|
||||
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename,
|
||||
module_name: File.basename(filename, File.extname(filename)).underscore}
|
||||
end
|
||||
private :command_substitutions
|
||||
|
||||
def copy_comments
|
||||
# copy each annotation and set the target_file.id
|
||||
unless(params[:annotations_arr].nil?)
|
||||
params[:annotations_arr].each do | annotation |
|
||||
#comment = Comment.new(annotation[1].permit(:user_id, :file_id, :user_type, :row, :column, :text, :created_at, :updated_at))
|
||||
comment = Comment.new(:user_id => annotation[1][:user_id], :file_id => annotation[1][:file_id], :user_type => current_user.class.name, :row => annotation[1][:row], :column => annotation[1][:column], :text => annotation[1][:text])
|
||||
source_file = CodeOcean::File.find(annotation[1][:file_id])
|
||||
params[:annotations_arr]&.each do |annotation|
|
||||
# comment = Comment.new(annotation[1].permit(:user_id, :file_id, :user_type, :row, :column, :text, :created_at, :updated_at))
|
||||
comment = Comment.new(user_id: annotation[1][:user_id], file_id: annotation[1][:file_id],
|
||||
user_type: current_user.class.name, row: annotation[1][:row], column: annotation[1][:column], text: annotation[1][:text])
|
||||
source_file = CodeOcean::File.find(annotation[1][:file_id])
|
||||
|
||||
# retrieve target file
|
||||
target_file = @submission.files.detect do |file|
|
||||
# file_id has to be that of a the former iteration OR of the initial file (if this is the first run)
|
||||
file.file_id == source_file.file_id || file.file_id == source_file.id #seems to be needed here: (check this): || file.file_id == source_file.id ; yes this is needed, for comments on templates as well as comments on files added by users.
|
||||
end
|
||||
|
||||
#save to assign an id
|
||||
target_file.save!
|
||||
|
||||
comment.file_id = target_file.id
|
||||
comment.save!
|
||||
# retrieve target file
|
||||
target_file = @submission.files.detect do |file|
|
||||
# file_id has to be that of a the former iteration OR of the initial file (if this is the first run)
|
||||
file.file_id == source_file.file_id || file.file_id == source_file.id # seems to be needed here: (check this): || file.file_id == source_file.id ; yes this is needed, for comments on templates as well as comments on files added by users.
|
||||
end
|
||||
|
||||
# save to assign an id
|
||||
target_file.save!
|
||||
|
||||
comment.file_id = target_file.id
|
||||
comment.save!
|
||||
end
|
||||
end
|
||||
|
||||
@ -75,30 +78,35 @@ class SubmissionsController < ApplicationController
|
||||
require 'zip'
|
||||
stringio = Zip::OutputStream.write_buffer do |zio|
|
||||
@files.each do |file|
|
||||
zio.put_next_entry(file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension))
|
||||
zio.write(file.content.present? ? file.content : file.native_file.read)
|
||||
zio.put_next_entry(if file.path.to_s == ''
|
||||
file.name_with_extension
|
||||
else
|
||||
File.join(file.path,
|
||||
file.name_with_extension)
|
||||
end)
|
||||
zio.write(file.content.presence || file.native_file.read)
|
||||
end
|
||||
|
||||
# zip exercise description
|
||||
zio.put_next_entry(t('activerecord.models.exercise.one') + '.txt')
|
||||
zio.write(@submission.exercise.title + "\r\n======================\r\n")
|
||||
zio.put_next_entry("#{t('activerecord.models.exercise.one')}.txt")
|
||||
zio.write("#{@submission.exercise.title}\r\n======================\r\n")
|
||||
zio.write(@submission.exercise.description)
|
||||
|
||||
# zip .co file
|
||||
zio.put_next_entry(".co")
|
||||
zio.write(File.read id_file)
|
||||
zio.put_next_entry('.co')
|
||||
zio.write(File.read(id_file))
|
||||
File.delete(id_file) if File.exist?(id_file)
|
||||
|
||||
# zip client scripts
|
||||
scripts_path = 'app/assets/remote_scripts'
|
||||
Dir.foreach(scripts_path) do |file|
|
||||
next if file == '.' or file == '..'
|
||||
zio.put_next_entry(File.join('.scripts', File.basename(file)))
|
||||
zio.write(File.read File.join(scripts_path, file))
|
||||
end
|
||||
next if (file == '.') || (file == '..')
|
||||
|
||||
zio.put_next_entry(File.join('.scripts', File.basename(file)))
|
||||
zio.write(File.read(File.join(scripts_path, file)))
|
||||
end
|
||||
end
|
||||
send_data(stringio.string, filename: @submission.exercise.title.tr(" ", "_") + ".zip")
|
||||
send_data(stringio.string, filename: "#{@submission.exercise.title.tr(' ', '_')}.zip")
|
||||
end
|
||||
|
||||
def download_file
|
||||
@ -128,7 +136,7 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def run
|
||||
# TODO reimplement SSEs with websocket commands
|
||||
# TODO: reimplement SSEs with websocket commands
|
||||
# with_server_sent_events do |server_sent_event|
|
||||
# output = @docker_client.execute_run_command(@submission, sanitize_filename)
|
||||
|
||||
@ -155,56 +163,54 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# socket is the socket into the container, tubesock is the socket to the client
|
||||
|
||||
# give the docker_client the tubesock object, so that it can send messages (timeout)
|
||||
@docker_client.tubesock = tubesock
|
||||
|
||||
container_request_time = Time.now
|
||||
container_request_time = Time.zone.now
|
||||
result = @docker_client.execute_run_command(@submission, sanitize_filename)
|
||||
tubesock.send_data JSON.dump({'cmd' => 'status', 'status' => result[:status]})
|
||||
@waiting_for_container_time = Time.now - container_request_time
|
||||
@waiting_for_container_time = Time.zone.now - container_request_time
|
||||
|
||||
if result[:status] == :container_running
|
||||
socket = result[:socket]
|
||||
command = result[:command]
|
||||
|
||||
socket.on :message do |event|
|
||||
Rails.logger.info( Time.now.getutc.to_s + ": Docker sending: " + event.data)
|
||||
Rails.logger.info("#{Time.zone.now.getutc}: Docker sending: #{event.data}")
|
||||
handle_message(event.data, tubesock, result[:container])
|
||||
end
|
||||
|
||||
socket.on :close do |event|
|
||||
socket.on :close do |_event|
|
||||
kill_socket(tubesock)
|
||||
end
|
||||
|
||||
tubesock.onmessage do |data|
|
||||
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
||||
Rails.logger.info("#{Time.zone.now.getutc}: Client sending: #{data}")
|
||||
# Check whether the client send a JSON command and kill container
|
||||
# if the command is 'client_kill', send it to docker otherwise.
|
||||
begin
|
||||
|
||||
parsed = JSON.parse(data) unless data == "\n"
|
||||
if parsed.class == Hash && parsed['cmd'] == 'client_kill'
|
||||
Rails.logger.debug("Client exited container.")
|
||||
if parsed.instance_of?(Hash) && parsed['cmd'] == 'client_kill'
|
||||
Rails.logger.debug('Client exited container.')
|
||||
@docker_client.kill_container(result[:container])
|
||||
else
|
||||
socket.send data
|
||||
Rails.logger.debug('Sent the received client data to docker:' + data)
|
||||
Rails.logger.debug("Sent the received client data to docker:#{data}")
|
||||
end
|
||||
rescue JSON::ParserError => error
|
||||
rescue JSON::ParserError => e
|
||||
socket.send data
|
||||
Rails.logger.debug('Rescued parsing error, sent the received client data to docker:' + data)
|
||||
Rails.logger.debug("Rescued parsing error, sent the received client data to docker:#{data}")
|
||||
Sentry.set_extras(data: data)
|
||||
end
|
||||
end
|
||||
|
||||
# Send command after all listeners are attached.
|
||||
# Newline required to flush
|
||||
@execution_request_time = Time.now
|
||||
socket.send command + "\n"
|
||||
Rails.logger.info('Sent command: ' + command.to_s)
|
||||
@execution_request_time = Time.zone.now
|
||||
socket.send "#{command}\n"
|
||||
Rails.logger.info("Sent command: #{command}")
|
||||
else
|
||||
kill_socket(tubesock)
|
||||
end
|
||||
@ -212,7 +218,7 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
|
||||
def kill_socket(tubesock)
|
||||
@container_execution_time = Time.now - @execution_request_time unless @execution_request_time.blank?
|
||||
@container_execution_time = Time.zone.now - @execution_request_time if @execution_request_time.present?
|
||||
# search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb)
|
||||
errors = extract_errors
|
||||
send_hints(tubesock, errors)
|
||||
@ -225,7 +231,7 @@ class SubmissionsController < ApplicationController
|
||||
if @run_output.blank? || @run_output&.strip == '{"cmd":"exit"}' || @run_output&.strip == 'timeout:'
|
||||
@raw_output ||= ''
|
||||
@run_output ||= ''
|
||||
parse_message t('exercises.implement.no_output', timestamp: l(Time.now, format: :short)), 'stdout', tubesock
|
||||
parse_message t('exercises.implement.no_output', timestamp: l(Time.zone.now, format: :short)), 'stdout', tubesock
|
||||
end
|
||||
|
||||
# Hijacked connection needs to be notified correctly
|
||||
@ -237,25 +243,26 @@ class SubmissionsController < ApplicationController
|
||||
@raw_output ||= ''
|
||||
@run_output ||= ''
|
||||
# Handle special commands first
|
||||
if /^#exit|{"cmd": "exit"}/.match(message)
|
||||
# Just call exit_container on the docker_client.
|
||||
# Do not call kill_socket for the websocket to the client here.
|
||||
# @docker_client.exit_container closes the socket to the container,
|
||||
# kill_socket is called in the "on close handler" of the websocket to the container
|
||||
@docker_client.exit_container(container)
|
||||
elsif /^#timeout/.match(message)
|
||||
@run_output = 'timeout: ' + @run_output # add information that this run timed out to the buffer
|
||||
else
|
||||
# Filter out information about run_command, test_command, user or working directory
|
||||
run_command = @submission.execution_environment.run_command % command_substitutions(sanitize_filename)
|
||||
test_command = @submission.execution_environment.test_command % command_substitutions(sanitize_filename)
|
||||
if test_command.blank?
|
||||
# If no test command is set, use the run_command for the RegEx below. Otherwise, no output will be displayed!
|
||||
test_command = run_command
|
||||
end
|
||||
unless /root@|:\/workspace|#{run_command}|#{test_command}|bash: cmd:canvasevent: command not found/.match(message)
|
||||
parse_message(message, 'stdout', tubesock, container)
|
||||
end
|
||||
case message
|
||||
when /^#exit|{"cmd": "exit"}/
|
||||
# Just call exit_container on the docker_client.
|
||||
# Do not call kill_socket for the websocket to the client here.
|
||||
# @docker_client.exit_container closes the socket to the container,
|
||||
# kill_socket is called in the "on close handler" of the websocket to the container
|
||||
@docker_client.exit_container(container)
|
||||
when /^#timeout/
|
||||
@run_output = "timeout: #{@run_output}" # add information that this run timed out to the buffer
|
||||
else
|
||||
# Filter out information about run_command, test_command, user or working directory
|
||||
run_command = @submission.execution_environment.run_command % command_substitutions(sanitize_filename)
|
||||
test_command = @submission.execution_environment.test_command % command_substitutions(sanitize_filename)
|
||||
if test_command.blank?
|
||||
# If no test command is set, use the run_command for the RegEx below. Otherwise, no output will be displayed!
|
||||
test_command = run_command
|
||||
end
|
||||
unless %r{root@|:/workspace|#{run_command}|#{test_command}|bash: cmd:canvasevent: command not found}.match?(message)
|
||||
parse_message(message, 'stdout', tubesock, container)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -263,58 +270,58 @@ class SubmissionsController < ApplicationController
|
||||
parsed = ''
|
||||
begin
|
||||
parsed = JSON.parse(message)
|
||||
if parsed.class == Hash and parsed.key?('cmd')
|
||||
if parsed.instance_of?(Hash) && parsed.key?('cmd')
|
||||
socket.send_data message
|
||||
Rails.logger.info('parse_message sent: ' + message)
|
||||
Rails.logger.info("parse_message sent: #{message}")
|
||||
@docker_client.exit_container(container) if container && parsed['cmd'] == 'exit'
|
||||
else
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => message}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
|
||||
Rails.logger.info("parse_message sent: #{JSON.dump(parsed)}")
|
||||
end
|
||||
rescue JSON::ParserError => e
|
||||
# Check wether the message contains multiple lines, if true try to parse each line
|
||||
if recursive and message.include? "\n"
|
||||
for part in message.split("\n")
|
||||
self.parse_message(part,output_stream,socket, container, false)
|
||||
if recursive && message.include?("\n")
|
||||
message.split("\n").each do |part|
|
||||
parse_message(part, output_stream, socket, container, false)
|
||||
end
|
||||
elsif message.include?('<img') || message.start_with?('{"cmd') || message.include?('"turtlebatch"')
|
||||
#Rails.logger.info('img foung')
|
||||
# Rails.logger.info('img foung')
|
||||
@buffering = true
|
||||
@buffer = ''
|
||||
@buffer += message
|
||||
#Rails.logger.info('Starting to buffer')
|
||||
elsif @buffering and message.include?('/>')
|
||||
# Rails.logger.info('Starting to buffer')
|
||||
elsif @buffering && message.include?('/>')
|
||||
@buffer += message
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>@buffer}
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => @buffer}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
#socket.send_data @buffer
|
||||
# socket.send_data @buffer
|
||||
@buffering = false
|
||||
#Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering and message.end_with?("}\r")
|
||||
# Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering && message.end_with?("}\r")
|
||||
@buffer += message
|
||||
socket.send_data @buffer
|
||||
@buffering = false
|
||||
#Rails.logger.info('Sent complete buffer')
|
||||
# Rails.logger.info('Sent complete buffer')
|
||||
elsif @buffering
|
||||
@buffer += message
|
||||
#Rails.logger.info('Appending to buffer')
|
||||
# Rails.logger.info('Appending to buffer')
|
||||
else
|
||||
#Rails.logger.info('else')
|
||||
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
|
||||
# Rails.logger.info('else')
|
||||
parsed = {'cmd' => 'write', 'stream' => output_stream, 'data' => message}
|
||||
socket.send_data JSON.dump(parsed)
|
||||
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
|
||||
Rails.logger.info("parse_message sent: #{JSON.dump(parsed)}")
|
||||
end
|
||||
ensure
|
||||
@raw_output += parsed['data'].to_s if parsed.class == Hash and parsed.key? 'data'
|
||||
@raw_output += parsed['data'].to_s if parsed.instance_of?(Hash) && parsed.key?('data')
|
||||
# save the data that was send to the run_output if there is enough space left. this will be persisted as a testrun with cause "run"
|
||||
@run_output += JSON.dump(parsed).to_s if @run_output.size <= max_run_output_buffer_size
|
||||
end
|
||||
end
|
||||
|
||||
def save_run_output
|
||||
unless @run_output.blank?
|
||||
@run_output = @run_output[(0..max_run_output_buffer_size-1)] # trim the string to max_message_buffer_size chars
|
||||
if @run_output.present?
|
||||
@run_output = @run_output[(0..max_run_output_buffer_size - 1)] # trim the string to max_message_buffer_size chars
|
||||
Testrun.create(
|
||||
file: @file,
|
||||
cause: 'run',
|
||||
@ -328,7 +335,7 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
def extract_errors
|
||||
results = []
|
||||
unless @raw_output.blank?
|
||||
if @raw_output.present?
|
||||
@submission.exercise.execution_environment.error_templates.each do |template|
|
||||
pattern = Regexp.new(template.signature).freeze
|
||||
if pattern.match(@raw_output)
|
||||
@ -361,7 +368,7 @@ class SubmissionsController < ApplicationController
|
||||
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))
|
||||
# send_hints(tubesock, StructuredError.where(submission: @submission))
|
||||
|
||||
tubesock.send_data JSON.dump({'cmd' => 'exit'})
|
||||
ensure
|
||||
@ -372,8 +379,9 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
def send_hints(tubesock, errors)
|
||||
return if @embed_options[:disable_hints]
|
||||
errors = errors.to_a.uniq { |e| e.hint}
|
||||
errors.each do | error |
|
||||
|
||||
errors = errors.to_a.uniq(&:hint)
|
||||
errors.each do |error|
|
||||
tubesock.send_data JSON.dump({cmd: 'hint', hint: error.hint, description: error.error_template.description})
|
||||
end
|
||||
end
|
||||
@ -384,7 +392,7 @@ class SubmissionsController < ApplicationController
|
||||
private :set_docker_client
|
||||
|
||||
def set_file
|
||||
@file = @files.detect { |file| file.name_with_extension == sanitize_filename }
|
||||
@file = @files.detect {|file| file.name_with_extension == sanitize_filename }
|
||||
head :not_found unless @file
|
||||
end
|
||||
private :set_file
|
||||
@ -406,11 +414,9 @@ class SubmissionsController < ApplicationController
|
||||
end
|
||||
private :set_submission
|
||||
|
||||
def show
|
||||
end
|
||||
def show; end
|
||||
|
||||
def statistics
|
||||
end
|
||||
def statistics; end
|
||||
|
||||
def test
|
||||
hijack do |tubesock|
|
||||
@ -436,10 +442,10 @@ class SubmissionsController < ApplicationController
|
||||
server_sent_event.write(nil, event: 'start')
|
||||
yield(server_sent_event) if block_given?
|
||||
server_sent_event.write({code: 200}, event: 'close')
|
||||
rescue => exception
|
||||
Sentry.capture_exception(exception)
|
||||
logger.error(exception.message)
|
||||
logger.error(exception.backtrace.join("\n"))
|
||||
rescue StandardError => e
|
||||
Sentry.capture_exception(e)
|
||||
logger.error(e.message)
|
||||
logger.error(e.backtrace.join("\n"))
|
||||
server_sent_event.write({code: 500}, event: 'close')
|
||||
ensure
|
||||
server_sent_event.close
|
||||
@ -457,16 +463,16 @@ class SubmissionsController < ApplicationController
|
||||
)
|
||||
|
||||
# create .co file
|
||||
path = "tmp/" + user.id.to_s + ".co"
|
||||
path = "tmp/#{user.id}.co"
|
||||
# parse validation token
|
||||
content = "#{remote_evaluation_mapping.validation_token}\n"
|
||||
# parse remote request url
|
||||
content += "#{request.base_url}/evaluate\n"
|
||||
@submission.files.each do |file|
|
||||
file_path = file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension)
|
||||
content += "#{file_path}=#{file.file_id.to_s}\n"
|
||||
content += "#{file_path}=#{file.file_id}\n"
|
||||
end
|
||||
File.open(path, "w+") do |f|
|
||||
File.open(path, 'w+') do |f|
|
||||
f.write(content)
|
||||
end
|
||||
path
|
||||
|
Reference in New Issue
Block a user