surveyor_gui 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +44 -0
- data/.travis.yml +17 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/MIT-LICENSE +20 -0
- data/README.md +159 -0
- data/Rakefile +66 -0
- data/app/controllers/answers_controller.rb +3 -0
- data/app/controllers/application_controller.rb +3 -0
- data/app/controllers/dependencys_controller.rb +286 -0
- data/app/controllers/question_groups_controller.rb +84 -0
- data/app/controllers/questions_controller.rb +187 -0
- data/app/controllers/survey_sections_controller.rb +80 -0
- data/app/controllers/surveyforms_controller.rb +4 -0
- data/app/controllers/surveyor_controller.rb +89 -0
- data/app/controllers/surveyor_gui/reports_controller.rb +339 -0
- data/app/controllers/surveyor_gui/responses_controller.rb +39 -0
- data/app/controllers/surveyor_gui/survey_controller.rb +16 -0
- data/app/facades/report_formatter.rb +44 -0
- data/app/facades/report_preview_wrapper.rb +11 -0
- data/app/facades/report_response_generator.rb +147 -0
- data/app/helpers/application_helper.rb +2 -0
- data/app/helpers/dependency_helper.rb +5 -0
- data/app/helpers/question_helper.rb +22 -0
- data/app/helpers/surveyform_helper.rb +179 -0
- data/app/helpers/surveyor_gui/report_helper.rb +19 -0
- data/app/helpers/surveyor_helper.rb +4 -0
- data/app/inputs/currency_input.rb +5 -0
- data/app/inputs/date_picker_input.rb +7 -0
- data/app/inputs/datetime_picker_input.rb +5 -0
- data/app/inputs/percent_input.rb +5 -0
- data/app/inputs/time_picker_input.rb +5 -0
- data/app/mailers/.gitkeep +0 -0
- data/app/models/.gitkeep +0 -0
- data/app/models/answer.rb +4 -0
- data/app/models/column.rb +3 -0
- data/app/models/dependency.rb +4 -0
- data/app/models/dependency_condition.rb +4 -0
- data/app/models/permitted_params_decorator.rb +80 -0
- data/app/models/question.rb +4 -0
- data/app/models/question_group.rb +4 -0
- data/app/models/question_type.rb +7 -0
- data/app/models/response.rb +4 -0
- data/app/models/response_set.rb +4 -0
- data/app/models/row.rb +3 -0
- data/app/models/survey.rb +4 -0
- data/app/models/survey_section.rb +4 -0
- data/app/models/surveyform.rb +103 -0
- data/app/uploaders/blob_uploader.rb +48 -0
- data/app/views/dependencys/_column.html.erb +1 -0
- data/app/views/dependencys/_dependency.html.erb +22 -0
- data/app/views/dependencys/_dependency_condition_fields.html.erb +48 -0
- data/app/views/dependencys/_form.html.erb +20 -0
- data/app/views/dependencys/blank.html.erb +0 -0
- data/app/views/dependencys/edit.html.erb +1 -0
- data/app/views/dependencys/new.html.erb +1 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/app/views/layouts/surveyor_gui_blank.html.erb +12 -0
- data/app/views/layouts/surveyor_gui_default.html.erb +16 -0
- data/app/views/layouts/surveyor_modified.html.erb +14 -0
- data/app/views/partials/_answer.html.haml +33 -0
- data/app/views/partials/_question.html.haml +33 -0
- data/app/views/partials/_question_group.html.haml +73 -0
- data/app/views/partials/_section.html.haml +13 -0
- data/app/views/question_groups/_form.html.erb +56 -0
- data/app/views/question_groups/_group_inline_field.html.erb +21 -0
- data/app/views/question_groups/_group_inline_fields.html.erb +25 -0
- data/app/views/question_groups/blank.html.erb +0 -0
- data/app/views/question_groups/edit.html.erb +1 -0
- data/app/views/question_groups/new.html.erb +1 -0
- data/app/views/questions/_answer_fields.html.erb +23 -0
- data/app/views/questions/_answer_options.html.erb +28 -0
- data/app/views/questions/_form.html.erb +65 -0
- data/app/views/questions/_grid_dropdown_columns.html.erb +10 -0
- data/app/views/questions/_grid_dropdown_fields.html.erb +42 -0
- data/app/views/questions/_grid_fields.html.erb +9 -0
- data/app/views/questions/_no_picks.html.erb +29 -0
- data/app/views/questions/_pick.html +21 -0
- data/app/views/questions/_picks.html.erb +12 -0
- data/app/views/questions/blank.html.erb +0 -0
- data/app/views/questions/edit.html.erb +1 -0
- data/app/views/questions/new.html.erb +1 -0
- data/app/views/survey_sections/_form.html.erb +13 -0
- data/app/views/survey_sections/blank.html.erb +0 -0
- data/app/views/survey_sections/edit.html.erb +1 -0
- data/app/views/survey_sections/new.html.erb +1 -0
- data/app/views/surveyforms/_form.html.erb +50 -0
- data/app/views/surveyforms/_question_field.html.erb +148 -0
- data/app/views/surveyforms/_question_group.html.erb +116 -0
- data/app/views/surveyforms/_question_group_fields.html.erb +3 -0
- data/app/views/surveyforms/_question_name_and_number.html.erb +6 -0
- data/app/views/surveyforms/_question_section.html.erb +15 -0
- data/app/views/surveyforms/_question_wrapper.html.erb +109 -0
- data/app/views/surveyforms/_survey_section_fields.html.erb +138 -0
- data/app/views/surveyforms/clone_survey.html.erb +13 -0
- data/app/views/surveyforms/edit.html.erb +5 -0
- data/app/views/surveyforms/index.html.erb +40 -0
- data/app/views/surveyforms/new.html.erb +1 -0
- data/app/views/surveyforms/show.html.erb +5 -0
- data/app/views/surveyor_gui/reports/_graphs.html.haml +21 -0
- data/app/views/surveyor_gui/reports/_grid.html.haml +42 -0
- data/app/views/surveyor_gui/reports/_grid_dropdown.html.haml +56 -0
- data/app/views/surveyor_gui/reports/_repeater.html.haml +28 -0
- data/app/views/surveyor_gui/reports/_show_report.html.haml +33 -0
- data/app/views/surveyor_gui/reports/_single_question.html.haml +70 -0
- data/app/views/surveyor_gui/reports/show.html.erb +14 -0
- data/app/views/surveyor_gui/responses/_grid.html.haml +20 -0
- data/app/views/surveyor_gui/responses/_grid_dropdown.html.haml +20 -0
- data/app/views/surveyor_gui/responses/_repeater.html.haml +22 -0
- data/app/views/surveyor_gui/responses/_survey_results.html.haml +40 -0
- data/app/views/surveyor_gui/responses/index.html.haml +24 -0
- data/app/views/surveyor_gui/responses/show.html.haml +42 -0
- data/app/views/surveyor_gui/shared/_grid_comments.html.haml +10 -0
- data/app/views/surveyor_gui/shared/_new_line.html.haml +2 -0
- data/app/views/surveyor_gui/shared/_pick_comments.html.haml +15 -0
- data/app/views/surveyor_gui/shared/_question_number.html.haml +9 -0
- data/app/views/surveyor_gui/shared/_report_data.html.haml +24 -0
- data/app/views/surveyor_gui/shared/_stars_report_data.html.haml +14 -0
- data/config.ru +4 -0
- data/config/environment.rb +0 -0
- data/config/routes.rb +71 -0
- data/db/migrate/20140307204049_add_template_to_surveys.rb +5 -0
- data/db/migrate/20140307235607_add_test_data_to_response_sets.rb +5 -0
- data/db/migrate/20140308171947_add_original_choice_to_answers.rb +5 -0
- data/db/migrate/20140308172118_add_blob_to_responses.rb +5 -0
- data/db/migrate/20140308172417_add_modifiable_to_survey_section.rb +5 -0
- data/db/migrate/20140308174532_add_modifiable_to_question.rb +5 -0
- data/db/migrate/20140308175305_add_dynamically_generate_to_questions.rb +5 -0
- data/db/migrate/20140311032923_add_dummy_blob_to_questions.rb +5 -0
- data/db/migrate/20140311160609_add_dynamic_source_to_questions.rb +5 -0
- data/db/migrate/20140311161714_add_report_code_to_questions.rb +5 -0
- data/db/migrate/20140530181134_add_is_comment_to_questions.rb +5 -0
- data/db/migrate/20140531012006_add_is_comment_to_answers.rb +5 -0
- data/db/migrate/20140531225529_create_rows.rb +9 -0
- data/db/migrate/20140601011151_create_columns.rb +11 -0
- data/db/migrate/20140602030330_add_column_id_to_answers.rb +5 -0
- data/db/migrate/20140603155606_add_column_id_to_responses.rb +5 -0
- data/db/migrate/20140606023527_add_column_id_to_dependency_conditions.rb +5 -0
- data/db/migrate/20140815165307_add_user_id_to_survey.rb +5 -0
- data/lib/assets/.gitkeep +0 -0
- data/lib/assets/images/addicon.png +0 -0
- data/lib/assets/images/datepicker.gif +0 -0
- data/lib/assets/images/delete.gif +0 -0
- data/lib/assets/images/delete.png +0 -0
- data/lib/assets/images/images/border.png +0 -0
- data/lib/assets/images/images/controls.png +0 -0
- data/lib/assets/images/images/loading.gif +0 -0
- data/lib/assets/images/images/loading_background.png +0 -0
- data/lib/assets/images/images/overlay.png +0 -0
- data/lib/assets/images/rails.png +0 -0
- data/lib/assets/images/star.gif +0 -0
- data/lib/assets/javascripts/.gitkeep +0 -0
- data/lib/assets/javascripts/surveyor_gui/jquery.MetaData.js +121 -0
- data/lib/assets/javascripts/surveyor_gui/jquery.blockUI.js +619 -0
- data/lib/assets/javascripts/surveyor_gui/jquery.rating.js +377 -0
- data/lib/assets/javascripts/surveyor_gui/jquery.validate.js +1188 -0
- data/lib/assets/javascripts/surveyor_gui/surveyor_add_ons.js +10 -0
- data/lib/assets/javascripts/surveyor_gui/surveyor_gui.js +1417 -0
- data/lib/assets/stylesheets/.gitkeep +0 -0
- data/lib/assets/stylesheets/surveyor_gui/jquery.rating.css +12 -0
- data/lib/assets/stylesheets/surveyor_gui/surveyor_add_ons.css +3 -0
- data/lib/assets/stylesheets/surveyor_gui/surveyor_gui.sass +650 -0
- data/lib/enumerable_extenders.rb +31 -0
- data/lib/generators/surveyor_gui/install_generator.rb +57 -0
- data/lib/generators/surveyor_gui/templates/app/assets/javascripts/surveyor_add_ons.js +2 -0
- data/lib/generators/surveyor_gui/templates/app/assets/javascripts/surveyor_gui_all.js +24 -0
- data/lib/generators/surveyor_gui/templates/app/assets/stylesheets/surveyor_add_ons.css.sass +1 -0
- data/lib/generators/surveyor_gui/templates/app/assets/stylesheets/surveyor_gui_all.css.sass +8 -0
- data/lib/generators/surveyor_gui/templates/app/helpers/surveyor_helper.rb +4 -0
- data/lib/generators/surveyor_gui/templates/app/models/response_set_user.rb +13 -0
- data/lib/generators/surveyor_gui/templates/app/views/layouts/surveyor_gui_default.html.erb +16 -0
- data/lib/generators/surveyor_gui/templates/config/initializers/simple_form.rb +140 -0
- data/lib/generators/surveyor_gui/templates/config/locales/en.yml +5 -0
- data/lib/surveyor_gui.rb +17 -0
- data/lib/surveyor_gui/engine.rb +30 -0
- data/lib/surveyor_gui/helpers/surveyor_helper_methods.rb +19 -0
- data/lib/surveyor_gui/models/answer_methods.rb +24 -0
- data/lib/surveyor_gui/models/column_methods.rb +43 -0
- data/lib/surveyor_gui/models/dependency_condition_methods.rb +53 -0
- data/lib/surveyor_gui/models/dependency_methods.rb +83 -0
- data/lib/surveyor_gui/models/question_and_group_shared_methods.rb +11 -0
- data/lib/surveyor_gui/models/question_group_methods.rb +55 -0
- data/lib/surveyor_gui/models/question_methods.rb +435 -0
- data/lib/surveyor_gui/models/question_type_methods.rb +493 -0
- data/lib/surveyor_gui/models/response_methods.rb +67 -0
- data/lib/surveyor_gui/models/response_set_methods.rb +54 -0
- data/lib/surveyor_gui/models/row_methods.rb +11 -0
- data/lib/surveyor_gui/models/survey_methods.rb +32 -0
- data/lib/surveyor_gui/models/survey_section_methods.rb +32 -0
- data/lib/surveyor_gui/surveyforms_controller_methods.rb +258 -0
- data/lib/surveyor_gui/version.rb +3 -0
- data/lib/tasks/.gitkeep +0 -0
- data/lib/tasks/surveyor_gui_tasks.rake +4 -0
- data/lib/templates/erb/scaffold/_form.html.erb +13 -0
- data/spec/controllers/surveyforms_controller_spec.rb +361 -0
- data/spec/controllers/surveyor_controller_spec.rb +303 -0
- data/spec/factories.rb +181 -0
- data/spec/features/create_survey_spec.rb +418 -0
- data/spec/features/dependencies_spec.rb +61 -0
- data/spec/features/rearrange_survey_spec.rb +118 -0
- data/spec/features/ui_spec.rb +469 -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 +116 -0
- data/spec/lib/common_spec.rb +37 -0
- data/spec/lib/parser_spec.rb +393 -0
- data/spec/lib/rake_kitchen_sink.rb +42 -0
- data/spec/lib/redcap_parser_spec.rb +129 -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 +452 -0
- data/spec/models/response_spec.rb +208 -0
- data/spec/models/survey_section_spec.rb +58 -0
- data/spec/models/survey_spec.rb +155 -0
- data/spec/models/surveyor_gui/question_spec.rb +60 -0
- data/spec/models/surveyor_gui/question_type_spec.rb +268 -0
- data/spec/models/validation_condition_spec.rb +98 -0
- data/spec/models/validation_spec.rb +64 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +119 -0
- data/spec/support/scenario_outline_helper.rb +39 -0
- data/spec/support/shared.rb +10 -0
- data/spec/support/surveyforms_creation_helpers.rb +312 -0
- data/spec/support/surveyforms_rearrangement_helpers.rb +170 -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/spec/views/questions/edit.html.erb_spec.rb +73 -0
- data/spec/views/surveyforms/edit.html.erb_spec.rb +126 -0
- data/surveyor_gui.gemspec +52 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.gitkeep +0 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/lib/assets/.gitkeep +0 -0
- data/test/dummy/log/.gitkeep +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/surveyor-gui_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- data/vendor/assets/stylesheets/.gitkeep +0 -0
- data/vendor/assets/stylesheets/custom.sass +5 -0
- data/vendor/plugins/.gitkeep +0 -0
- metadata +664 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module SurveyorGui
|
2
|
+
module Models
|
3
|
+
module DependencyConditionMethods
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send :attr_accessible, :dependency_id, :rule_key, :question_id, :operator, :answer_id,
|
7
|
+
:float_value, :integer_value, :join_operator, :column_id, :columns_attributes if defined? ActiveModel::MassAssignmentSecurity
|
8
|
+
base.send :belongs_to, :dependency
|
9
|
+
base.send :belongs_to, :column
|
10
|
+
base.send :default_scope, lambda{ base.order('rule_key') }
|
11
|
+
end
|
12
|
+
|
13
|
+
def join_operator
|
14
|
+
rule = Rules.new(self)
|
15
|
+
return rule.join_operator(self.rule_key)
|
16
|
+
end
|
17
|
+
|
18
|
+
def join_operator=(x)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Rules
|
23
|
+
|
24
|
+
attr_accessor :rules
|
25
|
+
|
26
|
+
def initialize(dependency_condition)
|
27
|
+
if dependency_condition.dependency
|
28
|
+
@rules = dependency_condition.dependency.rule.split(' ')
|
29
|
+
else
|
30
|
+
@rules = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def join_operator(rule_key)
|
35
|
+
_find_join_operator(_find_rule_key(rule_key))
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _find_rule_key(rule_key)
|
41
|
+
rules.index(rule_key)
|
42
|
+
end
|
43
|
+
|
44
|
+
def _find_join_operator(idx)
|
45
|
+
if idx && idx > 0
|
46
|
+
rules[idx-1]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module SurveyorGui
|
2
|
+
module Models
|
3
|
+
module DependencyMethods
|
4
|
+
def self.included(base)
|
5
|
+
# Associations
|
6
|
+
base.send :attr_accessible, :dependency_conditions_attributes if defined? ActiveModel::MassAssignmentSecurity
|
7
|
+
base.send :accepts_nested_attributes_for, :dependency_conditions, :reject_if => lambda { |d| d[:operator].blank?}, :allow_destroy => true
|
8
|
+
|
9
|
+
# # HACK: Remove the existing validates_numericality_of block. By default in Surveyor, it doesn't account for
|
10
|
+
# # question_id/question_group_id being nil when adding a new record - it never needed to. However, Surveyor_gui
|
11
|
+
# # adds accepts_nested_attributes_for dependency to the question model, which triggers the dependency validations
|
12
|
+
# # This means we do need to account for adding new records, and the validation has to be modified. Unfortunately,
|
13
|
+
# # in Rails 3.2 there is no easy way to modify an existing validation. We have to hack it out and replace it.
|
14
|
+
base.class_eval do
|
15
|
+
reset_callbacks(:validate)
|
16
|
+
|
17
|
+
def dependency_conditions_attributes=(dac)
|
18
|
+
dac = _set_rule_keys(dac)
|
19
|
+
assign_nested_attributes_for_collection_association(:dependency_conditions, dac)
|
20
|
+
_set_dependency_rule(dac)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
base.send :validates_numericality_of, :question_id, :if => Proc.new { |d| d.question_group_id.nil? && !d.new_record? }
|
26
|
+
base.send :validates_numericality_of, :question_group_id, :if => Proc.new { |d| d.question_id.nil? && !d.new_record?}
|
27
|
+
base.send :validates_presence_of, :rule
|
28
|
+
base.send :validates_format_of, :rule, :with => /\A(?:and|or|\)|\(|[A-Z]|\s)+\Z/ #TODO properly formed parenthesis etc.
|
29
|
+
|
30
|
+
# Attribute aliases
|
31
|
+
#base.send :alias_attribute, :dependent_question_id, :question_id
|
32
|
+
end
|
33
|
+
|
34
|
+
# need to overwrite the following methods because they rely on the nil? method. Actually, the parameter comes back as a string
|
35
|
+
# and even an empty string will not evaluate to nil. Changed to blank? instead.
|
36
|
+
def question_group_id=(i)
|
37
|
+
write_attribute(:question_id, nil) unless i.blank? #i.nil?
|
38
|
+
write_attribute(:question_group_id, i)
|
39
|
+
end
|
40
|
+
|
41
|
+
def question_id=(i)
|
42
|
+
write_attribute(:question_group_id, nil) unless i.blank? #i.nil?
|
43
|
+
write_attribute(:question_id, i)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def _set_rule_keys(dac)
|
49
|
+
dac_not_z = _remove_z_rules(dac)
|
50
|
+
dac_not_z.each_with_index do |(k, v), index|
|
51
|
+
v["rule_key"] = ("A".ord + index).chr
|
52
|
+
end
|
53
|
+
dac.merge(dac_not_z)
|
54
|
+
end
|
55
|
+
|
56
|
+
def _remove_z_rules(dac)
|
57
|
+
dac.reject{|k, v| v["rule_key"]=="Z"}
|
58
|
+
end
|
59
|
+
|
60
|
+
def _set_dependency_rule(dac)
|
61
|
+
rule = _derive_rule(dac)
|
62
|
+
write_attribute(:rule, rule)
|
63
|
+
end
|
64
|
+
|
65
|
+
def _derive_rule(dac)
|
66
|
+
rule = ''
|
67
|
+
dac.each_with_index do |(k, v), i|
|
68
|
+
rule += (i==0 ? '' : v["join_operator"] + ' ') + v["rule_key"] + ' '
|
69
|
+
end
|
70
|
+
rule.rstrip
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#class Dependency < ActiveRecord::Base
|
77
|
+
# include Surveyor::Models::DependencyMethods
|
78
|
+
#
|
79
|
+
# attr_accessible :question_id, :question_group, :rule, :dependency_conditions_attributes
|
80
|
+
# accepts_nested_attributes_for :dependency_conditions, :reject_if => lambda { |d| d[:operator].blank?}, :allow_destroy => true
|
81
|
+
# belongs_to :question
|
82
|
+
# validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|[0-9]+|\s)+$/ #TODO properly formed parenthesis etc.
|
83
|
+
#end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module SurveyorGui
|
2
|
+
module Models
|
3
|
+
module QuestionAndGroupSharedMethods
|
4
|
+
def controlling_questions
|
5
|
+
dependencies = []
|
6
|
+
dependencies << self.dependency
|
7
|
+
dependencies.map{|d| d.dependency_conditions.map{|dc| dc.question.part_of_group? ? dc.question.question_group.questions.last : dc.question}}.flatten.uniq
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module SurveyorGui
|
2
|
+
module Models
|
3
|
+
module QuestionGroupMethods
|
4
|
+
include QuestionAndGroupSharedMethods
|
5
|
+
def self.included(base)
|
6
|
+
base.send :attr_accessor, :is_mandatory, :survey_section_id
|
7
|
+
base.send :attr_writer, :question_id
|
8
|
+
base.send :attr_accessible, :questions_attributes if
|
9
|
+
defined? ActiveModel::MassAssignmentSecurity
|
10
|
+
base.send :accepts_nested_attributes_for, :questions, :allow_destroy => true
|
11
|
+
base.send :has_many, :columns
|
12
|
+
base.send :accepts_nested_attributes_for, :columns, :allow_destroy => true
|
13
|
+
base.send :accepts_nested_attributes_for, :dependency, :reject_if => lambda { |d| d[:rule].blank?}, :allow_destroy => true
|
14
|
+
end
|
15
|
+
|
16
|
+
def question_type_id
|
17
|
+
if !@question_type_id
|
18
|
+
@question_type = case display_type
|
19
|
+
when "inline"
|
20
|
+
:group_inline
|
21
|
+
when "default"
|
22
|
+
:group_default
|
23
|
+
when "repeater"
|
24
|
+
:repeater
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@question_type_id
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def question_type_id=(question_type_id)
|
32
|
+
case question_type_id
|
33
|
+
when "group_default"
|
34
|
+
write_attribute(:display_type, "default")
|
35
|
+
when "group_inline"
|
36
|
+
write_attribute(:display_type, "inline")
|
37
|
+
when "repeater"
|
38
|
+
write_attribute(:display_type, "repeater")
|
39
|
+
end
|
40
|
+
@question_type_id = question_type_id
|
41
|
+
end
|
42
|
+
|
43
|
+
def trim_columns(qty_to_trim)
|
44
|
+
columns = self.columns.order('id ASC')
|
45
|
+
columns.last(qty_to_trim).map{|c| c.destroy}
|
46
|
+
end
|
47
|
+
|
48
|
+
def question_id
|
49
|
+
self.questions.first.id if self.questions.first
|
50
|
+
end
|
51
|
+
|
52
|
+
#def controlling_questions in QuestionAndGroupSharedMethods
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,435 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
module SurveyorGui
|
3
|
+
module Models
|
4
|
+
module QuestionMethods
|
5
|
+
include QuestionAndGroupSharedMethods
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.send :attr_accessor, :dummy_answer, :dummy_answer_array, :type, :decimals
|
9
|
+
base.send :attr_writer, :grid_columns_textbox, :omit, :omit_text,
|
10
|
+
:other, :other_text, :comments_text, :comments, :dropdown_column_count
|
11
|
+
base.send :attr_accessible, :dummy_answer, :dummy_answer_array, :question_type, :question_type_id, :survey_section_id, :question_group_id,
|
12
|
+
:text, :pick, :reference_identifier, :display_order, :display_type,
|
13
|
+
:is_mandatory, :prefix, :suffix, :answers_attributes, :decimals, :dependency_attributes,
|
14
|
+
:hide_label, :dummy_blob, :dynamically_generate, :answers_textbox, :dropdown_column_count,
|
15
|
+
:grid_columns_textbox, :grid_rows_textbox, :omit_text, :omit, :other, :other_text, :is_comment, :comments, :comments_text,
|
16
|
+
:dynamic_source, :modifiable, :report_code, :question_group_attributes if
|
17
|
+
defined? ActiveModel::MassAssignmentSecurity
|
18
|
+
base.send :accepts_nested_attributes_for, :answers, :reject_if => lambda { |a| a[:text].blank?}, :allow_destroy => true
|
19
|
+
base.send :belongs_to, :survey_section
|
20
|
+
base.send :has_many, :responses
|
21
|
+
base.send :has_many, :dependency_conditions, :through=>:dependency, :dependent => :destroy
|
22
|
+
base.send :default_scope, lambda{ base.order('display_order')}
|
23
|
+
base.send :scope, :by_display_order, -> {base.order('display_order')}
|
24
|
+
### everything below this point must be commented out to run the rake tasks.
|
25
|
+
base.send :accepts_nested_attributes_for, :dependency, :reject_if => lambda { |d| d[:rule].blank?}, :allow_destroy => true
|
26
|
+
### everything below this point must be commented out to run the rake tasks.
|
27
|
+
|
28
|
+
base.send :mount_uploader, :dummy_blob, BlobUploader
|
29
|
+
base.send :belongs_to, :question_type
|
30
|
+
|
31
|
+
base.send :validate, :no_responses
|
32
|
+
base.send :before_destroy, :no_responses
|
33
|
+
base.send :after_save, :build_complex_questions
|
34
|
+
base.send :before_save, :make_room_for_question
|
35
|
+
|
36
|
+
base.send :scope, :is_not_comment, -> { base.where(is_comment: false) }
|
37
|
+
base.send :scope, :is_comment, -> { base.where(is_comment: true) }
|
38
|
+
|
39
|
+
base.class_eval do
|
40
|
+
|
41
|
+
def answers_attributes=(ans)
|
42
|
+
#don't set answer.text if question_type = number. In this case, text should get set by the prefix and suffix setters.
|
43
|
+
#note: Surveyor uses the answer.text field to store prefix and suffix for numbers.
|
44
|
+
#if not a number question, go ahead and set the text attribute as normal.
|
45
|
+
if @question_type_id!="number" && !ans.empty? && ans["0"]
|
46
|
+
ans["0"].merge!( {"original_choice"=>ans["0"]["text"]})
|
47
|
+
assign_nested_attributes_for_collection_association(:answers, ans)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_args
|
55
|
+
self.is_mandatory ||= false
|
56
|
+
self.display_type ||= "default"
|
57
|
+
self.pick ||= "none"
|
58
|
+
self.data_export_identifier ||= Surveyor::Common.normalize(text)
|
59
|
+
self.short_text ||= text
|
60
|
+
self.api_id ||= Surveyor::Common.generate_api_id
|
61
|
+
end
|
62
|
+
|
63
|
+
#prevent a question from being modified if responses have been submitted for the survey. Protects data integrity.
|
64
|
+
def no_responses
|
65
|
+
#below is code to fix a bizarre bug. When triggered by the "cut" function, for some reason survey_id is erased. Have not found reason yet. Temporary fix.
|
66
|
+
if !survey_section && self.id
|
67
|
+
self.reload
|
68
|
+
end
|
69
|
+
if self.id && self.survey_section && self.survey_section.survey
|
70
|
+
#this will be a problem if two people are editing the survey at the same time and do a survey preview - highly unlikely though.
|
71
|
+
self.survey_section.survey.response_sets.where('test_data = ?',true).each {|r| r.destroy}
|
72
|
+
end
|
73
|
+
if self.id && !survey_section.survey.template && survey_section.survey.response_sets.count>0
|
74
|
+
errors.add(:base,"Reponses have already been collected for this survey, therefore it cannot be modified. Please create a new survey instead.")
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def dynamically_generate
|
80
|
+
'false'
|
81
|
+
end
|
82
|
+
|
83
|
+
def question_type_id
|
84
|
+
QuestionType.categorize_question(self)
|
85
|
+
end
|
86
|
+
|
87
|
+
# #generates descriptions for different types of questions, including those that use widgets
|
88
|
+
def question_type
|
89
|
+
@question_type = QuestionType.find(question_type_id)
|
90
|
+
end
|
91
|
+
#
|
92
|
+
|
93
|
+
#setter for question type. Sets both pick and display_type
|
94
|
+
def question_type_id=(type)
|
95
|
+
case type
|
96
|
+
when "grid_one"
|
97
|
+
write_attribute(:pick, "one")
|
98
|
+
prep_picks
|
99
|
+
write_attribute(:display_type, "default")
|
100
|
+
_update_group_id
|
101
|
+
when "pick_one"
|
102
|
+
write_attribute(:pick, "one")
|
103
|
+
prep_picks
|
104
|
+
write_attribute(:display_type, "default")
|
105
|
+
_remove_group
|
106
|
+
when "slider"
|
107
|
+
write_attribute(:pick, "one")
|
108
|
+
prep_picks
|
109
|
+
write_attribute(:display_type, "slider")
|
110
|
+
when "stars"
|
111
|
+
write_attribute(:pick, "one")
|
112
|
+
write_attribute(:display_type, "stars")
|
113
|
+
prep_picks
|
114
|
+
when "dropdown"
|
115
|
+
write_attribute(:pick, "one")
|
116
|
+
write_attribute(:display_type, "dropdown")
|
117
|
+
prep_picks
|
118
|
+
when "pick_any"
|
119
|
+
write_attribute(:pick, "any")
|
120
|
+
prep_picks
|
121
|
+
write_attribute(:display_type, "default")
|
122
|
+
_remove_group
|
123
|
+
when "grid_any"
|
124
|
+
write_attribute(:pick, "any")
|
125
|
+
prep_picks
|
126
|
+
write_attribute(:display_type, "default")
|
127
|
+
_update_group_id
|
128
|
+
when "grid_dropdown"
|
129
|
+
write_attribute(:pick, "one")
|
130
|
+
prep_picks
|
131
|
+
write_attribute(:display_type, "dropdown")
|
132
|
+
_update_group_id
|
133
|
+
when "group_inline"
|
134
|
+
_update_group_id
|
135
|
+
when 'label'
|
136
|
+
write_attribute(:pick, "none")
|
137
|
+
write_attribute(:display_type, "label")
|
138
|
+
when 'box'
|
139
|
+
prep_not_picks('text')
|
140
|
+
when 'number'
|
141
|
+
prep_not_picks('float')
|
142
|
+
when 'date'
|
143
|
+
prep_not_picks('date')
|
144
|
+
when 'time'
|
145
|
+
prep_not_picks('time')
|
146
|
+
when 'datetime'
|
147
|
+
prep_not_picks('datetime')
|
148
|
+
when 'file'
|
149
|
+
prep_not_picks('blob')
|
150
|
+
when 'string'
|
151
|
+
prep_not_picks('string')
|
152
|
+
end
|
153
|
+
@question_type_id = type
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
#If the question involves picking from a list of choices, this sets response class.
|
158
|
+
def prep_picks
|
159
|
+
#write_attribute(:display_type, self.display_type || "default")
|
160
|
+
if self.display_type=='stars'
|
161
|
+
response_class='integer'
|
162
|
+
else
|
163
|
+
response_class='answer'
|
164
|
+
end
|
165
|
+
if self.answers.blank?
|
166
|
+
self.answers_attributes={'0'=>{'text'=>'default', 'response_class'=>response_class}}
|
167
|
+
else
|
168
|
+
self.answers.map{|a|a.response_class=response_class}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#if the question is not a pick from list of choices (but is a fill in the blank type question) and not multiple choice, this sets it
|
173
|
+
#accordingly.
|
174
|
+
def prep_not_picks(answer_type)
|
175
|
+
write_attribute(:pick, "none")
|
176
|
+
write_attribute(:display_type,"default")
|
177
|
+
if self.answers.blank?
|
178
|
+
#self.answers_attributes={'0'=>{'text'=>'default','response_class'=>answer_type, 'hide_label' => answer_type=='float' ? false : true}}
|
179
|
+
self.answers_attributes={'0'=>{'text'=>'default','response_class'=>answer_type, 'display_type' => answer_type=='float' ? 'default' : 'hidden_label'}}
|
180
|
+
else
|
181
|
+
self.answers.first.response_class=answer_type
|
182
|
+
#self.answers.first.hide_label = answer_type=='float' ? false : true
|
183
|
+
self.answers.first.display_type = answer_type=='float' ? 'default' : 'hidden_label'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
#number prefix getter. splits a number question into the actual answer and it's unit type. Eg, you might want a
|
188
|
+
#number to be prefixed with a dollar sign.
|
189
|
+
def prefix
|
190
|
+
if self.answers.first && self.answers.first.text.include?('|')
|
191
|
+
self.answers.first.text.split('|')[0]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
#number suffix getter. sometimes you want a number question to have a units of measure suffix, like "per day"
|
196
|
+
def suffix
|
197
|
+
if self.answers.first && self.answers.first.text.include?('|')
|
198
|
+
self.answers.first.text.split('|')[1]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
#sets the number prefix
|
203
|
+
def prefix=(pre)
|
204
|
+
if @question_type_id=="number"
|
205
|
+
if self.answers.blank?
|
206
|
+
self.answers_attributes={'0'=>{'text'=>pre+'|'}} unless pre.blank?
|
207
|
+
else
|
208
|
+
if pre.blank?
|
209
|
+
self.answers.first.text = 'default'
|
210
|
+
else
|
211
|
+
self.answers.first.text = pre+'|'
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
#sets the number suffix
|
218
|
+
def suffix=(suf)
|
219
|
+
if @question_type_id=="number"
|
220
|
+
if self.answers.first.blank? || self.answers.first.text.blank?
|
221
|
+
self.answers_attributes={'0'=>{'text'=>'|'+suf}} unless suf.blank?
|
222
|
+
else
|
223
|
+
if self.answers.first.text=='default'
|
224
|
+
self.answers.first.text='|'+suf
|
225
|
+
elsif self.answers.first.text.blank?
|
226
|
+
self.answers.first.text = '|'+suf
|
227
|
+
else
|
228
|
+
self.answers.first.text=self.answers.first.text+suf
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
def surveyresponse_class(response_sets)
|
236
|
+
if dependent?
|
237
|
+
response_sets.each do |r|
|
238
|
+
if triggered?(r)
|
239
|
+
return nil
|
240
|
+
end
|
241
|
+
end
|
242
|
+
return "q_hidden"
|
243
|
+
else
|
244
|
+
nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def question_description
|
249
|
+
## this is an expensive method - use sparingly
|
250
|
+
is_numbered? ? question_number.to_s + ') ' + text : text
|
251
|
+
end
|
252
|
+
|
253
|
+
def is_numbered?
|
254
|
+
case display_type
|
255
|
+
when 'label'
|
256
|
+
false
|
257
|
+
else
|
258
|
+
if part_of_group?
|
259
|
+
if question_group.questions.last.id == self.id
|
260
|
+
true
|
261
|
+
else
|
262
|
+
false
|
263
|
+
end
|
264
|
+
else
|
265
|
+
true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def question_number
|
271
|
+
##this is an expensive method - use sparingly
|
272
|
+
##should consider adding question_number attribute to table in future
|
273
|
+
if survey_section.id.nil?
|
274
|
+
nil
|
275
|
+
else
|
276
|
+
_preceding_questions_numbered.count
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
#def controlling_questions in QuestionAndGroupSharedMethods
|
282
|
+
|
283
|
+
def answers_textbox
|
284
|
+
self.answers.where('is_exclusive != ? and is_comment != ? and response_class != ?',true,true,"string").order('display_order asc').collect(&:text).join("\n")
|
285
|
+
end
|
286
|
+
|
287
|
+
def answers_textbox=(textbox)
|
288
|
+
#change updated_at as a hack to force dirty record for change on answers_textbox
|
289
|
+
write_attribute(:updated_at, Time.now)
|
290
|
+
@answers_textbox=textbox
|
291
|
+
end
|
292
|
+
|
293
|
+
def omit
|
294
|
+
@omit = self.answers.where('is_exclusive = ?',true).size > 0
|
295
|
+
end
|
296
|
+
|
297
|
+
def omit_text
|
298
|
+
answer = self.answers.where('is_exclusive = ?',true).first
|
299
|
+
@omit_text = (answer ? answer.text : "none of the above")
|
300
|
+
end
|
301
|
+
|
302
|
+
def other
|
303
|
+
@other = self.answers.where('response_class = ? and is_exclusive = ? and is_comment = ?',"string",false, false).size > 0
|
304
|
+
end
|
305
|
+
|
306
|
+
def other_text
|
307
|
+
answer = self.answers.where('response_class = ? and is_exclusive = ? and is_comment = ?',"string", false, false).first
|
308
|
+
@other_text = (answer ? answer.text : "Other")
|
309
|
+
end
|
310
|
+
|
311
|
+
def comments
|
312
|
+
if self.part_of_group?
|
313
|
+
@comments = self.question_group.questions.where('is_comment=?',true).size > 0
|
314
|
+
else
|
315
|
+
@comments = self.answers.where('is_comment=?',true).size > 0
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def comments_text
|
320
|
+
if self.part_of_group?
|
321
|
+
@comments_text = is_comment ? self.answers.first.text : "Comments"
|
322
|
+
else
|
323
|
+
answer = self.answers.where('is_comment=?',true).first
|
324
|
+
@comments_text = (answer ? answer.text : "Comments")
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def dropdown_column_count
|
329
|
+
@dropdown_column_count = @dropdown_column_count || (self.question_group ? self.question_group.columns.size : 1)
|
330
|
+
end
|
331
|
+
|
332
|
+
def grid_columns_textbox
|
333
|
+
self.answers.where('response_class != ? and is_exclusive = ?',"string",false).order('display_order asc').collect(&:text).join("\n")
|
334
|
+
end
|
335
|
+
|
336
|
+
def grid_rows_textbox
|
337
|
+
if self.question_group && self.question_group.questions
|
338
|
+
self.question_group.questions.where('is_comment=?',false).order('display_order asc').collect(&:text).join("\n")
|
339
|
+
else
|
340
|
+
nil
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def question_group_attributes=(params)
|
345
|
+
if question_group
|
346
|
+
question_group.update_attributes(params.except(:id))
|
347
|
+
@question_group_attributes=params
|
348
|
+
else
|
349
|
+
QuestionGroup.create!(params)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def text=(txt)
|
354
|
+
write_attribute(:text, txt)
|
355
|
+
if part_of_group? && question_group.display_type != "inline"
|
356
|
+
question_group.update_attributes(text: txt)
|
357
|
+
end
|
358
|
+
@text = txt
|
359
|
+
end
|
360
|
+
|
361
|
+
def grid_rows_textbox=(textbox)
|
362
|
+
write_attribute(:text, textbox.match(/.*\r*/).to_s.strip)
|
363
|
+
@grid_rows_textbox = textbox.gsub(/\r/,"")
|
364
|
+
end
|
365
|
+
|
366
|
+
def build_complex_questions
|
367
|
+
if (@answers_textbox && self.pick!="none") || @grid_columns_textbox || @grid_rows_textbox
|
368
|
+
self.question_type.build_complex_question_structure(
|
369
|
+
self,
|
370
|
+
answers_textbox: @answers_textbox,
|
371
|
+
omit_text: @omit_text,
|
372
|
+
is_exclusive: @omit=="1",
|
373
|
+
other_text: @other_text,
|
374
|
+
other: @other=="1",
|
375
|
+
comments_text: @comments_text,
|
376
|
+
comments: @comments=="1",
|
377
|
+
grid_columns_textbox: @grid_columns_textbox,
|
378
|
+
grid_rows_textbox: @grid_rows_textbox)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def next_display_order
|
383
|
+
if part_of_group?
|
384
|
+
self.question_group.questions.last.display_order + 1
|
385
|
+
else
|
386
|
+
display_order + 1
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def make_room_for_question
|
391
|
+
if new_record?
|
392
|
+
if Question.where('survey_section_id = ? and display_order = ?',survey_section_id, display_order).size > 0
|
393
|
+
Question.where(:survey_section_id => survey_section_id)
|
394
|
+
.where("display_order >= ?", display_order)
|
395
|
+
.update_all("display_order = display_order+1")
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def repeater?
|
401
|
+
part_of_group? ? (question_group.display_type=="repeater" ? true : false ) : false
|
402
|
+
end
|
403
|
+
|
404
|
+
private
|
405
|
+
|
406
|
+
def _update_group_id
|
407
|
+
@question_group = @question_group || self.question_group ||
|
408
|
+
QuestionGroup.create!(text: @text, display_type: :grid)
|
409
|
+
self.question_group_id = @question_group.id
|
410
|
+
end
|
411
|
+
|
412
|
+
def _remove_group
|
413
|
+
if part_of_group?
|
414
|
+
question_group.questions.map{|q| q.destroy if q.id != id}
|
415
|
+
write_attribute(:question_group_id, nil)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
def _preceding_questions_numbered
|
420
|
+
_preceding_questions.to_a.delete_if{|p| !p.is_numbered?}
|
421
|
+
end
|
422
|
+
|
423
|
+
def _preceding_questions
|
424
|
+
##all questions from previous sections, plus all questions with a lower display order than this one
|
425
|
+
Question.joins(:survey_section).where(
|
426
|
+
'(survey_id = ? and survey_sections.display_order < ?) or (survey_section_id = ? and questions.display_order <= ?)',
|
427
|
+
survey_section.survey_id,
|
428
|
+
survey_section.display_order,
|
429
|
+
survey_section.id,
|
430
|
+
display_order
|
431
|
+
)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|