<commit_before># 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 sync_to_runner_management]
  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
    # Add the current execution_environment if not already present in the list
    @docker_images |= [@execution_environment.docker_image]
  end

  def execute_command
    runner = Runner.for(current_user, @execution_environment)
    output = runner.execute_command(params[:command], raise_exception: false)
    render json: output.except(:messages)
  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 #{StatisticsHelper.working_time_larger_delta} 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?
      exposed_ports = if params[:execution_environment][:exposed_ports_list]
                        # Transform the `exposed_ports_list` to `exposed_ports` array
                        params[:execution_environment].delete(:exposed_ports_list).scan(/\d+/)
                      else
                        []
                      end

      params[:execution_environment].permit(:docker_image, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :cpu_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, exposed_ports: exposed_ports
      )
    end
  end
  private :execution_environment_params

  def index
    @execution_environments = ExecutionEnvironment.all.includes(:user).order(:name).paginate(page: params[:page], per_page: per_page_param)
    authorize!
  end

  def new
    @execution_environment = ExecutionEnvironment.new
    authorize!
  end

  def set_docker_images
    @docker_images ||= ExecutionEnvironment.pluck(:docker_image)
    @docker_images += Runner.strategy_class.available_images
  rescue Runner::Error => e
    flash[:warning] = html_escape e.message
  ensure
    @docker_images = @docker_images.sort.uniq
  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 = TestingFrameworkAdapter.descendants.find {|klass| klass.name == @execution_environment.testing_framework }
    end
  end

  def update
    update_and_respond(object: @execution_environment, params: execution_environment_params)
  end

  def sync_to_runner_management
    return unless Runner.management_active?

    begin
      Runner.strategy_class.sync_environment(@execution_environment)
    rescue Runner::Error => e
      Rails.logger.warn { "Runner error while synchronizing execution environment with id #{@execution_environment.id}: #{e.message}" }
      redirect_to @execution_environment, alert: t('execution_environments.index.synchronize.failure', error: e.message)
    else
      redirect_to @execution_environment, notice: t('execution_environments.index.synchronize.success')
    end
  end
<commit_msg>Update ExecutionEnvironment statistics and sync message<commit_after># 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 sync_to_runner_management]
  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
    # Add the current execution_environment if not already present in the list
    @docker_images |= [@execution_environment.docker_image]
  end

  def execute_command
    runner = Runner.for(current_user, @execution_environment)
    output = runner.execute_command(params[:command], raise_exception: false)
    render json: output.except(:messages)
  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 #{StatisticsHelper.working_time_larger_delta} 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 #{ExecutionEnvironment.sanitize_sql(['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 #{ExecutionEnvironment.sanitize_sql(['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?
      exposed_ports = if params[:execution_environment][:exposed_ports_list]
                        # Transform the `exposed_ports_list` to `exposed_ports` array
                        params[:execution_environment].delete(:exposed_ports_list).scan(/\d+/)
                      else
                        []
                      end

      params[:execution_environment].permit(:docker_image, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :cpu_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, exposed_ports: exposed_ports
      )
    end
  end
  private :execution_environment_params

  def index
    @execution_environments = ExecutionEnvironment.all.includes(:user).order(:name).paginate(page: params[:page], per_page: per_page_param)
    authorize!
  end

  def new
    @execution_environment = ExecutionEnvironment.new
    authorize!
  end

  def set_docker_images
    @docker_images ||= ExecutionEnvironment.pluck(:docker_image)
    @docker_images += Runner.strategy_class.available_images
  rescue Runner::Error => e
    flash[:warning] = html_escape e.message
  ensure
    @docker_images = @docker_images.sort.uniq
  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 = TestingFrameworkAdapter.descendants.find {|klass| klass.name == @execution_environment.testing_framework }
    end
  end

  def update
    update_and_respond(object: @execution_environment, params: execution_environment_params)
  end

  def sync_to_runner_management
    return unless Runner.management_active?

    begin
      Runner.strategy_class.sync_environment(@execution_environment)
    rescue Runner::Error => e
      Rails.logger.warn { "Runner error while synchronizing execution environment with id #{@execution_environment.id}: #{e.message}" }
      redirect_to @execution_environment, alert: t('execution_environments.index.synchronize.failure', error: html_escape(e.message))
    else
      redirect_to @execution_environment, notice: t('execution_environments.index.synchronize.success')
    end
  end

{'fraction_non_alphanumeric': 0.07518678725914274, 'fraction_numerical': 0.0011010617381046008, 'mean_word_length': 3.287255563047876, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '974336', 'n_tokens_mistral': 3803, 'n_tokens_neox': 3559, 'n_words': 813}