QutBioacoustics/baw-server

View on GitHub
app/controllers/audio_events_controller.rb

Summary

Maintainability
C
7 hrs
Test Coverage
# frozen_string_literal: true

class AudioEventsController < ApplicationController
  include Api::ControllerHelper

  skip_authorization_check only: [:show]

  # GET /audio_recordings/:audio_recording_id/audio_events
  def index
    do_authorize_class
    get_audio_recording

    @audio_events, opts = Settings.api_response.response_advanced(
      api_filter_params,
      Access::ByPermission.audio_events(current_user, audio_recording: @audio_recording),
      AudioEvent,
      AudioEvent.filter_settings
    )
    respond_index(opts)
  end

  # GET /audio_recordings/:audio_recording_id/audio_events/:id
  def show
    # allow logged-in users to access reference audio events
    # they would otherwise not have access to

    request_params = audio_event_show_params.to_h
    request_params[:audio_event_id] = request_params[:id]

    @audio_recording = auth_custom_audio_recording(request_params)
    @audio_event = auth_custom_audio_event(request_params, @audio_recording)

    respond_show
  end

  # GET /audio_recordings/:audio_recording_id/audio_events/new
  def new
    do_new_resource
    get_audio_recording
    do_set_attributes
    do_authorize_instance

    respond_new
  end

  # POST /audio_recordings/:audio_recording_id/audio_events
  def create
    do_new_resource
    do_set_attributes(audio_event_params)

    get_audio_recording
    do_authorize_instance

    if @audio_event.save!
      respond_create_success(audio_recording_audio_event_path(@audio_recording, @audio_event))
    else
      respond_change_fail
    end
  end

  # PUT|PATCH /audio_recordings/:audio_recording_id/audio_events/:id
  def update
    do_load_resource
    get_audio_recording
    do_authorize_instance

    if @audio_event.update(audio_event_params)
      respond_show
    else
      respond_change_fail
    end
  end

  # DELETE /audio_recordings/:audio_recording_id/audio_events/:id
  def destroy
    do_load_resource
    get_audio_recording
    do_authorize_instance

    @audio_event.destroy
    add_archived_at_header(@audio_event)

    respond_destroy
  end

  # GET|POST /audio_events/filter
  def filter
    do_authorize_class

    filter_response, opts = Settings.api_response.response_advanced(
      api_filter_params,
      Access::ByPermission.audio_events(current_user),
      AudioEvent,
      AudioEvent.filter_settings
    )
    respond_filter(filter_response, opts)
  end

  # GET /audio_recordings/:audio_recording_id/audio_events/download
  # GET /projects/:project_id/audio_events/download
  # GET /projects/:project_id/sites/:site_id/audio_events/download
  def download
    params_cleaned = CleanParams.perform(audio_event_download_params)

    is_authorized = false

    user = nil
    project = nil
    site = nil
    audio_recording = nil
    start_offset = nil
    end_offset = nil

    # check which params are available to authorise this request

    # user id
    if params_cleaned[:user_id]
      user = User.where(id: params_cleaned[:user_id].to_i).first
      unless user.blank?
        authorize! :audio_events, user
        is_authorized = true
      end
    end

    # project id
    if params_cleaned[:project_id]
      project = Project.where(id: params_cleaned[:project_id].to_i).first
      unless project.blank?
        authorize! :show, project
        is_authorized = true
      end
    end

    # site id
    if params_cleaned[:site_id]
      site = Site.where(id: params_cleaned[:site_id].to_i).first
      unless site.blank?
        authorize! :show, site
        is_authorized = true
      end
    end

    # audio recording id
    audio_recording_id = params_cleaned[:audio_recording_id] || params_cleaned[:recording_id] || params_cleaned[:audiorecording_id] || nil
    if audio_recording_id
      audio_recording = AudioRecording.where(id: audio_recording_id.to_i).first
      unless audio_recording.blank?
        authorize! :show, audio_recording
        is_authorized = true
      end
    end

    # start offset
    start_offset = if params_cleaned[:start_offset]
                     params_cleaned[:start_offset].to_f
                   else
                     0
                   end

    # end offset
    if params_cleaned[:end_offset]
      end_offset = params_cleaned[:end_offset].to_f
    elsif audio_recording
      end_offset = audio_recording.duration_seconds
    end

    # timezone
    timezone_name = params_cleaned[:selected_timezone_name] || 'UTC'

    unless is_authorized
      raise CustomErrors::RoutingArgumentError,
        'must provide existing audio_recording_id, start_offset, and end_offset or project_id or site_id or user_id'
    end

    # create file name
    time_now = Time.zone.now
    file_name_append = time_now.strftime('%Y%m%d-%H%M%S').to_s
    file_name = 'annotations'

    file_name = NameyWamey.create_user_name(user) unless user.blank?

    file_name = NameyWamey.create_project_name(project) unless project.blank?

    file_name = NameyWamey.create_site_name(site.projects.first, site) unless site.blank?

    unless audio_recording.blank?
      file_name = NameyWamey.create_audio_recording_name(audio_recording, start_offset, end_offset)
    end

    # create query

    query = AudioEvent.csv_query(user, project, site, audio_recording, start_offset, end_offset, timezone_name)
    query_sql = query.to_sql
    @formatted_annotations = AudioEvent.connection.select_all(query_sql)

    respond_to do |format|
      format.csv { render_csv("#{file_name.trim('.', '')}-#{file_name_append}") }
      format.json { render json: @formatted_annotations }
    end
  end

  private

  def audio_event_params
    params.require(:audio_event).permit(
      :audio_recording_id,
      :start_time_seconds, :end_time_seconds,
      :low_frequency_hertz, :high_frequency_hertz,
      :is_reference,
      # AT 2021: disabled. Nested associations are extremely complex,
      # and as far as we are aware, they are not used anywhere in production
      # TODO: remove on passing test suite
      #tags_attributes: [:is_taxonomic, :text, :type_of_tag, :retired, :notes],
      tag_ids: []
    )
  end

  def audio_event_index_params
    params.permit(
      :start_offset, :end_offset,
      :format, :audio_recording_id, audio_event: {}
    )
  end

  def audio_event_download_params
    params.permit(
      :audio_recording_id, :audioRecordingId, :audiorecording_id, :audiorecordingId, :recording_id, :recordingId,
      :user_id, :userId,
      :project_id, :projectId,
      :site_id, :siteId,
      :start_offset, :startOffset,
      :end_offset, :endOffset,
      :selected_timezone_name, :selectedTimezoneName,
      :format
    )
  end

  def audio_event_show_params
    params.permit(:id, :project_id, :site_id, :format, :audio_recording_id, audio_event: {})
  end

  def get_audio_recording
    @audio_recording = AudioRecording.find(params[:audio_recording_id])

    # avoid the same project assigned more than once to a site
    @audio_event.audio_recording = @audio_recording if defined?(@audio_event) && @audio_event.audio_recording.blank?
  end
end