# == Schema Information # # Table name: submissions # # id :integer not null, primary key # source_code :text # language_id :integer # stdin :text # expected_output :text # stdout :text # status_id :integer # created_at :datetime # finished_at :datetime # time :decimal(, ) # memory :integer # stderr :text # token :string # number_of_runs :integer # cpu_time_limit :decimal(, ) # cpu_extra_time :decimal(, ) # wall_time_limit :decimal(, ) # memory_limit :integer # stack_limit :integer # max_processes_and_or_threads :integer # enable_per_process_and_thread_time_limit :boolean # enable_per_process_and_thread_memory_limit :boolean # max_file_size :integer # compile_output :text # exit_code :integer # exit_signal :integer # message :text # wall_time :decimal(, ) # compiler_options :string # command_line_arguments :string # redirect_stderr_to_stdout :boolean # callback_url :string # additional_files :binary # enable_network :boolean # started_at :datetime # queued_at :datetime # updated_at :datetime # queue_host :string # execution_host :string # class Submission < ApplicationRecord validates :source_code, presence: true, unless: -> { is_project } validates :source_code, absence: true, if: -> { is_project } validates :additional_files, presence: true, if: -> { is_project } validates :language_id, presence: true validates :number_of_runs, numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_NUMBER_OF_RUNS } validates :cpu_time_limit, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Config::MAX_CPU_TIME_LIMIT } validates :cpu_extra_time, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Config::MAX_CPU_EXTRA_TIME } validates :wall_time_limit, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: Config::MAX_WALL_TIME_LIMIT } validates :memory_limit, numericality: { greater_than_or_equal_to: 2048, less_than_or_equal_to: Config::MAX_MEMORY_LIMIT } validates :stack_limit, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Config::MAX_STACK_LIMIT } validates :max_processes_and_or_threads, numericality: { greater_than: 0, less_than_or_equal_to: Config::MAX_MAX_PROCESSES_AND_OR_THREADS } validates :enable_per_process_and_thread_time_limit, inclusion: { in: [false], message: "this option cannot be enabled" }, unless: -> { Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT } validates :enable_per_process_and_thread_memory_limit, inclusion: { in: [false], message: "this option cannot be enabled" }, unless: -> { Config::ALLOW_ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT } validates :max_file_size, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Config::MAX_MAX_FILE_SIZE } validates :compiler_options, length: { maximum: 512 } validates :command_line_arguments, length: { maximum: 512 } validate :language_existence, :compiler_options_allowed, :command_line_arguments_allowed, :callbacks_allowed, :additional_files_allowed, :network_allowed before_create :generate_token before_validation :set_defaults enumeration :status default_scope { order(created_at: :desc) } self.per_page = 20 def source_code @decoded_source_code ||= Base64Service.decode(self[:source_code]) end def source_code=(value) super(value) self[:source_code] = Base64Service.encode(self[:source_code]) end def stdin @decoded_stdin ||= Base64Service.decode(self[:stdin]) end def stdin=(value) super(value) self[:stdin] = Base64Service.encode(self[:stdin]) end def stdout @decoded_stdout ||= Base64Service.decode(self[:stdout]) end def stdout=(value) super(value) self[:stdout] = Base64Service.encode(self[:stdout]) end def expected_output @decoded_expected_output ||= Base64Service.decode(self[:expected_output]) end def expected_output=(value) super(value) self[:expected_output] = Base64Service.encode(self[:expected_output]) end def stderr @decoded_stderr ||= Base64Service.decode(self[:stderr]) end def stderr=(value) super(value) self[:stderr] = Base64Service.encode(self[:stderr]) end def compile_output @decoded_compile_output ||= Base64Service.decode(self[:compile_output]) end def compile_output=(value) super(value) self[:compile_output] = Base64Service.encode(self[:compile_output]) end def language @language ||= Language.unscoped.find_by(id: language_id) end def status Status.find_by(id: status_id) end def status=(status) self.status_id = status.id end def is_project language.try(:is_project) || false end private def language_existence if not language errors.add(:language_id, "language with id #{language_id} doesn't exist") elsif language.is_archived errors.add(:language_id, "language with id #{language_id} is archived and cannot be used anymore") end end def compiler_options_allowed return if compiler_options.blank? unless Config::ENABLE_COMPILER_OPTIONS errors.add(:compiler_options, "setting compiler options is not allowed") return end if language && language.compile_cmd.nil? errors.add(:compiler_options, "setting compiler options is only allowed for compiled languages") return end @@allowed_languages ||= Config::ALLOWED_LANGUAGES_FOR_COMPILER_OPTIONS.collect{ |s| s + " " } if language && @@allowed_languages.present? && !language.name.starts_with?(*@@allowed_languages) @@allowed_languages_message ||= @@allowed_languages.size > 1 ? @@allowed_languages[0..-2].collect{ |s| s.strip }.join(", ") + " and " + @@allowed_languages[-1].strip : @@allowed_languages[0].strip errors.add(:compiler_options, "setting compiler options is only allowed for #{@@allowed_languages_message}") end end def command_line_arguments_allowed return if command_line_arguments.blank? unless Config::ENABLE_COMMAND_LINE_ARGUMENTS errors.add(:command_line_arguments, "setting command line arguments is not allowed") end end def callbacks_allowed return if callback_url.blank? unless Config::ENABLE_CALLBACKS errors.add(:callback_url, "setting callback is not allowed") end end def additional_files_allowed return if additional_files.blank? unless Config::ENABLE_ADDITIONAL_FILES errors.add(:additional_files, "setting additional files is not allowed") end end def network_allowed return if enable_network.blank? unless Config::ALLOW_ENABLE_NETWORK errors.add(:enable_network, "enabling network is not allowed") end end def generate_token begin self.token = SecureRandom.uuid end while self.class.exists?(token: token) end def set_defaults self.status ||= Status.queue self.number_of_runs ||= Config::NUMBER_OF_RUNS self.cpu_time_limit ||= Config::CPU_TIME_LIMIT self.cpu_extra_time ||= Config::CPU_EXTRA_TIME self.wall_time_limit ||= Config::WALL_TIME_LIMIT self.memory_limit ||= Config::MEMORY_LIMIT self.stack_limit ||= Config::STACK_LIMIT self.max_processes_and_or_threads ||= Config::MAX_PROCESSES_AND_OR_THREADS self.enable_per_process_and_thread_time_limit = NilValue.value_or_default( self.enable_per_process_and_thread_time_limit, Config::ENABLE_PER_PROCESS_AND_THREAD_TIME_LIMIT ) self.enable_per_process_and_thread_memory_limit = NilValue.value_or_default( self.enable_per_process_and_thread_memory_limit, Config::ENABLE_PER_PROCESS_AND_THREAD_MEMORY_LIMIT ) self.max_file_size ||= Config::MAX_FILE_SIZE self.redirect_stderr_to_stdout = NilValue.value_or_default( self.redirect_stderr_to_stdout, Config::REDIRECT_STDERR_TO_STDOUT ) self.enable_network = NilValue.value_or_default( self.enable_network, Config::ENABLE_NETWORK ) end end