diff --git a/Gemfile.lock b/Gemfile.lock index 3cea915..46c8033 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - surveyor (1.6.7) + surveyor (1.6.8) formtastic (>= 2.2.1) haml (>= 4.0) mustache (~> 0.99) diff --git a/README.md b/README.md index e6bbdcf..af45afd 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ based on the HeH version of the surveyor gem, these are my modifications to upda ### Status -current version: 1.6.7 +current version: 1.6.8 ## Requirements diff --git a/lib/surveyor/models/answer_methods.rb b/lib/surveyor/models/answer_methods.rb index e89cfcd..650207e 100644 --- a/lib/surveyor/models/answer_methods.rb +++ b/lib/surveyor/models/answer_methods.rb @@ -11,7 +11,7 @@ module AnswerMethods # Associations belongs_to :question has_many :responses - has_many :validations, :dependent => :destroy + has_many :validations, -> { includes validations: :validation_conditions }, :dependent => :destroy attr_accessible *PermittedParams.new.answer_attributes if defined? ActiveModel::MassAssignmentSecurity # Validations diff --git a/lib/surveyor/models/question_group_methods.rb b/lib/surveyor/models/question_group_methods.rb index ecb5a62..4c419ba 100644 --- a/lib/surveyor/models/question_group_methods.rb +++ b/lib/surveyor/models/question_group_methods.rb @@ -13,7 +13,7 @@ module QuestionGroupMethods included do - has_many :questions + has_many :questions, -> { includes answers: { validations: :validation_conditions } } has_one :dependency diff --git a/lib/surveyor/models/question_methods.rb b/lib/surveyor/models/question_methods.rb index fef9708..15df0f0 100644 --- a/lib/surveyor/models/question_methods.rb +++ b/lib/surveyor/models/question_methods.rb @@ -15,7 +15,7 @@ module QuestionMethods # Associations belongs_to :survey_section belongs_to :question_group, dependent: :destroy, required: false - has_many :answers, -> {includes :responses}, dependent: :destroy # it might not always have answers + has_many :answers, -> { includes validations: :validation_conditions } , dependent: :destroy # it might not always have answers has_one :dependency, dependent: :destroy belongs_to :correct_answer, class_name: "Answer", dependent: :destroy, required: false attr_accessible *PermittedParams.new.question_attributes if defined? ActiveModel::MassAssignmentSecurity @@ -59,10 +59,10 @@ def mandatory? def dependent? - self.dependency != nil + self.dependency != nil # FIXME: change to self.dependency.present? end - + # FIXME: replace with self.dependency&.is_met?(response_set) def triggered?(response_set) dependent? ? self.dependency.is_met?(response_set) : true end @@ -70,6 +70,10 @@ def triggered?(response_set) def css_class(response_set) [(dependent? ? "q_dependent" : nil), (triggered?(response_set) ? nil : "q_hidden"), custom_class].compact.join(" ") + puts " ------ " + puts " css_class: #{response_set.inspect}" + puts " q: #{self.inspect}" + puts " ------ " end diff --git a/lib/surveyor/models/response_set_methods.rb b/lib/surveyor/models/response_set_methods.rb index a7aba5c..b2f1437 100644 --- a/lib/surveyor/models/response_set_methods.rb +++ b/lib/surveyor/models/response_set_methods.rb @@ -11,8 +11,7 @@ module ResponseSetMethods belongs_to :survey belongs_to :user - has_many :responses, -> {includes :answer}, :dependent => :destroy - + has_many :responses, -> { includes :answer }, :dependent => :destroy accepts_nested_attributes_for :responses, :allow_destroy => true attr_accessible *PermittedParams.new.response_set_attributes if defined? ActiveModel::MassAssignmentSecurity @@ -31,152 +30,131 @@ module ClassMethods def has_blank_value?(hash) return true if hash["answer_id"].blank? return false if (q = Question.find_by_id(hash["question_id"])) and q.pick == "one" - hash.any? {|k, v| v.is_a?(Array) ? v.all? {|x| x.to_s.blank?} : v.to_s.blank?} + hash.any? { |k, v| v.is_a?(Array) ? v.all? { |x| x.to_s.blank? } : v.to_s.blank? } end end - def ensure_start_timestamp self.started_at ||= Time.now end - def ensure_identifiers self.access_code ||= Surveyor::Common.make_tiny_code - self.api_id ||= Surveyor::Common.generate_api_id + self.api_id ||= Surveyor::Common.generate_api_id end - def to_csv(access_code = false, print_header = true) result = Surveyor::Common.csv_impl.generate do |csv| if print_header csv << (access_code ? ["response set access code"] : []) + - csv_question_columns.map {|qcol| "question.#{qcol}"} + - csv_answer_columns.map {|acol| "answer.#{acol}"} + - csv_response_columns.map {|rcol| "response.#{rcol}"} + csv_question_columns.map { |qcol| "question.#{qcol}" } + + csv_answer_columns.map { |acol| "answer.#{acol}" } + + csv_response_columns.map { |rcol| "response.#{rcol}" } end responses.each do |response| csv << (access_code ? [self.access_code] : []) + - csv_question_columns.map {|qcol| response.question.send(qcol)} + - csv_answer_columns.map {|acol| response.answer.send(acol)} + - csv_response_columns.map {|rcol| response.send(rcol)} + csv_question_columns.map { |qcol| response.question.send(qcol) } + + csv_answer_columns.map { |acol| response.answer.send(acol) } + + csv_response_columns.map { |rcol| response.send(rcol) } end end result end - %w(question answer response).each do |model| define_method "csv_#{model}_columns" do model.capitalize.constantize.content_columns.map(&:name) - (model == "response" ? [] : %w(created_at updated_at)) end end - def as_json(options = nil) template_paths = ActionController::Base.view_paths.collect(&:to_path) Rabl.render(self, 'surveyor/show.json', :view_path => template_paths, :format => "hash") end - def complete! self.completed_at = Time.now end - def complete? !completed_at.nil? end - def correct? responses.all?(&:correct?) end - def correctness_hash { :questions => Survey.where(id: self.survey_id).includes(sections: :questions).first.sections.map(&:questions).flatten.compact.size, :responses => responses.compact.size, - :correct => responses.find_all(&:correct?).compact.size + :correct => responses.find_all(&:correct?).compact.size } end - def mandatory_questions_complete? progress_hash[:triggered_mandatory] == progress_hash[:triggered_mandatory_completed] end - def progress_hash - qs = Survey.where(id: self.survey_id).includes(sections: :questions).first.sections.map(&:questions).flatten - ds = dependencies(qs.map(&:id)) - triggered = qs - ds.select {|d| !d.is_met?(self)}.map(&:question) - { :questions => qs.compact.size, - :triggered => triggered.compact.size, - :triggered_mandatory => triggered.select {|q| q.mandatory?}.compact.size, - :triggered_mandatory_completed => triggered.select {|q| q.mandatory? and is_answered?(q)}.compact.size + qs = Survey.where(id: self.survey_id).includes(sections: :questions).first.sections.map(&:questions).flatten + ds = dependencies(qs.map(&:id)) + triggered = qs - ds.select { |d| !d.is_met?(self) }.map(&:question) + { :questions => qs.compact.size, + :triggered => triggered.compact.size, + :triggered_mandatory => triggered.select { |q| q.mandatory? }.compact.size, + :triggered_mandatory_completed => triggered.select { |q| q.mandatory? and is_answered?(q) }.compact.size } end - def is_answered?(question) %w(label image).include?(question.display_type) or !is_unanswered?(question) end - def is_unanswered?(question) - self.responses.detect {|r| r.question_id == question.id}.nil? + self.responses.detect { |r| r.question_id == question.id }.nil? end - def is_group_unanswered?(group) - group.questions.any? {|question| is_unanswered?(question)} + group.questions.any? { |question| is_unanswered?(question) } end - # Returns the number of response groups (count of group responses enterted) for this question group def count_group_responses(questions) - questions.map {|q| - responses.select {|r| + questions.map { |q| + responses.select { |r| (r.question_id.to_i == q.id.to_i) && !r.response_group.nil? }.group_by(&:response_group).size }.max end - def unanswered_dependencies unanswered_question_dependencies + unanswered_question_group_dependencies end - def unanswered_question_dependencies - dependencies.select {|d| d.question && self.is_unanswered?(d.question) && d.is_met?(self)}.map(&:question) + dependencies.select { |d| d.question && self.is_unanswered?(d.question) && d.is_met?(self) }.map(&:question) end - def unanswered_question_group_dependencies dependencies. - select {|d| d.question_group && self.is_group_unanswered?(d.question_group) && d.is_met?(self)}. - map(&:question_group) + select { |d| d.question_group && self.is_group_unanswered?(d.question_group) && d.is_met?(self) }. + map(&:question_group) end - def all_dependencies(question_ids = nil) - arr = dependencies(question_ids).partition {|d| d.is_met?(self)} + arr = dependencies(question_ids).partition { |d| d.is_met?(self) } { - :show => arr[0].map {|d| d.question_group_id.nil? ? "q_#{d.question_id}" : "g_#{d.question_group_id}"}, - :hide => arr[1].map {|d| d.question_group_id.nil? ? "q_#{d.question_id}" : "g_#{d.question_group_id}"} - # :invalid => invalid_hash # :question_id => "message" - the questions which are invalid + :show => arr[0].map { |d| d.question_group_id.nil? ? "q_#{d.question_id}" : "g_#{d.question_group_id}" }, + :hide => arr[1].map { |d| d.question_group_id.nil? ? "q_#{d.question_id}" : "g_#{d.question_group_id}" } + # :invalid => invalid_hash # :question_id => "message" - the questions which are invalid } end - # Check existence of responses to questions from a given survey_section def no_responses_for_section?(section) - !responses.any? {|r| r.survey_section_id == section.id} + !responses.any? { |r| r.survey_section_id == section.id } end - # ui_hash = parameters # the surveryor_gui gem created an answer type: a grid of checkboxes where many checked boxes for 1 questions are allowed # ( = the grid answer types) @@ -190,16 +168,16 @@ def update_from_ui_hash(ui_hash) api_id = response_hash['api_id'] fail "api_id missing from response #{ord}" unless api_id - existing = Response.where(:api_id => api_id).first + existing = Response.where(:api_id => api_id).first if self.class.has_blank_value?(response_hash) existing.destroy if existing else - updateable_attributes = response_hash.reject {|k, v| k == 'api_id'} + updateable_attributes = response_hash.reject { |k, v| k == 'api_id' } # this is where we check for an clean up malformed attributes for the answer_id: - cleaned_up_answer_attributes = fix_malformed_answer_ids updateable_attributes + cleaned_up_answer_attributes = fix_malformed_answer_ids updateable_attributes cleaned_up_answer_attributes['survey_section_id'] = Question.find(cleaned_up_answer_attributes['question_id']).survey_section.id if existing @@ -223,7 +201,6 @@ def update_from_ui_hash(ui_hash) end end - protected def fix_malformed_answer_ids(attribute_params) @@ -240,13 +217,12 @@ def fix_malformed_answer_ids(attribute_params) fixed_params end - def dependencies(question_ids = nil) question_ids = survey.sections.map(&:questions).flatten.map(&:id) if responses.blank? and question_ids.blank? - deps = Dependency.includes(:dependency_conditions).where({ :dependency_conditions => { :question_id => question_ids || responses.map(&:question_id) } }) + deps = Dependency.joins(:dependency_conditions).where({ dependency_conditions: { question_id: question_ids || responses.map(&:question_id) } }) # this is a work around for a bug in active_record in rails 2.3 which incorrectly eager-loads associatins when a # condition clause includes an association limiter - deps.each {|d| d.dependency_conditions.reload} + deps.each { |d| d.dependency_conditions.reload } deps end end diff --git a/lib/surveyor/models/survey_methods.rb b/lib/surveyor/models/survey_methods.rb index 81e495f..ba40d60 100644 --- a/lib/surveyor/models/survey_methods.rb +++ b/lib/surveyor/models/survey_methods.rb @@ -15,14 +15,16 @@ module SurveyMethods included do # Associations - has_many :sections, -> { includes(questions: {answers: :responses}) }, class_name: 'SurveySection', :dependent => :destroy + has_many :sections, + -> { includes(questions: { answers: { validations: :validation_conditions }, dependency: :dependency_conditions } ) }, + class_name: 'SurveySection', dependent: :destroy has_many :response_sets - has_many :translations, :class_name => "SurveyTranslation" + has_many :translations, class_name: "SurveyTranslation" attr_accessible *PermittedParams.new.survey_attributes if defined? ActiveModel::MassAssignmentSecurity # Validations validates_presence_of :title - validates_uniqueness_of :survey_version, :scope => :access_code, :message => "survey with matching access code and version already exists" + validates_uniqueness_of :survey_version, scope: :access_code, message: "survey with matching access code and version already exists" # Derived attributes before_save :generate_access_code @@ -68,7 +70,7 @@ def deactivate! def as_json(options = nil) template_paths = ActionController::Base.view_paths.collect(&:to_path) - Rabl.render(filtered_for_json, 'surveyor/export.json', :view_path => template_paths, :format => "hash") + Rabl.render(filtered_for_json, 'surveyor/export.json', view_path: template_paths, format: "hash") end ## @@ -87,7 +89,7 @@ def generate_access_code end def increment_version - surveys = self.class.select(:survey_version).where(:access_code => access_code).order("survey_version DESC") + surveys = self.class.select(:survey_version).where(access_code: access_code).order("survey_version DESC") next_version = surveys.any? ? surveys.first.survey_version.to_i + 1 : 0 self.survey_version = next_version @@ -97,8 +99,8 @@ def increment_version def translation(locale_symbol) # TODO cache the counter for translations; need a column if self.translations.where(locale: locale_symbol.to_s).count > 0 - t = self.translations.where(:locale => locale_symbol.to_s).first - {:title => self.title, :description => self.description}.with_indifferent_access.merge( + t = self.translations.where(locale: locale_symbol.to_s).first + {title: self.title, description: self.description}.with_indifferent_access.merge( t ? YAML.load(t.translation || "{}").with_indifferent_access : {} ) else diff --git a/lib/surveyor/models/survey_section_methods.rb b/lib/surveyor/models/survey_section_methods.rb index 15a68e6..196f792 100644 --- a/lib/surveyor/models/survey_section_methods.rb +++ b/lib/surveyor/models/survey_section_methods.rb @@ -7,7 +7,9 @@ module SurveySectionMethods included do # Associations - has_many :questions, -> { includes answers: :responses }, :dependent => :destroy + has_many :questions, + -> { includes answers: { validations: :validation_conditions }, dependency: :dependency_conditions }, + dependent: :destroy belongs_to :survey attr_accessible *PermittedParams.new.survey_section_attributes if defined? ActiveModel::MassAssignmentSecurity @@ -27,9 +29,9 @@ def default_args def questions_and_groups qs = [] - questions.each_with_index.map do |q,i| + questions.each_with_index.map do |q, i| if q.part_of_group? - if (i+1 >= questions.size) or (q.question_group_id != questions[i+1].question_group_id) + if (i + 1 >= questions.size) or (q.question_group_id != questions[i + 1].question_group_id) q.question_group end else @@ -39,7 +41,7 @@ def questions_and_groups end def translation(locale) - {:title => self.title, :description => self.description}.with_indifferent_access.merge( + { title: self.title, description: self.description }.with_indifferent_access.merge( (self.survey.translation(locale)[:survey_sections] || {})[self.reference_identifier] || {} ) end diff --git a/lib/surveyor/version.rb b/lib/surveyor/version.rb index a7b478f..77e8032 100644 --- a/lib/surveyor/version.rb +++ b/lib/surveyor/version.rb @@ -1,4 +1,4 @@ module Surveyor # uses ruby version 2.5.8 - VERSION = '1.6.7' + VERSION = '1.6.8' end diff --git a/surveyor-1.6.8.gem b/surveyor-1.6.8.gem new file mode 100644 index 0000000..5b5682b Binary files /dev/null and b/surveyor-1.6.8.gem differ