scottwillson/racing_on_rails

View on GitHub
app/controllers/admin/events_controller.rb

Summary

Maintainability
C
1 day
Test Coverage
# frozen_string_literal: true

module Admin
  # Show Schedule, add and edit Events, edit Results for Events. Promoters can view and edit their own events. Promoters can edit
  # fewer fields than administrators.
  class EventsController < Admin::AdminController
    before_action :assign_event, only: %i[edit update]
    before_action :require_administrator_or_promoter, only: %i[edit update]
    before_action :require_administrator, except: %i[edit update]
    before_action :assign_disciplines, only: %i[new create edit update]

    # schedule calendar  with links to admin Event pages
    # === Params
    # * year: (optional) defaults to current year
    # === Assigns
    # * schedule
    # * year
    def index
      @competitions = Event.where("type in (?)", Competitions::Competition::TYPES).year(@year)
      @schedule = Schedule::Schedule.new(@year, events_for_year(@year))
    end

    # Show results for Event
    # === Params
    # * id: SingleDayEvent id
    # === Assigns
    # * event
    # * disciplines: List of all Disciplines + blank
    def edit
      assign_previous
      @event.promoter = Person.find(params["promoter_id"]) if params["promoter_id"].present? && current_person.administrator?
    end

    # Show page to create new Event
    # === Params
    # * year: optional
    # === Assigns
    # * event: Unsaved SingleDayEvent
    def new
      assign_new_event

      respond_to do |format|
        format.html { render :edit }
        format.js { render(:update) { |page| page.redirect_to(action: :new, event: event_params) } }
      end
    end

    # Create new SingleDayEvent
    # === Params
    # * event: Attributes Hash for new event
    # === Assigns
    # * event: Unsaved SingleDayEvent
    # === Flash
    # * warn
    def create
      assign_new_event
      if @event.save
        expire_cache
        flash[:notice] = "Created #{@event.name}"
        redirect_to edit_admin_event_path(@event)
      else
        flash[:warn] = @event.errors.full_messages.join
        render :edit
      end
    end

    # Create new MultiDayEvent from 'prototype' SingleDayEvent
    # === Params
    # * id: SingleDayEvent
    # === Flash
    # * warn
    def create_from_children
      single_day_event = Event.find(params[:id])
      new_parent = MultiDayEvent.create_from_children(single_day_event.multi_day_event_children_with_no_parent)
      expire_cache
      redirect_to(action: :edit, id: new_parent.to_param)
    end

    # Update existing Event
    # === Params
    # * id
    # * event: Attributes Hash
    # === Assigns
    # * event: Unsaved Event
    # === Flash
    # * warn
    def update
      @event = Event.find(params[:id])

      event_type = params[:event].try(:[], :type)
      if event_type && Event::TYPES.include?(event_type) && @event.type != event_type
        # Rails 3 and strong parameters don't play nice here
        @event.update_column :type, event_type
        @event = Event.find(params[:id])
      end

      if @event.update(event_params)
        flash[:notice] = "Updated #{@event.name}"
        expire_cache
        redirect_to edit_admin_event_path(@event)
      else
        render :edit
      end
    end

    def update_attribute
      respond_to do |format|
        format.js do
          @event = Event.find(params[:id])
          @event.update! params[:name] => params[:value]
          expire_cache
          render plain: @event.send(params[:name]), content_type: "text/plain"
        end
      end
    end

    # Upload results from Excel spreadsheet.
    # Expects column headers in first row
    # Expects Race names in first column before set of results:
    # Senior Women Category 3
    # | 1 | Leitheiser | Ann | HFV |
    # === Params
    # * results_file
    # * id: Event ID
    # === Flash
    # * warn: List invalid columns
    # * notice
    def upload
      uploaded_file = params[:results_file]

      path = "#{Dir.tmpdir}/#{uploaded_file.original_filename}"
      File.open(path, "wb") do |f|
        f.print(uploaded_file.read)
      end

      temp_file = File.new(path)
      @event = Event.find(params[:id])

      results_file = if RacingAssociation.current.usac_results_format?
                       Results::USACResultsFile.new(temp_file, @event)
                     else
                       Results::ResultsFile.new(temp_file, @event)
                     end

      begin
        results_file.import
      rescue Ole::Storage::FormatError
        flash[:warn] = "Could not read results file. Try re-saving it in 'Excel 97-2004 Workbook format'"
        assign_disciplines
        return render(:edit)
      end

      expire_cache
      begin
        FileUtils.rm temp_file
      rescue StandardError
        nil
      end

      flash[:notice] = "Imported #{uploaded_file.original_filename}. "
      if results_file.custom_columns.present?
        flash[:notice] = flash[:notice] + "Found custom columns: #{results_file.custom_columns.map(&:to_s).map(&:humanize).join(', ')}. "
        if RacingAssociation.current.usac_results_format?
          flash[:notice] = "#{flash[:notice]}(If import file is USAC format, you should expect errors on Organization, Event Year, Event #, Race Date and Discipline.)"
        end
      end

      if results_file.import_warnings.present?
        flash[:notice] = "#{flash[:notice]}Import warnings: "
        results_file.import_warnings.each do |warning|
          flash[:notice] = "#{flash[:notice]}#{warning} "
        end
      end

      redirect_to(edit_admin_event_path(@event))
    end

    # Upload new Excel Schedule
    # See Schedule::Schedule.import for details
    # Redirects to schedule index if successful
    # === Params
    # * schedule_file
    # === Flash
    # * notice
    # * warn
    def upload_schedule
      uploaded_file = params[:schedule_file]
      path = "#{Dir.tmpdir}/#{uploaded_file.original_filename}"
      File.open(path, "wb") do |f|
        f.print(uploaded_file.read)
      end

      Schedule::Schedule.import(path)
      expire_cache
      flash[:notice] = "Uploaded schedule from #{uploaded_file.original_filename}"

      redirect_to(admin_events_path)
    end

    # Permanently destroy Event
    # === Params
    # * id
    # === Assigns
    # * event: if Event could not be deleted
    # === Flash
    # * notice
    def destroy
      @event = Event.find(params[:id])
      if @event.destroy
        expire_cache
        respond_to do |format|
          format.html do
            flash[:notice] = "Deleted #{@event.name}"
            if @event.parent
              redirect_to(edit_admin_event_path(@event.parent))
            else
              redirect_to(admin_events_path(year: @event.date.year))
            end
          end
          format.js
        end
      else
        respond_to do |format|
          format.html do
            flash[:notice] = "Could not delete #{@event.name}: #{@event.errors.full_messages.join}"
            redirect_to(admin_events_path(year: @event.date.year))
          end
          format.js { render "destroy_error" }
        end
      end
    end

    def destroy_races
      respond_to do |format|
        format.js do
          @event = Event.find(params[:id])
          # Remember races for view
          @races = @event.races.to_a.dup
          @event.destroy_races
          @races = @races.reject { |race| Race.exists?(race.id) }
          expire_cache
        end
      end
    end

    def set_parent
      child = Event.find(params[:child_id])
      parent = Event.find(params[:parent_id])
      child.parent = parent
      child.save!
      redirect_to(edit_admin_event_path(child))
    end

    # Add missing child Events to this Event (their parent)
    def add_children
      parent = Event.find(params[:parent_id])
      parent.missing_children.each do |child|
        child.parent = parent
        child.save!
      end
      redirect_to(edit_admin_event_path(parent))
    end

    protected

    def assign_disciplines
      @disciplines = Discipline.names
    end

    def assign_event
      @event = Event.find(params[:id])
    end

    def assign_new_event
      if params[:event].nil?
        @event = SingleDayEvent.new
        return true
      end

      _params = event_params.dup
      event_type = params[:event][:type]
      if event_type.blank?
        event_type = if _params[:parent_id].present?
                       "Event"
                     else
                       "SingleDayEvent"
                     end
      end

      raise "Unknown event type: #{event_type}" unless Event::TYPES.include?(event_type)

      @event = Object.const_get(event_type).new(_params)
    end

    def assign_current_admin_tab
      @current_admin_tab = "Schedule"
    end

    def events_for_year(year)
      single_day_events = SingleDayEvent.year(year)
      childless_multi_day_events = MultiDayEvent.where.not(id: single_day_events.map(&:parent_id).compact).year(year)
      single_day_events + childless_multi_day_events
    end

    private

    def assign_previous
      @previous = nil

      @previous = @event.previous_year if @event.races.empty?
    end

    def event_params
      _params = params.require(:event).permit(
        :additional_race_price,
        :all_events_discount,
        :atra_points_series,
        :auto_combined_results,
        :beginner_friendly,
        :canceled,
        :chief_referee,
        :city,
        :created_at,
        :custom_suggestion,
        :date,
        :discipline,
        :email,
        :end_date,
        :field_limit,
        :first_aid_provider,
        :flyer,
        :flyer_ad_fee,
        :flyer_approved,
        :human_date,
        :human_registration_ends_on,
        :human_registration_ends_at_time,
        :instructional,
        :junior_price,
        :membership_required,
        :name,
        :notes,
        :number_issuer_id,
        :override_registration_ends_at,
        :parent_id,
        :parent_name,
        :phone,
        :postponed,
        :post_event_fees,
        :practice,
        :pre_event_fees,
        :price,
        :prize_list,
        :promoter_id,
        :promoter_name,
        :promoter_pays_registration_fee,
        :refunds,
        :refund_policy,
        :region_id,
        :registration,
        :registration_ends_at,
        :registration_start_at,
        :registration_link,
        :registration_public,
        :sanctioned_by,
        :sanctioning_org_event_id,
        :state,
        :suggest_membership,
        :team_id,
        :team_name,
        :tentative,
        :time,
        :type,
        :velodrome_id,
        :website
      )
      _params.delete(:type)
      _params.delete(:created_by_id)
      _params.delete(:created_by_name)
      _params.delete(:created_by_type)
      _params.delete(:updated_by_id)
      _params.delete(:updated_by_name)
      _params.delete(:updated_by_type)
      _params
    end
  end
end