surveyor_gui 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +44 -0
  3. data/.travis.yml +17 -0
  4. data/Gemfile +7 -0
  5. data/LICENSE +21 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +159 -0
  8. data/Rakefile +66 -0
  9. data/app/controllers/answers_controller.rb +3 -0
  10. data/app/controllers/application_controller.rb +3 -0
  11. data/app/controllers/dependencys_controller.rb +286 -0
  12. data/app/controllers/question_groups_controller.rb +84 -0
  13. data/app/controllers/questions_controller.rb +187 -0
  14. data/app/controllers/survey_sections_controller.rb +80 -0
  15. data/app/controllers/surveyforms_controller.rb +4 -0
  16. data/app/controllers/surveyor_controller.rb +89 -0
  17. data/app/controllers/surveyor_gui/reports_controller.rb +339 -0
  18. data/app/controllers/surveyor_gui/responses_controller.rb +39 -0
  19. data/app/controllers/surveyor_gui/survey_controller.rb +16 -0
  20. data/app/facades/report_formatter.rb +44 -0
  21. data/app/facades/report_preview_wrapper.rb +11 -0
  22. data/app/facades/report_response_generator.rb +147 -0
  23. data/app/helpers/application_helper.rb +2 -0
  24. data/app/helpers/dependency_helper.rb +5 -0
  25. data/app/helpers/question_helper.rb +22 -0
  26. data/app/helpers/surveyform_helper.rb +179 -0
  27. data/app/helpers/surveyor_gui/report_helper.rb +19 -0
  28. data/app/helpers/surveyor_helper.rb +4 -0
  29. data/app/inputs/currency_input.rb +5 -0
  30. data/app/inputs/date_picker_input.rb +7 -0
  31. data/app/inputs/datetime_picker_input.rb +5 -0
  32. data/app/inputs/percent_input.rb +5 -0
  33. data/app/inputs/time_picker_input.rb +5 -0
  34. data/app/mailers/.gitkeep +0 -0
  35. data/app/models/.gitkeep +0 -0
  36. data/app/models/answer.rb +4 -0
  37. data/app/models/column.rb +3 -0
  38. data/app/models/dependency.rb +4 -0
  39. data/app/models/dependency_condition.rb +4 -0
  40. data/app/models/permitted_params_decorator.rb +80 -0
  41. data/app/models/question.rb +4 -0
  42. data/app/models/question_group.rb +4 -0
  43. data/app/models/question_type.rb +7 -0
  44. data/app/models/response.rb +4 -0
  45. data/app/models/response_set.rb +4 -0
  46. data/app/models/row.rb +3 -0
  47. data/app/models/survey.rb +4 -0
  48. data/app/models/survey_section.rb +4 -0
  49. data/app/models/surveyform.rb +103 -0
  50. data/app/uploaders/blob_uploader.rb +48 -0
  51. data/app/views/dependencys/_column.html.erb +1 -0
  52. data/app/views/dependencys/_dependency.html.erb +22 -0
  53. data/app/views/dependencys/_dependency_condition_fields.html.erb +48 -0
  54. data/app/views/dependencys/_form.html.erb +20 -0
  55. data/app/views/dependencys/blank.html.erb +0 -0
  56. data/app/views/dependencys/edit.html.erb +1 -0
  57. data/app/views/dependencys/new.html.erb +1 -0
  58. data/app/views/layouts/application.html.erb +14 -0
  59. data/app/views/layouts/surveyor_gui_blank.html.erb +12 -0
  60. data/app/views/layouts/surveyor_gui_default.html.erb +16 -0
  61. data/app/views/layouts/surveyor_modified.html.erb +14 -0
  62. data/app/views/partials/_answer.html.haml +33 -0
  63. data/app/views/partials/_question.html.haml +33 -0
  64. data/app/views/partials/_question_group.html.haml +73 -0
  65. data/app/views/partials/_section.html.haml +13 -0
  66. data/app/views/question_groups/_form.html.erb +56 -0
  67. data/app/views/question_groups/_group_inline_field.html.erb +21 -0
  68. data/app/views/question_groups/_group_inline_fields.html.erb +25 -0
  69. data/app/views/question_groups/blank.html.erb +0 -0
  70. data/app/views/question_groups/edit.html.erb +1 -0
  71. data/app/views/question_groups/new.html.erb +1 -0
  72. data/app/views/questions/_answer_fields.html.erb +23 -0
  73. data/app/views/questions/_answer_options.html.erb +28 -0
  74. data/app/views/questions/_form.html.erb +65 -0
  75. data/app/views/questions/_grid_dropdown_columns.html.erb +10 -0
  76. data/app/views/questions/_grid_dropdown_fields.html.erb +42 -0
  77. data/app/views/questions/_grid_fields.html.erb +9 -0
  78. data/app/views/questions/_no_picks.html.erb +29 -0
  79. data/app/views/questions/_pick.html +21 -0
  80. data/app/views/questions/_picks.html.erb +12 -0
  81. data/app/views/questions/blank.html.erb +0 -0
  82. data/app/views/questions/edit.html.erb +1 -0
  83. data/app/views/questions/new.html.erb +1 -0
  84. data/app/views/survey_sections/_form.html.erb +13 -0
  85. data/app/views/survey_sections/blank.html.erb +0 -0
  86. data/app/views/survey_sections/edit.html.erb +1 -0
  87. data/app/views/survey_sections/new.html.erb +1 -0
  88. data/app/views/surveyforms/_form.html.erb +50 -0
  89. data/app/views/surveyforms/_question_field.html.erb +148 -0
  90. data/app/views/surveyforms/_question_group.html.erb +116 -0
  91. data/app/views/surveyforms/_question_group_fields.html.erb +3 -0
  92. data/app/views/surveyforms/_question_name_and_number.html.erb +6 -0
  93. data/app/views/surveyforms/_question_section.html.erb +15 -0
  94. data/app/views/surveyforms/_question_wrapper.html.erb +109 -0
  95. data/app/views/surveyforms/_survey_section_fields.html.erb +138 -0
  96. data/app/views/surveyforms/clone_survey.html.erb +13 -0
  97. data/app/views/surveyforms/edit.html.erb +5 -0
  98. data/app/views/surveyforms/index.html.erb +40 -0
  99. data/app/views/surveyforms/new.html.erb +1 -0
  100. data/app/views/surveyforms/show.html.erb +5 -0
  101. data/app/views/surveyor_gui/reports/_graphs.html.haml +21 -0
  102. data/app/views/surveyor_gui/reports/_grid.html.haml +42 -0
  103. data/app/views/surveyor_gui/reports/_grid_dropdown.html.haml +56 -0
  104. data/app/views/surveyor_gui/reports/_repeater.html.haml +28 -0
  105. data/app/views/surveyor_gui/reports/_show_report.html.haml +33 -0
  106. data/app/views/surveyor_gui/reports/_single_question.html.haml +70 -0
  107. data/app/views/surveyor_gui/reports/show.html.erb +14 -0
  108. data/app/views/surveyor_gui/responses/_grid.html.haml +20 -0
  109. data/app/views/surveyor_gui/responses/_grid_dropdown.html.haml +20 -0
  110. data/app/views/surveyor_gui/responses/_repeater.html.haml +22 -0
  111. data/app/views/surveyor_gui/responses/_survey_results.html.haml +40 -0
  112. data/app/views/surveyor_gui/responses/index.html.haml +24 -0
  113. data/app/views/surveyor_gui/responses/show.html.haml +42 -0
  114. data/app/views/surveyor_gui/shared/_grid_comments.html.haml +10 -0
  115. data/app/views/surveyor_gui/shared/_new_line.html.haml +2 -0
  116. data/app/views/surveyor_gui/shared/_pick_comments.html.haml +15 -0
  117. data/app/views/surveyor_gui/shared/_question_number.html.haml +9 -0
  118. data/app/views/surveyor_gui/shared/_report_data.html.haml +24 -0
  119. data/app/views/surveyor_gui/shared/_stars_report_data.html.haml +14 -0
  120. data/config.ru +4 -0
  121. data/config/environment.rb +0 -0
  122. data/config/routes.rb +71 -0
  123. data/db/migrate/20140307204049_add_template_to_surveys.rb +5 -0
  124. data/db/migrate/20140307235607_add_test_data_to_response_sets.rb +5 -0
  125. data/db/migrate/20140308171947_add_original_choice_to_answers.rb +5 -0
  126. data/db/migrate/20140308172118_add_blob_to_responses.rb +5 -0
  127. data/db/migrate/20140308172417_add_modifiable_to_survey_section.rb +5 -0
  128. data/db/migrate/20140308174532_add_modifiable_to_question.rb +5 -0
  129. data/db/migrate/20140308175305_add_dynamically_generate_to_questions.rb +5 -0
  130. data/db/migrate/20140311032923_add_dummy_blob_to_questions.rb +5 -0
  131. data/db/migrate/20140311160609_add_dynamic_source_to_questions.rb +5 -0
  132. data/db/migrate/20140311161714_add_report_code_to_questions.rb +5 -0
  133. data/db/migrate/20140530181134_add_is_comment_to_questions.rb +5 -0
  134. data/db/migrate/20140531012006_add_is_comment_to_answers.rb +5 -0
  135. data/db/migrate/20140531225529_create_rows.rb +9 -0
  136. data/db/migrate/20140601011151_create_columns.rb +11 -0
  137. data/db/migrate/20140602030330_add_column_id_to_answers.rb +5 -0
  138. data/db/migrate/20140603155606_add_column_id_to_responses.rb +5 -0
  139. data/db/migrate/20140606023527_add_column_id_to_dependency_conditions.rb +5 -0
  140. data/db/migrate/20140815165307_add_user_id_to_survey.rb +5 -0
  141. data/lib/assets/.gitkeep +0 -0
  142. data/lib/assets/images/addicon.png +0 -0
  143. data/lib/assets/images/datepicker.gif +0 -0
  144. data/lib/assets/images/delete.gif +0 -0
  145. data/lib/assets/images/delete.png +0 -0
  146. data/lib/assets/images/images/border.png +0 -0
  147. data/lib/assets/images/images/controls.png +0 -0
  148. data/lib/assets/images/images/loading.gif +0 -0
  149. data/lib/assets/images/images/loading_background.png +0 -0
  150. data/lib/assets/images/images/overlay.png +0 -0
  151. data/lib/assets/images/rails.png +0 -0
  152. data/lib/assets/images/star.gif +0 -0
  153. data/lib/assets/javascripts/.gitkeep +0 -0
  154. data/lib/assets/javascripts/surveyor_gui/jquery.MetaData.js +121 -0
  155. data/lib/assets/javascripts/surveyor_gui/jquery.blockUI.js +619 -0
  156. data/lib/assets/javascripts/surveyor_gui/jquery.rating.js +377 -0
  157. data/lib/assets/javascripts/surveyor_gui/jquery.validate.js +1188 -0
  158. data/lib/assets/javascripts/surveyor_gui/surveyor_add_ons.js +10 -0
  159. data/lib/assets/javascripts/surveyor_gui/surveyor_gui.js +1417 -0
  160. data/lib/assets/stylesheets/.gitkeep +0 -0
  161. data/lib/assets/stylesheets/surveyor_gui/jquery.rating.css +12 -0
  162. data/lib/assets/stylesheets/surveyor_gui/surveyor_add_ons.css +3 -0
  163. data/lib/assets/stylesheets/surveyor_gui/surveyor_gui.sass +650 -0
  164. data/lib/enumerable_extenders.rb +31 -0
  165. data/lib/generators/surveyor_gui/install_generator.rb +57 -0
  166. data/lib/generators/surveyor_gui/templates/app/assets/javascripts/surveyor_add_ons.js +2 -0
  167. data/lib/generators/surveyor_gui/templates/app/assets/javascripts/surveyor_gui_all.js +24 -0
  168. data/lib/generators/surveyor_gui/templates/app/assets/stylesheets/surveyor_add_ons.css.sass +1 -0
  169. data/lib/generators/surveyor_gui/templates/app/assets/stylesheets/surveyor_gui_all.css.sass +8 -0
  170. data/lib/generators/surveyor_gui/templates/app/helpers/surveyor_helper.rb +4 -0
  171. data/lib/generators/surveyor_gui/templates/app/models/response_set_user.rb +13 -0
  172. data/lib/generators/surveyor_gui/templates/app/views/layouts/surveyor_gui_default.html.erb +16 -0
  173. data/lib/generators/surveyor_gui/templates/config/initializers/simple_form.rb +140 -0
  174. data/lib/generators/surveyor_gui/templates/config/locales/en.yml +5 -0
  175. data/lib/surveyor_gui.rb +17 -0
  176. data/lib/surveyor_gui/engine.rb +30 -0
  177. data/lib/surveyor_gui/helpers/surveyor_helper_methods.rb +19 -0
  178. data/lib/surveyor_gui/models/answer_methods.rb +24 -0
  179. data/lib/surveyor_gui/models/column_methods.rb +43 -0
  180. data/lib/surveyor_gui/models/dependency_condition_methods.rb +53 -0
  181. data/lib/surveyor_gui/models/dependency_methods.rb +83 -0
  182. data/lib/surveyor_gui/models/question_and_group_shared_methods.rb +11 -0
  183. data/lib/surveyor_gui/models/question_group_methods.rb +55 -0
  184. data/lib/surveyor_gui/models/question_methods.rb +435 -0
  185. data/lib/surveyor_gui/models/question_type_methods.rb +493 -0
  186. data/lib/surveyor_gui/models/response_methods.rb +67 -0
  187. data/lib/surveyor_gui/models/response_set_methods.rb +54 -0
  188. data/lib/surveyor_gui/models/row_methods.rb +11 -0
  189. data/lib/surveyor_gui/models/survey_methods.rb +32 -0
  190. data/lib/surveyor_gui/models/survey_section_methods.rb +32 -0
  191. data/lib/surveyor_gui/surveyforms_controller_methods.rb +258 -0
  192. data/lib/surveyor_gui/version.rb +3 -0
  193. data/lib/tasks/.gitkeep +0 -0
  194. data/lib/tasks/surveyor_gui_tasks.rake +4 -0
  195. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  196. data/spec/controllers/surveyforms_controller_spec.rb +361 -0
  197. data/spec/controllers/surveyor_controller_spec.rb +303 -0
  198. data/spec/factories.rb +181 -0
  199. data/spec/features/create_survey_spec.rb +418 -0
  200. data/spec/features/dependencies_spec.rb +61 -0
  201. data/spec/features/rearrange_survey_spec.rb +118 -0
  202. data/spec/features/ui_spec.rb +469 -0
  203. data/spec/fixtures/REDCapDemoDatabase_DataDictionary.csv +127 -0
  204. data/spec/fixtures/chinese_survey.rb +14 -0
  205. data/spec/fixtures/everything.rb +215 -0
  206. data/spec/fixtures/favorites-ish.rb +22 -0
  207. data/spec/fixtures/favorites.rb +22 -0
  208. data/spec/fixtures/feelings.rb +38 -0
  209. data/spec/fixtures/lifestyle.rb +55 -0
  210. data/spec/fixtures/numbers.rb +21 -0
  211. data/spec/fixtures/redcap_new_headers.csv +1 -0
  212. data/spec/fixtures/redcap_siblings.csv +1 -0
  213. data/spec/fixtures/redcap_whitespace.csv +1 -0
  214. data/spec/helpers/formtastic_custom_input_spec.rb +15 -0
  215. data/spec/helpers/surveyor_helper_spec.rb +116 -0
  216. data/spec/lib/common_spec.rb +37 -0
  217. data/spec/lib/parser_spec.rb +393 -0
  218. data/spec/lib/rake_kitchen_sink.rb +42 -0
  219. data/spec/lib/redcap_parser_spec.rb +129 -0
  220. data/spec/lib/unparser_spec.rb +126 -0
  221. data/spec/models/answer_spec.rb +144 -0
  222. data/spec/models/dependency_condition_spec.rb +428 -0
  223. data/spec/models/dependency_spec.rb +90 -0
  224. data/spec/models/question_group_spec.rb +66 -0
  225. data/spec/models/question_spec.rb +176 -0
  226. data/spec/models/response_set_spec.rb +452 -0
  227. data/spec/models/response_spec.rb +208 -0
  228. data/spec/models/survey_section_spec.rb +58 -0
  229. data/spec/models/survey_spec.rb +155 -0
  230. data/spec/models/surveyor_gui/question_spec.rb +60 -0
  231. data/spec/models/surveyor_gui/question_type_spec.rb +268 -0
  232. data/spec/models/validation_condition_spec.rb +98 -0
  233. data/spec/models/validation_spec.rb +64 -0
  234. data/spec/spec.opts +4 -0
  235. data/spec/spec_helper.rb +119 -0
  236. data/spec/support/scenario_outline_helper.rb +39 -0
  237. data/spec/support/shared.rb +10 -0
  238. data/spec/support/surveyforms_creation_helpers.rb +312 -0
  239. data/spec/support/surveyforms_rearrangement_helpers.rb +170 -0
  240. data/spec/support/surveyor_api_helpers.rb +15 -0
  241. data/spec/support/surveyor_ui_helpers.rb +108 -0
  242. data/spec/support/wait_for_ajax.rb +11 -0
  243. data/spec/views/questions/edit.html.erb_spec.rb +73 -0
  244. data/spec/views/surveyforms/edit.html.erb_spec.rb +126 -0
  245. data/surveyor_gui.gemspec +52 -0
  246. data/test/dummy/README.rdoc +261 -0
  247. data/test/dummy/Rakefile +7 -0
  248. data/test/dummy/app/assets/javascripts/application.js +15 -0
  249. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  250. data/test/dummy/app/controllers/application_controller.rb +3 -0
  251. data/test/dummy/app/helpers/application_helper.rb +2 -0
  252. data/test/dummy/app/mailers/.gitkeep +0 -0
  253. data/test/dummy/app/models/.gitkeep +0 -0
  254. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  255. data/test/dummy/config.ru +4 -0
  256. data/test/dummy/config/application.rb +59 -0
  257. data/test/dummy/config/boot.rb +10 -0
  258. data/test/dummy/config/database.yml +25 -0
  259. data/test/dummy/config/environment.rb +5 -0
  260. data/test/dummy/config/environments/development.rb +37 -0
  261. data/test/dummy/config/environments/production.rb +67 -0
  262. data/test/dummy/config/environments/test.rb +37 -0
  263. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  264. data/test/dummy/config/initializers/inflections.rb +15 -0
  265. data/test/dummy/config/initializers/mime_types.rb +5 -0
  266. data/test/dummy/config/initializers/secret_token.rb +7 -0
  267. data/test/dummy/config/initializers/session_store.rb +8 -0
  268. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  269. data/test/dummy/config/locales/en.yml +5 -0
  270. data/test/dummy/config/routes.rb +58 -0
  271. data/test/dummy/lib/assets/.gitkeep +0 -0
  272. data/test/dummy/log/.gitkeep +0 -0
  273. data/test/dummy/public/404.html +26 -0
  274. data/test/dummy/public/422.html +26 -0
  275. data/test/dummy/public/500.html +25 -0
  276. data/test/dummy/public/favicon.ico +0 -0
  277. data/test/dummy/script/rails +6 -0
  278. data/test/surveyor-gui_test.rb +7 -0
  279. data/test/test_helper.rb +15 -0
  280. data/vendor/assets/stylesheets/.gitkeep +0 -0
  281. data/vendor/assets/stylesheets/custom.sass +5 -0
  282. data/vendor/plugins/.gitkeep +0 -0
  283. metadata +664 -0
