# == Schema Information
#
# Table name: sections
#
# id :integer not null, primary key
# description :text
# modifiable :boolean
# number :integer
# title :string
# created_at :datetime
# updated_at :datetime
# phase_id :integer
#
# Indexes
#
# index_sections_on_phase_id (phase_id)
#
# Foreign Keys
#
# fk_rails_... (phase_id => phases.id)
#
class Section < ActiveRecord::Base
include ValidationMessages
include ValidationValues
include ActsAsSortable
# ================
# = Associations =
# ================
belongs_to :phase
belongs_to :organisation
has_many :questions, dependent: :destroy
has_one :template, through: :phase
# ===============
# = Validations =
# ===============
validates :phase, presence: { message: PRESENCE_MESSAGE }
validates :title, presence: { message: PRESENCE_MESSAGE }
# validates :description, presence: { message: PRESENCE_MESSAGE }
validates :number, presence: { message: PRESENCE_MESSAGE },
uniqueness: { scope: :phase_id,
message: UNIQUENESS_MESSAGE }
validates :modifiable, inclusion: { in: BOOLEAN_VALUES,
message: INCLUSION_MESSAGE }
# =============
# = Callbacks =
# =============
# TODO: Move this down to DB constraints
before_validation :set_modifiable
before_validation :set_number
# =====================
# = Nested Attributes =
# =====================
accepts_nested_attributes_for :questions,
reject_if: -> (a) { a[:text].blank? },
allow_destroy: true
# ==========
# = Scopes =
# ==========
# The sections for this Phase that have been added by the admin
#
# @!scope class
# @return [ActiveRecord::Relation] Returns the sections that are modifiable
scope :modifiable, -> { where(modifiable: true) }
# The sections for this Phase that were part of the original Template
#
# @!scope class
# @return [ActiveRecord::Relation] Returns the sections that aren't modifiable
scope :not_modifiable, -> { where(modifiable: false) }
# ===========================
# = Public instance methods =
# ===========================
##
# return the title of the section
#
# @return [String] the title of the section
def to_s
"#{title}"
end
# Returns the number of answered questions for a given plan
def num_answered_questions(plan)
return 0 if plan.nil?
plan.answers.includes({ question: :question_format }, :question_options)
.where(question_id: question_ids)
.to_a
.count(&:is_valid?)
end
def deep_copy(**options)
copy = self.dup
copy.modifiable = options.fetch(:modifiable, self.modifiable)
copy.phase_id = options.fetch(:phase_id, nil)
copy.save!(validate: false) if options.fetch(:save, false)
options[:section_id] = copy.id
self.questions.map{ |question| copy.questions << question.deep_copy(options) }
return copy
end
# Can't be modified as it was duplicatd over from another Phase.
def template?
!modifiable?
end
private
# ============================
# = Private instance methods =
# ============================
def set_modifiable
self.modifiable = true if modifiable.nil?
end
def set_number
return if phase.nil?
self.number ||= phase.sections.maximum(:number) + 1
end
end