Note that we are not including Poll::PartialResults for open-ended questions resutls. The reason is that we do not contemplate the possibility of there being open questions in booths. Manually counting and introducing the votes in the system is not feasible.
124 lines
3.3 KiB
Ruby
124 lines
3.3 KiB
Ruby
class Poll::Question < ApplicationRecord
|
|
include Measurable
|
|
|
|
acts_as_paranoid column: :hidden_at
|
|
include ActsAsParanoidAliases
|
|
|
|
translates :title, touch: true
|
|
include Globalizable
|
|
|
|
belongs_to :poll
|
|
belongs_to :author, -> { with_hidden }, class_name: "User", inverse_of: :poll_questions
|
|
|
|
has_many :comments, as: :commentable, inverse_of: :commentable
|
|
has_many :answers, class_name: "Poll::Answer"
|
|
has_many :question_options, -> { order "given_order asc" },
|
|
class_name: "Poll::Question::Option",
|
|
inverse_of: :question,
|
|
dependent: :destroy
|
|
has_many :partial_results
|
|
has_one :votation_type, as: :questionable, dependent: :destroy
|
|
belongs_to :proposal
|
|
|
|
validates_translation :title, presence: true, length: { minimum: 4 }
|
|
validates :author, presence: true
|
|
validates :poll_id, presence: true, if: proc { |question| question.poll.nil? }
|
|
|
|
accepts_nested_attributes_for :question_options, reject_if: :all_blank, allow_destroy: true
|
|
accepts_nested_attributes_for :votation_type
|
|
|
|
delegate :multiple?, :open?, :vote_type, to: :votation_type, allow_nil: true
|
|
|
|
scope :sort_for_list, -> { order(Arel.sql("poll_questions.proposal_id IS NULL"), :created_at) }
|
|
scope :for_render, -> { includes(:author, :proposal) }
|
|
|
|
def copy_attributes_from_proposal(proposal)
|
|
if proposal.present?
|
|
self.author = proposal.author
|
|
self.author_visible_name = proposal.author.name
|
|
self.proposal_id = proposal.id
|
|
send(:"#{localized_attr_name_for(:title, Globalize.locale)}=", proposal.title)
|
|
end
|
|
end
|
|
|
|
def options_total_votes
|
|
question_options.reduce(0) { |total, question_option| total + question_option.total_votes }
|
|
end
|
|
|
|
def most_voted_option_id
|
|
question_options.max_by(&:total_votes)&.id
|
|
end
|
|
|
|
def options_with_read_more?
|
|
options_with_read_more.any?
|
|
end
|
|
|
|
def options_with_read_more
|
|
question_options.select(&:with_read_more?)
|
|
end
|
|
|
|
def unique?
|
|
votation_type.nil? || votation_type.unique?
|
|
end
|
|
|
|
def accepts_options?
|
|
votation_type.nil? || votation_type.accepts_options?
|
|
end
|
|
|
|
def max_votes
|
|
if multiple?
|
|
votation_type.max_votes
|
|
else
|
|
1
|
|
end
|
|
end
|
|
|
|
def find_or_initialize_user_answer(user, option_id: nil, answer_text: nil)
|
|
answer = answers.find_or_initialize_by(find_by_attributes(user, option_id))
|
|
|
|
if accepts_options?
|
|
option = question_options.find(option_id)
|
|
answer.option = option
|
|
answer.answer = option.title
|
|
else
|
|
answer.answer = answer_text
|
|
end
|
|
|
|
answer
|
|
end
|
|
|
|
def open_ended_valid_answers_count
|
|
answers.count
|
|
end
|
|
|
|
def open_ended_blank_answers_count
|
|
poll.voters.count - open_ended_valid_answers_count
|
|
end
|
|
|
|
def open_ended_valid_percentage
|
|
return 0.0 if open_ended_total_answers.zero?
|
|
|
|
(open_ended_valid_answers_count * 100.0) / open_ended_total_answers
|
|
end
|
|
|
|
def open_ended_blank_percentage
|
|
return 0.0 if open_ended_total_answers.zero?
|
|
|
|
(open_ended_blank_answers_count * 100.0) / open_ended_total_answers
|
|
end
|
|
|
|
private
|
|
|
|
def find_by_attributes(user, option_id)
|
|
if multiple?
|
|
{ author: user, option_id: option_id }
|
|
else
|
|
{ author: user }
|
|
end
|
|
end
|
|
|
|
def open_ended_total_answers
|
|
open_ended_valid_answers_count + open_ended_blank_answers_count
|
|
end
|
|
end
|