# == Schema Information
#
# Table name: phases
#
# id :integer not null, primary key
# title :string
# description :text
# number :integer
# template_id :integer
# created_at :datetime
# updated_at :datetime
# modifiable :boolean
# versionable_id :string(36)
#
# Indexes
#
# index_phases_on_versionable_id (versionable_id)
# phases_template_id_idx (template_id)
#
# [+Project:+] DMPRoadmap
# [+Description:+] This model describes informmation about the phase of a plan, it's title, order of display and which template it belongs to.
#
# [+Created:+] 03/09/2014
# [+Copyright:+] Digital Curation Centre and University of California Curation Center
class Phase < ActiveRecord::Base
include ValidationMessages
include ValidationValues
include ActsAsSortable
include VersionableModel
prepend Dmpopidor::Models::Phase
##
# Sort order: Number ASC
default_scope { order(number: :asc) }
# ================
# = Associations =
# ================
belongs_to :template, touch: true
belongs_to :plan
has_one :prefix_section, -> (phase) {
modifiable.where("number < ?",
phase.sections.not_modifiable.minimum(:number))
}, class_name: "Section"
has_many :sections, dependent: :destroy
has_many :questions, through: :sections
has_many :answers, through: :questions
has_many :annotations, through: :questions
has_many :template_sections, -> {
not_modifiable
}, class_name: "Section"
has_many :suffix_sections, -> (phase) {
modifiable.where(<<~SQL, phase_id: phase.id, modifiable: false)
sections.number > (SELECT MAX(number) FROM sections
WHERE sections.modifiable = :modifiable
AND sections.phase_id = :phase_id)
SQL
}, class_name: "Section"
# ===============
# = Validations =
# ===============
validates :title, presence: { message: PRESENCE_MESSAGE }
validates :number, presence: { message: PRESENCE_MESSAGE },
uniqueness: { message: UNIQUENESS_MESSAGE,
scope: :template_id }
validates :template, presence: { message: PRESENCE_MESSAGE }
validates :modifiable, inclusion: { in: BOOLEAN_VALUES,
message: INCLUSION_MESSAGE }
# ==========
# = Scopes =
# ==========
scope :titles, -> (template_id) {
Phase.where(template_id: template_id).select(:id, :title)
}
def deep_copy(**options)
copy = self.dup
copy.modifiable = options.fetch(:modifiable, self.modifiable)
copy.template_id = options.fetch(:template_id, nil)
copy.save!(validate:false) if options.fetch(:save, false)
options[:phase_id] = copy.id
self.sections.each{ |section| copy.sections << section.deep_copy(options) }
return copy
end
# TODO: Move this to Plan model as `num_answered_questions(phase=nil)`
# Returns the number of answered question for the phase.
def num_answered_questions(plan)
plan&.num_answered_questions.to_i
end
# Returns the number of questions for a phase. Note, this method becomes useful
# for when sections and their questions are eager loaded so that avoids SQL queries.
def num_questions
n = 0
self.sections.each do |s|
n+= s.questions.size()
end
n
end
# SEE MODULE
def visibility_allowed?(plan)
value = Rational(num_answered_questions(plan), plan.num_questions) * 100
value >= Rails.application.config.default_plan_percentage_answered.to_f
end
end