@@ -0,0 +1,42 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require 'rake'
3
+
4
+ describe Surveyor::Parser do
5
+ before do
6
+ @rake = Rake::Application.new
7
+ Rake.application = @rake
8
+ Rake.application.add_import "#{gem_path('surveyor')}/lib/tasks/surveyor_tasks.rake"
9
+ Rake.application.load_imports
10
+ Rake::Task.define_task(:environment)
11
+ end
12
+ it "should return properly parse the kitchen sink survey" do
13
+ ENV["FILE"]="surveys/kitchen_sink_survey.rb"
14
+ @rake["surveyor"].invoke
15
+
16
+ Survey.count.should == 1
17
+ SurveySection.count.should == 2
18
+ Question.count.should == 51
19
+ Answer.count.should == 252
20
+ Dependency.count.should == 8
21
+ DependencyCondition.count.should == 12
22
+ QuestionGroup.count.should == 6
23
+
24
+ Survey.all.map(&:destroy)
25
+ end
26
+ it "should return properly parse a UTF8 survey" do
27
+ pending "failing - not clear why - await update of surveyor"
28
+ ENV["FILE"]="../spec/fixtures/chinese_survey.rb"
29
+ @rake["surveyor"].invoke
30
+
31
+ Survey.count.should == 1
32
+ SurveySection.count.should == 1
33
+ Question.count.should == 3
34
+ Answer.count.should == 15
35
+ Dependency.count.should == 0
36
+ DependencyCondition.count.should == 0
37
+ QuestionGroup.count.should == 1
38
+
39
+ Survey.all.map(&:destroy)
40
+ end
41
+
42
+ end
@@ -0,0 +1,129 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Surveyor::RedcapParser do
4
+ let(:parser){ Surveyor::RedcapParser.new }
5
+ it "returns a survey object" do
6
+ x = %("Variable / Field Name","Form Name","Field Units","Section Header","Field Type","Field Label","Choices OR Calculations","Field Note","Text Validation Type","Text Validation Min","Text Validation Max",Identifier?,"Branching Logic (Show field only if...)","Required Field?"\nstudy_id,demographics,,,text,"Study ID",,,,,,,,)
7
+ parser.parse(x, "redcaptest").is_a?(Survey).should be_true
8
+ end
9
+ context "parses" do
10
+ it "basic questions from REDCap" do
11
+ file = "REDCapDemoDatabase_DataDictionary.csv"
12
+ parser.parse File.read(File.join(Rails.root, '..', 'spec', 'fixtures', file)), file
13
+ expect(Survey.count).to eq(1)
14
+ expect(Question.count).to eq(143)
15
+ expect(Answer.count).to eq(233)
16
+ expect(Dependency.count).to eq(2)
17
+ expect(DependencyCondition.count).to eq(3)
18
+ dependencies = [{rule: "A", question_reference_identifier: "given_birth"},
19
+ {rule: "A and B", question_reference_identifier: "num_children"}]
20
+ dependencies.each{|attrs| (expect(Dependency.where(rule: attrs[:rule]).first.question.reference_identifier).to eq(attrs[:question_reference_identifier])) if attrs[:question_reference_identifier] }
21
+ dependency_conditions_a = [{rule_key: "A", operator: "==", question_reference_identifier: "sex", answer_reference_identifier: "0"}]
22
+ dependency_conditions_ab = [{rule_key: "A", operator: "==", question_reference_identifier: "sex", answer_reference_identifier: "0"},
23
+ {rule_key: "B", operator: "==", question_reference_identifier: "given_birth", answer_reference_identifier: "1"}]
24
+ dependency_conditions_a.each{|attrs| (expect(Dependency.where(rule: "A").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.answer.reference_identifier).to eq(attrs[:answer_reference_identifier])) if attrs[:answer_reference_identifier] }
25
+ dependency_conditions_a.each{|attrs| (expect(Dependency.where(rule: "A").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.question.reference_identifier).to eq(attrs[:question_reference_identifier])) if attrs[:question_reference_identifier] }
26
+ dependency_conditions_a.each{|attrs| (expect(Dependency.where(rule: "A").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.operator).to eq(attrs[:operator])) if attrs[:operator] }
27
+ dependency_conditions_ab.each{|attrs| (expect(Dependency.where(rule: "A and B").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.answer.reference_identifier).to eq(attrs[:answer_reference_identifier])) if attrs[:answer_reference_identifier] }
28
+ dependency_conditions_ab.each{|attrs| (expect(Dependency.where(rule: "A and B").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.question.reference_identifier).to eq(attrs[:question_reference_identifier])) if attrs[:question_reference_identifier] }
29
+ dependency_conditions_ab.each{|attrs| (expect(Dependency.where(rule: "A and B").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.operator).to eq(attrs[:operator])) if attrs[:operator] }
30
+ end
31
+ it "question level dependencies from REDCap" do
32
+ file = "redcap_siblings.csv"
33
+ parser.parse File.read(File.join(Rails.root, '..', 'spec', 'fixtures', file)), file
34
+ expect(Dependency.count).to eq(1)
35
+ expect(DependencyCondition.count).to eq(1)
36
+ dependencies = [{rule: "A", question_reference_identifier: "sib1yob"}]
37
+ dependencies.each{|attrs| (expect(Dependency.where(rule: attrs[:rule]).first.question.reference_identifier).to eq(attrs[:question_reference_identifier])) if attrs[:question_reference_identifier] }
38
+ dependency_conditions = [{rule_key: "A", operator: ">", question_reference_identifier: "sibs"}]
39
+ dependency_conditions.each{|attrs| (expect(Dependency.where(rule: "A").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.question.reference_identifier).to eq(attrs[:question_reference_identifier])) if attrs[:question_reference_identifier] }
40
+ dependency_conditions.each{|attrs| (expect(Dependency.where(rule: "A").first.dependency_conditions.where(rule_key: attrs[:rule_key]).first.operator).to eq(attrs[:operator])) if attrs[:operator] }
41
+ end
42
+ it "different headers from REDCap" do
43
+ file = "redcap_new_headers.csv"
44
+ parser.parse File.read(File.join(Rails.root, '..', 'spec', 'fixtures', file)), file
45
+ expect(Survey.count).to eq(1)
46
+ expect(Question.count).to eq(1)
47
+ expect(Answer.count).to eq(2)
48
+ end
49
+ it "different whitespace from REDCap" do
50
+ file = "redcap_whitespace.csv"
51
+ parser.parse File.read(File.join(Rails.root, '..', 'spec', 'fixtures', file)), file
52
+ expect(Survey.count).to eq(1)
53
+ expect(Question.count).to eq(2)
54
+ expect(Answer.count).to eq(7)
55
+ answers = [{reference_identifier: "1", text: "Lexapro"},
56
+ {reference_identifier: "2", text: "Celexa"},
57
+ {reference_identifier: "3", text: "Prozac"},
58
+ {reference_identifier: "4", text: "Paxil"},
59
+ {reference_identifier: "5", text: "Zoloft"},
60
+ {reference_identifier: "0", text: "No"},
61
+ {reference_identifier: "1", text: "Yes"}]
62
+ answers.each{|attrs| (expect(Answer.where(text: attrs[:text]).first.reference_identifier).to eq(attrs[:reference_identifier]))}
63
+ end
64
+ end
65
+ context "helper methods" do
66
+ it "requires specific columns" do
67
+ # with standard fields
68
+ x = %w(field_units choices_or_calculations text_validation_type 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)
69
+ parser.missing_columns(x).should be_blank
70
+ # without field_units
71
+ y = %w(choices_or_calculations text_validation_type 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)
72
+ parser.missing_columns(y).should be_blank
73
+ # choices_or_calculations => choices_calculations_or_slider_labels
74
+ z = %w(field_units choices_calculations_or_slider_labels text_validation_type 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)
75
+ parser.missing_columns(z).should be_blank
76
+ # text_validation_type => text_validation_type_or_show_slider_number
77
+ a = %w(field_units choices_or_calculations text_validation_type_or_show_slider_number 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)
78
+ parser.missing_columns(a).should be_blank
79
+ end
80
+ it "decomposes dependency rules" do
81
+ # basic
82
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q12]="1"').should == {:rule => "A", :components => ['[f1_q12]="1"']}
83
+ # spacing
84
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q9] = "1"').should == {:rule => "A", :components => ['[f1_q9] = "1"']}
85
+ # and
86
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[pre_q88]="1" and [pre_q90]="1"').should == {:rule => "A and B", :components => ['[pre_q88]="1"', '[pre_q90]="1"']}
87
+ # or
88
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q111]="1" or [second_q111]="3"').should == {:rule => "A or B", :components => ['[second_q111]="1"', '[second_q111]="3"']}
89
+ # or and
90
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q100]="1" or [second_q100]="3" and [second_q101]="1"').should == {:rule => "A or B and C", :components => ['[second_q100]="1"', '[second_q100]="3"', '[second_q101]="1"']}
91
+ # and or
92
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[second_q4]="1" and [second_q11]="1" or [second_q11]="98"').should == {:rule => "A and B or C", :components => ['[second_q4]="1"', '[second_q11]="1"', '[second_q11]="98"']}
93
+ # or or or
94
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5"').should == {:rule => "A or B or C or D", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"']}
95
+ # and with different operator
96
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q15] >= 21 and [f1_q28] ="1"').should == {:rule => "A and B", :components => ['[f1_q15] >= 21', '[f1_q28] ="1"']}
97
+ end
98
+ it "decomposes nested dependency rules" do
99
+ # external parenthesis
100
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('([pre_q74]="1" or [pre_q74]="2" or [pre_q74]="4" or [pre_q74]="5") and [pre_q76]="2"').should == {:rule => "(A or B or C or D) and E", :components => ['[pre_q74]="1"', '[pre_q74]="2"', '[pre_q74]="4"', '[pre_q74]="5"', '[pre_q76]="2"']}
101
+ # internal parenthesis
102
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[f1_q10(4)]="1"').should == {:rule => "A", :components => ['[f1_q10(4)]="1"']}
103
+ # internal and external parenthesis
104
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('([f1_q7(11)] = "1" or [initial_52] = "1") and [pre_q76]="2"').should == {:rule => "(A or B) and C", :components => ['[f1_q7(11)] = "1"', '[initial_52] = "1"', '[pre_q76]="2"']}
105
+ end
106
+ it "decomposes shortcut dependency rules" do
107
+ # 'or' on the right of the operator
108
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_108] = "1" or "2"').should == {:rule => "A or B", :components => ['[initial_108] = "1"', '[initial_108] = "2"']}
109
+ # multiple 'or' on the right
110
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_52] = "1" or "2" or "3"').should == {:rule => "A or B or C", :components => ['[initial_52] = "1"', '[initial_52] = "2"', '[initial_52] = "3"']}
111
+ # commas on the right
112
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_189] = "1, 2, 3"').should == {:rule => "(A and B and C)", :components => ['[initial_189] = "1"', '[initial_189] = "2"', '[initial_189] = "3"']}
113
+ # multiple internal parenthesis on the left
114
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_rule('[initial_119(1)(2)(3)(4)(5)] = "1"').should == {:rule => "(A and B and C and D and E)", :components => ['[initial_119(1)] = "1"', '[initial_119(2)] = "1"', '[initial_119(3)] = "1"', '[initial_119(4)] = "1"', '[initial_119(5)] = "1"']}
115
+ end
116
+ it "decomposes components" do
117
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[initial_52] = "1"').should == {:question_reference => 'initial_52', :operator => '==', :answer_reference => '1'}
118
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[initial_119(2)] = "1"').should == {:question_reference => 'initial_119', :operator => '==', :answer_reference => '2'}
119
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component('[f1_q15] >= 21').should == {:question_reference => 'f1_q15', :operator => '>=', :integer_value => '21'}
120
+ # basic, blanks
121
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]=''").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => ''}
122
+ # basic, negatives
123
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[f1_q15]='-2'").should == {:question_reference => 'f1_q15', :operator => '==', :answer_reference => '-2'}
124
+ # internal parenthesis
125
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[hiprep_heat2(97)] = '1'").should == {:question_reference => 'hiprep_heat2', :operator => '==', :answer_reference => '97'}
126
+ Dependency.new.extend(SurveyorRedcapParserDependencyMethods).decompose_component("[hi_event1_type] <> ''").should == {:question_reference => 'hi_event1_type', :operator => '!=', :answer_reference => ''}
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,126 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Surveyor::Unparser do
4
+ before(:each) do
5
+ @survey = Survey.new(:title => "Simple survey", :description => "very simple")
6
+ @section = @survey.sections.build(:title => "Simple section")
7
+ end
8
+
9
+ it "should unparse a basic survey, section, and question" do
10
+ q1 = @section.questions.build(:text => "What is your favorite color?", :reference_identifier => 1, :pick => :one)
11
+ a11 = q1.answers.build(:text => "red", :response_class => "answer", :reference_identifier => 1, :question => q1)
12
+ a12 = q1.answers.build(:text => "green", :response_class => "answer", :reference_identifier => 2, :question => q1)
13
+ a13 = q1.answers.build(:text => "blue", :response_class => "answer", :reference_identifier => 3, :question => q1)
14
+ a14 = q1.answers.build(:text => "Other", :response_class => "string", :reference_identifier => 4, :question => q1)
15
+ a15 = q1.answers.build(:text => "Omit", :reference_identifier => 5, :question => q1, :is_exclusive => true)
16
+ q2 = @section.questions.build(:text => "What is your name?", :reference_identifier => 2, :pick => :none)
17
+ a21 = q2.answers.build(:response_class => "string", :reference_identifier => 1, :question => q2)
18
+ Surveyor::Unparser.unparse(@survey).should ==
19
+ <<-dsl
20
+ survey "Simple survey", :description=>"very simple" do
21
+ section "Simple section" do
22
+
23
+ q_1 "What is your favorite color?", :pick=>"one"
24
+ a_1 "red"
25
+ a_2 "green"
26
+ a_3 "blue"
27
+ a_4 :other, :string
28
+ a_5 :omit
29
+
30
+ q_2 "What is your name?"
31
+ a_1 :string
32
+ end
33
+ end
34
+ dsl
35
+ end
36
+
37
+ it "should unparse groups" do
38
+ q3 = @section.questions.build(:text => "Happy?")
39
+ a31 = q3.answers.build(:text => "Yes", :question => q3)
40
+ a32 = q3.answers.build(:text => "Maybe", :question => q3)
41
+ a33 = q3.answers.build(:text => "No", :question => q3)
42
+
43
+ q4 = @section.questions.build(:text => "Energized?")
44
+ a41 = q4.answers.build(:text => "Yes", :question => q4)
45
+ a42 = q4.answers.build(:text => "Maybe", :question => q4)
46
+ a43 = q4.answers.build(:text => "No", :question => q4)
47
+
48
+ g1 = q3.build_question_group(:text => "How are you feeling?", :display_type => "grid")
49
+ q4.question_group = g1
50
+ g1.questions = [q3, q4]
51
+
52
+ q5 = @section.questions.build(:text => "Model")
53
+ a51 = q5.answers.build(:response_class => "string", :question => q3)
54
+
55
+ g2 = q5.build_question_group(:text => "Tell us about the cars you own", :display_type => "repeater")
56
+ g2.questions = [q5]
57
+
58
+ Surveyor::Unparser.unparse(@survey).should ==
59
+ <<-dsl
60
+ survey "Simple survey", :description=>"very simple" do
61
+ section "Simple section" do
62
+
63
+ grid "How are you feeling?" do
64
+ a "Yes"
65
+ a "Maybe"
66
+ a "No"
67
+ q "Happy?"
68
+ q "Energized?"
69
+ end
70
+
71
+ repeater "Tell us about the cars you own" do
72
+ q "Model"
73
+ a :string
74
+ end
75
+ end
76
+ end
77
+ dsl
78
+ end
79
+
80
+ it "should unparse a basic survey, section, and question" do
81
+ q6 = @section.questions.build(:text => "What... is your name? (e.g. It is 'Arthur', King of the Britons)", :reference_identifier => "montypython3")
82
+ a61 = q6.answers.build(:response_class => "string", :reference_identifier => 1, :question => q6)
83
+
84
+ q7 = @section.questions.build(:text => "What... is your quest? (e.g. To seek the Holy Grail)", :display_type => "label")
85
+ d1 = q7.build_dependency(:rule => "A", :question => q7)
86
+ dc1 = d1.dependency_conditions.build(:dependency => d1, :question => q6, :answer => a61, :operator => "==", :string_value => "It is 'Arthur', King of the Britons", :rule_key => "A")
87
+
88
+ q8 = @section.questions.build(:text => "How many pets do you own?")
89
+ a81 = q8.answers.build(:response_class => "integer", :question => q8)
90
+ v1 = a81.validations.build(:rule => "A", :answer => a81)
91
+ vc1 = v1.validation_conditions.build(:operator => ">=", :integer_value => 0, :validation => v1, :rule_key => "A")
92
+
93
+ q9 = @section.questions.build(:text => "Pick your favorite date AND time", :custom_renderer => "/partials/custom_question")
94
+ a91 = q9.answers.build(:response_class => "datetime", :question => q9)
95
+
96
+ q10 = @section.questions.build(:text => "What time do you usually take a lunch break?", :reference_identifier => "time_lunch")
97
+ a101 = q10.answers.build(:response_class => "time", :reference_identifier => 1, :question => q10)
98
+
99
+ Surveyor::Unparser.unparse(@survey).should ==
100
+ <<-dsl
101
+ survey "Simple survey", :description=>"very simple" do
102
+ section "Simple section" do
103
+
104
+ q_montypython3 "What... is your name? (e.g. It is 'Arthur', King of the Britons)"
105
+ a_1 :string
106
+
107
+ label "What... is your quest? (e.g. To seek the Holy Grail)"
108
+ dependency :rule=>"A"
109
+ condition_A :q_montypython3, "==", {:string_value=>"It is 'Arthur', King of the Britons", :answer_reference=>"1"}
110
+
111
+ q "How many pets do you own?"
112
+ a :integer
113
+ validation :rule=>"A"
114
+ condition_A ">=", :integer_value=>0
115
+
116
+ q "Pick your favorite date AND time", :custom_renderer=>"/partials/custom_question"
117
+ a :datetime
118
+
119
+ q_time_lunch "What time do you usually take a lunch break?"
120
+ a_1 :time
121
+ end
122
+ end
123
+ dsl
124
+ end
125
+ end
126
+
@@ -0,0 +1,144 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ describe Answer do
5
+ let(:answer){ FactoryGirl.create(:answer) }
6
+
7
+ context "when creating" do
8
+ it { answer.should be_valid }
9
+ it "deletes validation when deleted" do
10
+ v_id = FactoryGirl.create(:validation, :answer => answer).id
11
+ answer.destroy
12
+ Validation.find_by_id(v_id).should be_nil
13
+ end
14
+ end
15
+
16
+ context "with mustache text substitution" do
17
+ require 'mustache'
18
+ let(:mustache_context){ Class.new(::Mustache){ def site; "Northwestern"; end; def foo; "bar"; end } }
19
+ it "subsitutes Mustache context variables" do
20
+ answer.text = "You are in {{site}}"
21
+ answer.in_context(answer.text, mustache_context).should == "You are in Northwestern"
22
+ answer.text_for(nil, mustache_context).should == "You are in Northwestern"
23
+
24
+ answer.help_text = "{{site}} is your site"
25
+ answer.in_context(answer.help_text, mustache_context).should == "Northwestern is your site"
26
+ answer.help_text_for(mustache_context).should == "Northwestern is your site"
27
+
28
+ answer.default_value = "{{site}}"
29
+ answer.in_context(answer.default_value, mustache_context).should == "Northwestern"
30
+ answer.default_value_for(mustache_context).should == "Northwestern"
31
+ end
32
+ end
33
+
34
+ context "with translations" do
35
+ require 'yaml'
36
+ let(:survey){ FactoryGirl.create(:survey) }
37
+ let(:survey_section){ FactoryGirl.create(:survey_section) }
38
+ let(:survey_translation){
39
+ FactoryGirl.create(:survey_translation, :locale => :es, :translation => {
40
+ :questions => {
41
+ :name => {
42
+ :answers => {
43
+ :name => {
44
+ :help_text => "Mi nombre es..."
45
+ }
46
+ }
47
+ }
48
+ }
49
+ }.to_yaml)
50
+ }
51
+ let(:question){ FactoryGirl.create(:question, :reference_identifier => "name") }
52
+ before do
53
+ answer.reference_identifier = "name"
54
+ answer.help_text = "My name is..."
55
+ answer.text = nil
56
+ answer.question = question
57
+ question.survey_section = survey_section
58
+ survey_section.survey = survey
59
+ survey.translations << survey_translation
60
+ end
61
+ it "returns its own translation" do
62
+ answer.translation(:es)[:help_text].should == "Mi nombre es..."
63
+ end
64
+ it "returns translations in views" do
65
+ answer.help_text_for(nil, :es).should == "Mi nombre es..."
66
+ end
67
+ it "returns its own default values" do
68
+ answer.translation(:de).should == {"text" => nil, "help_text" => "My name is...", "default_value" => nil}
69
+ end
70
+ it "returns default values in views" do
71
+ answer.help_text_for(nil, :de).should == "My name is..."
72
+ end
73
+ end
74
+
75
+ context "handling strings" do
76
+ it "#split preserves strings" do
77
+ answer.split(answer.text).should == "My favorite color is clear"
78
+ end
79
+ it "#split(:pre) preserves strings" do
80
+ answer.split(answer.text, :pre).should == "My favorite color is clear"
81
+ end
82
+ it "#split(:post) preserves strings" do
83
+ answer.split(answer.text, :post).should == ""
84
+ end
85
+ it "#split splits strings" do
86
+ answer.text = "before|after|extra"
87
+ answer.split(answer.text).should == "before|after|extra"
88
+ end
89
+ it "#split(:pre) splits strings" do
90
+ answer.text = "before|after|extra"
91
+ answer.split(answer.text, :pre).should == "before"
92
+ end
93
+ it "#split(:post) splits strings" do
94
+ answer.text = "before|after|extra"
95
+ answer.split(answer.text, :post).should == "after|extra"
96
+ end
97
+ end
98
+
99
+ context "for views" do
100
+ it "#text_for with #display_type == image" do
101
+ answer.text = "rails.png"
102
+ answer.display_type = :image
103
+ answer.text_for.should =~ /<img alt="Rails" src="\/(images|assets)\/rails\.png" \/>/
104
+ end
105
+ it "#text_for with #display_type == hidden_label" do
106
+ answer.text = "Red"
107
+ answer.text_for.should == "Red"
108
+ answer.display_type = "hidden_label"
109
+ answer.text_for.should == ""
110
+ end
111
+ it "#default_value_for"
112
+ it "#help_text_for"
113
+ it "reports DOM ready #css_class from #custom_class" do
114
+ answer.custom_class = "foo bar"
115
+ answer.css_class.should == "foo bar"
116
+ end
117
+ it "reports DOM ready #css_class from #custom_class and #is_exclusive" do
118
+ answer.custom_class = "foo bar"
119
+ answer.is_exclusive = true
120
+ answer.css_class.should == "exclusive foo bar"
121
+ end
122
+ it "#text_for preserves strings" do
123
+ answer.text_for.should == "My favorite color is clear"
124
+ end
125
+ it "#text_for(:pre) preserves strings" do
126
+ answer.text_for(:pre).should == "My favorite color is clear"
127
+ end
128
+ it "#text_for(:post) preserves strings" do
129
+ answer.text_for(:post).should == ""
130
+ end
131
+ it "#text_for splits strings" do
132
+ answer.text = "before|after|extra"
133
+ answer.text_for.should == "before|after|extra"
134
+ end
135
+ it "#text_for(:pre) splits strings" do
136
+ answer.text = "before|after|extra"
137
+ answer.text_for(:pre).should == "before"
138
+ end
139
+ it "#text_for(:post) splits strings" do
140
+ answer.text = "before|after|extra"
141
+ answer.text_for(:post).should == "after|extra"
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,428 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+
4
+ describe DependencyCondition do
5
+ it "should have a list of operators" do
6
+ %w(== != < > <= >=).each do |operator|
7
+ DependencyCondition.operators.include?(operator).should be_true
8
+ end
9
+ end
10
+
11
+ describe "instance" do
12
+ before(:each) do
13
+ @dependency_condition = DependencyCondition.new(
14
+ :dependency_id => 1, :question_id => 45, :operator => "==",
15
+ :answer_id => 23, :rule_key => "A")
16
+ end
17
+
18
+ it "should be valid" do
19
+ @dependency_condition.should be_valid
20
+ end
21
+
22
+ it "should be invalid without a parent dependency_id, question_id" do
23
+ # this causes issues with building and saving
24
+ # @dependency_condition.dependency_id = nil
25
+ # @dependency_condition.should have(1).errors_on(:dependency_id)
26
+ # @dependency_condition.question_id = nil
27
+ # @dependency_condition.should have(1).errors_on(:question_id)
28
+ end
29
+
30
+ it "should be invalid without an operator" do
31
+ @dependency_condition.operator = nil
32
+ @dependency_condition.should have(2).errors_on(:operator)
33
+ end
34
+
35
+ it "should be invalid without a rule_key" do
36
+ @dependency_condition.should be_valid
37
+ @dependency_condition.rule_key = nil
38
+ @dependency_condition.should_not be_valid
39
+ @dependency_condition.should have(1).errors_on(:rule_key)
40
+ end
41
+
42
+ it "should have unique rule_key within the context of a dependency" do
43
+ @dependency_condition.should be_valid
44
+ DependencyCondition.create(
45
+ :dependency_id => 2, :question_id => 46, :operator => "==",
46
+ :answer_id => 14, :rule_key => "B")
47
+ @dependency_condition.rule_key = "B" # rule key uniquness is scoped by dependency_id
48
+ @dependency_condition.dependency_id = 2
49
+ @dependency_condition.should_not be_valid
50
+ @dependency_condition.should have(1).errors_on(:rule_key)
51
+ end
52
+
53
+ it "should have an operator in DependencyCondition.operators" do
54
+ DependencyCondition.operators.each do |o|
55
+ @dependency_condition.operator = o
56
+ @dependency_condition.should have(0).errors_on(:operator)
57
+ end
58
+ @dependency_condition.operator = "#"
59
+ @dependency_condition.should have(1).error_on(:operator)
60
+ end
61
+ it "should have a properly formed count operator" do
62
+ %w(count>1 count<1 count>=1 count<=1 count==1 count!=1).each do |o|
63
+ @dependency_condition.operator = o
64
+ @dependency_condition.should have(0).errors_on(:operator)
65
+ end
66
+ %w(count> count< count>= count<= count== count!=).each do |o|
67
+ @dependency_condition.operator = o
68
+ @dependency_condition.should have(1).errors_on(:operator)
69
+ end
70
+ %w(count=1 count><1 count<>1 count!1 count!!1 count=>1 count=<1).each do |o|
71
+ @dependency_condition.operator = o
72
+ @dependency_condition.should have(1).errors_on(:operator)
73
+ end
74
+ %w(count= count>< count<> count! count!! count=> count=< count> count< count>= count<= count== count!=).each do |o|
75
+ @dependency_condition.operator = o
76
+ @dependency_condition.should have(1).errors_on(:operator)
77
+ end
78
+ end
79
+ end
80
+
81
+ it "returns true for != with no responses" do
82
+ question = FactoryGirl.create(:question)
83
+ dependency_condition = FactoryGirl.create(:dependency_condition, :rule_key => "C", :question => question)
84
+ rs = FactoryGirl.create(:response_set)
85
+ dependency_condition.to_hash(rs).should == {:C => false}
86
+ end
87
+
88
+
89
+ it "should not assume that Response#as is not nil" do
90
+ # q_HEIGHT_FT "Portion of height in whole feet (e.g., 5)",
91
+ # :pick=>:one
92
+ # a :integer
93
+ # a_neg_1 "Refused"
94
+ # a_neg_2 "Don't know"
95
+ # label "Provided value is outside of the suggested range (4 to 7 feet). This value is admissible, but you may wish to verify."
96
+ # dependency :rule=>"A or B"
97
+ # condition_A :q_HEIGHT_FT, "<", {:integer_value => "4"}
98
+ # condition_B :q_HEIGHT_FT, ">", {:integer_value => "7"}
99
+
100
+ answer = FactoryGirl.create(:answer, :response_class => :integer)
101
+ @dependency_condition = DependencyCondition.new(
102
+ :dependency => FactoryGirl.create(:dependency),
103
+ :question => answer.question,
104
+ :answer => answer,
105
+ :operator => ">",
106
+ :integer_value => 4,
107
+ :rule_key => "A")
108
+
109
+ response = FactoryGirl.create(:response, :answer => answer, :question => answer.question)
110
+ response_set = response.response_set
111
+ response.integer_value.should == nil
112
+
113
+ @dependency_condition.to_hash(response_set).should == {:A => false}
114
+ end
115
+
116
+ describe "evaluate '==' operator" do
117
+ before(:each) do
118
+ @a = FactoryGirl.create(:answer, :response_class => "answer")
119
+ @b = FactoryGirl.create(:answer, :question => @a.question)
120
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
121
+ @rs = @r.response_set
122
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "D")
123
+ @dc.as(:answer).should == @r.as(:answer)
124
+ end
125
+
126
+ it "with checkbox/radio type response" do
127
+ @dc.to_hash(@rs).should == {:D => true}
128
+ @dc.answer = @b
129
+ @dc.to_hash(@rs).should == {:D => false}
130
+ end
131
+
132
+ it "with string value response" do
133
+ @a.update_attributes(:response_class => "string")
134
+ @r.update_attributes(:string_value => "hello123")
135
+ @dc.string_value = "hello123"
136
+ @dc.to_hash(@rs).should == {:D => true}
137
+ @r.update_attributes(:string_value => "foo_abc")
138
+ @dc.to_hash(@rs).should == {:D => false}
139
+ end
140
+
141
+ it "with a text value response" do
142
+ @a.update_attributes(:response_class => "text")
143
+ @r.update_attributes(:text_value => "hello this is some text for comparison")
144
+ @dc.text_value = "hello this is some text for comparison"
145
+ @dc.to_hash(@rs).should == {:D => true}
146
+ @r.update_attributes(:text_value => "Not the same text")
147
+ @dc.to_hash(@rs).should == {:D => false}
148
+ end
149
+
150
+ it "with an integer value response" do
151
+ @a.update_attributes(:response_class => "integer")
152
+ @r.update_attributes(:integer_value => 10045)
153
+ @dc.integer_value = 10045
154
+ @dc.to_hash(@rs).should == {:D => true}
155
+ @r.update_attributes(:integer_value => 421)
156
+ @dc.to_hash(@rs).should == {:D => false}
157
+ end
158
+
159
+ it "with a float value response" do
160
+ @a.update_attributes(:response_class => "float")
161
+ @r.update_attributes(:float_value => 121.1)
162
+ @dc.float_value = 121.1
163
+ @dc.to_hash(@rs).should == {:D => true}
164
+ @r.update_attributes(:float_value => 130.123)
165
+ @dc.to_hash(@rs).should == {:D => false}
166
+ end
167
+ end
168
+
169
+ describe "evaluate '!=' operator" do
170
+ before(:each) do
171
+ @a = FactoryGirl.create(:answer)
172
+ @b = FactoryGirl.create(:answer, :question => @a.question)
173
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
174
+ @rs = @r.response_set
175
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "!=", :rule_key => "E")
176
+ @dc.as(:answer).should == @r.as(:answer)
177
+ end
178
+
179
+ it "with checkbox/radio type response" do
180
+ @dc.to_hash(@rs).should == {:E => false}
181
+ @dc.answer_id = @a.id.to_i+1
182
+ @dc.to_hash(@rs).should == {:E => true}
183
+ end
184
+
185
+ it "with string value response" do
186
+ @a.update_attributes(:response_class => "string")
187
+ @r.update_attributes(:string_value => "hello123")
188
+ @dc.string_value = "hello123"
189
+ @dc.to_hash(@rs).should == {:E => false}
190
+ @r.update_attributes(:string_value => "foo_abc")
191
+ @dc.to_hash(@rs).should == {:E => true}
192
+ end
193
+
194
+ it "with a text value response" do
195
+ @a.update_attributes(:response_class => "text")
196
+ @r.update_attributes(:text_value => "hello this is some text for comparison")
197
+ @dc.text_value = "hello this is some text for comparison"
198
+ @dc.to_hash(@rs).should == {:E => false}
199
+ @r.update_attributes(:text_value => "Not the same text")
200
+ @dc.to_hash(@rs).should == {:E => true}
201
+ end
202
+
203
+ it "with an integer value response" do
204
+ @a.update_attributes(:response_class => "integer")
205
+ @r.update_attributes(:integer_value => 10045)
206
+ @dc.integer_value = 10045
207
+ @dc.to_hash(@rs).should == {:E => false}
208
+ @r.update_attributes(:integer_value => 421)
209
+ @dc.to_hash(@rs).should == {:E => true}
210
+ end
211
+
212
+ it "with a float value response" do
213
+ @a.update_attributes(:response_class => "float")
214
+ @r.update_attributes(:float_value => 121.1)
215
+ @dc.float_value = 121.1
216
+ @dc.to_hash(@rs).should == {:E => false}
217
+ @r.update_attributes(:float_value => 130.123)
218
+ @dc.to_hash(@rs).should == {:E => true}
219
+ end
220
+ end
221
+
222
+ describe "evaluate the '<' operator" do
223
+ before(:each) do
224
+ @a = FactoryGirl.create(:answer)
225
+ @b = FactoryGirl.create(:answer, :question => @a.question)
226
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
227
+ @rs = @r.response_set
228
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "<", :rule_key => "F")
229
+ @dc.as(:answer).should == @r.as(:answer)
230
+ end
231
+
232
+ it "with an integer value response" do
233
+ @a.update_attributes(:response_class => "integer")
234
+ @r.update_attributes(:integer_value => 50)
235
+ @dc.integer_value = 100
236
+ @dc.to_hash(@rs).should == {:F => true}
237
+ @r.update_attributes(:integer_value => 421)
238
+ @dc.to_hash(@rs).should == {:F => false}
239
+ end
240
+
241
+ it "with a float value response" do
242
+ @a.update_attributes(:response_class => "float")
243
+ @r.update_attributes(:float_value => 5.1)
244
+ @dc.float_value = 121.1
245
+ @dc.to_hash(@rs).should == {:F => true}
246
+ @r.update_attributes(:float_value => 130.123)
247
+ @dc.to_hash(@rs).should == {:F => false}
248
+ end
249
+ end
250
+
251
+ describe "evaluate the '<=' operator" do
252
+ before(:each) do
253
+ @a = FactoryGirl.create(:answer)
254
+ @b = FactoryGirl.create(:answer, :question => @a.question)
255
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
256
+ @rs = @r.response_set
257
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "<=", :rule_key => "G")
258
+ @dc.as(:answer).should == @r.as(:answer)
259
+ end
260
+
261
+ it "with an integer value response" do
262
+ @a.update_attributes(:response_class => "integer")
263
+ @r.update_attributes(:integer_value => 50)
264
+ @dc.integer_value = 100
265
+ @dc.to_hash(@rs).should == {:G => true}
266
+ @r.update_attributes(:integer_value => 100)
267
+ @dc.to_hash(@rs).should == {:G => true}
268
+ @r.update_attributes(:integer_value => 421)
269
+ @dc.to_hash(@rs).should == {:G => false}
270
+ end
271
+
272
+ it "with a float value response" do
273
+ @a.update_attributes(:response_class => "float")
274
+ @r.update_attributes(:float_value => 5.1)
275
+ @dc.float_value = 121.1
276
+ @dc.to_hash(@rs).should == {:G => true}
277
+ @r.update_attributes(:float_value => 121.1)
278
+ @dc.to_hash(@rs).should == {:G => true}
279
+ @r.update_attributes(:float_value => 130.123)
280
+ @dc.to_hash(@rs).should == {:G => false}
281
+ end
282
+
283
+ end
284
+
285
+ describe "evaluate the '>' operator" do
286
+ before(:each) do
287
+ @a = FactoryGirl.create(:answer)
288
+ @b = FactoryGirl.create(:answer, :question => @a.question)
289
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
290
+ @rs = @r.response_set
291
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => ">", :rule_key => "H")
292
+ @dc.as(:answer).should == @r.as(:answer)
293
+ end
294
+
295
+ it "with an integer value response" do
296
+ @a.update_attributes(:response_class => "integer")
297
+ @r.update_attributes(:integer_value => 50)
298
+ @dc.integer_value = 100
299
+ @dc.to_hash(@rs).should == {:H => false}
300
+ @r.update_attributes(:integer_value => 421)
301
+ @dc.to_hash(@rs).should == {:H => true}
302
+ end
303
+
304
+ it "with a float value response" do
305
+ @a.update_attributes(:response_class => "float")
306
+ @r.update_attributes(:float_value => 5.1)
307
+ @dc.float_value = 121.1
308
+ @dc.to_hash(@rs).should == {:H => false}
309
+ @r.update_attributes(:float_value => 130.123)
310
+ @dc.to_hash(@rs).should == {:H => true}
311
+ end
312
+ end
313
+
314
+ describe "evaluate the '>=' operator" do
315
+ before(:each) do
316
+ @a = FactoryGirl.create(:answer)
317
+ @b = FactoryGirl.create(:answer, :question => @a.question)
318
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a)
319
+ @rs = @r.response_set
320
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => ">=", :rule_key => "I")
321
+ @dc.as(:answer).should == @r.as(:answer)
322
+ end
323
+
324
+ it "with an integer value response" do
325
+ @a.update_attributes(:response_class => "integer")
326
+ @r.update_attributes(:integer_value => 50)
327
+ @dc.integer_value = 100
328
+ @dc.to_hash(@rs).should == {:I => false}
329
+ @r.update_attributes(:integer_value => 100)
330
+ @dc.to_hash(@rs).should == {:I => true}
331
+ @r.update_attributes(:integer_value => 421)
332
+ @dc.to_hash(@rs).should == {:I => true}
333
+ end
334
+
335
+ it "with a float value response" do
336
+ @a.update_attributes(:response_class => "float")
337
+ @r.update_attributes(:float_value => 5.1)
338
+ @dc.float_value = 121.1
339
+ @dc.to_hash(@rs).should == {:I => false}
340
+ @r.update_attributes(:float_value => 121.1)
341
+ @dc.to_hash(@rs).should == {:I => true}
342
+ @r.update_attributes(:float_value => 130.123)
343
+ @dc.to_hash(@rs).should == {:I => true}
344
+ end
345
+ end
346
+
347
+ describe "evaluating with response_class string" do
348
+ it "should compare answer ids when the dependency condition string_value is nil" do
349
+ @a = FactoryGirl.create(:answer, :response_class => "string")
350
+ @b = FactoryGirl.create(:answer, :question => @a.question)
351
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a, :string_value => "")
352
+ @rs = @r.response_set
353
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "J")
354
+ @dc.to_hash(@rs).should == {:J => true}
355
+ end
356
+
357
+ it "should compare strings when the dependency condition string_value is not nil, even if it is blank" do
358
+ @a = FactoryGirl.create(:answer, :response_class => "string")
359
+ @b = FactoryGirl.create(:answer, :question => @a.question)
360
+ @r = FactoryGirl.create(:response, :question => @a.question, :answer => @a, :string_value => "foo")
361
+ @rs = @r.response_set
362
+ @dc = FactoryGirl.create(:dependency_condition, :question => @a.question, :answer => @a, :operator => "==", :rule_key => "K", :string_value => "foo")
363
+ @dc.to_hash(@rs).should == {:K => true}
364
+
365
+ @r.update_attributes(:string_value => "")
366
+ @dc.string_value = ""
367
+ @dc.to_hash(@rs).should == {:K => true}
368
+ end
369
+ end
370
+
371
+ describe "evaluate 'count' operator" do
372
+ before(:each) do
373
+ @q = FactoryGirl.create(:question)
374
+ @dc = DependencyCondition.new(:operator => "count>2", :rule_key => "M", :question => @q)
375
+ @as = []
376
+ 3.times do
377
+ @as << FactoryGirl.create(:answer, :question => @q, :response_class => "answer")
378
+ end
379
+ @rs = FactoryGirl.create(:response_set)
380
+ @as.slice(0,2).each do |a|
381
+ FactoryGirl.create(:response, :question => @q, :answer => a, :response_set => @rs)
382
+ end
383
+ @rs.save
384
+ end
385
+
386
+ it "with operator with >" do
387
+ @dc.to_hash(@rs).should == {:M => false}
388
+ FactoryGirl.create(:response, :question => @q, :answer => @as.last, :response_set => @rs)
389
+ @rs.reload.responses.count.should == 3
390
+ @dc.to_hash(@rs.reload).should == {:M => true}
391
+ end
392
+
393
+ it "with operator with <" do
394
+ @dc.operator = "count<2"
395
+ @dc.to_hash(@rs).should == {:M => false}
396
+ @dc.operator = "count<3"
397
+ @dc.to_hash(@rs).should == {:M => true}
398
+ end
399
+
400
+ it "with operator with <=" do
401
+ @dc.operator = "count<=1"
402
+ @dc.to_hash(@rs).should == {:M => false}
403
+ @dc.operator = "count<=2"
404
+ @dc.to_hash(@rs).should == {:M => true}
405
+ @dc.operator = "count<=3"
406
+ @dc.to_hash(@rs).should == {:M => true}
407
+ end
408
+
409
+ it "with operator with >=" do
410
+ @dc.operator = "count>=1"
411
+ @dc.to_hash(@rs).should == {:M => true}
412
+ @dc.operator = "count>=2"
413
+ @dc.to_hash(@rs).should == {:M => true}
414
+ @dc.operator = "count>=3"
415
+ @dc.to_hash(@rs).should == {:M => false}
416
+ end
417
+
418
+ it "with operator with !=" do
419
+ @dc.operator = "count!=1"
420
+ @dc.to_hash(@rs).should == {:M => true}
421
+ @dc.operator = "count!=2"
422
+ @dc.to_hash(@rs).should == {:M => false}
423
+ @dc.operator = "count!=3"
424
+ @dc.to_hash(@rs).should == {:M => true}
425
+ end
426
+ end
427
+
428
+ end