surveyor_gui 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|