upgrade_surveyor 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/.rspec +1 -0
- data/.travis.yml +18 -0
- data/CHANGELOG.md +253 -0
- data/Gemfile +5 -0
- data/Gemfile.rails_version +25 -0
- data/MIT-LICENSE +20 -0
- data/README.md +164 -0
- data/Rakefile +65 -0
- data/app/controllers/surveyor_controller.rb +5 -0
- data/app/helpers/results_helper.rb +20 -0
- data/app/helpers/survey_form_builder.rb +37 -0
- data/app/helpers/surveyor_helper.rb +3 -0
- data/app/inputs/quiet_input.rb +5 -0
- data/app/inputs/surveyor_check_boxes_input.rb +35 -0
- data/app/inputs/surveyor_radio_input.rb +18 -0
- data/app/models/answer.rb +3 -0
- data/app/models/dependency.rb +3 -0
- data/app/models/dependency_condition.rb +3 -0
- data/app/models/permitted_params.rb +102 -0
- data/app/models/question.rb +3 -0
- data/app/models/question_group.rb +3 -0
- data/app/models/response.rb +4 -0
- data/app/models/response_set.rb +3 -0
- data/app/models/survey.rb +3 -0
- data/app/models/survey_section.rb +4 -0
- data/app/models/survey_translation.rb +4 -0
- data/app/models/validation.rb +3 -0
- data/app/models/validation_condition.rb +3 -0
- data/app/views/layouts/results.html.erb +13 -0
- data/app/views/layouts/surveyor_default.html.erb +12 -0
- data/app/views/partials/_answer.html.haml +25 -0
- data/app/views/partials/_dependents.html.haml +5 -0
- data/app/views/partials/_question.html.haml +28 -0
- data/app/views/partials/_question_group.html.haml +44 -0
- data/app/views/partials/_section.html.haml +12 -0
- data/app/views/partials/_section_menu.html.haml +12 -0
- data/app/views/surveyor/edit.html.haml +21 -0
- data/app/views/surveyor/export.json.rabl +85 -0
- data/app/views/surveyor/new.html.haml +24 -0
- data/app/views/surveyor/show.html.haml +74 -0
- data/app/views/surveyor/show.json.rabl +15 -0
- data/config/routes.rb +8 -0
- data/doc/REPRESENTATIONS.md +34 -0
- data/doc/api_id_schema.json +7 -0
- data/doc/question types.png +0 -0
- data/doc/response_set_schema.json +54 -0
- data/doc/surveyor question combinations.png +0 -0
- data/doc/surveyor reject or delete decision matrix.png +0 -0
- data/doc/surveyor_models.png +0 -0
- data/doc/surveyor_models2.png +0 -0
- data/doc/surveyor_timestamp_schema.json +9 -0
- data/lib/assets/images/surveyor/next.gif +0 -0
- data/lib/assets/images/surveyor/prev.gif +0 -0
- data/lib/assets/images/surveyor/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/lib/assets/images/surveyor/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/assets/images/surveyor/ui-icons_222222_256x240.png +0 -0
- data/lib/assets/images/surveyor/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/assets/images/surveyor/ui-icons_454545_256x240.png +0 -0
- data/lib/assets/images/surveyor/ui-icons_888888_256x240.png +0 -0
- data/lib/assets/images/surveyor/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/assets/javascripts/surveyor/jquery-1.9.0.js +9555 -0
- data/lib/assets/javascripts/surveyor/jquery-ui-1.10.0.custom.js +14850 -0
- data/lib/assets/javascripts/surveyor/jquery-ui-timepicker-addon.js +1919 -0
- data/lib/assets/javascripts/surveyor/jquery.maskedinput.js +338 -0
- data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
- data/lib/assets/javascripts/surveyor/jquery.surveyor.js +154 -0
- data/lib/assets/stylesheets/surveyor/jquery-ui-1.10.0.custom.sass +1380 -0
- data/lib/assets/stylesheets/surveyor/jquery-ui-timepicker-addon.css +11 -0
- data/lib/assets/stylesheets/surveyor/reset.css +48 -0
- data/lib/assets/stylesheets/surveyor/results.css +125 -0
- data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
- data/lib/assets/stylesheets/surveyor.sass +132 -0
- data/lib/generators/surveyor/custom_generator.rb +18 -0
- data/lib/generators/surveyor/install_generator.rb +98 -0
- data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +6 -0
- data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
- data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +40 -0
- data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +13 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_en.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_ko.yml +19 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +14 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +14 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_input_mask_attributes_to_answer.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +18 -0
- data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +38 -0
- data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +30 -0
- data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +28 -0
- data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +37 -0
- data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +34 -0
- data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +30 -0
- data/lib/generators/surveyor/templates/db/migrate/create_survey_translations.rb +19 -0
- data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +32 -0
- data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +33 -0
- data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +21 -0
- data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
- data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
- data/lib/generators/surveyor/templates/surveys/EXTENDING_SURVEYOR.md +52 -0
- data/lib/generators/surveyor/templates/surveys/MODIFYING_SURVEYOR.md +91 -0
- data/lib/generators/surveyor/templates/surveys/date_survey.rb +16 -0
- data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +280 -0
- data/lib/generators/surveyor/templates/surveys/languages.rb +14 -0
- data/lib/generators/surveyor/templates/surveys/quiz.rb +11 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.es.yml +18 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.he.yml +18 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.ko.yml +18 -0
- data/lib/generators/surveyor/templates/vendor/assets/stylesheets/custom.sass +5 -0
- data/lib/surveyor/acts_as_response.rb +19 -0
- data/lib/surveyor/common.rb +59 -0
- data/lib/surveyor/engine.rb +15 -0
- data/lib/surveyor/helpers/formtastic_custom_input.rb +13 -0
- data/lib/surveyor/helpers/surveyor_helper_methods.rb +86 -0
- data/lib/surveyor/models/answer_methods.rb +76 -0
- data/lib/surveyor/models/dependency_condition_methods.rb +65 -0
- data/lib/surveyor/models/dependency_methods.rb +57 -0
- data/lib/surveyor/models/question_group_methods.rb +62 -0
- data/lib/surveyor/models/question_methods.rb +103 -0
- data/lib/surveyor/models/response_methods.rb +125 -0
- data/lib/surveyor/models/response_set_methods.rb +187 -0
- data/lib/surveyor/models/survey_methods.rb +95 -0
- data/lib/surveyor/models/survey_section_methods.rb +48 -0
- data/lib/surveyor/models/survey_translation_methods.rb +28 -0
- data/lib/surveyor/models/validation_condition_methods.rb +42 -0
- data/lib/surveyor/models/validation_methods.rb +41 -0
- data/lib/surveyor/mustache_context.rb +13 -0
- data/lib/surveyor/parser.rb +427 -0
- data/lib/surveyor/redcap_parser.rb +288 -0
- data/lib/surveyor/surveyor_controller_methods.rb +237 -0
- data/lib/surveyor/unparser.rb +161 -0
- data/lib/surveyor/version.rb +3 -0
- data/lib/surveyor.rb +13 -0
- data/lib/tasks/surveyor_tasks.rake +88 -0
- data/rails/init.rb +1 -0
- data/spec/controllers/surveyor_controller_spec.rb +304 -0
- data/spec/factories.rb +166 -0
- data/spec/features/ajax_spec.rb +205 -0
- data/spec/features/api_spec.rb +157 -0
- data/spec/features/duplicate_prevention_spec.rb +50 -0
- data/spec/features/translations_spec.rb +116 -0
- data/spec/features/ui_spec.rb +466 -0
- data/spec/fixtures/REDCapDemoDatabase_DataDictionary.csv +127 -0
- data/spec/fixtures/chinese_survey.rb +14 -0
- data/spec/fixtures/everything.rb +215 -0
- data/spec/fixtures/favorites-ish.rb +22 -0
- data/spec/fixtures/favorites.rb +22 -0
- data/spec/fixtures/feelings.rb +38 -0
- data/spec/fixtures/lifestyle.rb +55 -0
- data/spec/fixtures/numbers.rb +21 -0
- data/spec/fixtures/redcap_new_headers.csv +1 -0
- data/spec/fixtures/redcap_siblings.csv +1 -0
- data/spec/fixtures/redcap_whitespace.csv +1 -0
- data/spec/helpers/formtastic_custom_input_spec.rb +15 -0
- data/spec/helpers/surveyor_helper_spec.rb +114 -0
- data/spec/lib/common_spec.rb +37 -0
- data/spec/lib/parser_spec.rb +393 -0
- data/spec/lib/rake_kitchen_sink.rb +40 -0
- data/spec/lib/redcap_parser_spec.rb +129 -0
- data/spec/lib/tasks_spec.rake +26 -0
- data/spec/lib/unparser_spec.rb +126 -0
- data/spec/models/answer_spec.rb +144 -0
- data/spec/models/dependency_condition_spec.rb +428 -0
- data/spec/models/dependency_spec.rb +90 -0
- data/spec/models/question_group_spec.rb +66 -0
- data/spec/models/question_spec.rb +176 -0
- data/spec/models/response_set_spec.rb +447 -0
- data/spec/models/response_spec.rb +205 -0
- data/spec/models/survey_section_spec.rb +58 -0
- data/spec/models/survey_spec.rb +155 -0
- data/spec/models/validation_condition_spec.rb +98 -0
- data/spec/models/validation_spec.rb +64 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/support/shared.rb +5 -0
- data/spec/support/surveyor_api_helpers.rb +15 -0
- data/spec/support/surveyor_ui_helpers.rb +108 -0
- data/spec/support/wait_for_ajax.rb +11 -0
- data/surveyor.gemspec +40 -0
- data/switch.sh +53 -0
- metadata +510 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
survey "Favorites" do
|
3
|
+
section "Foods" do
|
4
|
+
# In a quiz, both the questions and the answers need to have reference identifiers
|
5
|
+
# Here, the question has reference_identifier: "1", and the answers: "oint", "tweet", and "moo"
|
6
|
+
question_1 "What is the best meat?", :pick => :one, :correct => "oink"
|
7
|
+
a_oink "bacon"
|
8
|
+
a_tweet "chicken"
|
9
|
+
a_moo "beef"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
title: "Un idioma nunca es suficiente"
|
3
|
+
survey_sections:
|
4
|
+
one:
|
5
|
+
title: "Uno"
|
6
|
+
two:
|
7
|
+
title: "Dos"
|
8
|
+
question_groups:
|
9
|
+
hello:
|
10
|
+
text: "¡Hola!"
|
11
|
+
questions:
|
12
|
+
name:
|
13
|
+
text: "¿Cómo se llama Usted?"
|
14
|
+
answers:
|
15
|
+
name:
|
16
|
+
help_text: "Mi nombre es..."
|
17
|
+
color:
|
18
|
+
text: "¿Cuál es tu color favorito?"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
title: "ידיעת שפה אחת אינה מספיקה"
|
3
|
+
survey_sections:
|
4
|
+
one:
|
5
|
+
title: "אחת"
|
6
|
+
two:
|
7
|
+
title: "שנים"
|
8
|
+
question_groups:
|
9
|
+
hello:
|
10
|
+
text: "שלום"
|
11
|
+
questions:
|
12
|
+
name:
|
13
|
+
text: "מה שמך?"
|
14
|
+
answers:
|
15
|
+
name:
|
16
|
+
help_text: "שמי..."
|
17
|
+
color:
|
18
|
+
text: "מהו הצבע האהוב עליך?"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
title: "한가지 언어로는 충분치 않습니다."
|
3
|
+
survey_sections:
|
4
|
+
one:
|
5
|
+
title: "하나"
|
6
|
+
two:
|
7
|
+
title: "두"
|
8
|
+
question_groups:
|
9
|
+
hello:
|
10
|
+
text: "안녕하십니까"
|
11
|
+
questions:
|
12
|
+
name:
|
13
|
+
text: "성함이 어떻게 되십니까?"
|
14
|
+
answers:
|
15
|
+
name:
|
16
|
+
help_text: "제 이름은 ... 입니다"
|
17
|
+
color:
|
18
|
+
text: "좋아하는 색은 무엇입니까?"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module ActsAsResponse
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# Returns the response as a particular response_class type
|
6
|
+
def as(type_symbol)
|
7
|
+
return case type_symbol.to_sym
|
8
|
+
when :string, :text, :integer, :float, :datetime
|
9
|
+
self.send("#{type_symbol}_value".to_sym)
|
10
|
+
when :date
|
11
|
+
self.datetime_value.nil? ? nil : self.datetime_value.to_date
|
12
|
+
when :time
|
13
|
+
self.datetime_value.nil? ? nil : self.datetime_value.to_time
|
14
|
+
else # :answer_id
|
15
|
+
self.answer_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'uuidtools'
|
3
|
+
|
4
|
+
module Surveyor
|
5
|
+
class Common
|
6
|
+
OPERATORS = %w(== != < > <= >= =~)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
if SecureRandom.respond_to?(:urlsafe_base64)
|
10
|
+
def make_tiny_code
|
11
|
+
# 7 random bytes is increased to ~10 characters (sans padding) by
|
12
|
+
# base64 encoding
|
13
|
+
SecureRandom.urlsafe_base64(7)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
def make_tiny_code
|
17
|
+
s = [SecureRandom.random_bytes(7)].pack("m*")
|
18
|
+
s.delete!("\n")
|
19
|
+
s.tr!("+/", "-_")
|
20
|
+
s.delete!("=")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_normalized_string(text)
|
25
|
+
words_to_omit = %w(a be but has have in is it of on or the to when)
|
26
|
+
col_text = text.to_s.gsub(/(<[^>]*>)|\n|\t/su, ' ') # Remove html tags
|
27
|
+
col_text.downcase! # Remove capitalization
|
28
|
+
col_text.gsub!(/\"|\'/u, '') # Remove potential problem characters
|
29
|
+
col_text.gsub!(/\(.*?\)/u,'') # Remove text inside parens
|
30
|
+
col_text.gsub!(/\W/u, ' ') # Remove all other non-word characters
|
31
|
+
cols = (col_text.split(' ') - words_to_omit)
|
32
|
+
(cols.size > 5 ? cols[-5..-1] : cols).join("_")
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :normalize :to_normalized_string
|
36
|
+
|
37
|
+
def generate_api_id
|
38
|
+
UUIDTools::UUID.random_create.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# @private Intended for internal use only.
|
43
|
+
#
|
44
|
+
# Loads and uses either `FasterCSV` (for Ruby 1.8) or the stdlib `CSV`
|
45
|
+
# (for Ruby 1.9+).
|
46
|
+
#
|
47
|
+
# @return [Class] either `CSV` for `FasterCSV`.
|
48
|
+
def csv_impl
|
49
|
+
@csv_impl ||= if RUBY_VERSION < '1.9'
|
50
|
+
require 'fastercsv'
|
51
|
+
FasterCSV
|
52
|
+
else
|
53
|
+
require 'csv'
|
54
|
+
CSV
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'surveyor'
|
3
|
+
require 'haml' # required for view resolution
|
4
|
+
|
5
|
+
module Surveyor
|
6
|
+
class Engine < Rails::Engine
|
7
|
+
root = File.expand_path('../../', __FILE__)
|
8
|
+
config.autoload_paths << root
|
9
|
+
config.to_prepare do
|
10
|
+
Dir.glob(File.expand_path('../../../app/inputs/*_input*.rb', __FILE__)).each do |c|
|
11
|
+
require_dependency(c)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module Helpers
|
3
|
+
module FormtasticCustomInput
|
4
|
+
def input_html_options_with(choice, response_class)
|
5
|
+
input_html_options.merge(choice_html_options(choice)).merge({:id => (input_html_options[:id] || "answer_id").gsub("answer_id", response_class_to_method(response_class).to_s), :class => [input_html_options[:class], response_class].join(" ")})
|
6
|
+
end
|
7
|
+
def response_class_to_method(type_sym)
|
8
|
+
# doesn't handle response_class == answer, and doesn't have to
|
9
|
+
"#{type_sym.to_s}_value".to_sym
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module Helpers
|
3
|
+
module SurveyorHelperMethods
|
4
|
+
|
5
|
+
# Layout: stylsheets and javascripts
|
6
|
+
def surveyor_includes
|
7
|
+
stylesheet_link_tag('surveyor_all') + javascript_include_tag('surveyor_all')
|
8
|
+
end
|
9
|
+
# Helper for displaying warning/notice/error flash messages
|
10
|
+
def flash_messages(types)
|
11
|
+
types.map{|type| content_tag(:div, "#{flash[type]}".html_safe, :class => type.to_s)}.join.html_safe
|
12
|
+
end
|
13
|
+
# Section: dependencies, menu, previous and next
|
14
|
+
def dependency_explanation_helper(question,response_set)
|
15
|
+
# Attempts to explain why this dependent question needs to be answered by referenced the dependent question and users response
|
16
|
+
trigger_responses = []
|
17
|
+
dependent_questions = Question.find_all_by_id(question.dependency.dependency_conditions.map(&:question_id)).uniq
|
18
|
+
response_set.responses.find_all_by_question_id(dependent_questions.map(&:id)).uniq.each do |resp|
|
19
|
+
trigger_responses << resp.to_s
|
20
|
+
end
|
21
|
+
" You answered "#{trigger_responses.join("" and "")}" to the question "#{dependent_questions.map(&:text).join("","")}""
|
22
|
+
end
|
23
|
+
def menu_button_for(section)
|
24
|
+
submit_tag(section.translation(I18n.locale)[:title], :name => "section[#{section.id}]")
|
25
|
+
end
|
26
|
+
def previous_section
|
27
|
+
# use copy in memory instead of making extra db calls
|
28
|
+
prev_index = [(@sections.index(@section) || 0) - 1, 0].max
|
29
|
+
submit_tag(t('surveyor.previous_section').html_safe, :name => "section[#{@sections[prev_index].id}]") unless @sections[0] == @section
|
30
|
+
end
|
31
|
+
def next_section
|
32
|
+
# use copy in memory instead of making extra db calls
|
33
|
+
next_index = [(@sections.index(@section) || @sections.count) + 1, @sections.count].min
|
34
|
+
@sections.last == @section ? submit_tag(t('surveyor.click_here_to_finish').html_safe, :name => "finish") : submit_tag(t('surveyor.next_section').html_safe, :name => "section[#{@sections[next_index].id}]")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Questions
|
38
|
+
def q_text(q, context=nil, locale=nil)
|
39
|
+
"#{next_question_number(q) unless (q.dependent? or q.display_type == "label" or q.display_type == "image" or q.part_of_group?)}#{q.text_for(nil, context, locale)}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def next_question_number(question)
|
43
|
+
@n ||= 0
|
44
|
+
"<span class='qnum'>#{@n += 1}) </span>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Responses
|
48
|
+
def rc_to_attr(type_sym)
|
49
|
+
case type_sym.to_s
|
50
|
+
when /^answer$/ then :answer_id
|
51
|
+
else "#{type_sym.to_s}_value".to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def rc_to_as(type_sym)
|
56
|
+
case type_sym.to_s
|
57
|
+
when /(integer|float|date|time|datetime)/ then :string
|
58
|
+
else type_sym
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def generate_pick_none_input_html(value, default_value, css_class, response_class, disabled, input_mask, input_mask_placeholder)
|
63
|
+
html = {}
|
64
|
+
html[:class] = [response_class,css_class].reject{ |c| c.blank? }
|
65
|
+
html[:value] = value.blank? ? default_value : value
|
66
|
+
html[:disabled] = disabled unless disabled.blank?
|
67
|
+
if input_mask
|
68
|
+
html[:'data-input-mask'] = input_mask
|
69
|
+
html[:'data-input-mask-placeholder'] = input_mask_placeholder unless input_mask_placeholder.blank?
|
70
|
+
end
|
71
|
+
html
|
72
|
+
end
|
73
|
+
|
74
|
+
# Responses
|
75
|
+
def response_for(response_set, question, answer = nil, response_group = nil)
|
76
|
+
return nil unless response_set && question && question.id
|
77
|
+
result = response_set.responses.detect{|r| (r.question_id == question.id) && (answer.blank? ? true : r.answer_id == answer.id) && (r.response_group.blank? ? true : r.response_group.to_i == response_group.to_i)}
|
78
|
+
result.blank? ? response_set.responses.build(:question_id => question.id, :response_group => response_group) : result
|
79
|
+
end
|
80
|
+
def response_idx(increment = true)
|
81
|
+
@rc ||= 0
|
82
|
+
(increment ? @rc += 1 : @rc).to_s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'surveyor/common'
|
2
|
+
module Surveyor
|
3
|
+
module Models
|
4
|
+
module AnswerMethods
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::Validations
|
7
|
+
include MustacheContext
|
8
|
+
include ActiveModel::ForbiddenAttributesProtection
|
9
|
+
|
10
|
+
included do
|
11
|
+
# Associations
|
12
|
+
belongs_to :question
|
13
|
+
has_many :responses
|
14
|
+
has_many :validations, :dependent => :destroy
|
15
|
+
attr_accessible *PermittedParams.new.answer_attributes if defined? ActiveModel::MassAssignmentSecurity
|
16
|
+
|
17
|
+
# Validations
|
18
|
+
validates_presence_of :text
|
19
|
+
end
|
20
|
+
|
21
|
+
# Instance Methods
|
22
|
+
def initialize(*args)
|
23
|
+
super(*args)
|
24
|
+
default_args
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_args
|
28
|
+
self.is_exclusive ||= false
|
29
|
+
self.display_type ||= "default"
|
30
|
+
self.response_class ||= "answer"
|
31
|
+
self.short_text ||= text
|
32
|
+
self.data_export_identifier ||= Surveyor::Common.normalize(text)
|
33
|
+
self.api_id ||= Surveyor::Common.generate_api_id
|
34
|
+
end
|
35
|
+
def display_type=(val)
|
36
|
+
write_attribute(:display_type, val.nil? ? nil : val.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def css_class
|
40
|
+
[(is_exclusive ? "exclusive" : nil), custom_class].compact.join(" ")
|
41
|
+
end
|
42
|
+
|
43
|
+
def text_for(position = nil, context = nil, locale = nil)
|
44
|
+
return "" if display_type == "hidden_label"
|
45
|
+
imaged(split(in_context(translation(locale)[:text], context), position))
|
46
|
+
end
|
47
|
+
def help_text_for(context = nil, locale = nil)
|
48
|
+
in_context(translation(locale)[:help_text], context)
|
49
|
+
end
|
50
|
+
def default_value_for(context = nil, locale = nil)
|
51
|
+
in_context(translation(locale)[:default_value], context)
|
52
|
+
end
|
53
|
+
def split(text, position=nil)
|
54
|
+
case position
|
55
|
+
when :pre
|
56
|
+
text.split("|",2)[0]
|
57
|
+
when :post
|
58
|
+
text.split("|",2)[1]
|
59
|
+
else
|
60
|
+
text
|
61
|
+
end.to_s
|
62
|
+
end
|
63
|
+
def translation(locale)
|
64
|
+
{:text => self.text, :help_text => self.help_text, :default_value => self.default_value}.with_indifferent_access.merge(
|
65
|
+
(self.question.translation(locale)[:answers] || {})[self.reference_identifier] || {}
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def imaged(text)
|
72
|
+
self.display_type == "image" && !text.blank? ? ActionController::Base.helpers.image_tag(text) : text
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module Models
|
3
|
+
module DependencyConditionMethods
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include Surveyor::ActsAsResponse # includes "as" instance method
|
7
|
+
include ActiveModel::ForbiddenAttributesProtection
|
8
|
+
|
9
|
+
included do
|
10
|
+
# Associations
|
11
|
+
belongs_to :answer
|
12
|
+
belongs_to :dependency
|
13
|
+
belongs_to :dependent_question, :foreign_key => :question_id, :class_name => :question
|
14
|
+
belongs_to :question
|
15
|
+
attr_accessible *PermittedParams.new.dependency_condition_attributes if defined? ActiveModel::MassAssignmentSecurity
|
16
|
+
|
17
|
+
# Validations
|
18
|
+
validates_presence_of :operator, :rule_key
|
19
|
+
validate :validates_operator
|
20
|
+
validates_uniqueness_of :rule_key, :scope => :dependency_id
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def operators
|
25
|
+
Surveyor::Common::OPERATORS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Instance methods
|
30
|
+
def to_hash(response_set)
|
31
|
+
# all responses to associated question
|
32
|
+
responses = question.blank? ? [] : response_set.responses.where("responses.answer_id in (?)", question.answer_ids)
|
33
|
+
if self.operator.match /^count(>|>=|<|<=|==|!=)\d+$/
|
34
|
+
op, i = self.operator.scan(/^count(>|>=|<|<=|==|!=)(\d+)$/).flatten
|
35
|
+
# logger.warn({rule_key.to_sym => responses.count.send(op, i.to_i)})
|
36
|
+
return {rule_key.to_sym => (op == "!=" ? !responses.count.send("==", i.to_i) : responses.count.send(op, i.to_i))}
|
37
|
+
elsif operator == "!=" and (responses.blank? or responses.none?{|r| r.answer.id == self.answer.id})
|
38
|
+
# logger.warn( {rule_key.to_sym => true})
|
39
|
+
return {rule_key.to_sym => true}
|
40
|
+
elsif response = responses.to_a.detect{|r| r.answer.id == self.answer.id}
|
41
|
+
klass = response.answer.response_class
|
42
|
+
klass = "answer" if self.as(klass).nil? # it should compare answer ids when the dependency condition *_value is nil
|
43
|
+
case self.operator
|
44
|
+
when "==", "<", ">", "<=", ">="
|
45
|
+
# logger.warn( {rule_key.to_sym => response.as(klass).send(self.operator, self.as(klass))})
|
46
|
+
return {rule_key.to_sym => !response.as(klass).nil? && response.as(klass).send(self.operator, self.as(klass))}
|
47
|
+
when "!="
|
48
|
+
# logger.warn( {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))})
|
49
|
+
return {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# logger.warn({rule_key.to_sym => false})
|
53
|
+
{rule_key.to_sym => false}
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def validates_operator
|
59
|
+
errors.add(:operator, "Invalid operator") unless
|
60
|
+
Surveyor::Common::OPERATORS.include?(self.operator) ||
|
61
|
+
self.operator && self.operator.match(/^count(<|>|==|>=|<=|!=)(\d+)/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module Models
|
3
|
+
module DependencyMethods
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
include ActiveModel::Validations
|
6
|
+
include ActiveModel::ForbiddenAttributesProtection
|
7
|
+
|
8
|
+
included do
|
9
|
+
# Associations
|
10
|
+
belongs_to :question
|
11
|
+
belongs_to :question_group
|
12
|
+
has_many :dependency_conditions, :dependent => :destroy
|
13
|
+
attr_accessible *PermittedParams.new.dependency_attributes if defined? ActiveModel::MassAssignmentSecurity
|
14
|
+
|
15
|
+
# Validations
|
16
|
+
validates_presence_of :rule
|
17
|
+
validates_format_of :rule, :with => /\A(?:and|or|\)|\(|[A-Z]|\s)+\Z/ #TODO properly formed parenthesis etc.
|
18
|
+
validates_numericality_of :question_id, :if => Proc.new { |d| d.question_group_id.nil? }
|
19
|
+
validates_numericality_of :question_group_id, :if => Proc.new { |d| d.question_id.nil? }
|
20
|
+
|
21
|
+
# Attribute aliases
|
22
|
+
alias_attribute :dependent_question_id, :question_id
|
23
|
+
end
|
24
|
+
|
25
|
+
# Instance Methods
|
26
|
+
def question_group_id=(i)
|
27
|
+
write_attribute(:question_id, nil) unless i.nil?
|
28
|
+
write_attribute(:question_group_id, i)
|
29
|
+
end
|
30
|
+
|
31
|
+
def question_id=(i)
|
32
|
+
write_attribute(:question_group_id, nil) unless i.nil?
|
33
|
+
write_attribute(:question_id, i)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Has this dependency has been met in the context of response_set?
|
37
|
+
# Substitutes the conditions hash into the rule and evaluates it
|
38
|
+
def is_met?(response_set)
|
39
|
+
ch = conditions_hash(response_set)
|
40
|
+
return false if ch.blank?
|
41
|
+
# logger.debug "rule: #{self.rule.inspect}"
|
42
|
+
# logger.debug "rexp: #{rgx.inspect}"
|
43
|
+
# logger.debug "keyp: #{ch.inspect}"
|
44
|
+
# logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
|
45
|
+
rgx = Regexp.new(self.dependency_conditions.map{|dc| ["a","o"].include?(dc.rule_key) ? "\\b#{dc.rule_key}(?!nd|r)\\b" : "\\b#{dc.rule_key}\\b"}.join("|")) # exclude and, or
|
46
|
+
eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
|
47
|
+
end
|
48
|
+
|
49
|
+
# A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
|
50
|
+
def conditions_hash(response_set)
|
51
|
+
hash = {}
|
52
|
+
self.dependency_conditions.each{|dc| hash.merge!(dc.to_hash(response_set))}
|
53
|
+
return hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|