SpeciesFileGroup/taxonworks

View on GitHub
app/controllers/leads_controller.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class LeadsController < ApplicationController
  include DataControllerConfiguration::ProjectDataControllerConfiguration
  before_action :set_lead, only: %i[
    edit create_for_edit update destroy show show_all show_all_print all_texts
    destroy_couplet insert_couplet delete_couplet duplicate update_meta otus]

  # GET /leads
  # GET /leads.json
  def index
    respond_to do |format|
      format.html {
        one_week_ago = Time.now.utc.to_date - 7
        @recent_objects = Lead
          .roots_with_data(sessions_current_project_id)
          .where('key_updated_at > ?', one_week_ago)
          .reorder(key_updated_at: :desc)
          .limit(10)

        render '/shared/data/all/index'
      }
      format.json {
        if params[:load_root_otus]
          @leads = Lead.roots_with_data(sessions_current_project_id, true)
        else
          @leads = Lead.roots_with_data(sessions_current_project_id)
        end
      }
    end
  end

  def list
    @leads = Lead.
      roots_with_data(sessions_current_project_id).page(params[:page])
  end

  # GET /leads/1/all_texts.json
  def all_texts
    leads = Lead
      .select(:id, :text, :origin_label)
      .with_project_id(sessions_current_project_id)
      .order(:text)
    ancestor_ids = @lead.ancestor_ids
    @texts = leads.filter_map {|o|
      if o.id != @lead.id && !ancestor_ids.include?(o.id)
        {
          id: o.id,
          label: o.origin_label,
          text: o.text.nil? ? '' : o.text.truncate(40)
        }
      end
    }
  end

  # GET /leads/1
  # GET /leads/1.json
  def show
    children = @lead.children
    if children.size == 2
      expand_lead
    else
      @left = nil
      @right = nil
      @left_future = []
      @right_future = []
      @parents = @lead.ancestors.reverse
    end
  end

  def show_all
    @key = @lead.all_children
  end

  def show_all_print
    @key = @lead.all_children_standard_key
  end

  # GET /leads/new
  def new
    respond_to do |format|
      format.html { redirect_to new_lead_task_path }
    end
  end

  # GET /leads/1/edit
  def edit
    redirect_to new_lead_task_path lead_id: @lead.id
  end

  # POST /leads/1/create_for_edit.json
  def create_for_edit
    if @lead.children.size == 0
      new_couplet
    end
    expand_lead
  end

  # POST /leads
  # POST /leads.json
  def create
    @lead = Lead.new(lead_params)
    respond_to do |format|
      if @lead.save
        new_couplet # we make two blank couplets so we can show the key
        expand_lead
        format.json {}
      else
        format.json { render json: @lead.errors, status: :unprocessable_entity}
      end
    end
  end

  # POST /leads/1.json/insert_couplet
  def insert_couplet
    @lead.insert_couplet

    respond_to do |format|
      format.json { head :no_content }
    end
  end

  # PATCH/PUT /leads/1
  # PATCH/PUT /leads/1.json
  def update
    @lead = Lead.find(params[:lead][:id])
    @left = Lead.find(params[:left][:id])
    @right = Lead.find(params[:right][:id])
    respond_to do |format|
      begin
        @lead.update!(lead_params)
        @left.update!(lead_params(:left))
        @right.update!(lead_params(:right))
        # Note that future changes when redirect is updated.
        expand_lead
        format.json {}
      rescue
        @lead.errors.merge!(@left)
        @lead.errors.merge!(@right)
        format.json { render json: @lead.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /leads/1
  # DELETE /leads/1.json
  def destroy
    if @lead.parent_id
      respond_to do |format|
        flash[:error] = 'Delete aborted - you can only delete on root nodes.'
        format.html { redirect_back(fallback_location: (request.referer || root_path)) }
        format.json { head :no_content, status: :unprocessable_entity }
      end
      return
    else
      begin
        @lead.transaction_nuke
        respond_to do |format|
          flash[:notice] = 'Key was succesfully destroyed.'
          format.html { destroy_redirect @lead }
          format.json { head :no_content }
        end
      rescue # TODO: add specifics
        respond_to do |format|
          flash[:error] = 'Delete failed!'
          format.html { redirect_back(fallback_location: (request.referer || root_path)) }
          format.json { render json: @lead.errors, status: :unprocessable_entity }
        end
      end
    end
  end

  # For destroying a couplet with no children on either side.
  def destroy_couplet
    respond_to do |format|
      if @lead.parent_id.present?
        if @lead.destroy_couplet
          format.json { head :no_content }
        else
          @lead.errors.add(:delete, 'failed - is there a node redirecting to one of these?')
          format.json {
            render json: @lead.errors, status: :unprocessable_entity
          }
        end
      else
        @lead.errors.add(:destroy, "failed - can't delete the only couplet.")
        format.json {
          render json: @lead.errors, status: :unprocessable_entity
        }
      end
    end
  end

  # For deleting a couplet where one side has no children but the other might;
  # the side with children is reparented.
  def delete_couplet
    respond_to do |format|
      if @lead.destroy_couplet
        format.json { head :no_content }
      else
        @lead.errors.add(:delete, 'failed - is there a node redirecting to one of these?')
        format.json {
          render json: @lead.errors, status: :unprocessable_entity
        }
      end
    end
  end

  # POST /leads/1/duplicate
  def duplicate
    respond_to do |format|
      format.html {
        if !@lead.parent_id
          @lead.dupe
          flash[:notice] = 'Key cloned.'
        else
          flash[:error] = 'Clone aborted - you can only clone on a root node.'
        end
        redirect_to action: :list
      }
    end
  end

  # POST /leads/1/update_meta.json
  def update_meta
    respond_to do |format|
      if @lead.update(lead_params)
        format.json {}
      else
        format.json { render json: @lead.errors, status: :unprocessable_entity}
      end
    end
  end

  # GET /leads/1/otus.json
  def otus
    leads_list = @lead.self_and_descendants.where.not(otu_id: nil).includes(:otu)

    @otus = leads_list.to_a.map{|l| l.otu}.uniq(&:id)

    respond_to do |format|
      format.json {}
    end
  end

  def autocomplete
    @leads = ::Queries::Lead::Autocomplete.new(
      params.require(:term),
      project_id: sessions_current_project_id,
    ).autocomplete
  end

  def search
    if params[:id].blank?
      redirect_to(lead_path,
                  alert: 'You must select an item from the list with a click or tab press before clicking show.')
    else
      redirect_to lead_path(params[:id])
    end
  end

  # GET /leads/download
  def download
    send_data Export::CSV.generate_csv(
        Lead.where(project_id: sessions_current_project_id)
      ),
      type: 'text',
      filename: "leads_#{DateTime.now}.tsv"
  end

  private

  def set_lead
    @lead = Lead.find(params[:id])
  end

  def lead_params(require = :lead)
    params.require(require).permit(
      :otu_id, :text, :origin_label, :description, :redirect_id,
      :link_out, :link_out_text, :is_public, :position
    )
  end

  def expand_lead
    # Assumes the parent node is @lead and @lead has two children
    @left = @lead.children[0]
    @right = @lead.children[1]
    @left_future = @left.future
    @right_future = @right.future
    @parents = @lead.ancestors.reverse
  end

  def new_couplet
    # Assumes the parent node is @lead
    return if @lead.children.size > 0
    @left = @lead.children.create!
    @right = @lead.children.create!
    @left_future = @left.future
    @right_future =  @right.future
  end

  def no_grandchildren(lead)
    children = lead.children
    (children.size == 2) and (children[0].children.size == 0) and (children[1].children.size == 0)
  end
end