WikiEducationFoundation/WikiEduDashboard

View on GitHub
config/initializers/surveys.rb

Summary

Maintainability
A
3 hrs
Test Coverage
require 'active_support'
require 'rapidfire'
require 'csv'

Rails.application.config.to_prepare do

  Rapidfire::QuestionGroup.class_eval do
    has_paper_trail
    has_many :question_group_conditionals, foreign_key: 'rapidfire_question_group_id'
    has_many :campaigns, through: :question_group_conditionals
    has_and_belongs_to_many :surveys, join_table: 'surveys_question_groups', foreign_key: 'rapidfire_question_group_id'
  end

  Rapidfire::AnswerGroup.class_eval do
    belongs_to :user
  end

  Rapidfire::Answer.class_eval do
    has_one :user, through: :answer_group

    def notification(survey_id, curr_user=nil)
      if curr_user.nil?
        # if curr_user is nil, then we haven't preloaded the user's notifications
        notifications = user.survey_notifications.completed.includes(survey_assignment: :survey)
      else
        # if it isn't nil, then we have preloaded the user's notifications via includes in app/views/surveys/_question_group.html.haml
        notifications = curr_user.survey_notifications.completed.includes(survey_assignment: :survey)
      end
      return nil if notifications.empty?
      notifications.map {|n| n if n.survey.id == survey_id }.compact.first
    end

    def course(survey_id, curr_user=nil)
      n = notification(survey_id, curr_user)
      return nil if n.nil?
      n.course
    end

    def courses_user_role(survey_id)
      n = notification(survey_id)
      return nil if n.nil?
      role = CoursesUsers.find(n.courses_users_id).role
      CoursesUsers::Roles.constants[role + 1].to_s
    end
  end

  Rapidfire::QuestionGroupsController.class_eval do
    before_action :require_admin_permissions
    before_action :set_tags, only: [:new, :edit]

    def new
      @question_group = Rapidfire::QuestionGroup.new
      @question_group_tags = []
    end

    def create
      @question_group = Rapidfire::QuestionGroup.new(question_group_params)
      if @question_group.save
        respond_to do |format|
          if params.key?(:redirect_to_edit)
            format.html { redirect_to new_question_group_question_path(@question_group) + "?cancellable" }
          else
            format.html { redirect_to question_groups_path }
          end
        end
      else
        respond_to do |format|
          format.html { render :new }
        end
      end
    end

    def edit
      @question_group = Rapidfire::QuestionGroup.find(params[:id])
      @question_group_tags = @question_group.tags.nil? ? [] : @question_group.tags.split(',')
    end

    def destroy
      @question_group = Rapidfire::QuestionGroup.find(params[:id])
      @question_group.destroy

      respond_to do |format|
        format.html { redirect_to question_groups_path }
        format.js
      end
    end

    private

    def question_group_params
      join_tags
      params.require(:question_group).permit(:name, :tags, campaign_ids: [])
    end

    def join_tags
      tags = params[:question_group][:tags]
      params[:question_group][:tags] = tags.nil? ? "" : tags.join(',')
    end

    def set_tags
      @available_tags = Tag.distinct.pluck(:tag)
    end
  end

  Rapidfire::Question.class_eval do
    has_paper_trail
    scope :course_data_questions, ->{where("course_data_type <> ''")}
    serialize :alert_conditions, Hash

    def self.for_conditionals(question_id)
      where.not(id: question_id).where("conditionals IS NULL OR conditionals = ''")
    end

    def to_csv
      CSV.generate do |csv|
        csv << ['Question',
                'Grouped Matrix Question',
                'Answer',
                'Follow Up Question',
                'Follow Up Answer',
                'Username',
                'User Email']
        answers.order(:answer_text).collect do |answer|
          csv << [
            question_text,
            validation_rules[:grouped_question],
            answer.answer_text,
            follow_up_question_text,
            answer.follow_up_answer_text,
            answer.user.username,
            answer.user.email
          ]
        end
      end
    end
  end

  Rapidfire::QuestionsController.class_eval do
    private

    def save_and_redirect(params, on_error_key)
      @question_form = Rapidfire::QuestionForm.new(params)
      @question_form.save
      if @question_form.errors.empty?
        respond_to do |format|
          format.html { redirect_to edit_question_group_path(@question_group) }
          format.js
        end
      else
        respond_to do |format|
          format.html { render on_error_key.to_sym }
          format.js
        end
      end
    end
  end

  Rapidfire::ApplicationController.class_eval do
    layout 'surveys'
  end

  Rapidfire::AnswerGroupsController.class_eval do
    include SurveysHelper
    before_action :set_course, only: [:new, :create]
    after_action :add_course_to_answer_group, only: [:create]

    def add_course_to_answer_group
      answer_group = @answer_group_builder.to_model
      answer_group.update_attribute(:course_id, @course&.id)
    end
  end

  Rapidfire::QuestionForm.class_eval do

    COURSE_DATA_ANSWER_TYPES =
      [
        "Students",
        "Articles",
        "WikiEdu Staff"
      ]

    AVAILABLE_SURVEY_QUESTIONS =
      [
       Rapidfire::Questions::Checkbox,
       Rapidfire::Questions::Date,
       Rapidfire::Questions::Long,
       Rapidfire::Questions::Numeric,
       Rapidfire::Questions::Radio,
       Rapidfire::Questions::Select,
       Rapidfire::Questions::Short,
       Rapidfire::Questions::Text,
       Rapidfire::Questions::RangeInput
      ]
    SURVEY_QUESTION_TYPES = AVAILABLE_SURVEY_QUESTIONS.inject({}) do |result, question|
      question_name = question.to_s.split("::").last
      result[question_name] = question.to_s
      result
    end

    attr_accessor :question_group, :question,
      :type, :question_text, :answer_options, :answer_presence,
      :answer_minimum_length, :answer_maximum_length, :multiple,
      :answer_greater_than_or_equal_to, :answer_less_than_or_equal_to, :answer_grouped,
      :answer_grouped_question, :answer_range_minimum, :answer_range_maximum,
      :answer_range_increment, :answer_range_divisions, :answer_range_format,
      :follow_up_question_text, :conditionals, :course_data_type, :placeholder_text, :track_sentiment

    def save
      @question.new_record? ? create_question : update_question
    end

    private
    def create_question
      klass = nil
      if SURVEY_QUESTION_TYPES.values.include?(type)
        klass = type.constantize
      else
        errors.add(:type, :invalid)
        return false
      end
      @question = klass.create(to_question_params)
    end

    def to_question_params
      {
        :type => type,
        :question_group => question_group,
        :question_text  => question_text,
        :answer_options => answer_options,
        :follow_up_question_text => follow_up_question_text,
        :conditionals => conditionals,
        :multiple => multiple,
        :course_data_type => course_data_type,
        :placeholder_text => placeholder_text,
        :track_sentiment => track_sentiment,
        :validation_rules => {
          :presence => !conditionals.nil? && !conditionals.empty? ? "0" : answer_presence,
          :grouped => answer_grouped,
          :grouped_question => answer_grouped_question,
          :minimum  => answer_minimum_length,
          :maximum  => answer_maximum_length,
          :range_minimum  => answer_range_minimum,
          :range_maximum  => answer_range_maximum,
          :range_increment    => answer_range_increment,
          :range_divisions    => answer_range_divisions,
          :range_format    => answer_range_format,
          :greater_than_or_equal_to => answer_greater_than_or_equal_to,
          :less_than_or_equal_to    => answer_less_than_or_equal_to
        }
      }
    end

    def from_question_to_attributes(question)
      self.type = question.type
      self.question_group  = question.question_group
      self.question_text   = question.question_text
      self.answer_options  = question.answer_options
      self.follow_up_question_text = question.follow_up_question_text
      self.conditionals = question.conditionals
      self.multiple = question.multiple
      self.course_data_type = question.course_data_type
      self.placeholder_text = placeholder_text
      self.track_sentiment = question.track_sentiment
      self.answer_presence =  question.rules[:presence]
      self.answer_grouped = question.rules[:grouped]
      self.answer_grouped_question = question.rules[:grouped_question]
      self.answer_minimum_length = question.rules[:minimum]
      self.answer_maximum_length = question.rules[:maximum]
      self.answer_range_minimum = question.rules[:range_minimum]
      self.answer_range_maximum = question.rules[:range_maximum]
      self.answer_range_increment = question.rules[:range_increment]
      self.answer_range_divisions = question.rules[:range_divisions]
      self.answer_range_format = question.rules[:range_format]
      self.answer_greater_than_or_equal_to = question.rules[:greater_than_or_equal_to]
      self.answer_less_than_or_equal_to    = question.rules[:less_than_or_equal_to]
    end
  end
end