Newer
Older
dmpopidor / app / models / answer.rb
# frozen_string_literal: true
# == Schema Information
#
# Table name: answers
#
#  id           :integer          not null, primary key
#  is_common    :boolean          default(FALSE)
#  lock_version :integer          default(0)
#  text         :text
#  created_at   :datetime
#  updated_at   :datetime
#  dataset_id   :integer
#  plan_id      :integer
#  question_id  :integer
#  user_id      :integer
#
# Indexes
#
#  answers_plan_id_idx          (plan_id)
#  answers_question_id_idx      (question_id)
#  answers_user_id_idx          (user_id)
#  index_answers_on_dataset_id  (dataset_id)
#
# Foreign Keys
#
#  fk_rails_...  (dataset_id => datasets.id)
#  fk_rails_...  (plan_id => plans.id)
#  fk_rails_...  (question_id => questions.id)
#  fk_rails_...  (user_id => users.id)
#

class Answer < ActiveRecord::Base

  include ValidationMessages


  # ================
  # = Associations =
  # ================

  belongs_to :question

  belongs_to :user

  belongs_to :plan

  belongs_to :dataset

  has_many :notes, dependent: :destroy

  has_and_belongs_to_many :question_options, join_table: "answers_question_options"

  has_many :notes


  # ===============
  # = Validations =
  # ===============

  validates :plan, presence: { message: PRESENCE_MESSAGE }

  validates :user, presence: { message: PRESENCE_MESSAGE }

  #validates :question, presence: { message: PRESENCE_MESSAGE },
  #                    uniqueness: { message: UNIQUENESS_MESSAGE,
  #                                  scope: :plan_id }

  # =============
  # = Callbacks =
  # =============

  after_save :set_plan_complete


  ##
  # deep copy the given answer
  #
  # answer - question_option to be deep copied
  #
  # Returns Answer
  def self.deep_copy(answer)
    answer_copy = answer.dup
    answer.question_options.each do |opt|
      answer_copy.question_options << opt
    end
    answer_copy
  end

  # This method helps to decide if an answer option (:radiobuttons, :checkbox, etc ) in
  # form views should be checked or not
  #
  # Returns Boolean
  def has_question_option(option_id)
    question_option_ids.include?(option_id)
  end

  # If the answer's question is option_based, it is checked if exist any question_option
  # selected. For non option_based (e.g. textarea or textfield), it is checked the
  # presence of text
  #
  # Returns Boolean
  def is_valid?
    if question.present?
      if question.question_format.option_based?
        return question_options.any?
      else  # (e.g. textarea or textfield question formats)
        return text.present?
      end
    end
    false
  end

  # Answer notes whose archived is blank sorted by updated_at in descending order
  #
  # Returns Array
  def non_archived_notes
    notes.select { |n| n.archived.blank? }.sort! { |x, y| x.created_at <=> y.created_at }
  end

  # Returns True if answer text is blank, false otherwise specificly we want to remove
  # empty hml tags and check.
  #
  # Returns Boolean
  def is_blank?
    if text.present?
      return text.gsub(/<\/?p>/, "").gsub(/<br\s?\/?>/, "").chomp.blank?
    end
    # no text so blank
    true
  end

  # The parsed JSON hash for the current answer object. Generates a new hash if none
  # exists for rda_questions.
  #
  # Returns Hash
  def answer_hash
    default = { "standards" => {}, "text" => "" }
    begin
      h = text.nil? ? default : JSON.parse(text)
    rescue JSON::ParserError => e
      h = default
    end
    h
  end

  ##
  # Given a hash of standards and a comment value, this updates answer text for
  # rda_questions
  #
  # standards - A Hash of standards
  # text      - A String with option comment text
  #
  # Returns String
  def update_answer_hash(standards = {}, text = "")
    h = {}
    h["standards"] = standards
    h["text"] = text
    self.text = h.to_json
  end

  def set_plan_complete
    return unless plan_id?
    complete = plan.no_questions_matches_no_answers?
    if plan.complete != complete
      plan.update!(complete: complete)
    else
      # Force updated_at changes if nothing changed since save only saves if changes
      # were made to the record
      plan.touch
    end
  end

end