# 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"]} rescue [] new_colors = self.result_criteria.map{|c| c["color"].blank? ? '#ffffff' : c["color"]} rescue [] 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"]} rescue [] old_range = self.result_criteria_was.map{|c| c["range"]} rescue [] new_questions = self.result_criteria.map{|c| c["questions"]} rescue [] new_range = self.result_criteria.map{|c| c["range"]} rescue [] 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? ) ('').html_safe else '' end end def self.write(data) unless data.deadline && Time.now > data.deadline ('').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 answers[i]["scored_points"] = answer.scored_points 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