Newer
Older
dmpopidor / app / controllers / madmp_fragments_controller.rb
# frozen_string_literal: true

class MadmpFragmentsController < ApplicationController

  after_action :verify_authorized
  include DynamicFormHelper

  # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
  # rubocop:disable Metrics/MethodLength
  def create
    p_params = permitted_params
    @schemas = MadmpSchema.all
    schema = @schemas.find(p_params[:schema_id])
    source = p_params[:source]
    classname = schema.classname
    parent_id = p_params[:parent_id] unless classname.eql?("person")

    @fragment = MadmpFragment.new(
      dmp_id: p_params[:dmp_id],
      parent_id: parent_id,
      madmp_schema: schema,
      additional_info: {
        "property_name" => p_params[:property_name]
      }
    )
    @fragment.classname = classname

    authorize @fragment

    if source.eql?("form")
      @fragment.answer = Answer.create!(
        research_output_id: p_params[:answer][:research_output_id],
        plan_id: p_params[:answer][:plan_id],
        question_id: p_params[:answer][:question_id],
        lock_version: p_params[:answer][:lock_version],
        is_common: p_params[:answer][:is_common],
        user_id: current_user.id
      )
      @fragment.instantiate
    else
      data = data_reformater(
        schema.schema,
        schema_params(schema)
      )
      if MadmpFragment.fragment_exists?(data, schema, p_params[:dmp_id], parent_id)
        render json: {
          "error" => d_("dmpopidor", "Element is already present in your plan.")
        }, status: 409
        return
      end

      additional_info = @fragment.additional_info.merge(
        "validations" => MadmpFragment.validate_data(data, schema.schema)
      )
      @fragment.assign_attributes(
        additional_info: additional_info
      )
      @fragment.instantiate
      @fragment.save_form_fragment(data, schema)
    end

    return unless @fragment.present?

    if source.eql?("list-modal")
      property_name = @fragment.additional_info["property_name"]
      render json: {
        "fragment_id" =>  @fragment.parent_id,
        "source" => source,
        "html" => render_fragment_list(
          @fragment.dmp_id,
          parent_id,
          schema.id,
          property_name,
          p_params[:template_locale],
          p_params[:query_id]
        )
      }.to_json
    elsif source.eql?("select-modal")
      render json: {
        "fragment_id" =>  @fragment.id,
        "source" => source,
        "html" => render_fragment_select(@fragment)
      }.to_json
    else
      render json: render_fragment_form(@fragment, @stale_fragment)
    end
  end
  # rubocop:enable Metrics/MethodLength
  # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity

  def load_form
    @fragment = MadmpFragment.find(params[:id])
    @schemas = MadmpSchema.all
    authorize @fragment

    return unless @fragment.present?

    render json: render_fragment_form(@fragment, @stale_fragment)
  end

  # rubocop:disable Metrics/AbcSize
  # rubocop:disable Metrics/MethodLength
  def update
    p_params = permitted_params
    @schemas = MadmpSchema.all
    schema = @schemas.find(p_params[:schema_id])
    source = p_params[:source]

    data = data_reformater(
      schema.schema,
      schema_params(schema)
    )

    # rubocop:disable Metrics/BlockLength
    Answer.transaction do
      begin
        @fragment = MadmpFragment.find_by(
          id: params[:id],
          dmp_id: p_params[:dmp_id]
        )
        authorize @fragment

        if MadmpFragment.fragment_exists?(
          data, schema, p_params[:dmp_id], @fragment.parent_id, params[:id]
        )
          render json: {
            "error" => d_("dmpopidor", "Element is already present in your plan.")
          }, status: 409
          return
        end

        # data = @fragment.data.merge(data)
        additional_info = @fragment.additional_info.merge(
          "validations" => MadmpFragment.validate_data(data, schema.schema)
        )
        @fragment.assign_attributes(
          # data: data,
          additional_info: additional_info,
          madmp_schema_id: schema.id
        )
        if p_params[:source].eql?("form") && @fragment.answer.present?
          @fragment.answer.update!(
            lock_version: p_params[:answer][:lock_version],
            is_common: p_params[:answer][:is_common],
            user_id: current_user.id
          )
        end
        # @fragment.save!
        @fragment.save_form_fragment(data, schema)
      rescue ActiveRecord::StaleObjectError
        @stale_fragment = @fragment
        @fragment = MadmpFragment.find_by(
          id: params[:id],
          dmp_id: p_params[:dmp_id]
        )
      end
      # rubocop:enable Metrics/BlockLength
    end

    return unless @fragment.present?

    if source.eql?("list-modal")
      property_name = @fragment.additional_info["property_name"]
      render json: {
        "fragment_id" =>  @fragment.parent_id,
        "source" => source,
        "html" => render_fragment_list(
          @fragment.dmp_id,
          @fragment.parent_id,
          schema.id,
          property_name,
          p_params[:template_locale],
          p_params[:query_id]
        )
      }.to_json
    elsif source.eql?("select-modal")
      render json: {
        "fragment_id" =>  @fragment.id,
        "source" => source,
        "html" => render_fragment_select(@fragment)
      }.to_json
    else
      render json: render_fragment_form(@fragment, @stale_fragment)
    end
  end
  # rubocop:enable Metrics/MethodLength
  # rubocop:enable Metrics/AbcSize

  def change_schema
    @fragment = MadmpFragment.find(params[:id])
    @schemas = MadmpSchema.all
    target_schema = @schemas.find(params[:schema_id])

    authorize @fragment

    return unless @fragment.present? && @fragment.schema_conversion(target_schema)

    render json: render_fragment_form(@fragment, @stale_fragment)
  end

  def new_edit_linked
    @schemas = MadmpSchema.all
    @schema = @schemas.find(params[:schema_id])

    @parent_fragment = MadmpFragment.find(params[:parent_id])
    @classname = @schema.classname
    @readonly = false
    @template_locale = params[:template_locale]
    @source = params[:source]
    @property_name = params[:property_name]
    @query_id = params[:query_id]

    dmp_id = @parent_fragment.classname == "dmp" ? @parent_fragment.id : @parent_fragment.dmp_id
    if params[:fragment_id].present?
      @fragment = MadmpFragment.find(params[:fragment_id])
    else
      parent_id = @parent_fragment.id unless @classname.eql?("person")
      @fragment = MadmpFragment.new(
        dmp_id: dmp_id,
        parent_id: parent_id,
        additional_info: {
          "property_name" => params[:property_name]
        }
      )
    end
    authorize @fragment
    respond_to do |format|
      format.html
      format.js { render partial: "shared/dynamic_form/linked_fragment" }
    end
  end

  def show_linked
    @fragment = MadmpFragment.find(params[:fragment_id])
    @schemas = MadmpSchema.all
    @schema = @fragment.madmp_schema
    @classname = @fragment.classname
    @parent_fragment = @fragment.parent
    @readonly = true
    @template_locale = params[:template_locale]
    authorize @fragment
    respond_to do |format|
      format.html
      format.js { render partial: "shared/dynamic_form/linked_fragment" }
    end
  end

  # rubocop:disable Metrics/AbcSize
  # rubocop:disable Metrics/MethodLength
  def create_from_registry_value
    parent_fragment = MadmpFragment.find(params[:parent_id])
    schema = MadmpSchema.find(params[:schema_id])
    template_locale = params[:locale]
    query_id = params[:query_id]
    readonly = params[:readonly] == "true"
    is_custom = params[:custom_value].present? ? true : false

    @fragment = MadmpFragment.new(
      dmp_id: parent_fragment.dmp_id,
      parent_id: parent_fragment.id,
      madmp_schema: schema,
      data: {},
      additional_info: {
        "property_name" => params[:property_name]
      }
    )
    @fragment.classname = schema.classname
    authorize @fragment

    if is_custom
      @fragment.additional_info = @fragment.additional_info.merge("custom_value" => params[:custom_value])
      @fragment.save!
    else
      @registry_value = RegistryValue.find(params[:registry_value_id])

      if MadmpFragment.fragment_exists?(
        @registry_value.data, schema, parent_fragment.dmp_id, parent_fragment.id
      )
        render json: {
          "error" => d_("dmpopidor", "Element is already present in your plan.")
        }, status: 409
        return
      end

      @fragment.save_form_fragment(@registry_value.data, schema)
    end

    render json: {
      "fragment_id" =>  parent_fragment.id,
      "query_id" => query_id,
      "html" => render_fragment_list(
        @fragment.dmp_id,
        parent_fragment.id,
        @fragment.madmp_schema_id,
        params[:property_name],
        template_locale,
        query_id,
        readonly
      )
    }
  end
  # rubocop:enable Metrics/MethodLength
  # rubocop:enable Metrics/AbcSize

  def create_contributor
    parent_fragment = MadmpFragment.find(params[:parent_id])
    schema = MadmpSchema.find(params[:schema_id])
    template_locale = params[:locale]
    query_id = params[:query_id]
    person_id = params[:person_id]

    @contributor = MadmpFragment.new(
      dmp_id: parent_fragment.dmp_id,
      parent_id: parent_fragment.id,
      madmp_schema: schema,
      data: {
        "person" => { "dbid" => person_id.to_i },
        "role" => params[:role]
      },
      additional_info: {
        "property_name" => params[:property_name]
      }
    )
    @contributor.classname = schema.classname
    authorize @contributor
    return unless @contributor.save!

    render json: {
      "fragment_id" =>  parent_fragment.id,
      "query_id" => query_id,
      "html" => render_fragment_list(
        @contributor.dmp_id,
        parent_fragment.id,
        @contributor.madmp_schema_id,
        params[:property_name],
        template_locale,
        query_id,
        true
      )
    }
  end

  def destroy
    @fragment = MadmpFragment.find(params[:id])
    query_id = params[:query_id]
    readonly = params[:readonly] == "true"
    parent_id = @fragment.parent_id
    dmp_id = @fragment.dmp_id
    property_name = @fragment.additional_info["property_name"]

    authorize @fragment
    return unless @fragment.destroy

    MadmpFragment.find(parent_id).update_children_references if parent_id.present?
    render json: {
      "fragment_id" =>  parent_id,
      "query_id" => query_id,
      "html" => render_fragment_list(
        dmp_id, parent_id, @fragment.madmp_schema_id,
        property_name, params[:template_locale], query_id, readonly
      )
    }
  end

  def load_fragments
    @dmp_fragment = MadmpFragment.find(params[:dmp_id])
    search_term = params[:term] || ""
    fragment_list = MadmpFragment.where(
      dmp_id: @dmp_fragment.id,
      madmp_schema_id: params[:schema_id]
    )
    formatted_list = fragment_list.select { |f| f.to_s.downcase.include?(search_term) }
                                  .map    { |f| { "id" => f.id, "text" => f.to_s } }
    authorize @dmp_fragment
    render json: {
      "results" => formatted_list
    }
  end

  private

  def render_fragment_list(dmp_id, parent_id, schema_id, property_name, template_locale, query_id = nil, readonly = false)
    schema = MadmpSchema.find(schema_id)
    case schema.classname
    when "research_output"
      @plan = Fragment::Dmp.where(id: dmp_id).first.plan
      render_to_string(
        partial: "research_outputs/list",
        locals: {
          plan: @plan,
          research_outputs: @plan.research_outputs,
          readonly: readonly
        }
      )
    else
      obj_list = MadmpFragment.where(
        dmp_id: dmp_id,
        parent_id: parent_id
      ).where("additional_info->>'property_name' = ?", property_name)
      render_to_string(
        partial: "shared/dynamic_form/linked_fragment/list",
        locals: {
          parent_id: parent_id,
          obj_list: obj_list,
          schema_id: schema_id,
          readonly: readonly,
          deletable: true,
          template_locale: template_locale,
          query_id: query_id
        }
      )
    end
  end

  def render_fragment_select(fragment)
    select_values = MadmpFragment.where(
      dmp_id: fragment.dmp_id,
      madmp_schema_id: fragment.madmp_schema_id
    )
    render_to_string(
      partial: "shared/dynamic_form/linked_fragment/select_options",
      locals: {
        selected_value: fragment.id,
        select_values: select_values
      }
    )
  end

  def render_fragment_form(fragment, stale_fragment = nil)
    answer = fragment.answer
    question = answer&.question
    research_output = answer&.research_output
    section = question&.section
    plan = fragment.plan
    template = plan.template
    run_parameters = fragment.madmp_schema.extract_run_parameters
    editable = plan.editable_by?(current_user)

    {
      "fragment_id" => fragment.id,
      "answer" => {
        "id" => answer&.id
      },
      "question" => {
        "id" => question&.id,
        "answer_lock_version" => answer&.lock_version,
        "locking" => stale_fragment ?
          render_to_string(partial: "madmp_fragments/locking", locals: {
            question: question,
            answer: answer,
            fragment: stale_fragment,
            research_output: research_output,
            user: answer&.user
          }, formats: [:html]) :
          nil,
        "form" => render_to_string(partial: "madmp_fragments/edit", locals: {
          template: template,
          question: question,
          answer: answer,
          fragment: fragment,
          madmp_schema: fragment.madmp_schema,
          research_output: research_output,
          dmp_id: fragment.dmp_id,
          parent_id: fragment.parent_id,
          pickable_schemas: MadmpSchema.where(classname: fragment.classname).order(:label),
          readonly: !editable,
          base_template_org: template.base_org
        }, formats: [:html]),
        "form_run" => run_parameters.present? ? 
          render_to_string(partial: "shared/dynamic_form/codebase/show", locals: {
            fragment: fragment,
            parameters: run_parameters,
            template_locale: template.locale
          }, formats: [:html]) : nil,
        "answer_status" => answer.present? ?
          render_to_string(partial: "answers/status", locals: {
            answer: answer
        }, formats: [:html]) :
        nil
      },
      "section" => {
        "id" => section&.id
      },
      "plan" => {
        "id" => plan.id,
        "progress" => section.present? ?
          render_to_string(partial: "plans/progress", locals: {
            plan: plan,
            current_phase: section.phase
        }, formats: [:html]) :
        nil
      },
      "research_output" => {
        "id" => research_output&.id
      }
    }.to_json
  end

  # Get the parameters conresponding to the schema
  def schema_params(schema, flat = false)
    s_params = schema.generate_strong_params(flat)
    params.require(:madmp_fragment).permit(s_params)
  end

  def permitted_params
    permit_arr = [:id, :dmp_id, :parent_id, :schema_id, :source, :template_locale,
                  :property_name, :query_id, answer: %i[id plan_id research_output_id
                             question_id lock_version is_common]
                ]
    params.require(:madmp_fragment).permit(permit_arr)
  end

end