SpeciesFileGroup/taxonworks

View on GitHub
app/controllers/sources_controller.rb

Summary

Maintainability
B
4 hrs
Test Coverage
class SourcesController < ApplicationController
  include DataControllerConfiguration::SharedDataControllerConfiguration

  before_action :set_source, only: [:show, :edit, :update, :destroy, :clone, :api_show]
  after_action -> { set_pagination_headers(:sources) }, only: [:index, :api_index ], if: :json_request?

  # GET /sources
  # GET /sources.json
  def index
    respond_to do |format|
      format.html do
        @recent_objects = Source.created_this_week.order(updated_at: :desc).limit(10)
        render '/shared/data/all/index'
      end
      format.json {
        @sources = Queries::Source::Filter.new(params).all
        .order(:cached)
        .page(params[:page])
        .per(params[:per])
      }
      format.bib {
        # TODO - handle count and download
        @sources = Queries::Source::Filter.new(params).all
        .order(:cached)
        .page(params[:page])
        .per(params[:per] || 2000)
      }
    end
  end

  def list
    @sources = Source.page(params[:page])
  end

  # GET /sources/1
  # GET /sources/1.json
  def show
  end

  # POST /sources/1/clone.json
  def clone
    respond_to do |format|

      # Don't panic, this `clone` is custom, see source.rb
      @source = @source.clone

      if @source.valid?

        @source.project_sources << ProjectSource.new(project_id: sessions_current_project_id)

        format.html { redirect_to edit_source_path(@source), notice: 'Clone successful, on new record.' }
        format.json { render :show }
      else
        format.html { redirect_to edit_source_path(@source), notice: 'Clone failed.  On original original.' }
        format.json { render json: @source.errors, status: :unprocessable_entity }
      end
    end
  end

  # GET /sources/1/edit
  def edit
    redirect_to new_source_task_path(source_id: @source.id), notice: 'Editing in new interface.'
  end

  # GET /sources/new
  def new
    redirect_to new_source_task_path, notice: 'Redirected to new interface.'
  end

  # POST /sources
  # POST /sources.json
  def create
    params[:source].merge!( { project_sources_attributes: [{project_id: sessions_current_project_id}] } )
    @source = new_source

    respond_to do |format|

      if @source.save
        format.html { redirect_to url_for(@source.metamorphosize),
                      notice: "#{@source.type} successfully created." }
        format.json { render action: 'show', status: :created, location: @source.metamorphosize }
      else
        format.html { render action: 'new' }
        format.json { render json: @source.errors, status: :unprocessable_entity }
      end

    end
  end

  # GET /sources/select_options
  def select_options
    @sources = Source.select_optimized(sessions_current_user_id, sessions_current_project_id, params[:klass])
  end

  def attributes
    render json: ::Source.columns.select{
      |a| Queries::Source::Filter::ATTRIBUTES.include?(
        a.name.to_sym)
    }.collect{|b| {'name' => b.name, 'type' => b.type } }
  end

  # GET /sources/citation_object_types.json
  def citation_object_types
    render json: Source.joins(:citations)
      .where(citations: {project_id: sessions_current_project_id})
      .select('citations.project_id, citations.citation_object_type')
      .distinct
      .pluck(:citation_object_type).sort
  end

  # GET /sources/csl_types.json
  def csl_types
    render json: ::CSL_STYLES
  end

  def parse
    @source = new_source
    render '/sources/show'
  end

  # PATCH/PUT /sources/1
  # PATCH/PUT /sources/1.json
  def update
    respond_to do |format|
      if @source.update(source_params)
        # We go through this dance to handle changing types from verbatim to other
        @source = @source.becomes!(@source.type.safe_constantize)
        @source.reload # necessary to reload the cached value.
        format.html { redirect_to url_for(@source.metamorphosize), notice: 'Source was successfully updated.' }
        format.json { render :show, status: :ok, location: @source.metamorphosize }
      else
        format.html { render action: 'edit' }
        format.json { render json: @source.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /sources/1
  # DELETE /sources/1.json
  def destroy
    if @source.destroy
      respond_to do |format|
        format.html { redirect_to sources_url, notice: "Destroyed source #{@source.cached}" }
        format.json { head :no_content }
      end
    else
      respond_to do |format|
        format.html { render action: :show, notice: 'failed to destroy the source, there is likely data associated with it' }
        format.json { render json: @source.errors, status: :unprocessable_entity }
      end
    end
  end

  def autocomplete
    @term = params.require(:term)
    @sources = Queries::Source::Autocomplete.new(
      @term,
      **autocomplete_params
    ).autocomplete
  end

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

  # GET /sources/batch_load
  def batch_load
  end

  # PATCH /sources/batch_update.json?source_query=<>&serial_id
  def batch_update
    if r = Source::Bibtex.batch_update(
        preview: params[:preview], 
        source: source_params.merge(by: sessions_current_user_id),
        source_query: params[:source_query],
    )
      render json: r.to_json, status: :ok
    else
      render json: {}, status: :unprocessable_entity
    end
  end

  def preview_bibtex_batch_load
    file = params.require(:file)
    redirect_to batch_load_sources_path, notice: 'No file has been selected.' and return if file.blank?
    file_ok, mimetype = Utilities::Files.recognized_batch_file_type?(file.tempfile)
    if !file_ok
      redirect_to batch_load_sources_path,
        notice: "File '#{file.original_filename}' is of type '#{mimetype}', and not processable as BibTex."
    else
      @sources, message = Source.batch_preview(file.tempfile)
      if @sources.size > 0
        sha256 = Digest::SHA256.file(file.tempfile)
        cookies[:batch_sources_md5] = sha256.hexdigest
        render 'sources/batch_load/bibtex/bibtex_batch_preview'
      else
        redirect_to batch_load_sources_path,
          notice: "Error parsing BibTeX :#{message}."
      end
    end
  end

  def create_bibtex_batch_load
    file = params.require(:file)
    redirect_to batch_load_sources_path, notice: 'no file has been selected' and return if file.blank?
    sha256 = Digest::SHA256.file(file.tempfile)
    if cookies[:batch_sources_md5] == sha256.hexdigest
      if result_hash = Source.batch_create(file.tempfile)
        # error in results?
        @count = result_hash[:count]
        @sources = result_hash[:records]
        flash[:notice] = "Successfully batch created #{@count} sources."
        render 'sources/batch_load/bibtex/bibtex_batch_create'
      else
        flash[:notice] = 'Failed to create sources.'
        redirect_to batch_load_sources_path
      end
    else
      flash[:notice] = 'Batch upload must be previewed before it can be created.'
      redirect_to batch_load_sources_path
    end
  end

  # GET /sources/download
  def download
    send_data Export::CSV.generate_csv(
      Source.joins(:project_sources)
      .where(project_sources: {project_id: sessions_current_project_id})
      .all),
    type: 'text', filename: "sources_#{DateTime.now}.tsv"
  end

  # GET /sources/generate.json?<filter params>
  def generate
    sources = Queries::Source::Filter.new(params).all.page(params[:page]).per(params[:per] || 2000)
    @download = ::Export::Bibtex.download(
      sources,
      request.url,
      (params[:is_public] == 'true' ? true : false),
      params[:style_id]
    )
    render '/downloads/show.json'
  end

  # GET /api/v1/sources
  def api_index
    @sources = Queries::Source::Filter.new(params.merge!(api: true)).all
      .order('sources.id')
      .page(params[:page]).per(params[:per])
    render '/sources/api/v1/index'
  end

  # GET /api/v1/sources/:id
  def api_show
    render '/sources/api/v1/show'
  end

  private

  def new_source
    if params[:bibtex_input].blank?
      Source.new(source_params)
    else
      Source::Bibtex.new_from_bibtex_text(params[:bibtex_input])
    end
  end

  def autocomplete_params
    params.permit(:limit_to_project).merge(project_id: sessions_current_project_id).to_h.symbolize_keys
  end

  def set_source
    @source = Source.find(params[:id])
    @recent_object = @source
  end

  def batch_params
  end

  def source_params
    params.require(:source).permit(
      :serial_id, :address, :annote, :author, :booktitle, :chapter,
      :crossref, :edition, :editor, :howpublished, :institution,
      :journal, :key, :month, :note, :number, :organization, :pages,
      :publisher, :school, :series, :title, :type, :volume, :doi,
      :abstract, :copyright, :language, :stated_year, :verbatim,
      :bibtex_type, :day, :year, :isbn, :issn, :verbatim_contents,
      :verbatim_keywords, :language_id, :translator, :year_suffix, :url, :type, :style_id,
      :convert_to_bibtex,
      project_sources_attributes: [:project_id],
      roles_attributes: [
        :id,
        :_destroy,
        :type,
        :person_id,
        :position,
        person_attributes: [
          :last_name, :first_name, :suffix, :prefix
        ]
      ]
    )
  end
end