hssc_surveyor 1.4.1.pre
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.
- data/.gitignore +28 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +198 -0
- data/Gemfile +5 -0
- data/Gemfile.rails_version +19 -0
- data/MIT-LICENSE +20 -0
- data/README.md +186 -0
- data/Rakefile +105 -0
- data/app/controllers/surveyor_controller.rb +6 -0
- data/app/helpers/results_helper.rb +20 -0
- data/app/helpers/survey_form_builder.rb +37 -0
- data/app/helpers/surveyor_helper.rb +3 -0
- data/app/inputs/quiet_input.rb +5 -0
- data/app/inputs/surveyor_check_boxes_input.rb +35 -0
- data/app/inputs/surveyor_radio_input.rb +18 -0
- data/app/models/answer.rb +4 -0
- data/app/models/dependency.rb +4 -0
- data/app/models/dependency_condition.rb +4 -0
- data/app/models/question.rb +4 -0
- data/app/models/question_group.rb +5 -0
- data/app/models/response.rb +5 -0
- data/app/models/response_set.rb +4 -0
- data/app/models/survey.rb +4 -0
- data/app/models/survey_section.rb +5 -0
- data/app/models/survey_section_sweeper.rb +15 -0
- data/app/models/survey_translation.rb +5 -0
- data/app/models/validation.rb +4 -0
- data/app/models/validation_condition.rb +4 -0
- data/app/views/layouts/results.html.erb +13 -0
- data/app/views/layouts/surveyor_default.html.erb +12 -0
- data/app/views/partials/_answer.html.haml +25 -0
- data/app/views/partials/_dependents.html.haml +5 -0
- data/app/views/partials/_question.html.haml +41 -0
- data/app/views/partials/_question_group.html.haml +44 -0
- data/app/views/partials/_section.html.haml +12 -0
- data/app/views/partials/_section_menu.html.haml +12 -0
- data/app/views/surveyor/edit.html.haml +24 -0
- data/app/views/surveyor/export.json.rabl +85 -0
- data/app/views/surveyor/new.html.haml +24 -0
- data/app/views/surveyor/show.html.haml +74 -0
- data/app/views/surveyor/show.json.rabl +15 -0
- data/ci-exec.sh +56 -0
- data/config/routes.rb +8 -0
- data/cucumber.yml +10 -0
- data/doc/REPRESENTATIONS.md +34 -0
- data/doc/api_id_schema.json +7 -0
- data/doc/question types.png +0 -0
- data/doc/response_set_schema.json +54 -0
- data/doc/surveyor question combinations.png +0 -0
- data/doc/surveyor reject or delete decision matrix.png +0 -0
- data/doc/surveyor_models.png +0 -0
- data/doc/surveyor_models2.png +0 -0
- data/doc/surveyor_timestamp_schema.json +9 -0
- data/features/ajax_submissions.feature +140 -0
- data/features/export_to_json.feature +344 -0
- data/features/internationalization.feature +121 -0
- data/features/no_duplicates.feature +110 -0
- data/features/show_survey.feature +71 -0
- data/features/step_definitions/parser_steps.rb +145 -0
- data/features/step_definitions/surveyor_steps.rb +325 -0
- data/features/step_definitions/ui_steps.rb +25 -0
- data/features/step_definitions/web_steps.rb +225 -0
- data/features/support/REDCapDemoDatabase_DataDictionary.csv +127 -0
- data/features/support/database_cleaner.rb +16 -0
- data/features/support/env.rb +56 -0
- data/features/support/hooks.rb +4 -0
- data/features/support/paths.rb +39 -0
- data/features/support/redcap_new_headers.csv +1 -0
- data/features/support/redcap_siblings.csv +1 -0
- data/features/support/redcap_whitespace.csv +1 -0
- data/features/support/selectors.rb +39 -0
- 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 +895 -0
- data/features/surveyor_dependencies.feature +476 -0
- data/features/surveyor_parser.feature +504 -0
- data/features/z_redcap_parser.feature +62 -0
- data/lib/assets/images/surveyor/next.gif +0 -0
- data/lib/assets/images/surveyor/prev.gif +0 -0
- data/lib/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/assets/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/lib/assets/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/assets/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/assets/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/assets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/lib/assets/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/assets/images/ui-icons_222222_256x240.png +0 -0
- data/lib/assets/images/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/assets/images/ui-icons_454545_256x240.png +0 -0
- data/lib/assets/images/ui-icons_888888_256x240.png +0 -0
- data/lib/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/assets/javascripts/surveyor/jquery-1.9.0.js +9555 -0
- data/lib/assets/javascripts/surveyor/jquery-ui-1.10.0.custom.js +14850 -0
- data/lib/assets/javascripts/surveyor/jquery-ui-timepicker-addon.js +1919 -0
- data/lib/assets/javascripts/surveyor/jquery.maskedinput.js +338 -0
- data/lib/assets/javascripts/surveyor/jquery.selectToUISlider.js +240 -0
- data/lib/assets/javascripts/surveyor/jquery.surveyor.js +156 -0
- data/lib/assets/stylesheets/surveyor/jquery-ui-1.10.0.custom.css +1174 -0
- data/lib/assets/stylesheets/surveyor/jquery-ui-timepicker-addon.css +11 -0
- data/lib/assets/stylesheets/surveyor/reset.css +48 -0
- data/lib/assets/stylesheets/surveyor/results.css +125 -0
- data/lib/assets/stylesheets/surveyor/ui.slider.extras.css +110 -0
- data/lib/assets/stylesheets/surveyor.sass +132 -0
- data/lib/generators/surveyor/custom_generator.rb +18 -0
- data/lib/generators/surveyor/install_generator.rb +102 -0
- data/lib/generators/surveyor/templates/app/assets/javascripts/surveyor_all.js +6 -0
- data/lib/generators/surveyor/templates/app/assets/stylesheets/surveyor_all.css +9 -0
- data/lib/generators/surveyor/templates/app/controllers/surveyor_controller.rb +40 -0
- data/lib/generators/surveyor/templates/app/views/layouts/surveyor_custom.html.erb +13 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_en.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_es.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_he.yml +19 -0
- data/lib/generators/surveyor/templates/config/locales/surveyor_ko.yml +19 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_id_to_question_groups.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids.rb +14 -0
- data/lib/generators/surveyor/templates/db/migrate/add_api_ids_to_response_sets_and_responses.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_attachment_to_response.rb +5 -0
- data/lib/generators/surveyor/templates/db/migrate/add_correct_answer_id_to_questions.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_default_value_to_answers.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_order_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_display_type_to_answers.rb +14 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_response_sets.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_index_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_input_mask_attributes_to_answer.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_section_id_to_responses.rb +12 -0
- data/lib/generators/surveyor/templates/db/migrate/add_unique_index_on_access_code_and_version_in_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/add_unique_indicies.rb +18 -0
- data/lib/generators/surveyor/templates/db/migrate/add_version_to_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/api_ids_must_be_unique.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_answers.rb +38 -0
- data/lib/generators/surveyor/templates/db/migrate/create_dependencies.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_dependency_conditions.rb +30 -0
- data/lib/generators/surveyor/templates/db/migrate/create_question_groups.rb +28 -0
- data/lib/generators/surveyor/templates/db/migrate/create_questions.rb +37 -0
- data/lib/generators/surveyor/templates/db/migrate/create_response_sets.rb +23 -0
- data/lib/generators/surveyor/templates/db/migrate/create_responses.rb +34 -0
- data/lib/generators/surveyor/templates/db/migrate/create_survey_sections.rb +30 -0
- data/lib/generators/surveyor/templates/db/migrate/create_survey_translations.rb +19 -0
- data/lib/generators/surveyor/templates/db/migrate/create_surveys.rb +32 -0
- data/lib/generators/surveyor/templates/db/migrate/create_validation_conditions.rb +33 -0
- data/lib/generators/surveyor/templates/db/migrate/create_validations.rb +21 -0
- data/lib/generators/surveyor/templates/db/migrate/drop_unique_index_on_access_code_in_surveys.rb +10 -0
- data/lib/generators/surveyor/templates/db/migrate/update_blank_api_ids_on_question_group.rb +22 -0
- data/lib/generators/surveyor/templates/db/migrate/update_blank_versions_on_surveys.rb +13 -0
- data/lib/generators/surveyor/templates/surveys/EXTENDING_SURVEYOR.md +52 -0
- data/lib/generators/surveyor/templates/surveys/MODIFYING_SURVEYOR.md +91 -0
- data/lib/generators/surveyor/templates/surveys/date_survey.rb +16 -0
- data/lib/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +284 -0
- data/lib/generators/surveyor/templates/surveys/languages.rb +14 -0
- data/lib/generators/surveyor/templates/surveys/quiz.rb +11 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.es.yml +18 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.he.yml +18 -0
- data/lib/generators/surveyor/templates/surveys/translations/languages.ko.yml +18 -0
- data/lib/generators/surveyor/templates/vendor/assets/stylesheets/custom.sass +5 -0
- data/lib/surveyor/acts_as_response.rb +17 -0
- data/lib/surveyor/common.rb +59 -0
- data/lib/surveyor/engine.rb +10 -0
- data/lib/surveyor/helpers/asset_pipeline.rb +13 -0
- data/lib/surveyor/helpers/formtastic_custom_input.rb +13 -0
- data/lib/surveyor/helpers/surveyor_helper_methods.rb +103 -0
- data/lib/surveyor/models/answer_methods.rb +86 -0
- data/lib/surveyor/models/dependency_condition_methods.rb +72 -0
- data/lib/surveyor/models/dependency_methods.rb +60 -0
- data/lib/surveyor/models/question_group_methods.rb +61 -0
- data/lib/surveyor/models/question_methods.rb +115 -0
- data/lib/surveyor/models/response_methods.rb +132 -0
- data/lib/surveyor/models/response_set_methods.rb +196 -0
- data/lib/surveyor/models/survey_methods.rb +103 -0
- data/lib/surveyor/models/survey_section_methods.rb +56 -0
- data/lib/surveyor/models/survey_translation_methods.rb +33 -0
- data/lib/surveyor/models/validation_condition_methods.rb +56 -0
- data/lib/surveyor/models/validation_methods.rb +48 -0
- data/lib/surveyor/mustache_context.rb +11 -0
- data/lib/surveyor/parser.rb +428 -0
- data/lib/surveyor/redcap_parser.rb +289 -0
- data/lib/surveyor/surveyor_controller_methods.rb +237 -0
- data/lib/surveyor/unparser.rb +147 -0
- data/lib/surveyor/version.rb +3 -0
- data/lib/surveyor.rb +13 -0
- data/lib/tasks/surveyor_tasks.rake +88 -0
- data/rails/init.rb +1 -0
- data/spec/controllers/surveyor_controller_spec.rb +307 -0
- data/spec/factories.rb +161 -0
- data/spec/helpers/formtastic_custom_input_spec.rb +15 -0
- data/spec/helpers/surveyor_helper_spec.rb +118 -0
- data/spec/lib/benchmark_spec.rb +22 -0
- data/spec/lib/chinese_survey.rb +14 -0
- data/spec/lib/common_spec.rb +34 -0
- data/spec/lib/parser_spec.rb +204 -0
- data/spec/lib/rake_kitchen_sink.rb +40 -0
- data/spec/lib/redcap_parser_spec.rb +75 -0
- data/spec/lib/tasks_spec.rake +26 -0
- data/spec/lib/unparser_spec.rb +126 -0
- data/spec/models/answer_spec.rb +175 -0
- data/spec/models/dependency_condition_spec.rb +439 -0
- data/spec/models/dependency_spec.rb +101 -0
- data/spec/models/question_group_spec.rb +93 -0
- data/spec/models/question_spec.rb +207 -0
- data/spec/models/response_set_spec.rb +477 -0
- data/spec/models/response_spec.rb +218 -0
- data/spec/models/survey_section_spec.rb +85 -0
- data/spec/models/survey_spec.rb +191 -0
- data/spec/models/validation_condition_spec.rb +113 -0
- data/spec/models/validation_spec.rb +74 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +56 -0
- data/stacktests.sh +65 -0
- data/surveyor.gemspec +45 -0
- metadata +657 -0
@@ -0,0 +1,289 @@
|
|
1
|
+
%w(survey survey_section question_group question dependency dependency_condition answer validation validation_condition).each {|model| require model }
|
2
|
+
require 'active_support' # for humanize
|
3
|
+
module Surveyor
|
4
|
+
class RedcapParserError < StandardError; end
|
5
|
+
class RedcapParser
|
6
|
+
class << self; attr_accessor :options end
|
7
|
+
|
8
|
+
# Attributes
|
9
|
+
attr_accessor :context
|
10
|
+
|
11
|
+
# Class methods
|
12
|
+
def self.parse(str, filename, options={})
|
13
|
+
self.options = options
|
14
|
+
Surveyor::RedcapParser.rake_trace "\n"
|
15
|
+
Surveyor::RedcapParser.new.parse(str, filename)
|
16
|
+
Surveyor::RedcapParser.rake_trace "\n"
|
17
|
+
end
|
18
|
+
def self.rake_trace(str)
|
19
|
+
self.options ||= {}
|
20
|
+
print str if self.options[:trace] == true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Instance methods
|
24
|
+
def initialize
|
25
|
+
self.context = {}
|
26
|
+
self.context[:dependency_conditions] = []
|
27
|
+
end
|
28
|
+
def parse(str, filename)
|
29
|
+
csvlib = Surveyor::Common.csv_impl
|
30
|
+
begin
|
31
|
+
csvlib.parse(str, :headers => :first_row, :return_headers => true, :header_converters => :symbol) do |r|
|
32
|
+
if r.header_row? # header row
|
33
|
+
return Surveyor::RedcapParser.rake_trace "Missing headers: #{missing_columns(r.headers).inspect}\n\n" unless missing_columns(r.headers).blank?
|
34
|
+
context[:survey] = Survey.new(:title => filename)
|
35
|
+
Surveyor::RedcapParser.rake_trace "survey_#{context[:survey].access_code} "
|
36
|
+
else # non-header rows
|
37
|
+
SurveySection.new.extend(SurveyorRedcapParserSurveySectionMethods).build_or_set(context, r)
|
38
|
+
Question.new.extend(SurveyorRedcapParserQuestionMethods).build_and_set(context, r)
|
39
|
+
Answer.new.extend(SurveyorRedcapParserAnswerMethods).build_and_set(context, r)
|
40
|
+
Validation.new.extend(SurveyorRedcapParserValidationMethods).build_and_set(context, r)
|
41
|
+
Dependency.new.extend(SurveyorRedcapParserDependencyMethods).build_and_set(context, r)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
resolve_references
|
45
|
+
Surveyor::RedcapParser.rake_trace context[:survey].save ? "saved. " : " not saved! #{context[:survey].errors.full_messages.join(", ")} "
|
46
|
+
# Surveyor::RedcapParser.rake_trace context[:survey].sections.map(&:questions).flatten.map(&:answers).flatten.map{|x| x.errors.each_full{|y| y}.join}.join
|
47
|
+
rescue csvlib::MalformedCSVError
|
48
|
+
raise Surveyor::RedcapParserError, "Oops. Not a valid CSV file."
|
49
|
+
# ensure
|
50
|
+
end
|
51
|
+
return context[:survey]
|
52
|
+
end
|
53
|
+
def missing_columns(r)
|
54
|
+
missing = []
|
55
|
+
missing << "choices_or_calculations" unless r.map(&:to_s).include?("choices_or_calculations") or r.map(&:to_s).include?("choices_calculations_or_slider_labels")
|
56
|
+
missing << "text_validation_type" unless r.map(&:to_s).include?("text_validation_type") or r.map(&:to_s).include?("text_validation_type_or_show_slider_number")
|
57
|
+
missing += (static_required_columns - r.map(&:to_s))
|
58
|
+
end
|
59
|
+
def static_required_columns
|
60
|
+
# no longer requiring field_units
|
61
|
+
%w(variable__field_name form_name section_header field_type field_label field_note text_validation_min text_validation_max identifier branching_logic_show_field_only_if required_field)
|
62
|
+
end
|
63
|
+
def resolve_references
|
64
|
+
context[:dependency_conditions].each do |dc|
|
65
|
+
Surveyor::RedcapParser.rake_trace "resolve(#{dc.question_reference},#{dc.answer_reference})"
|
66
|
+
if dc.answer_reference.blank? and (context[:question_references][dc.question_reference].answers.size == 1)
|
67
|
+
Surveyor::RedcapParser.rake_trace "...found "
|
68
|
+
dc.question = context[:question_references][dc.question_reference]
|
69
|
+
dc.answer = dc.question.answers.first
|
70
|
+
elsif answer = context[:answer_references][dc.question_reference][dc.answer_reference]
|
71
|
+
Surveyor::RedcapParser.rake_trace "...found "
|
72
|
+
dc.answer = answer
|
73
|
+
dc.question = context[:question_references][dc.question_reference]
|
74
|
+
else
|
75
|
+
Surveyor::RedcapParser.rake_trace "\n!!! failed lookup for dependency_condition q: #{question_reference} a: #{question_reference}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Surveyor models with extra parsing methods
|
83
|
+
|
84
|
+
# SurveySection model
|
85
|
+
module SurveyorRedcapParserSurveySectionMethods
|
86
|
+
def build_or_set(context, r)
|
87
|
+
unless context[:survey_section] && context[:survey_section].reference_identifier == r[:form_name]
|
88
|
+
if match = context[:survey].sections.detect{|ss| ss.reference_identifier == r[:form_name]}
|
89
|
+
context[:current_survey_section] = match
|
90
|
+
else
|
91
|
+
self.attributes = (
|
92
|
+
{:title => r[:form_name].to_s.humanize,
|
93
|
+
:reference_identifier => r[:form_name],
|
94
|
+
:display_order => context[:survey].sections.size })
|
95
|
+
context[:survey].sections << context[:survey_section] = self
|
96
|
+
Surveyor::RedcapParser.rake_trace "survey_section_#{context[:survey_section].reference_identifier} "
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Question model
|
103
|
+
module SurveyorRedcapParserQuestionMethods
|
104
|
+
def build_and_set(context, r)
|
105
|
+
if !r[:section_header].blank?
|
106
|
+
context[:survey_section].questions.build({:display_type => "label", :text => r[:section_header], :display_order => context[:survey_section].questions.size})
|
107
|
+
Surveyor::RedcapParser.rake_trace "label_ "
|
108
|
+
end
|
109
|
+
self.attributes = ({
|
110
|
+
:reference_identifier => r[:variable__field_name],
|
111
|
+
:text => r[:field_label],
|
112
|
+
:help_text => r[:field_note],
|
113
|
+
:is_mandatory => (/^y/i.match r[:required_field]) ? true : false,
|
114
|
+
:pick => pick_from_field_type(r[:field_type]),
|
115
|
+
:display_type => display_type_from_field_type(r[:field_type]),
|
116
|
+
:display_order => context[:survey_section].questions.size
|
117
|
+
})
|
118
|
+
context[:survey_section].questions << context[:question] = self
|
119
|
+
unless context[:question].reference_identifier.blank?
|
120
|
+
context[:question_references] ||= {}
|
121
|
+
context[:question_references][context[:question].reference_identifier] = context[:question]
|
122
|
+
end
|
123
|
+
Surveyor::RedcapParser.rake_trace "question_#{context[:question].reference_identifier} "
|
124
|
+
end
|
125
|
+
def pick_from_field_type(ft)
|
126
|
+
{"checkbox" => :any, "radio" => :one}[ft] || :none
|
127
|
+
end
|
128
|
+
def display_type_from_field_type(ft)
|
129
|
+
{"text" => :string, "dropdown" => :dropdown, "notes" => :text}[ft]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Dependency model
|
134
|
+
module SurveyorRedcapParserDependencyMethods
|
135
|
+
def build_and_set(context, r)
|
136
|
+
unless (bl = r[:branching_logic_show_field_only_if]).blank?
|
137
|
+
# TODO: forgot to tie rule key to component, counting on the sequence of components
|
138
|
+
letters = ('A'..'Z').to_a
|
139
|
+
hash = decompose_rule(bl)
|
140
|
+
self.attributes = {:rule => hash[:rule]}
|
141
|
+
context[:question].dependency = context[:dependency] = self
|
142
|
+
hash[:components].each do |component|
|
143
|
+
dc = context[:dependency].dependency_conditions.build(decompose_component(component).merge({ :rule_key => letters.shift } ))
|
144
|
+
context[:dependency_conditions] << dc
|
145
|
+
end
|
146
|
+
Surveyor::RedcapParser.rake_trace "dependency(#{hash[:rule]}) "
|
147
|
+
end
|
148
|
+
end
|
149
|
+
def decompose_component(str)
|
150
|
+
# [initial_52] = "1" or [f1_q15] = '' or [f1_q15] = '-2' or [hi_event1_type] <> ''
|
151
|
+
if match = str.match(/^\[(\w+)\] ?([!=><]+) ?['"](-?\w*)['"]$/)
|
152
|
+
{:question_reference => match[1], :operator => match[2].gsub(/^=$/, "==").gsub(/^<>$/, "!="), :answer_reference => match[3]}
|
153
|
+
# [initial_119(2)] = "1" or [hiprep_heat2(97)] = '1'
|
154
|
+
elsif match = str.match(/^\[(\w+)\((\w+)\)\] ?([!=><]+) ?['"]1['"]$/)
|
155
|
+
{:question_reference => match[1], :operator => match[3].gsub(/^=$/, "==").gsub(/^<>$/, "!="), :answer_reference => match[2]}
|
156
|
+
# [f1_q15] >= 21 or [f1_q15] >= -21
|
157
|
+
elsif match = str.match(/^\[(\w+)\] ?([!=><]+) ?(-?\d+)$/)
|
158
|
+
{:question_reference => match[1], :operator => match[2].gsub(/^=$/, "==").gsub(/^<>$/, "!="), :integer_value => match[3]}
|
159
|
+
else
|
160
|
+
Surveyor::RedcapParser.rake_trace "\n!!! skipping dependency_condition #{str}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
def decompose_rule(str)
|
164
|
+
# see spec/lib/redcap_parser_spec.rb for examples
|
165
|
+
letters = ('A'..'Z').to_a
|
166
|
+
rule = str
|
167
|
+
components = str.split(/\band\b|\bor\b|\((?!\d)|\)(?!\(|\])/).reject(&:blank?).map(&:strip)
|
168
|
+
components.each_with_index do |part, i|
|
169
|
+
# internal commas on the right side of the operator e.g. '[initial_189] = "1, 2, 3"'
|
170
|
+
if match = part.match(/^(\[[^\]]+\][^\"]+)"([0-9 ]+,[0-9 ,]+)"$/)
|
171
|
+
nums = match[2].split(",").map(&:strip)
|
172
|
+
components[i] = nums.map{|x| "#{match[1]}\"#{x}\""}
|
173
|
+
# sub in rule key
|
174
|
+
rule = rule.gsub(part, "(#{nums.map{letters.shift}.join(' and ')})")
|
175
|
+
# multiple internal parenthesis on the left e.g. '[initial_119(1)(2)(3)(4)(6)] = "1"'
|
176
|
+
elsif match = part.match(/^\[(\w+)(\(\d+\)\([\d\(\)]+)\]([^\"]+"\d+")$/)
|
177
|
+
nums = match[2].split(/\(|\)/).reject(&:blank?).map(&:strip)
|
178
|
+
components[i] = nums.map{|x| "[#{match[1]}(#{x})]#{match[3]}"}
|
179
|
+
# sub in rule key
|
180
|
+
rule = rule.gsub(part, "(#{nums.map{letters.shift}.join(' and ')})")
|
181
|
+
else
|
182
|
+
# 'or' on the right of the operator
|
183
|
+
components[i] = components[i-1].gsub(/"(\d+)"/, part) if part.match(/^"(\d+)"$/) && i != 0
|
184
|
+
# sub in rule key
|
185
|
+
rule = rule.gsub(part){letters.shift}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
{:rule => rule, :components => components.flatten}
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# DependencyCondition model
|
193
|
+
module SurveyorRedcapParserDependencyConditionMethods
|
194
|
+
DependencyCondition.instance_eval do
|
195
|
+
attr_accessor :question_reference, :answer_reference
|
196
|
+
attr_accessible :question_reference, :answer_reference
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Answer model
|
201
|
+
module SurveyorRedcapParserAnswerMethods
|
202
|
+
def build_and_set(context, r)
|
203
|
+
case r[:field_type]
|
204
|
+
when "text"
|
205
|
+
self.attributes = {
|
206
|
+
:response_class => "string",
|
207
|
+
:text => "Text",
|
208
|
+
:display_order => context[:question].answers.size }
|
209
|
+
context[:question].answers << context[:answer] = self
|
210
|
+
when "notes"
|
211
|
+
self.attributes = {
|
212
|
+
:response_class => "text",
|
213
|
+
:text => "Notes",
|
214
|
+
:display_order => context[:question].answers.size }
|
215
|
+
context[:question].answers << context[:answer] = self
|
216
|
+
when "file"
|
217
|
+
Surveyor::RedcapParser.rake_trace "\n!!! skipping answer: file"
|
218
|
+
end
|
219
|
+
(r[:choices_or_calculations] || r[:choices_calculations_or_slider_labels]).to_s.split("|").each do |pair|
|
220
|
+
aref, atext = pair.split(",").map(&:strip)
|
221
|
+
if aref.blank? or atext.blank? or (aref.to_i.to_s != aref)
|
222
|
+
Surveyor::RedcapParser.rake_trace "\n!!! skipping answer #{pair}"
|
223
|
+
else
|
224
|
+
a = Answer.new({
|
225
|
+
:reference_identifier => aref,
|
226
|
+
:text => atext,
|
227
|
+
:display_order => context[:question].answers.size })
|
228
|
+
context[:question].answers << context[:answer] = a
|
229
|
+
unless context[:question].reference_identifier.blank? or aref.blank? or !context[:answer].valid?
|
230
|
+
context[:answer_references] ||= {}
|
231
|
+
context[:answer_references][context[:question].reference_identifier] ||= {}
|
232
|
+
context[:answer_references][context[:question].reference_identifier][aref] = context[:answer]
|
233
|
+
end
|
234
|
+
Surveyor::RedcapParser.rake_trace "#{context[:answer].errors.full_messages}, #{context[:answer].inspect}" unless context[:answer].valid?
|
235
|
+
Surveyor::RedcapParser.rake_trace "answer_#{context[:answer].reference_identifier} "
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# Validation model
|
242
|
+
module SurveyorRedcapParserValidationMethods
|
243
|
+
def build_and_set(context, r)
|
244
|
+
# text_validation_type text_validation_min text_validation_max
|
245
|
+
min = r[:text_validation_min].to_s.blank? ? nil : r[:text_validation_min].to_s
|
246
|
+
max = r[:text_validation_max].to_s.blank? ? nil : r[:text_validation_max].to_s
|
247
|
+
type = r[:text_validation_type].to_s.blank? ? nil : r[:text_validation_type].to_s
|
248
|
+
if min or max
|
249
|
+
context[:question].answers.each do |a|
|
250
|
+
self.rule = (min ? max ? "A and B" : "A" : "B")
|
251
|
+
a.validations << context[:validation] = self
|
252
|
+
context[:validation].validation_conditions.build(:rule_key => "A", :operator => ">=", :integer_value => min) if min
|
253
|
+
context[:validation].validation_conditions.build(:rule_key => "B", :operator => "<=", :integer_value => max) if max
|
254
|
+
end
|
255
|
+
elsif type
|
256
|
+
# date email integer number phone
|
257
|
+
case r[:text_validation_type]
|
258
|
+
when "date"
|
259
|
+
context[:question].display_type = :date if context[:question].display_type == :string
|
260
|
+
when "email"
|
261
|
+
context[:question].answers.each do |a|
|
262
|
+
self.rule = "A"
|
263
|
+
a.validations << context[:validation] = self
|
264
|
+
context[:validation].validation_conditions.build(:rule_key => "A", :operator => "=~", :regexp => "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$")
|
265
|
+
end
|
266
|
+
when "integer"
|
267
|
+
context[:question].display_type = :integer if context[:question].display_type == :string
|
268
|
+
context[:question].answers.each do |a|
|
269
|
+
self.rule = "A"
|
270
|
+
a.validations << context[:validation] = self
|
271
|
+
context[:validation].validation_conditions.build(:rule_key => "A", :operator => "=~", :regexp => "\d+")
|
272
|
+
end
|
273
|
+
when "number"
|
274
|
+
context[:question].display_type = :float if context[:question].display_type == :string
|
275
|
+
context[:question].answers.each do |a|
|
276
|
+
self.rule = "A"
|
277
|
+
a.validations << context[:validation] = self
|
278
|
+
context[:validation].validation_conditions.build(:rule_key => "A", :operator => "=~", :regexp => "^\d*(,\d{3})*(\.\d*)?$")
|
279
|
+
end
|
280
|
+
when "phone"
|
281
|
+
context[:question].answers.each do |a|
|
282
|
+
self.rule = "A"
|
283
|
+
a.validations << context[:validation] = self
|
284
|
+
context[:validation].validation_conditions.build(:rule_key => "A", :operator => "=~", :regexp => "\d{3}.*\d{4}")
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'rabl'
|
2
|
+
Rabl.register!
|
3
|
+
Rabl.configure {|config| config.include_child_root = false }
|
4
|
+
Rabl.configure {|config| config.include_json_root = false }
|
5
|
+
module Surveyor
|
6
|
+
module SurveyorControllerMethods
|
7
|
+
def self.included(base)
|
8
|
+
base.send :before_filter, :get_current_user, :only => [:new, :create]
|
9
|
+
base.send :before_filter, :determine_if_javascript_is_enabled, :only => [:create, :update]
|
10
|
+
base.send :before_filter, :set_response_set_and_render_context, :only => [:edit, :show]
|
11
|
+
base.send :layout, 'surveyor_default'
|
12
|
+
base.send :before_filter, :set_locale
|
13
|
+
end
|
14
|
+
|
15
|
+
# Actions
|
16
|
+
def new
|
17
|
+
@surveys_by_access_code = Survey.order("created_at DESC, survey_version DESC").all.group_by(&:access_code)
|
18
|
+
redirect_to surveyor_index unless surveyor_index == surveyor.available_surveys_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
surveys = Survey.where(:access_code => params[:survey_code]).order("survey_version DESC")
|
23
|
+
if params[:survey_version].blank?
|
24
|
+
@survey = surveys.first
|
25
|
+
else
|
26
|
+
@survey = surveys.where(:survey_version => params[:survey_version]).first
|
27
|
+
end
|
28
|
+
@response_set = ResponseSet.
|
29
|
+
create(:survey => @survey, :user_id => (@current_user.nil? ? @current_user : @current_user.id))
|
30
|
+
if (@survey && @response_set)
|
31
|
+
flash[:notice] = t('surveyor.survey_started_success')
|
32
|
+
redirect_to(surveyor.edit_my_survey_path(
|
33
|
+
:survey_code => @survey.access_code, :response_set_code => @response_set.access_code))
|
34
|
+
else
|
35
|
+
flash[:notice] = t('surveyor.Unable_to_find_that_survey')
|
36
|
+
redirect_to surveyor_index
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def show
|
41
|
+
# @response_set is set in before_filter - set_response_set_and_render_context
|
42
|
+
if @response_set
|
43
|
+
@survey = @response_set.survey
|
44
|
+
respond_to do |format|
|
45
|
+
format.html #{render :action => :show}
|
46
|
+
format.csv {
|
47
|
+
send_data(@response_set.to_csv, :type => 'text/csv; charset=utf-8; header=present',
|
48
|
+
:filename => "#{@response_set.updated_at.strftime('%Y-%m-%d')}_#{@response_set.access_code}.csv")
|
49
|
+
}
|
50
|
+
format.json
|
51
|
+
end
|
52
|
+
else
|
53
|
+
flash[:notice] = t('surveyor.unable_to_find_your_responses')
|
54
|
+
redirect_to surveyor_index
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def edit
|
59
|
+
# @response_set is set in before_filter - set_response_set_and_render_context
|
60
|
+
if @response_set
|
61
|
+
@survey = Survey.with_sections.find_by_id(@response_set.survey_id)
|
62
|
+
@sections = @survey.sections
|
63
|
+
@section = @sections.with_includes.find(section_id_from(params) || :first) || @sections.with_includes.first
|
64
|
+
set_dependents
|
65
|
+
else
|
66
|
+
flash[:notice] = t('surveyor.unable_to_find_your_responses')
|
67
|
+
redirect_to surveyor_index
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def update
|
72
|
+
question_ids_for_dependencies = (params[:r] || []).map{|k,v| v["question_id"] }.compact.uniq
|
73
|
+
saved = load_and_update_response_set_with_retries
|
74
|
+
|
75
|
+
return redirect_with_message(surveyor_finish, :notice, t('surveyor.completed_survey')) if saved && params[:finish]
|
76
|
+
|
77
|
+
respond_to do |format|
|
78
|
+
format.html do
|
79
|
+
if @response_set.nil?
|
80
|
+
return redirect_with_message(surveyor.available_surveys_path, :notice, t('surveyor.unable_to_find_your_responses'))
|
81
|
+
else
|
82
|
+
flash[:notice] = t('surveyor.unable_to_update_survey') unless saved
|
83
|
+
redirect_to surveyor.edit_my_survey_path(:anchor => anchor_from(params[:section]), :section => section_id_from(params))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
format.js do
|
87
|
+
if @response_set
|
88
|
+
render :json => @response_set.reload.all_dependencies(question_ids_for_dependencies)
|
89
|
+
else
|
90
|
+
render :text => "No response set #{params[:response_set_code]}",
|
91
|
+
:status => 404
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def load_and_update_response_set_with_retries(remaining=2)
|
98
|
+
begin
|
99
|
+
load_and_update_response_set
|
100
|
+
rescue ActiveRecord::StatementInvalid => e
|
101
|
+
if remaining > 0
|
102
|
+
load_and_update_response_set_with_retries(remaining - 1)
|
103
|
+
else
|
104
|
+
raise e
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def load_and_update_response_set
|
110
|
+
ResponseSet.transaction do
|
111
|
+
@response_set = ResponseSet.
|
112
|
+
find_by_access_code(params[:response_set_code], :include => {:responses => :answer})
|
113
|
+
if @response_set
|
114
|
+
saved = true
|
115
|
+
if params[:r]
|
116
|
+
@response_set.update_from_ui_hash(params[:r])
|
117
|
+
end
|
118
|
+
if params[:finish]
|
119
|
+
@response_set.complete!
|
120
|
+
saved &= @response_set.save
|
121
|
+
end
|
122
|
+
saved
|
123
|
+
else
|
124
|
+
false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
private :load_and_update_response_set
|
129
|
+
|
130
|
+
def export
|
131
|
+
surveys = Survey.where(:access_code => params[:survey_code]).order("survey_version DESC")
|
132
|
+
s = params[:survey_version].blank? ? surveys.first : surveys.where(:survey_version => params[:survey_version]).first
|
133
|
+
render_404 and return if s.blank?
|
134
|
+
@survey = s.filtered_for_json
|
135
|
+
end
|
136
|
+
|
137
|
+
def render_404
|
138
|
+
head :status => 404
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
def url_options
|
143
|
+
((I18n.locale == I18n.default_locale) ? {} : {:locale => I18n.locale}).merge(super)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
# This is a hook method for surveyor-using applications to override and provide the context object
|
149
|
+
def render_context
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
|
153
|
+
# Filters
|
154
|
+
def get_current_user
|
155
|
+
@current_user = self.respond_to?(:current_user) ? self.current_user : nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def set_response_set_and_render_context
|
159
|
+
@response_set = ResponseSet.
|
160
|
+
find_by_access_code(params[:response_set_code], :include => {:responses => [:question, :answer]})
|
161
|
+
@render_context = render_context
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_locale
|
165
|
+
if params[:new_locale]
|
166
|
+
I18n.locale = params[:new_locale]
|
167
|
+
elsif params[:locale]
|
168
|
+
I18n.locale = params[:locale]
|
169
|
+
else
|
170
|
+
I18n.locale = I18n.default_locale
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Params: the name of some submit buttons store the section we'd like to go
|
175
|
+
# to. for repeater questions, an anchor to the repeater group is also stored
|
176
|
+
# e.g. params[:section] = {"1"=>{"question_group_1"=>"<= add row"}}
|
177
|
+
def section_id_from(p = {})
|
178
|
+
if p[:section] && p[:section].respond_to?(:keys)
|
179
|
+
p[:section].keys.first
|
180
|
+
elsif p[:section]
|
181
|
+
p[:section]
|
182
|
+
elsif p[:current_section]
|
183
|
+
p[:current_section]
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def anchor_from(p)
|
188
|
+
p.respond_to?(:keys) && p[p.keys.first].respond_to?(:keys) ? p[p.keys.first].keys.first : nil
|
189
|
+
end
|
190
|
+
|
191
|
+
def surveyor_index
|
192
|
+
surveyor.available_surveys_path
|
193
|
+
end
|
194
|
+
def surveyor_finish
|
195
|
+
surveyor.available_surveys_path
|
196
|
+
end
|
197
|
+
|
198
|
+
def redirect_with_message(path, message_type, message)
|
199
|
+
respond_to do |format|
|
200
|
+
format.html do
|
201
|
+
flash[message_type] = message if !message.blank? and !message_type.blank?
|
202
|
+
redirect_to path
|
203
|
+
end
|
204
|
+
format.js do
|
205
|
+
render :text => message, :status => 403
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# @dependents are necessary in case the client does not have javascript enabled
|
212
|
+
# Whether or not javascript is enabled is determined by a hidden field set in the surveyor/edit.html form
|
213
|
+
def set_dependents
|
214
|
+
if session[:surveyor_javascript] && session[:surveyor_javascript] == "enabled"
|
215
|
+
@dependents = []
|
216
|
+
else
|
217
|
+
@dependents = get_unanswered_dependencies_minus_section_questions
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def get_unanswered_dependencies_minus_section_questions
|
222
|
+
@response_set.unanswered_dependencies - @section.questions || []
|
223
|
+
end
|
224
|
+
|
225
|
+
##
|
226
|
+
# If the hidden field surveyor_javascript_enabled is set to true
|
227
|
+
# cf. surveyor/edit.html.haml
|
228
|
+
# the set the session variable [:surveyor_javascript] to "enabled"
|
229
|
+
def determine_if_javascript_is_enabled
|
230
|
+
if params[:surveyor_javascript_enabled] && params[:surveyor_javascript_enabled].to_s == "true"
|
231
|
+
session[:surveyor_javascript] = "enabled"
|
232
|
+
else
|
233
|
+
session[:surveyor_javascript] = "not_enabled"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
%w(survey survey_section question_group question dependency dependency_condition answer validation validation_condition).each {|model| require model }
|
2
|
+
module Surveyor
|
3
|
+
class Unparser
|
4
|
+
# Class methods
|
5
|
+
def self.unparse(survey)
|
6
|
+
survey.unparse(dsl = "")
|
7
|
+
dsl
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Surveyor models with extra parsing methods
|
13
|
+
class Survey < ActiveRecord::Base
|
14
|
+
# block
|
15
|
+
|
16
|
+
def unparse(dsl)
|
17
|
+
attrs = (self.attributes.diff Survey.new(:title => title).attributes).delete_if{|k,v| %w(created_at updated_at inactive_at id title access_code api_id).include? k}.symbolize_keys!
|
18
|
+
dsl << "survey \"#{title}\""
|
19
|
+
dsl << (attrs.blank? ? " do\n" : ", #{attrs.inspect.gsub(/\{|\}/, "")} do\n")
|
20
|
+
sections.each{|section| section.unparse(dsl)}
|
21
|
+
dsl << "end\n"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class SurveySection < ActiveRecord::Base
|
25
|
+
# block
|
26
|
+
|
27
|
+
def unparse(dsl)
|
28
|
+
attrs = (self.attributes.diff SurveySection.new(:title => title).attributes).delete_if{|k,v| %w(created_at updated_at id survey_id).include? k}.symbolize_keys!
|
29
|
+
group_questions = []
|
30
|
+
dsl << " section \"#{title}\""
|
31
|
+
dsl << (attrs.blank? ? " do\n" : ", #{attrs.inspect.gsub(/\{|\}/, "")} do\n")
|
32
|
+
questions.each_with_index do |question, index|
|
33
|
+
if question.solo?
|
34
|
+
question.unparse(dsl)
|
35
|
+
else # gather up the group questions
|
36
|
+
group_questions << question
|
37
|
+
if (index + 1 >= questions.size) or (question.question_group != questions[index + 1].question_group)
|
38
|
+
# this is the last question of the section, or the group
|
39
|
+
question.question_group.unparse(dsl)
|
40
|
+
end
|
41
|
+
group_questions = []
|
42
|
+
end
|
43
|
+
end
|
44
|
+
dsl << " end\n"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
class QuestionGroup < ActiveRecord::Base
|
48
|
+
# block
|
49
|
+
|
50
|
+
def unparse(dsl)
|
51
|
+
attrs = (self.attributes.diff QuestionGroup.new(:text => text).attributes).delete_if{|k,v| %w(created_at updated_at id api_id).include?(k) or (k == "display_type" && %w(grid repeater default).include?(v))}.symbolize_keys!
|
52
|
+
method = (%w(grid repeater).include?(display_type) ? display_type : "group")
|
53
|
+
dsl << "\n"
|
54
|
+
dsl << " #{method} \"#{text}\""
|
55
|
+
dsl << (attrs.blank? ? " do\n" : ", #{attrs.inspect.gsub(/\{|\}/, "")} do\n")
|
56
|
+
questions.first.answers.each{|answer| answer.unparse(dsl)} if display_type == "grid"
|
57
|
+
questions.each{|question| question.unparse(dsl)}
|
58
|
+
dsl << " end\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
class Question < ActiveRecord::Base
|
62
|
+
# nonblock
|
63
|
+
|
64
|
+
def unparse(dsl)
|
65
|
+
attrs = (self.attributes.diff Question.new(:text => text).attributes).delete_if{|k,v| %w(created_at updated_at reference_identifier id survey_section_id question_group_id api_id).include?(k) or (k == "display_type" && v == "label")}.symbolize_keys!
|
66
|
+
dsl << (solo? ? "\n" : " ")
|
67
|
+
if display_type == "label"
|
68
|
+
dsl << " label"
|
69
|
+
else
|
70
|
+
dsl << " q"
|
71
|
+
end
|
72
|
+
dsl << "_#{reference_identifier}" unless reference_identifier.blank?
|
73
|
+
dsl << " \"#{text}\""
|
74
|
+
dsl << (attrs.blank? ? "\n" : ", #{attrs.inspect.gsub(/\{|\}/, "")}\n")
|
75
|
+
if solo? or question_group.display_type != "grid"
|
76
|
+
answers.each{|answer| answer.unparse(dsl)}
|
77
|
+
end
|
78
|
+
dependency.unparse(dsl) if dependency
|
79
|
+
end
|
80
|
+
end
|
81
|
+
class Dependency < ActiveRecord::Base
|
82
|
+
# nonblock
|
83
|
+
|
84
|
+
def unparse(dsl)
|
85
|
+
attrs = (self.attributes.diff Dependency.new.attributes).delete_if{|k,v| %w(created_at updated_at id question_id).include?(k) }.symbolize_keys!
|
86
|
+
dsl << " " if question.part_of_group?
|
87
|
+
dsl << " dependency"
|
88
|
+
dsl << (attrs.blank? ? "\n" : " #{attrs.inspect.gsub(/\{|\}/, "")}\n")
|
89
|
+
dependency_conditions.each{|dependency_condition| dependency_condition.unparse(dsl)}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
class DependencyCondition < ActiveRecord::Base
|
93
|
+
# nonblock
|
94
|
+
|
95
|
+
def unparse(dsl)
|
96
|
+
attrs = (self.attributes.diff Dependency.new.attributes).delete_if{|k,v| %w(created_at updated_at question_id question_group_id rule_key rule operator id dependency_id answer_id).include? k}.symbolize_keys!
|
97
|
+
dsl << " " if dependency.question.part_of_group?
|
98
|
+
dsl << " condition"
|
99
|
+
dsl << "_#{rule_key}" unless rule_key.blank?
|
100
|
+
dsl << " :q_#{question.reference_identifier}, \"#{operator}\""
|
101
|
+
dsl << (attrs.blank? ? ", {:answer_reference=>\"#{answer && answer.reference_identifier}\"}\n" : ", {#{attrs.inspect.gsub(/\{|\}/, "")}, :answer_reference=>\"#{answer && answer.reference_identifier}\"}\n")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
class Answer < ActiveRecord::Base
|
105
|
+
# nonblock
|
106
|
+
|
107
|
+
def unparse(dsl)
|
108
|
+
attrs = (self.attributes.diff Answer.new(:text => text).attributes).delete_if{|k,v| %w(created_at updated_at reference_identifier response_class id question_id api_id).include? k}.symbolize_keys!
|
109
|
+
attrs.delete(:is_exclusive) if text == "Omit" && is_exclusive == true
|
110
|
+
attrs.merge!({:is_exclusive => false}) if text == "Omit" && is_exclusive == false
|
111
|
+
dsl << " " if question.part_of_group?
|
112
|
+
dsl << " a"
|
113
|
+
dsl << "_#{reference_identifier}" unless reference_identifier.blank?
|
114
|
+
if response_class.to_s.titlecase == text && attrs == {:display_type => "hidden_label"}
|
115
|
+
dsl << " :#{response_class}"
|
116
|
+
else
|
117
|
+
dsl << [ text.blank? ? nil : text == "Other" ? " :other" : text == "Omit" ? " :omit" : " \"#{text}\"",
|
118
|
+
(response_class.blank? or response_class == "answer") ? nil : " #{response_class.to_sym.inspect}",
|
119
|
+
attrs.blank? ? nil : " #{attrs.inspect.gsub(/\{|\}/, "")}\n"].compact.join(",")
|
120
|
+
end
|
121
|
+
dsl << "\n"
|
122
|
+
validations.each{|validation| validation.unparse(dsl)}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
class Validation < ActiveRecord::Base
|
126
|
+
# nonblock
|
127
|
+
|
128
|
+
def unparse(dsl)
|
129
|
+
attrs = (self.attributes.diff Validation.new.attributes).delete_if{|k,v| %w(created_at updated_at id answer_id).include?(k) }.symbolize_keys!
|
130
|
+
dsl << " " if answer.question.part_of_group?
|
131
|
+
dsl << " validation"
|
132
|
+
dsl << (attrs.blank? ? "\n" : " #{attrs.inspect.gsub(/\{|\}/, "")}\n")
|
133
|
+
validation_conditions.each{|validation_condition| validation_condition.unparse(dsl)}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
class ValidationCondition < ActiveRecord::Base
|
137
|
+
# nonblock
|
138
|
+
|
139
|
+
def unparse(dsl)
|
140
|
+
attrs = (self.attributes.diff ValidationCondition.new.attributes).delete_if{|k,v| %w(created_at updated_at operator rule_key id validation_id).include? k}.symbolize_keys!
|
141
|
+
dsl << " " if validation.answer.question.part_of_group?
|
142
|
+
dsl << " condition"
|
143
|
+
dsl << "_#{rule_key}" unless rule_key.blank?
|
144
|
+
dsl << " \"#{operator}\""
|
145
|
+
dsl << (attrs.blank? ? "\n" : ", #{attrs.inspect.gsub(/\{|\}/, "")}\n")
|
146
|
+
end
|
147
|
+
end
|