survey/app/models/questionnaire_survey.rb

588 lines
24 KiB
Ruby

# encoding: utf-8
class QuestionnaireSurvey
include Mongoid::Document
include Mongoid::Timestamps
include Slug
ResultChart = 0
ResultExtern = 1
ResultFile = 2
ResultCriteria = 3
include OrbitCategory::Categorizable
scope :can_display, ->{where(is_hidden: false)}
field :need_assign_color, :type => Boolean, :default => false
field :need_update_color, :type => Boolean, :default => false
field :enable_consent_feature, :type => Boolean, :default => false
field :consent_contents, :type => String, :default => "", :localize => true
field :already_fix_data, :type => Boolean, :default => false
field :copy_id
field :except_clone_relations, :type=>Array, :default => []
field :title, as: :slug_title, type: String, localize: true
field :description, :localize => true
field :create_user_id
field :update_user_id
field :postdate, :type => DateTime
field :deadline, :type => DateTime
field :is_hidden, :type => Boolean, :default => false
field :needs_login, :type => Boolean, :default => false
field :answer_repeat, :type => Boolean, :default => false
field :total_points, type: Integer, :default => 0
field :total_weight, type: Integer
field :result_type, :type => Integer, :default => 0
field :extern_link
mount_uploader :upload_file, AssetUploader
field :result_criteria, type: Array, :default => []
field :jump_mode, :type => Boolean, :default => false
field :redirect_mode, :type => Boolean, :default => false
field :redirect_url, :type => String
# validates :title, :at_least_one => true
# validates :title, :presence => { :message => I18n.t("survey.title") }
has_many :survey_questions, :autosave => true, :dependent => :destroy
has_many :survey_answers, :autosave => true, :dependent => :destroy
has_many :survey_answer_groups, :autosave => true, :dependent => :destroy
has_many :survey_sections, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :survey_questions, :allow_destroy => true
before_save :check_deadline, :check_need_update #, :update_avliable_language
has_many :survey_paginations, :autosave => true, :dependent => :destroy
accepts_nested_attributes_for :survey_paginations, :allow_destroy => true
before_create do
self.already_fix_data = true
if self.copy_id.present?
clone_new(true)
self.created_at = DateTime.now
self.updated_at = DateTime.now
end
end
def check_need_update
if self.result_criteria_changed?
old_colors = self.result_criteria_was.map{|c| c["color"].blank? ? '#ffffff' : c["color"]}
new_colors = self.result_criteria.map{|c| c["color"].blank? ? '#ffffff' : c["color"]}
new_colors_uniq_count = new_colors.uniq.count
self.need_assign_color = (new_colors_uniq_count > 1)
if old_colors != new_colors
self.need_update_color = true
elsif new_colors_uniq_count > 1
old_questions = self.result_criteria_was.map{|c| c["questions"]}
old_range = self.result_criteria_was.map{|c| c["range"]}
new_questions = self.result_criteria.map{|c| c["questions"]}
new_range = self.result_criteria.map{|c| c["range"]}
if old_questions != new_questions || old_range != new_range
self.need_update_color = true
end
end
end
end
def update_answers_color
puts "Updating answer's color"
tmp_result_criteria = self.result_criteria.map{|c| {"q"=>c["questions"].map{|q| q.to_i},"r"=>c["range"].map{|r| r.to_i},"c"=>c["color"].blank? ? '#ffffff' : c["color"],"t"=>c["type"].to_i}}
survey_questions_count = self.survey_questions.count
weight_relations = self.survey_questions.map{|q| [q.id.to_s,(q.weight.nil? ? 1 : q.weight)]}.to_h
if tmp_result_criteria.select{|criteria| (criteria["q"][0] != 1 || criteria["q"][1] != survey_questions_count rescue true)}.count != 0
self.survey_answers.each do |a|
answer_model_attrs = a.attributes
tmp_result_criteria.each do |c|
total_weight = 0
range = (c["r"].count != 2 ? [0,100] : c["r"])
questions = c["q"].map{|q| (q == 0 ? 0 : q - 1)}
total = 0
questions.each do |x|
if c["t"] == 1
k = weight_relations.keys[x]
if k && answer_model_attrs.has_key?(k)
total_weight += weight_relations[k]
end
end
total += a.individual_total[x].to_i
end
if c["t"] == 0
if (range[0]..range[1]).cover?(total)
a.color = c["color"]
a.save
break
end
else
avg = (total / total_weight rescue 0)
if (range[0]..range[1]).cover?(avg)
a.color = c["color"]
a.save
break
end
end
end
end
else
tmp_result_criteria.each do |c|
range = (c["r"].count != 2 ? [0,100] : c["r"])
self.survey_answers.where(:scored_points.gte=>c["r"][0]).and(:scored_points.lte=>c["r"][1]).update_all(:color=>c["c"])
end
end
self.update(:need_update_color=>false)
end
def get_answer_repeat
self.needs_login && self.answer_repeat
end
def update_user
User.find(update_user_id) rescue nil
end
def update_user=(user)
self.update_user_id = user.id
end
def self.time_range(data)
r = "#{data.postdate.to_date}"
if data.deadline
r += " - #{data.deadline.to_date}"
else
r += " - #{I18n.t(:no_deadline)}"
end
r
end
def self.result(data)
if ( data.result_type == QuestionnaireSurvey::ResultChart && data.deadline && Time.now > data.deadline ) ||
( data.result_type == QuestionnaireSurvey::ResultExtern && !data.extern_link.blank? ) ||
( data.result_type == QuestionnaireSurvey::ResultFile && data.upload_file? )
('<a target="_blank" href="'+ OrbitHelper.url_to_show(data.to_param) + '?method=result&force_chart=true' + '"><span class="result"><i class="icon-align-left"></i></span></a>').html_safe
else
''
end
end
def self.write(data)
unless data.deadline && Time.now > data.deadline
('<a target="_blank" href="'+ OrbitHelper.url_to_show(data.to_param) + '"><span class="write"><i class="icon-edit"></i></span></a>').html_safe
else
''
end
end
def generate_chart_data(match_args={})
survey_questions = self.survey_questions.all
tmp_answer_repeat = self.needs_login && self.answer_repeat
survey_answers = []
if tmp_answer_repeat
survey_answers = self.survey_answers.where(match_args).order_by(created_at: :desc)
else
survey_answer_ids = self.survey_answer_groups.where(match_args).pluck(:survey_answer_ids).map{|ids| ids ? ids[-1] : nil}
survey_answers = self.survey_answers.where(:id.in=>survey_answer_ids).order_by(created_at: :desc)
end
survey_answers = survey_answers.to_a
chart_data = {}
answers =(0...survey_answers.count).map{{}}
agree_trans = I18n.t("survey.true")
disagree_trans = I18n.t("survey.false")
survey_answers.each_with_index do |answer,i|
answers[i]["name"] = User.find(answer.user).member_name rescue ""
answers[i]["time"] = answer.created_at.strftime((I18n.locale == :zh_tw ? "%Y/%m/%d %H:%M" : "%h %d, %Y - %H:%M"))
if self.enable_consent_feature
answers[i]["agree"] = (answer.consent_used ? agree_trans : disagree_trans)
end
end
SurveysHelper.set_locale(I18n.locale)
survey_questions.each do |question|
qid = question.id.to_s
use_custom_option = question.custom_option_new_option
chart_data[qid] = {}
case question.type
when SurveyQuestion::Radio, SurveyQuestion::Select
options = question.survey_question_options.map{|v| [v.name,v.id.to_s]}
survey_answers.each_with_index do |answer,i|
answers[i][qid] = SurveysHelper.parse_exel_value(answer[qid],
options: options,
chart_data: chart_data[qid],
use_custom_option: use_custom_option)
end
when SurveyQuestion::Check
options = question.survey_question_options.map{|v| [v.name,v.id.to_s]}
survey_answers.each_with_index do |answer,i|
answers[i][qid] = SurveysHelper.parse_exel_checkbox_value(answer[qid],
options: options,
chart_data: chart_data[qid],
use_custom_option: use_custom_option)
end
when SurveyQuestion::Radiogroup
options = question.survey_question_options.map{|v| [v.name,v.id.to_s]}
radiogroups = question.survey_question_radiogroups.map{|v| [v.name,v.id.to_s]}
survey_answers.each_with_index do |answer,i|
answers[i][qid] = SurveysHelper.parse_exel_rg_value(answer[qid],
options: options,
radiogroups: radiogroups,
chart_data: chart_data[qid],
use_custom_option: use_custom_option)
end
when SurveyQuestion::DoubleLevelOption
custom_option_title = question.custom_option_title.to_s
survey_answers.each_with_index do |answer,i|
answers[i][qid] = SurveysHelper.parse_exel_double_level_value(answer[qid],
other_title: custom_option_title,
use_custom_option: use_custom_option,
chart_data: chart_data[qid])
end
else
survey_answers.each_with_index do |answer,i|
answers[i][qid] = answer[qid]
end
end
end
[chart_data, survey_questions, answers]
end
def expired?
(self.deadline < Time.now) rescue false
end
def clone_new(clone_mode=false)
@records_all = {}
if clone_mode
clone_target = self.class.find(object.copy_id) rescue nil
else
clone_target = self
end
new_object,clone_target = clone_new_for_object(self,clone_target,clone_mode)
new_object
end
def clone_new_for_object(object,clone_target=nil,clone_mode=false)
if clone_mode
new_object = object
clone_target = object.class.find(object.copy_id) rescue nil if clone_target.nil?
else
clone_target = object if clone_target.nil?
new_object = object.dup
end
return if (self.except_clone_relations.to_s.include?(new_object.class.to_s.underscore) rescue false)
@records_all["#{new_object.class.to_s.underscore.singularize}_ids"] = {} if @records_all["#{new_object.class.to_s.underscore.singularize}_ids"].nil?
begin
@records_all["#{new_object.class.to_s.underscore.singularize}_ids"][clone_target.id] = object.id
rescue
nil
end
if !clone_target.nil? && !new_object.nil?
initialize_fields = ["uid","created_at","updated_at"]
initialize_fields.each do |f|
new_object.send("#{f}=",nil) if new_object.fields.keys.include?(f)
end
relations_fields = clone_target.relations.except("impressions").keys
all_fields = clone_target.fields.keys - relations_fields
all_fields = all_fields - relations_fields.map{|k| "#{k}_id"}
all_fields = all_fields - relations_fields.map{|k| "#{k.singularize}_ids"}
new_object_class_name = new_object.class.to_s.underscore
relations_fields = clone_target.relations.except("impressions").keys
unsort_relation_keys = clone_target.relations.keys
unless @parent_level
fields_to_delete = [new_object_class_name]
tmp_relations_fields = [new_object_class_name]
while relations_fields.count > 0
tmp_singularize_relations_fields = tmp_relations_fields.map{|f| f.singularize}
approve_append = nil
relations_fields.each do |k|
belongs_to_class = clone_target.relations[k].class_name.constantize.relations.select{|k,v| v.macro == :belongs_to}.keys
has_many_class = clone_target.relations[k].class_name.constantize.relations.select{|k,v| v.macro == :has_many}.keys
if (belongs_to_class - tmp_singularize_relations_fields).count == 0
other_has_many_class = (has_many_class - unsort_relation_keys)
if other_has_many_class.count == 0
tmp_relations_fields << k
else
result = other_has_many_class.map do |k|
belongs_to_class = k.classify.constantize.relations.select{|kk,v| v.macro == :belongs_to}.keys
has_many_class = k.classify.constantize.relations.select{|kk,v| v.macro == :has_many}.keys
if (belongs_to_class - tmp_singularize_relations_fields).count == 0
true
else
fields_to_delete = fields_to_delete.concat(belongs_to_class)
tmp_relations_fields.concat(belongs_to_class)
false
end
end
if result.select{|t| !t}.count == 0
if (fields_to_delete.map{|f| f.pluralize} - tmp_relations_fields).count == 0
tmp_relations_fields << k
elsif clone_target.relations[k].class_name.constantize.fields.keys.include?("key")
tmp_relations_fields << k
elsif (clone_target.relations[k].class_name.constantize.relations.keys.map{|f| f.singularize} & fields_to_delete).count != 0
approve_append = k
end
end
end
elsif !unsort_relation_keys.include?(clone_target.relations[k].class_name.underscore) && !unsort_relation_keys.include?(clone_target.relations[k].class_name.underscore.pluralize)
tmp_relations_fields << k
end
end
tmp_relations_fields << approve_append if approve_append.present?
approve_append = nil
relations_fields = relations_fields - tmp_relations_fields
end
relations_fields = tmp_relations_fields
fields_to_delete.each{|f| relations_fields.delete(f)}
@clone_mode = clone_mode
end
@parent_level = true
if clone_mode
all_fields.each do |f|
next if f == "uid"
unless new_object.send("#{f}_changed?") && new_object.send("#{f}_changed_from_default?")
new_object.send("#{f}=",clone_target.send(f))
end
end
end
relations_fields.each do |f|
no_dup_flag = false
if clone_target.relations[f].macro == :belongs_to || clone_target.relations[f].macro == :has_one
no_dup_flag = new_object.send(f).present?
elsif clone_target.relations[f].macro == :has_many
no_dup_flag = new_object.send(f).to_a.count != 0
elsif clone_target.relations[f].macro == :embeds_many #Fix localize fields
if new_object.send(f).to_a.count != 0
need_fix_fields = new_object.send(f).to_a[0].fields.select{|k,v| (v.options[:localize] rescue false)}.keys
locale = I18n.locale.to_s
embeded_records = new_object.send(f).map do |embeded_record|
need_fix_fields.each do |f|
if (embeded_record[f][locale].class != String rescue false)
embeded_record.send("#{f}_translations=",embeded_record[f][locale])
else
embeded_record.send("#{f}_translations=",embeded_record[f])
end
end
embeded_record
end
new_object.send("#{f}=",embeded_records)
end
end
if clone_target.relations[f].macro == :belongs_to || clone_target.relations[f].class_name == "MemberProfile"
if @records_all["#{f}_ids"].nil?
new_object.send("#{f}_id=",clone_target.send("#{f}_id"))
else
new_object.send("#{f}_id=",(@records_all["#{f}_ids"][clone_target.send("#{f}_id")]))
end
elsif clone_target.relations[f].macro == :has_one
next if (self.except_clone_relations.to_s.include?(f) rescue false)
need_clone_relation = clone_target.send(f)
clone_relation = new_object.send(f)
clone_relation = need_clone_relation.dup if clone_relation.nil?
initialize_fields.each do |f|
clone_relation.send("#{f}=",nil) if clone_relation.fields.keys.include?(f)
end
check_fields = clone_relation.fields.except(initialize_fields)
check_fields.keys.each do |f|
if (clone_relation.send(f).class.to_s.match(/uploader/i) rescue false)
if clone_relation[f].blank? && (clone_relation.send(f).file.nil? rescue true)
clone_relation[f] = r[f]
source_filepath = r.send(f).file.file
if @clone_mode
dest_filepath = clone_relation.send(f).file.file
FileUtils.mkdir_p(File.dirname(dest_filepath))
FileUtils.cp(source_filepath,dest_filepath)
end
elsif (clone_relation.send(f).file rescue nil)
clone_relation[f] = File.basename(clone_relation.send(f).file.file.to_s)
end
file_flag = true
end
end
new_object.send("#{f}=",clone_relation)
elsif clone_target.relations[f].macro == :has_many
next if (self.except_clone_relations.to_s.include?(f) rescue false)
clone_relations = []
need_clone_relations = clone_target.send(f).asc(:_id).to_a
file_flag = false
need_clone_relations.each_with_index do |r,i|
clone_relation = new_object.send(f)[i]
clone_relation = r.dup if clone_relation.nil?
initialize_fields.each do |f|
clone_relation.send("#{f}=",nil) if clone_relation.fields.keys.include?(f)
end
check_fields = clone_relation.fields.except(initialize_fields)
check_fields.keys.each do |f|
if (clone_relation.send(f).class.to_s.match(/uploader/i) rescue false)
if clone_relation[f].blank? && (clone_relation.send(f).file.nil? rescue true)
clone_relation[f] = r[f]
source_filepath = r.send(f).file.file
if @clone_mode
dest_filepath = clone_relation.send(f).file.file
FileUtils.mkdir_p(File.dirname(dest_filepath))
FileUtils.cp(source_filepath,dest_filepath)
end
elsif (clone_relation.send(f).file rescue nil)
clone_relation[f] = File.basename(clone_relation.send(f).file.file.to_s)
end
file_flag = true
end
end
clone_relations << clone_relation
end
if !no_dup_flag || (no_dup_flag && file_flag)
new_object_relations = new_object.send(f).to_a
if new_object_relations.count != 0
if clone_relations.count > new_object_relations.count
clone_relations = clone_relations[0...new_object_relations.count]
else
clone_relations = clone_relations.concat(new_object.send(f)[clone_relations.count...new_object_relations.count])
end
new_object.send("#{f}=",clone_relations)
else
new_object.send("#{f}=",clone_relations)
end
end
count_array = (0...new_object.send(f).to_a.count).to_a
count_array.each do |i|
clone_new_for_object(new_object.send(f)[i],need_clone_relations[i],true)
end
end
end
new_object.copy_id = clone_target.id if new_object.fields.keys.include?("copy_id")
return new_object, clone_target
end
end
def update_answer_score
if self.survey_questions.where(:need_update_score => true).count != 0
self.update(:need_update_color=>true)
puts "Updating answer's scores"
tmp_weights = []
tmp_score_data = self.survey_questions.map do |question|
qid = question.id.to_s
tmp = {}
weight = (question.weight.nil? ? 1 : question.weight)
tmp_weights << weight
case question.type
when SurveyQuestion::Radio, SurveyQuestion::Select, SurveyQuestion::Check, SurveyQuestion::Radiogroup
question.survey_question_options.each do |opt|
oid = opt.id.to_s
tmp[oid] = {"base"=>(opt.points.to_i * weight)}
end
when SurveyQuestion::Oneline, SurveyQuestion::Multiline, SurveyQuestion::DateTime
tmp = 0
when SurveyQuestion::DoubleLevelOption
question.survey_question_options.each do |opt|
oid = opt.id.to_s
tmp[oid] = {"base"=>(opt.points.to_i * weight)}
opt.level2.each do |opt2|
tmp[oid][opt2.id.to_s] = opt2.points.to_i * weight
end
end
end
[qid,tmp]
end.to_h
self.survey_answers.each do |answer|
total = 0
individual_total = []
tmp_total_weight = 0
tmp_score_data.each_with_index do |(qid,tmp),i|
if answer[qid]
tmp_total_weight += tmp_weights[i]
if tmp.class == Fixnum #Oneline, Multiline, DateTime
total += tmp
individual_total << tmp
else
if answer[qid].class == Array #Check , DoubleLevelOption
t = 0
answer[qid].each do |d|
if d.class != BSON::Document
break
end
level2s = []
oid = nil
if d.has_key?("level1_id")
oid = d["level1_id"]
level2s = Array(d["level1_ids"])
else
oid = d['oid']
end
t2 = tmp[oid]["base"] rescue nil
if t2
t += t2
level2s.each do |level2_id|
t += tmp[oid][level2_id]
end
end
end
total += t
individual_total << t
elsif answer[qid].class == BSON::Document #Radio, Select, Radiogroup
if (answer[qid].has_key?('oid') rescue true)
t = tmp[answer[qid]['oid']]["base"] rescue nil
if t
total += t
individual_total << t
end
else
tmp[answer[qid]].each do |oid,d|
t = tmp[oid]["base"] rescue nil
if t
total += t
individual_total << t
end
end
end
else
total += 0
individual_total << 0
end
end
else
total += 0
individual_total << 0
end
end
answer.scored_points = total
answer.individual_total = individual_total
if tmp_total_weight != 0
answer.avg_points = (total.to_f / tmp_total_weight).round
else
answer.avg_points = 0
end
answer.save
end
self.survey_questions.update_all(:need_update_score => false)
end
end
def update_total_weight
self.total_weight = self.survey_questions.pluck(:weight).map{|w| w.nil? ? 1 : w}.sum
self.save
end
def get_total_weight(ans=nil)
if ans.nil?
if self.total_weight.nil?
self.update_total_weight
self.total_weight
else
self.total_weight
end
else
self.survey_questions.where(:id.in=>ans.attributes.keys).pluck(:weight).map{|w| w.nil? ? 1 : w}.sum
end
end
protected
def check_deadline
if(!self.deadline.nil? and (self.deadline < self.postdate ))
self.deadline = nil
end
end
# def update_avliable_language
# VALID_LOCALES.each do |locale|
# if (title_translations[locale].blank? rescue true)
# self["available_for_#{locale}".to_sym] = false
# else
# self["available_for_#{locale}".to_sym] = true
# end
# end
# end
# paginates_per 10
end