surveyor 0.22.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +83 -0
- data/Gemfile.rails_version +7 -1
- data/README.md +114 -16
- data/Rakefile +15 -9
- 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/views/partials/_answer.html.haml +4 -4
- data/app/views/partials/_question.html.haml +7 -7
- data/app/views/partials/_question_group.html.haml +9 -8
- data/app/views/surveyor/export.json.rabl +28 -25
- data/app/views/surveyor/new.html.haml +8 -5
- data/app/views/surveyor/show.json.rabl +3 -2
- data/ci-exec.sh +13 -7
- data/cucumber.yml +3 -3
- data/doc/REPRESENTATIONS.md +34 -0
- data/doc/api_id_schema.json +7 -0
- data/doc/response_set_schema.json +54 -0
- data/doc/surveyor question combinations.png +0 -0
- data/doc/surveyor_timestamp_schema.json +9 -0
- data/features/ajax_submissions.feature +140 -0
- data/features/export_to_json.feature +182 -34
- data/features/no_duplicates.feature +110 -0
- data/features/show_survey.feature +1 -1
- data/features/step_definitions/parser_steps.rb +25 -2
- data/features/step_definitions/surveyor_steps.rb +145 -20
- data/features/step_definitions/ui_steps.rb +3 -0
- data/features/support/database_cleaner.rb +16 -0
- data/features/support/env.rb +21 -17
- data/features/support/simultaneous_ajax.rb +101 -0
- data/features/support/single_quit_selenium_driver.rb +23 -0
- data/features/support/slow_updates.rb +18 -0
- data/features/surveyor.feature +174 -44
- data/features/surveyor_dependencies.feature +80 -39
- data/features/surveyor_parser.feature +114 -20
- data/features/z_redcap_parser.feature +0 -1
- data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/next.gif +0 -0
- data/lib/{generators/surveyor/templates/public → assets}/images/surveyor/prev.gif +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/lib/assets/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ef8c08_256x240.png +0 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/surveyor → assets/images}/ui-icons_ffffff_256x240.png +0 -0
- data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui-timepicker-addon.js +23 -23
- data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery-ui.js +125 -125
- data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
- data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.surveyor.js +52 -57
- data/lib/{generators/surveyor/templates/public → assets}/javascripts/surveyor/jquery.tools.min.js +7 -7
- data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/dateinput.css +13 -13
- data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui-timepicker-addon.css +0 -0
- data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/jquery-ui.custom.css +17 -17
- data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/reset.css +1 -1
- data/lib/{generators/surveyor/templates/public → assets}/stylesheets/surveyor/results.css +0 -0
- data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
- data/lib/{generators/surveyor/templates/public/stylesheets/sass → assets/stylesheets}/surveyor.sass +15 -7
- data/lib/generators/surveyor/custom_generator.rb +3 -2
- data/lib/generators/surveyor/install_generator.rb +59 -17
- data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +5 -0
- data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
- data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +2 -1
- data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +1 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +1 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +1 -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 +3 -2
- 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 +6 -5
- data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +2 -1
- data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +2 -1
- data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +5 -4
- data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +4 -3
- data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +1 -0
- data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +10 -9
- data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +5 -4
- data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +4 -3
- data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +5 -4
- data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +3 -2
- 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/date_survey.rb +1 -0
- data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +54 -24
- data/lib/generators/surveyor/templates/surveys/quiz.rb +1 -0
- data/lib/generators/surveyor/templates/{public/stylesheets/sass → vendor/assets/stylesheets}/custom.sass +1 -1
- data/lib/surveyor/common.rb +16 -31
- data/lib/surveyor/engine.rb +2 -4
- data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
- data/lib/surveyor/helpers/formtastic_custom_input.rb +17 -0
- data/lib/surveyor/helpers/surveyor_helper_methods.rb +10 -8
- data/lib/surveyor/models/answer_methods.rb +3 -0
- data/lib/surveyor/models/dependency_condition_methods.rb +27 -28
- data/lib/surveyor/models/dependency_methods.rb +3 -0
- data/lib/surveyor/models/question_group_methods.rb +3 -0
- data/lib/surveyor/models/question_methods.rb +10 -7
- data/lib/surveyor/models/response_methods.rb +16 -0
- data/lib/surveyor/models/response_set_methods.rb +71 -64
- data/lib/surveyor/models/survey_methods.rb +19 -28
- data/lib/surveyor/models/survey_section_methods.rb +3 -0
- data/lib/surveyor/models/validation_condition_methods.rb +4 -2
- data/lib/surveyor/models/validation_methods.rb +3 -0
- data/lib/surveyor/parser.rb +198 -148
- data/lib/surveyor/redcap_parser.rb +120 -80
- data/lib/surveyor/surveyor_controller_methods.rb +86 -37
- data/lib/surveyor/version.rb +2 -2
- data/lib/surveyor.rb +5 -6
- data/lib/tasks/surveyor_tasks.rake +19 -7
- data/spec/controllers/surveyor_controller_spec.rb +166 -92
- data/spec/factories.rb +33 -32
- data/spec/helpers/formtastic_custom_input_spec.rb +16 -0
- data/spec/lib/common_spec.rb +0 -39
- data/spec/lib/redcap_parser_spec.rb +24 -24
- data/spec/models/answer_spec.rb +12 -0
- data/spec/models/dependency_condition_spec.rb +279 -323
- data/spec/models/dependency_spec.rb +10 -0
- data/spec/models/question_group_spec.rb +12 -0
- data/spec/models/question_spec.rb +12 -0
- data/spec/models/response_set_spec.rb +189 -139
- data/spec/models/response_spec.rb +60 -0
- data/spec/models/survey_section_spec.rb +9 -0
- data/spec/models/survey_spec.rb +72 -9
- data/spec/models/validation_condition_spec.rb +9 -1
- data/spec/models/validation_spec.rb +10 -0
- data/spec/spec_helper.rb +25 -6
- data/surveyor.gemspec +5 -4
- metadata +332 -291
- data/features/step_definitions/common_steps.rb +0 -3
- data/lib/formtastic/surveyor_builder.rb +0 -82
- data/lib/generators/surveyor/templates/public/javascripts/surveyor/jquery.blockUI.js +0 -499
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
class CreateSurveys < ActiveRecord::Migration
|
2
3
|
def self.up
|
3
4
|
create_table :surveys do |t|
|
@@ -15,12 +16,12 @@ class CreateSurveys < ActiveRecord::Migration
|
|
15
16
|
# Expiry
|
16
17
|
t.datetime :active_at
|
17
18
|
t.datetime :inactive_at
|
18
|
-
|
19
|
+
|
19
20
|
# Display
|
20
21
|
t.string :css_url
|
21
|
-
|
22
|
+
|
22
23
|
t.string :custom_class
|
23
|
-
|
24
|
+
|
24
25
|
t.timestamps
|
25
26
|
end
|
26
27
|
end
|
@@ -1,13 +1,14 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
class CreateValidationConditions < ActiveRecord::Migration
|
2
3
|
def self.up
|
3
4
|
create_table :validation_conditions do |t|
|
4
5
|
# Context
|
5
6
|
t.integer :validation_id
|
6
7
|
t.string :rule_key
|
7
|
-
|
8
|
-
# Conditional
|
8
|
+
|
9
|
+
# Conditional
|
9
10
|
t.string :operator
|
10
|
-
|
11
|
+
|
11
12
|
# Optional external reference
|
12
13
|
t.integer :question_id
|
13
14
|
t.integer :answer_id
|
@@ -21,7 +22,7 @@ class CreateValidationConditions < ActiveRecord::Migration
|
|
21
22
|
t.string :string_value
|
22
23
|
t.string :response_other
|
23
24
|
t.string :regexp
|
24
|
-
|
25
|
+
|
25
26
|
t.timestamps
|
26
27
|
end
|
27
28
|
end
|
@@ -1,15 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
class CreateValidations < ActiveRecord::Migration
|
2
3
|
def self.up
|
3
4
|
create_table :validations do |t|
|
4
5
|
# Context
|
5
6
|
t.integer :answer_id # the answer to validate
|
6
|
-
|
7
|
+
|
7
8
|
# Conditional
|
8
9
|
t.string :rule
|
9
10
|
|
10
11
|
# Message
|
11
12
|
t.string :message
|
12
|
-
|
13
|
+
|
13
14
|
t.timestamps
|
14
15
|
end
|
15
16
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
class Survey < ActiveRecord::Base; end
|
3
|
+
class Question < ActiveRecord::Base; end
|
4
|
+
class QuestionGroup < ActiveRecord::Base; end
|
5
|
+
class Answer < ActiveRecord::Base; end
|
6
|
+
class Response < ActiveRecord::Base; end
|
7
|
+
class ResponseSet < ActiveRecord::Base; end
|
8
|
+
|
9
|
+
class UpdateBlankApiIdsOnQuestionGroup < ActiveRecord::Migration
|
10
|
+
def self.up
|
11
|
+
check = [Survey, Question, QuestionGroup, Answer, Response, ResponseSet]
|
12
|
+
check.each do |clazz|
|
13
|
+
clazz.where('api_id IS ?', nil).each do |c|
|
14
|
+
c.api_id = Surveyor::Common.generate_api_id
|
15
|
+
c.save!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.down
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
class Survey < ActiveRecord::Base; end
|
3
|
+
class UpdateBlankVersionsOnSurveys < ActiveRecord::Migration
|
4
|
+
def self.up
|
5
|
+
Survey.where('survey_version IS ?', nil).each do |s|
|
6
|
+
s.survey_version = 0
|
7
|
+
s.save!
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.down
|
12
|
+
end
|
13
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
survey "Kitchen Sink survey" do
|
2
3
|
|
3
4
|
section "Basic questions" do
|
@@ -21,7 +22,7 @@ survey "Kitchen Sink survey" do
|
|
21
22
|
a_4 "yellow"
|
22
23
|
a :omit
|
23
24
|
|
24
|
-
# A dependent question, with conditions and rule to logically join them
|
25
|
+
# A dependent question, with conditions and rule to logically join them
|
25
26
|
# the question's reference identifier is "2a", and the answer's reference_identifier is "1"
|
26
27
|
# question reference identifiers used in conditions need to be unique on a survey for the lookups to work
|
27
28
|
q_2a "Please explain why you don't like this color?"
|
@@ -32,7 +33,7 @@ survey "Kitchen Sink survey" do
|
|
32
33
|
condition_C :q_2, "==", :a_3
|
33
34
|
condition_D :q_2, "==", :a_4
|
34
35
|
|
35
|
-
# A dependant question demonstrating the count operator. The
|
36
|
+
# A dependant question demonstrating the count operator. The
|
36
37
|
# dependency condition checks the answer count for the referenced question.
|
37
38
|
# It understands conditions of the form count> count< count>= count<=
|
38
39
|
# count!=
|
@@ -44,23 +45,41 @@ survey "Kitchen Sink survey" do
|
|
44
45
|
# When :pick isn't specified, the default is :none (no checkbox or radio button)
|
45
46
|
q_montypython3 "What... is your name? (e.g. It is 'Arthur', King of the Britons)"
|
46
47
|
a_1 :string
|
47
|
-
|
48
|
+
|
48
49
|
# Dependency conditions can refer to any value, not just answer_id. An answer_reference still needs to be specified, to know which answer you would like to check
|
49
50
|
q_montypython4 "What... is your quest? (e.g. To seek the Holy Grail)"
|
50
51
|
a_1 :string
|
51
52
|
dependency :rule => "A"
|
52
53
|
condition_A :q_montypython3, "==", {:string_value => "It is 'Arthur', King of the Britons", :answer_reference => "1"}
|
53
|
-
|
54
|
+
|
54
55
|
# http://www.imdb.com/title/tt0071853/quotes
|
55
56
|
q_montypython5 "What... is the air-speed velocity of an unladen swallow? (e.g. What do you mean? An African or European swallow?)"
|
56
57
|
a_1 :string
|
57
58
|
dependency :rule => "A"
|
58
59
|
condition_A :q_montypython4, "==", {:string_value => "To seek the Holy Grail", :answer_reference => "1"}
|
59
|
-
|
60
|
+
|
60
61
|
label "Huh? I-- I don't know that! Auuuuuuuugh!"
|
61
62
|
dependency :rule => "A"
|
62
63
|
condition_A :q_montypython5, "==", {:string_value => "What do you mean? An African or European swallow?", :answer_reference => "1"}
|
63
|
-
|
64
|
+
|
65
|
+
q_cooling_1 "How do you cool your home?", :pick => :one
|
66
|
+
a_1 "Fans"
|
67
|
+
a_2 "Window AC"
|
68
|
+
a_3 "Central AC"
|
69
|
+
a_4 "Passive"
|
70
|
+
|
71
|
+
# When using !=, also use count>0 if you want to make sure the user responds
|
72
|
+
q_cooling_2 "How much does it cost to run your non-passive cooling solutions? (This question hidden until you respond 'How do you cool your home?')"
|
73
|
+
dependency :rule => "A and B"
|
74
|
+
condition_A :q_cooling_1, "!=", :a_4
|
75
|
+
condition_B :q_cooling_1, "count>0"
|
76
|
+
a_1 "$", :float
|
77
|
+
|
78
|
+
# Using != alone means the dependent question is shown by default
|
79
|
+
label "Please consider passive cooling solutions (This question always shown, unless you respond 'Passive')"
|
80
|
+
dependency :rule => "A"
|
81
|
+
condition_A :q_cooling_1, "!=", :a_4
|
82
|
+
|
64
83
|
# Surveys, sections, questions, groups, and answers all take the following reference arguments
|
65
84
|
# :reference_identifier # usually from paper
|
66
85
|
# :data_export_identifier # data export
|
@@ -70,39 +89,39 @@ survey "Kitchen Sink survey" do
|
|
70
89
|
a "who", :string
|
71
90
|
a "what", :string
|
72
91
|
a "where", :string
|
73
|
-
|
92
|
+
|
74
93
|
# Various types of responses can be accepted, and validated
|
75
94
|
q "How many pets do you own?"
|
76
95
|
a :integer
|
77
96
|
validation :rule => "A"
|
78
97
|
condition_A ">=", :integer_value => 0
|
79
|
-
|
98
|
+
|
80
99
|
# Surveys, sections, questions, groups, and answers also take a custom css class for covenience in custom styling
|
81
100
|
q "What is your address?", :custom_class => 'address'
|
82
101
|
a :text, :custom_class => 'mapper'
|
83
102
|
# validations can use regexp values
|
84
103
|
validation :rule => "A"
|
85
104
|
condition_A "=~", :regexp => "[0-9a-zA-z\. #]"
|
86
|
-
|
105
|
+
|
87
106
|
# Questions, groups, and answers take a custom renderer (a partial in the application's views dir)
|
88
107
|
# defaults are "/partials/question_group", "/partials/question", "/partials/answer", so the custom renderers should have a different name
|
89
108
|
q "Pick your favorite date AND time" #, :custom_renderer => "/partials/custom_question"
|
90
109
|
a :datetime
|
91
|
-
|
110
|
+
|
92
111
|
q_time_lunch "What time do you usually take a lunch break?"
|
93
112
|
a_1 :time
|
94
|
-
|
113
|
+
|
95
114
|
q "When would you like to meet for dinner?"
|
96
115
|
a :date
|
97
|
-
|
116
|
+
|
98
117
|
# Sliders deprecate to select boxes when javascript is off
|
99
|
-
# Valid Ruby code may be used to
|
118
|
+
# Valid Ruby code may be used to shorten repetitive tasks
|
100
119
|
q "Adjust the slider to reflect your level of awesomeness", :pick => :one, :display_type => :slider
|
101
120
|
(1..10).to_a.each{|num| a num.to_s}
|
102
121
|
|
103
122
|
q "How much do you like Ruby?", :pick => :one, :display_type => :slider
|
104
123
|
["not at all", "a little", "some", "a lot", "a ton"].each{|level| a level}
|
105
|
-
|
124
|
+
|
106
125
|
# The "|" pipe is used to place labels before or after the input elements
|
107
126
|
q "How much money do you want?"
|
108
127
|
a "$|USD", :float
|
@@ -121,7 +140,7 @@ survey "Kitchen Sink survey" do
|
|
121
140
|
q "Choose your Illinois county", :pick => :one, :display_type => :dropdown
|
122
141
|
["Adams","Alexander","Bond", "Boone",
|
123
142
|
"Brown","Bureau","Calhoun","Carroll","Cass",
|
124
|
-
"Champaign", "Christian", "Clark","Clay",
|
143
|
+
"Champaign", "Christian", "Clark","Clay",
|
125
144
|
"Clinton", "Coles", "Cook", "Crawford","Cumberland","DeKalb",
|
126
145
|
"De Witt","Douglas","DuPage","Edgar", "Edwards",
|
127
146
|
"Effingham","Fayette", "Ford","Franklin","Fulton",
|
@@ -139,7 +158,7 @@ survey "Kitchen Sink survey" do
|
|
139
158
|
"St. Clair","Stark", "Stephenson","Tazewell","Union",
|
140
159
|
"Vermilion","Wabash","Warren","Washington","Wayne",
|
141
160
|
"White","Whiteside","Will","Williamson","Winnebago","Woodford"].each{ |county| a county}
|
142
|
-
|
161
|
+
|
143
162
|
# When an is_exclusive answer is checked, it unchecks all other options and disables them (using Javascript)
|
144
163
|
q "Choose your favorite meats", :display_type => :inline, :pick => :any
|
145
164
|
a "Chicken"
|
@@ -157,7 +176,7 @@ survey "Kitchen Sink survey" do
|
|
157
176
|
end
|
158
177
|
|
159
178
|
section "Complicated questions" do
|
160
|
-
|
179
|
+
|
161
180
|
# Grids are useful for repeated questions with the same set of answers, which are specified before the questions
|
162
181
|
grid "Tell us how often do you cover these each day" do
|
163
182
|
a "1"
|
@@ -167,7 +186,7 @@ survey "Kitchen Sink survey" do
|
|
167
186
|
q "Knees", :pick => :one
|
168
187
|
q "Toes", :pick => :one
|
169
188
|
end
|
170
|
-
|
189
|
+
|
171
190
|
# "grid" is a shortcut for group with :display_type => :grid
|
172
191
|
# The answers will be repeated every 10 rows, but long grids aren't recommended as they are generally tedious
|
173
192
|
grid "Tell us how you feel today day" do
|
@@ -180,7 +199,7 @@ survey "Kitchen Sink survey" do
|
|
180
199
|
q "sad|happy", :pick => :one
|
181
200
|
q "limp|perky", :pick => :one
|
182
201
|
end
|
183
|
-
|
202
|
+
|
184
203
|
grid "For each of the car types checked, what type of options would you prefer?" do
|
185
204
|
a "Leather seats"
|
186
205
|
a "Shiny rims"
|
@@ -196,25 +215,36 @@ survey "Kitchen Sink survey" do
|
|
196
215
|
q "Wagon", :pick => :any
|
197
216
|
q "Other", :pick => :any
|
198
217
|
end
|
199
|
-
|
218
|
+
|
200
219
|
q "Please rank the following foods based on how much you like them"
|
201
220
|
a "|pizza", :integer
|
202
221
|
a "|salad", :integer
|
203
222
|
a "|sushi", :integer
|
204
223
|
a "|ice cream", :integer
|
205
224
|
a "|breakfast ceral", :integer
|
206
|
-
|
225
|
+
|
207
226
|
# :other, :string allows someone to specify both the "other" and some other information
|
208
227
|
q "Choose your favorite utensils and enter frequency of use (daily, weekly, monthly, etc...)", :pick => :any
|
209
228
|
a "spoon", :string
|
210
229
|
a "fork", :string
|
211
230
|
a "knife", :string
|
212
231
|
a :other, :string
|
213
|
-
|
232
|
+
|
233
|
+
q "What is your birth date?", :pick => :one
|
234
|
+
a "I was born on", :date
|
235
|
+
a "Refused"
|
236
|
+
|
237
|
+
q "At what time were you born?", :pick => :any
|
238
|
+
a "I was born at", :time
|
239
|
+
a "This time is approximate"
|
240
|
+
|
241
|
+
q "When would you like to schedule your next appointment?"
|
242
|
+
a :datetime
|
243
|
+
|
214
244
|
q_car "Do you own a car?", :pick => :one
|
215
245
|
a_y "Yes"
|
216
246
|
a_n "No"
|
217
|
-
|
247
|
+
|
218
248
|
# Repeaters allow multiple responses to a question or set of questions
|
219
249
|
repeater "Tell us about the cars you own" do
|
220
250
|
dependency :rule => "A"
|
@@ -232,7 +262,7 @@ survey "Kitchen Sink survey" do
|
|
232
262
|
q "Year"
|
233
263
|
a :string
|
234
264
|
end
|
235
|
-
|
265
|
+
|
236
266
|
repeater "Tell us the name and age of your siblings" do
|
237
267
|
q "Sibling"
|
238
268
|
a "Name", :string
|
data/lib/surveyor/common.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
1
|
+
require 'securerandom'
|
1
2
|
require 'uuidtools'
|
2
3
|
|
3
4
|
module Surveyor
|
4
5
|
class Common
|
5
|
-
RAND_CHARS = [('a'..'z'), ('A'..'Z'), (0..9)].map{|r| r.to_a}.flatten.join
|
6
6
|
OPERATORS = %w(== != < > <= >= =~)
|
7
|
-
|
7
|
+
|
8
8
|
class << self
|
9
|
-
|
10
|
-
|
11
|
-
(
|
12
|
-
|
13
|
-
|
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!("=")
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
@@ -20,35 +27,13 @@ module Surveyor
|
|
20
27
|
col_text.downcase! # Remove capitalization
|
21
28
|
col_text.gsub!(/\"|\'/u, '') # Remove potential problem characters
|
22
29
|
col_text.gsub!(/\(.*?\)/u,'') # Remove text inside parens
|
23
|
-
col_text.gsub!(/\W/u, ' ') # Remove all other non-word characters
|
30
|
+
col_text.gsub!(/\W/u, ' ') # Remove all other non-word characters
|
24
31
|
cols = (col_text.split(' ') - words_to_omit)
|
25
32
|
(cols.size > 5 ? cols[-5..-1] : cols).join("_")
|
26
33
|
end
|
27
|
-
|
28
|
-
def equal_json_excluding_wildcards(a,b)
|
29
|
-
return false if a.nil? or b.nil?
|
30
|
-
a = a.is_a?(String) ? JSON.load(a) : JSON.load(a.to_json)
|
31
|
-
b = b.is_a?(String) ? JSON.load(b) : JSON.load(b.to_json)
|
32
|
-
deep_compare_excluding_wildcards(a,b)
|
33
|
-
end
|
34
|
-
def deep_compare_excluding_wildcards(a,b)
|
35
|
-
return false if a.class != b.class
|
36
|
-
if a.is_a?(Hash)
|
37
|
-
return false if a.size != b.size
|
38
|
-
a.each do |k,v|
|
39
|
-
return false if deep_compare_excluding_wildcards(v,b[k]) == false
|
40
|
-
end
|
41
|
-
elsif a.is_a?(Array)
|
42
|
-
return false if a.size != b.size
|
43
|
-
a.each_with_index{|e,i| return false if deep_compare_excluding_wildcards(e,b[i]) == false }
|
44
|
-
else
|
45
|
-
return (a == "*") || (b == "*") || (a == b)
|
46
|
-
end
|
47
|
-
true
|
48
|
-
end
|
49
34
|
|
50
35
|
alias :normalize :to_normalized_string
|
51
|
-
|
36
|
+
|
52
37
|
def generate_api_id
|
53
38
|
UUIDTools::UUID.random_create.to_s
|
54
39
|
end
|
data/lib/surveyor/engine.rb
CHANGED
@@ -4,9 +4,7 @@ require 'haml' # required for view resolution
|
|
4
4
|
|
5
5
|
module Surveyor
|
6
6
|
class Engine < Rails::Engine
|
7
|
-
|
8
|
-
|
9
|
-
# load "tasks/surveyor_tasks.rake"
|
10
|
-
# end
|
7
|
+
root = File.expand_path('../../', __FILE__)
|
8
|
+
config.autoload_paths << root
|
11
9
|
end
|
12
10
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Surveyor
|
2
|
+
module Helpers
|
3
|
+
module AssetPipeline
|
4
|
+
##
|
5
|
+
# Returns whether or not the asset pipeline is present and enabled.
|
6
|
+
#
|
7
|
+
# The detection scheme used here was ripped from jquery-rails.
|
8
|
+
def asset_pipeline_enabled?
|
9
|
+
::Rails.version >= "3.1" && ::Rails.application.config.assets.enabled
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
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(response_class)
|
8
|
+
# doesn't handle response_class == answer, and doesn't have to
|
9
|
+
case response_class.to_s
|
10
|
+
when /^other_and_string$/ then :string_value
|
11
|
+
when /^date|time$/ then :datetime_value
|
12
|
+
else "#{response_class}_value".to_sym
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'surveyor/helpers/asset_pipeline'
|
2
|
+
|
1
3
|
module Surveyor
|
2
4
|
module Helpers
|
3
5
|
module SurveyorHelperMethods
|
6
|
+
include AssetPipeline
|
7
|
+
|
4
8
|
# Layout: stylsheets and javascripts
|
5
9
|
def surveyor_includes
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def surveyor_javascripts
|
12
|
-
javascript_include_tag 'surveyor/jquery.tools.min', 'surveyor/jquery-ui', 'surveyor/jquery-ui-timepicker-addon', 'surveyor/jquery.surveyor', 'surveyor/jquery.blockUI'
|
10
|
+
if asset_pipeline_enabled?
|
11
|
+
stylesheet_link_tag('surveyor_all') + javascript_include_tag('surveyor_all')
|
12
|
+
else
|
13
|
+
stylesheet_link_tag('surveyor/reset', 'surveyor/dateinput', 'surveyor/jquery-ui.custom', 'surveyor/jquery-ui-timepicker-addon', 'surveyor', 'custom') + javascript_include_tag('surveyor/jquery.tools.min', 'surveyor/jquery-ui', 'surveyor/jquery-ui-timepicker-addon', 'surveyor/jquery.surveyor')
|
14
|
+
end
|
13
15
|
end
|
14
16
|
# Helper for displaying warning/notice/error flash messages
|
15
17
|
def flash_messages(types)
|
@@ -96,4 +98,4 @@ module Surveyor
|
|
96
98
|
end
|
97
99
|
end
|
98
100
|
end
|
99
|
-
end
|
101
|
+
end
|
@@ -20,6 +20,9 @@ module Surveyor
|
|
20
20
|
# base.send :validates_numericality_of, :question_id, :allow_nil => false, :only_integer => true
|
21
21
|
@@validations_already_included = true
|
22
22
|
end
|
23
|
+
|
24
|
+
# Whitelisting attributes
|
25
|
+
base.send :attr_accessible, :question, :question_id, :text, :short_text, :help_text, :weight, :response_class, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_order, :is_exclusive, :display_length, :custom_class, :custom_renderer, :default_value, :display_type
|
23
26
|
end
|
24
27
|
|
25
28
|
include RenderText
|
@@ -7,7 +7,7 @@ module Surveyor
|
|
7
7
|
base.send :belongs_to, :dependency
|
8
8
|
base.send :belongs_to, :dependent_question, :foreign_key => :question_id, :class_name => :question
|
9
9
|
base.send :belongs_to, :question
|
10
|
-
|
10
|
+
|
11
11
|
@@validations_already_included ||= nil
|
12
12
|
unless @@validations_already_included
|
13
13
|
# Validations
|
@@ -16,12 +16,15 @@ module Surveyor
|
|
16
16
|
base.send :validates_uniqueness_of, :rule_key, :scope => :dependency_id
|
17
17
|
# this causes issues with building and saving
|
18
18
|
# base.send :validates_numericality_of, :question_id, :dependency_id
|
19
|
-
|
19
|
+
|
20
20
|
@@validations_already_included = true
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
base.send :include, Surveyor::ActsAsResponse # includes "as" instance method
|
24
24
|
|
25
|
+
# Whitelisting attributes
|
26
|
+
base.send :attr_accessible, :dependency, :question, :answer, :dependency_id, :rule_key, :question_id, :operator, :answer_id, :datetime_value, :integer_value, :float_value, :unit, :text_value, :string_value, :response_other
|
27
|
+
|
25
28
|
# Class methods
|
26
29
|
base.instance_eval do
|
27
30
|
def operators
|
@@ -34,33 +37,29 @@ module Surveyor
|
|
34
37
|
def to_hash(response_set)
|
35
38
|
# all responses to associated question
|
36
39
|
responses = question.blank? ? [] : response_set.responses.where("responses.answer_id in (?)", question.answer_ids).all
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
when /^count[<>=]{1,2}\d+$/
|
56
|
-
op, i = self.operator.scan(/^count([<>!=]{1,2})(\d+)$/).flatten
|
57
|
-
responses.count.send(op, i.to_i)
|
58
|
-
when /^count!=\d+$/
|
59
|
-
!(responses.count == self.operator.scan(/\d+/).first.to_i)
|
60
|
-
else
|
61
|
-
false
|
40
|
+
if self.operator.match /^count(>|>=|<|<=|=|!=)\d+$/
|
41
|
+
op, i = self.operator.scan(/^count(>|>=|<|<=|=|!=)(\d+)$/).flatten
|
42
|
+
# logger.warn({rule_key.to_sym => responses.count.send(op, i.to_i)})
|
43
|
+
return {rule_key.to_sym => (op == "!=" ? !responses.count.send("==", i.to_i) : responses.count.send(op, i.to_i))}
|
44
|
+
elsif operator == "!=" and (responses.blank? or responses.none?{|r| r.answer.id == self.answer.id})
|
45
|
+
# logger.warn( {rule_key.to_sym => true})
|
46
|
+
return {rule_key.to_sym => true}
|
47
|
+
elsif response = responses.detect{|r| r.answer.id == self.answer.id}
|
48
|
+
klass = response.answer.response_class
|
49
|
+
klass = "answer" if self.as(klass).nil?
|
50
|
+
case self.operator
|
51
|
+
when "==", "<", ">", "<=", ">="
|
52
|
+
# logger.warn( {rule_key.to_sym => response.as(klass).send(self.operator, self.as(klass))})
|
53
|
+
return {rule_key.to_sym => response.as(klass).send(self.operator, self.as(klass))}
|
54
|
+
when "!="
|
55
|
+
# logger.warn( {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))})
|
56
|
+
return {rule_key.to_sym => !response.as(klass).send("==", self.as(klass))}
|
57
|
+
end
|
62
58
|
end
|
59
|
+
# logger.warn({rule_key.to_sym => false})
|
60
|
+
{rule_key.to_sym => false}
|
63
61
|
end
|
62
|
+
|
64
63
|
protected
|
65
64
|
|
66
65
|
def validates_operator
|
@@ -18,6 +18,9 @@ module Surveyor
|
|
18
18
|
@@validations_already_included = true
|
19
19
|
end
|
20
20
|
|
21
|
+
# Whitelisting attributes
|
22
|
+
base.send :attr_accessible, :question, :question_group, :question_id, :question_group_id, :rule
|
23
|
+
|
21
24
|
# Attribute aliases
|
22
25
|
base.send :alias_attribute, :dependent_question_id, :question_id
|
23
26
|
end
|
@@ -7,6 +7,9 @@ module Surveyor
|
|
7
7
|
# Associations
|
8
8
|
base.send :has_many, :questions
|
9
9
|
base.send :has_one, :dependency
|
10
|
+
|
11
|
+
# Whitelisting attributes
|
12
|
+
base.send :attr_accessible, :text, :help_text, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_type, :custom_class, :custom_renderer
|
10
13
|
end
|
11
14
|
|
12
15
|
include RenderText
|
@@ -9,11 +9,11 @@ module Surveyor
|
|
9
9
|
base.send :belongs_to, :question_group, :dependent => :destroy
|
10
10
|
base.send :has_many, :answers, :order => "display_order ASC", :dependent => :destroy # it might not always have answers
|
11
11
|
base.send :has_one, :dependency, :dependent => :destroy
|
12
|
-
base.send :
|
12
|
+
base.send :belongs_to, :correct_answer, :class_name => "Answer", :dependent => :destroy
|
13
13
|
|
14
14
|
# Scopes
|
15
15
|
base.send :default_scope, :order => "display_order ASC"
|
16
|
-
|
16
|
+
|
17
17
|
@@validations_already_included ||= nil
|
18
18
|
unless @@validations_already_included
|
19
19
|
# Validations
|
@@ -21,10 +21,13 @@ module Surveyor
|
|
21
21
|
# this causes issues with building and saving
|
22
22
|
#, :survey_section_id
|
23
23
|
base.send :validates_inclusion_of, :is_mandatory, :in => [true, false]
|
24
|
-
|
24
|
+
|
25
25
|
@@validations_already_included = true
|
26
|
-
|
26
|
+
|
27
27
|
end
|
28
|
+
|
29
|
+
# Whitelisting attributes
|
30
|
+
base.send :attr_accessible, :survey_section, :question_group, :survey_section_id, :question_group_id, :text, :short_text, :help_text, :pick, :reference_identifier, :data_export_identifier, :common_namespace, :common_identifier, :display_order, :display_type, :is_mandatory, :display_width, :custom_class, :custom_renderer, :correct_answer_id
|
28
31
|
end
|
29
32
|
|
30
33
|
include RenderText
|
@@ -43,7 +46,7 @@ module Surveyor
|
|
43
46
|
self.short_text ||= text
|
44
47
|
self.api_id ||= Surveyor::Common.generate_api_id
|
45
48
|
end
|
46
|
-
|
49
|
+
|
47
50
|
def pick=(val)
|
48
51
|
write_attribute(:pick, val.nil? ? nil : val.to_s)
|
49
52
|
end
|
@@ -71,11 +74,11 @@ module Surveyor
|
|
71
74
|
def solo?
|
72
75
|
self.question_group.nil?
|
73
76
|
end
|
74
|
-
|
77
|
+
|
75
78
|
def split_text(part = nil)
|
76
79
|
(part == :pre ? text.split("|",2)[0] : (part == :post ? text.split("|",2)[1] : text)).to_s
|
77
80
|
end
|
78
|
-
|
81
|
+
|
79
82
|
def renderer(g = question_group)
|
80
83
|
r = [g ? g.renderer.to_s : nil, display_type].compact.join("_")
|
81
84
|
r.blank? ? :default : r.to_sym
|