Coursemology/coursemology2

View on GitHub
app/controllers/course/assessment/question/programming_controller.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# frozen_string_literal: true
class Course::Assessment::Question::ProgrammingController < Course::Assessment::Question::Controller
  include Course::Assessment::Question::KoditsuQuestionConcern

  build_and_authorize_new_question :programming_question,
                                   class: Course::Assessment::Question::Programming, only: [:new, :create]
  load_and_authorize_resource :programming_question,
                              class: 'Course::Assessment::Question::Programming',
                              through: :assessment, parent: false,
                              except: [:new, :create, :generate, :codaveri_languages]
  before_action :load_question_assessment, only: [:edit, :update, :update_question_setting]
  before_action :set_attributes_for_programming_question, except: [:generate, :codaveri_languages]

  def new
    respond_to do |format|
      format.json { format_test_cases }
    end
  end

  def create
    @programming_question.package_type = programming_question_params.key?(:file) ? :zip_upload : :online_editor
    process_package

    is_course_koditsu_enabled = current_course.component_enabled?(Course::KoditsuPlatformComponent)

    if @programming_question.save
      load_question_assessment

      create_question_in_koditsu if is_course_koditsu_enabled && @assessment.is_koditsu_enabled

      render_success_json true
    else
      render_failure_json
    end
  end

  def edit
    respond_to do |format|
      format.json do
        @meta = programming_package_service.extract_meta if @programming_question.edit_online?
        format_test_cases
      end
    end
  end

  def update
    result = @programming_question.class.transaction do
      @question_assessment.skill_ids = programming_question_params[:question_assessment].
                                       try(:[], :skill_ids)
      @programming_question.assign_attributes(programming_question_params.
                                              except(:question_assessment))
      process_package

      raise ActiveRecord::Rollback unless @programming_question.save

      @question = @programming_question.acting_as
      @question.update!(is_synced_with_koditsu: false)

      is_course_koditsu_enabled = current_course.component_enabled?(Course::KoditsuPlatformComponent)

      edit_koditsu_question if is_course_koditsu_enabled && @assessment.is_koditsu_enabled

      true
    end

    if result
      render_success_json false
    else
      render_failure_json
    end
  end

  def codaveri_languages
    render json: {
      languages: Coursemology::Polyglot::Language.
        where(enabled: true).
        order(weight: :desc).
        filter_map do |language|
          if CodaveriAsyncApiService.language_valid_for_codaveri?(language)
            {
              id: language.id,
              name: language.name,
              disabled: !language.enabled,
              editorMode: language.ace_mode
            }
          end
        end
    }, status: :ok
  end

  def generate # rubocop:disable Metrics/AbcSize
    language = Coursemology::Polyglot::Language.where(id: params[:language_id]).first

    unless CodaveriAsyncApiService.language_valid_for_codaveri?(language)
      render json: {
        success: false,
        message: 'Language not supported'
      }, status: :bad_request
    end

    generation_service = Course::Assessment::Question::CodaveriProblemGenerationService.new(
      @assessment,
      params[:custom_prompt],
      # TODO: move these declarations (polyglot_language_name and _version) to polyglot repo
      language.name.split[0].downcase,
      language.name.split[1],
      params[:difficulty]
    )
    generated_problem = generation_service.codaveri_generate_problem
    render json: generated_problem, status: :ok
  end

  def update_question_setting
    if @programming_question.update(programming_question_setting_params)
      head :ok
    else
      error = @programming_question.errors.full_messages.to_sentence
      render json: { errors: error }, status: :bad_request
    end
  end

  def destroy
    koditsu_question_id = @programming_question.acting_as.koditsu_question_id

    success = @programming_question.class.transaction do
      raise ActiveRecord::Rollback unless @programming_question.destroy

      is_course_koditsu_enabled = current_course.component_enabled?(Course::KoditsuPlatformComponent)

      if is_course_koditsu_enabled && @assessment.is_koditsu_enabled
        @assessment.update!(is_synced_with_koditsu: false)
        remove_question_from_assessment_in_koditsu(koditsu_question_id)

        delete_koditsu_question(koditsu_question_id)
      end

      true
    end

    if success
      head :ok
    else
      error = @programming_question.errors.full_messages.to_sentence
      render json: { errors: error }, status: :bad_request
    end
  end

  private

  def format_test_cases
    @public_test_cases = []
    @private_test_cases = []
    @evaluation_test_cases = []

    @programming_question.test_cases.each do |test_case|
      @public_test_cases << test_case if test_case.public_test?
      @private_test_cases << test_case if test_case.private_test?
      @evaluation_test_cases << test_case if test_case.evaluation_test?
    end
  end

  def set_attributes_for_programming_question
    @programming_question.max_time_limit = current_course.programming_max_time_limit
  end

  def programming_question_params
    params.require(:question_programming).permit(
      :title, :description, :staff_only_comments, :maximum_grade,
      :language_id, :memory_limit, :time_limit, :attempt_limit,
      :live_feedback_enabled, :live_feedback_custom_prompt,
      :is_low_priority, :is_codaveri, *attachment_params,
      question_assessment: { skill_ids: [] }
    )
  end

  def programming_question_setting_params
    params.require(:question_programming).permit(:is_codaveri, :live_feedback_enabled)
  end

  def render_success_json(redirect_to_edit)
    render partial: 'response', locals: { redirect_to_edit: redirect_to_edit }
  end

  def render_failure_json
    render json: { errors: @programming_question.errors.full_messages.to_sentence }, status: :bad_request
  end

  def process_package
    return unless @programming_question.edit_online?

    programming_package_service(params).generate_package
    @meta = programming_package_service(params).extract_meta
    @programming_question.multiple_file_submission = @meta[:data]['submit_as_file'] || false
  end

  def programming_package_service(params = nil)
    @service ||= Course::Assessment::Question::Programming::ProgrammingPackageService.new(
      @programming_question, params
    )
  end

  def load_question_assessment
    @question_assessment = load_question_assessment_for(@programming_question)
  end
end