surveyor_gui 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,53 @@
1
+ module SurveyorGui
2
+ module Models
3
+ module DependencyConditionMethods
4
+
5
+ def self.included(base)
6
+ base.send :attr_accessible, :dependency_id, :rule_key, :question_id, :operator, :answer_id,
7
+ :float_value, :integer_value, :join_operator, :column_id, :columns_attributes if defined? ActiveModel::MassAssignmentSecurity
8
+ base.send :belongs_to, :dependency
9
+ base.send :belongs_to, :column
10
+ base.send :default_scope, lambda{ base.order('rule_key') }
11
+ end
12
+
13
+ def join_operator
14
+ rule = Rules.new(self)
15
+ return rule.join_operator(self.rule_key)
16
+ end
17
+
18
+ def join_operator=(x)
19
+ end
20
+
21
+
22
+ class Rules
23
+
24
+ attr_accessor :rules
25
+
26
+ def initialize(dependency_condition)
27
+ if dependency_condition.dependency
28
+ @rules = dependency_condition.dependency.rule.split(' ')
29
+ else
30
+ @rules = []
31
+ end
32
+ end
33
+
34
+ def join_operator(rule_key)
35
+ _find_join_operator(_find_rule_key(rule_key))
36
+ end
37
+
38
+ private
39
+
40
+ def _find_rule_key(rule_key)
41
+ rules.index(rule_key)
42
+ end
43
+
44
+ def _find_join_operator(idx)
45
+ if idx && idx > 0
46
+ rules[idx-1]
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,83 @@
1
+ module SurveyorGui
2
+ module Models
3
+ module DependencyMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :attr_accessible, :dependency_conditions_attributes if defined? ActiveModel::MassAssignmentSecurity
7
+ base.send :accepts_nested_attributes_for, :dependency_conditions, :reject_if => lambda { |d| d[:operator].blank?}, :allow_destroy => true
8
+
9
+ # # HACK: Remove the existing validates_numericality_of block. By default in Surveyor, it doesn't account for
10
+ # # question_id/question_group_id being nil when adding a new record - it never needed to. However, Surveyor_gui
11
+ # # adds accepts_nested_attributes_for dependency to the question model, which triggers the dependency validations
12
+ # # This means we do need to account for adding new records, and the validation has to be modified. Unfortunately,
13
+ # # in Rails 3.2 there is no easy way to modify an existing validation. We have to hack it out and replace it.
14
+ base.class_eval do
15
+ reset_callbacks(:validate)
16
+
17
+ def dependency_conditions_attributes=(dac)
18
+ dac = _set_rule_keys(dac)
19
+ assign_nested_attributes_for_collection_association(:dependency_conditions, dac)
20
+ _set_dependency_rule(dac)
21
+ end
22
+
23
+
24
+ end
25
+ base.send :validates_numericality_of, :question_id, :if => Proc.new { |d| d.question_group_id.nil? && !d.new_record? }
26
+ base.send :validates_numericality_of, :question_group_id, :if => Proc.new { |d| d.question_id.nil? && !d.new_record?}
27
+ base.send :validates_presence_of, :rule
28
+ base.send :validates_format_of, :rule, :with => /\A(?:and|or|\)|\(|[A-Z]|\s)+\Z/ #TODO properly formed parenthesis etc.
29
+
30
+ # Attribute aliases
31
+ #base.send :alias_attribute, :dependent_question_id, :question_id
32
+ end
33
+
34
+ # need to overwrite the following methods because they rely on the nil? method. Actually, the parameter comes back as a string
35
+ # and even an empty string will not evaluate to nil. Changed to blank? instead.
36
+ def question_group_id=(i)
37
+ write_attribute(:question_id, nil) unless i.blank? #i.nil?
38
+ write_attribute(:question_group_id, i)
39
+ end
40
+
41
+ def question_id=(i)
42
+ write_attribute(:question_group_id, nil) unless i.blank? #i.nil?
43
+ write_attribute(:question_id, i)
44
+ end
45
+
46
+ private
47
+
48
+ def _set_rule_keys(dac)
49
+ dac_not_z = _remove_z_rules(dac)
50
+ dac_not_z.each_with_index do |(k, v), index|
51
+ v["rule_key"] = ("A".ord + index).chr
52
+ end
53
+ dac.merge(dac_not_z)
54
+ end
55
+
56
+ def _remove_z_rules(dac)
57
+ dac.reject{|k, v| v["rule_key"]=="Z"}
58
+ end
59
+
60
+ def _set_dependency_rule(dac)
61
+ rule = _derive_rule(dac)
62
+ write_attribute(:rule, rule)
63
+ end
64
+
65
+ def _derive_rule(dac)
66
+ rule = ''
67
+ dac.each_with_index do |(k, v), i|
68
+ rule += (i==0 ? '' : v["join_operator"] + ' ') + v["rule_key"] + ' '
69
+ end
70
+ rule.rstrip
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ #class Dependency < ActiveRecord::Base
77
+ # include Surveyor::Models::DependencyMethods
78
+ #
79
+ # attr_accessible :question_id, :question_group, :rule, :dependency_conditions_attributes
80
+ # accepts_nested_attributes_for :dependency_conditions, :reject_if => lambda { |d| d[:operator].blank?}, :allow_destroy => true
81
+ # belongs_to :question
82
+ # validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|[0-9]+|\s)+$/ #TODO properly formed parenthesis etc.
83
+ #end
@@ -0,0 +1,11 @@
1
+ module SurveyorGui
2
+ module Models
3
+ module QuestionAndGroupSharedMethods
4
+ def controlling_questions
5
+ dependencies = []
6
+ dependencies << self.dependency
7
+ dependencies.map{|d| d.dependency_conditions.map{|dc| dc.question.part_of_group? ? dc.question.question_group.questions.last : dc.question}}.flatten.uniq
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ module SurveyorGui
2
+ module Models
3
+ module QuestionGroupMethods
4
+ include QuestionAndGroupSharedMethods
5
+ def self.included(base)
6
+ base.send :attr_accessor, :is_mandatory, :survey_section_id
7
+ base.send :attr_writer, :question_id
8
+ base.send :attr_accessible, :questions_attributes if
9
+ defined? ActiveModel::MassAssignmentSecurity
10
+ base.send :accepts_nested_attributes_for, :questions, :allow_destroy => true
11
+ base.send :has_many, :columns
12
+ base.send :accepts_nested_attributes_for, :columns, :allow_destroy => true
13
+ base.send :accepts_nested_attributes_for, :dependency, :reject_if => lambda { |d| d[:rule].blank?}, :allow_destroy => true
14
+ end
15
+
16
+ def question_type_id
17
+ if !@question_type_id
18
+ @question_type = case display_type
19
+ when "inline"
20
+ :group_inline
21
+ when "default"
22
+ :group_default
23
+ when "repeater"
24
+ :repeater
25
+ end
26
+ else
27
+ @question_type_id
28
+ end
29
+ end
30
+
31
+ def question_type_id=(question_type_id)
32
+ case question_type_id
33
+ when "group_default"
34
+ write_attribute(:display_type, "default")
35
+ when "group_inline"
36
+ write_attribute(:display_type, "inline")
37
+ when "repeater"
38
+ write_attribute(:display_type, "repeater")
39
+ end
40
+ @question_type_id = question_type_id
41
+ end
42
+
43
+ def trim_columns(qty_to_trim)
44
+ columns = self.columns.order('id ASC')
45
+ columns.last(qty_to_trim).map{|c| c.destroy}
46
+ end
47
+
48
+ def question_id
49
+ self.questions.first.id if self.questions.first
50
+ end
51
+
52
+ #def controlling_questions in QuestionAndGroupSharedMethods
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,435 @@
1
+ require 'stringio'
2
+ module SurveyorGui
3
+ module Models
4
+ module QuestionMethods
5
+ include QuestionAndGroupSharedMethods
6
+
7
+ def self.included(base)
8
+ base.send :attr_accessor, :dummy_answer, :dummy_answer_array, :type, :decimals
9
+ base.send :attr_writer, :grid_columns_textbox, :omit, :omit_text,
10
+ :other, :other_text, :comments_text, :comments, :dropdown_column_count
11
+ base.send :attr_accessible, :dummy_answer, :dummy_answer_array, :question_type, :question_type_id, :survey_section_id, :question_group_id,
12
+ :text, :pick, :reference_identifier, :display_order, :display_type,
13
+ :is_mandatory, :prefix, :suffix, :answers_attributes, :decimals, :dependency_attributes,
14
+ :hide_label, :dummy_blob, :dynamically_generate, :answers_textbox, :dropdown_column_count,
15
+ :grid_columns_textbox, :grid_rows_textbox, :omit_text, :omit, :other, :other_text, :is_comment, :comments, :comments_text,
16
+ :dynamic_source, :modifiable, :report_code, :question_group_attributes if
17
+ defined? ActiveModel::MassAssignmentSecurity
18
+ base.send :accepts_nested_attributes_for, :answers, :reject_if => lambda { |a| a[:text].blank?}, :allow_destroy => true
19
+ base.send :belongs_to, :survey_section
20
+ base.send :has_many, :responses
21
+ base.send :has_many, :dependency_conditions, :through=>:dependency, :dependent => :destroy
22
+ base.send :default_scope, lambda{ base.order('display_order')}
23
+ base.send :scope, :by_display_order, -> {base.order('display_order')}
24
+ ### everything below this point must be commented out to run the rake tasks.
25
+ base.send :accepts_nested_attributes_for, :dependency, :reject_if => lambda { |d| d[:rule].blank?}, :allow_destroy => true
26
+ ### everything below this point must be commented out to run the rake tasks.
27
+
28
+ base.send :mount_uploader, :dummy_blob, BlobUploader
29
+ base.send :belongs_to, :question_type
30
+
31
+ base.send :validate, :no_responses
32
+ base.send :before_destroy, :no_responses
33
+ base.send :after_save, :build_complex_questions
34
+ base.send :before_save, :make_room_for_question
35
+
36
+ base.send :scope, :is_not_comment, -> { base.where(is_comment: false) }
37
+ base.send :scope, :is_comment, -> { base.where(is_comment: true) }
38
+
39
+ base.class_eval do
40
+
41
+ def answers_attributes=(ans)
42
+ #don't set answer.text if question_type = number. In this case, text should get set by the prefix and suffix setters.
43
+ #note: Surveyor uses the answer.text field to store prefix and suffix for numbers.
44
+ #if not a number question, go ahead and set the text attribute as normal.
45
+ if @question_type_id!="number" && !ans.empty? && ans["0"]
46
+ ans["0"].merge!( {"original_choice"=>ans["0"]["text"]})
47
+ assign_nested_attributes_for_collection_association(:answers, ans)
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ def default_args
55
+ self.is_mandatory ||= false
56
+ self.display_type ||= "default"
57
+ self.pick ||= "none"
58
+ self.data_export_identifier ||= Surveyor::Common.normalize(text)
59
+ self.short_text ||= text
60
+ self.api_id ||= Surveyor::Common.generate_api_id
61
+ end
62
+
63
+ #prevent a question from being modified if responses have been submitted for the survey. Protects data integrity.
64
+ def no_responses
65
+ #below is code to fix a bizarre bug. When triggered by the "cut" function, for some reason survey_id is erased. Have not found reason yet. Temporary fix.
66
+ if !survey_section && self.id
67
+ self.reload
68
+ end
69
+ if self.id && self.survey_section && self.survey_section.survey
70
+ #this will be a problem if two people are editing the survey at the same time and do a survey preview - highly unlikely though.
71
+ self.survey_section.survey.response_sets.where('test_data = ?',true).each {|r| r.destroy}
72
+ end
73
+ if self.id && !survey_section.survey.template && survey_section.survey.response_sets.count>0
74
+ errors.add(:base,"Reponses have already been collected for this survey, therefore it cannot be modified. Please create a new survey instead.")
75
+ return false
76
+ end
77
+ end
78
+
79
+ def dynamically_generate
80
+ 'false'
81
+ end
82
+
83
+ def question_type_id
84
+ QuestionType.categorize_question(self)
85
+ end
86
+
87
+ # #generates descriptions for different types of questions, including those that use widgets
88
+ def question_type
89
+ @question_type = QuestionType.find(question_type_id)
90
+ end
91
+ #
92
+
93
+ #setter for question type. Sets both pick and display_type
94
+ def question_type_id=(type)
95
+ case type
96
+ when "grid_one"
97
+ write_attribute(:pick, "one")
98
+ prep_picks
99
+ write_attribute(:display_type, "default")
100
+ _update_group_id
101
+ when "pick_one"
102
+ write_attribute(:pick, "one")
103
+ prep_picks
104
+ write_attribute(:display_type, "default")
105
+ _remove_group
106
+ when "slider"
107
+ write_attribute(:pick, "one")
108
+ prep_picks
109
+ write_attribute(:display_type, "slider")
110
+ when "stars"
111
+ write_attribute(:pick, "one")
112
+ write_attribute(:display_type, "stars")
113
+ prep_picks
114
+ when "dropdown"
115
+ write_attribute(:pick, "one")
116
+ write_attribute(:display_type, "dropdown")
117
+ prep_picks
118
+ when "pick_any"
119
+ write_attribute(:pick, "any")
120
+ prep_picks
121
+ write_attribute(:display_type, "default")
122
+ _remove_group
123
+ when "grid_any"
124
+ write_attribute(:pick, "any")
125
+ prep_picks
126
+ write_attribute(:display_type, "default")
127
+ _update_group_id
128
+ when "grid_dropdown"
129
+ write_attribute(:pick, "one")
130
+ prep_picks
131
+ write_attribute(:display_type, "dropdown")
132
+ _update_group_id
133
+ when "group_inline"
134
+ _update_group_id
135
+ when 'label'
136
+ write_attribute(:pick, "none")
137
+ write_attribute(:display_type, "label")
138
+ when 'box'
139
+ prep_not_picks('text')
140
+ when 'number'
141
+ prep_not_picks('float')
142
+ when 'date'
143
+ prep_not_picks('date')
144
+ when 'time'
145
+ prep_not_picks('time')
146
+ when 'datetime'
147
+ prep_not_picks('datetime')
148
+ when 'file'
149
+ prep_not_picks('blob')
150
+ when 'string'
151
+ prep_not_picks('string')
152
+ end
153
+ @question_type_id = type
154
+ end
155
+
156
+
157
+ #If the question involves picking from a list of choices, this sets response class.
158
+ def prep_picks
159
+ #write_attribute(:display_type, self.display_type || "default")
160
+ if self.display_type=='stars'
161
+ response_class='integer'
162
+ else
163
+ response_class='answer'
164
+ end
165
+ if self.answers.blank?
166
+ self.answers_attributes={'0'=>{'text'=>'default', 'response_class'=>response_class}}
167
+ else
168
+ self.answers.map{|a|a.response_class=response_class}
169
+ end
170
+ end
171
+
172
+ #if the question is not a pick from list of choices (but is a fill in the blank type question) and not multiple choice, this sets it
173
+ #accordingly.
174
+ def prep_not_picks(answer_type)
175
+ write_attribute(:pick, "none")
176
+ write_attribute(:display_type,"default")
177
+ if self.answers.blank?
178
+ #self.answers_attributes={'0'=>{'text'=>'default','response_class'=>answer_type, 'hide_label' => answer_type=='float' ? false : true}}
179
+ self.answers_attributes={'0'=>{'text'=>'default','response_class'=>answer_type, 'display_type' => answer_type=='float' ? 'default' : 'hidden_label'}}
180
+ else
181
+ self.answers.first.response_class=answer_type
182
+ #self.answers.first.hide_label = answer_type=='float' ? false : true
183
+ self.answers.first.display_type = answer_type=='float' ? 'default' : 'hidden_label'
184
+ end
185
+ end
186
+
187
+ #number prefix getter. splits a number question into the actual answer and it's unit type. Eg, you might want a
188
+ #number to be prefixed with a dollar sign.
189
+ def prefix
190
+ if self.answers.first && self.answers.first.text.include?('|')
191
+ self.answers.first.text.split('|')[0]
192
+ end
193
+ end
194
+
195
+ #number suffix getter. sometimes you want a number question to have a units of measure suffix, like "per day"
196
+ def suffix
197
+ if self.answers.first && self.answers.first.text.include?('|')
198
+ self.answers.first.text.split('|')[1]
199
+ end
200
+ end
201
+
202
+ #sets the number prefix
203
+ def prefix=(pre)
204
+ if @question_type_id=="number"
205
+ if self.answers.blank?
206
+ self.answers_attributes={'0'=>{'text'=>pre+'|'}} unless pre.blank?
207
+ else
208
+ if pre.blank?
209
+ self.answers.first.text = 'default'
210
+ else
211
+ self.answers.first.text = pre+'|'
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ #sets the number suffix
218
+ def suffix=(suf)
219
+ if @question_type_id=="number"
220
+ if self.answers.first.blank? || self.answers.first.text.blank?
221
+ self.answers_attributes={'0'=>{'text'=>'|'+suf}} unless suf.blank?
222
+ else
223
+ if self.answers.first.text=='default'
224
+ self.answers.first.text='|'+suf
225
+ elsif self.answers.first.text.blank?
226
+ self.answers.first.text = '|'+suf
227
+ else
228
+ self.answers.first.text=self.answers.first.text+suf
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+
235
+ def surveyresponse_class(response_sets)
236
+ if dependent?
237
+ response_sets.each do |r|
238
+ if triggered?(r)
239
+ return nil
240
+ end
241
+ end
242
+ return "q_hidden"
243
+ else
244
+ nil
245
+ end
246
+ end
247
+
248
+ def question_description
249
+ ## this is an expensive method - use sparingly
250
+ is_numbered? ? question_number.to_s + ') ' + text : text
251
+ end
252
+
253
+ def is_numbered?
254
+ case display_type
255
+ when 'label'
256
+ false
257
+ else
258
+ if part_of_group?
259
+ if question_group.questions.last.id == self.id
260
+ true
261
+ else
262
+ false
263
+ end
264
+ else
265
+ true
266
+ end
267
+ end
268
+ end
269
+
270
+ def question_number
271
+ ##this is an expensive method - use sparingly
272
+ ##should consider adding question_number attribute to table in future
273
+ if survey_section.id.nil?
274
+ nil
275
+ else
276
+ _preceding_questions_numbered.count
277
+ end
278
+ end
279
+
280
+
281
+ #def controlling_questions in QuestionAndGroupSharedMethods
282
+
283
+ def answers_textbox
284
+ self.answers.where('is_exclusive != ? and is_comment != ? and response_class != ?',true,true,"string").order('display_order asc').collect(&:text).join("\n")
285
+ end
286
+
287
+ def answers_textbox=(textbox)
288
+ #change updated_at as a hack to force dirty record for change on answers_textbox
289
+ write_attribute(:updated_at, Time.now)
290
+ @answers_textbox=textbox
291
+ end
292
+
293
+ def omit
294
+ @omit = self.answers.where('is_exclusive = ?',true).size > 0
295
+ end
296
+
297
+ def omit_text
298
+ answer = self.answers.where('is_exclusive = ?',true).first
299
+ @omit_text = (answer ? answer.text : "none of the above")
300
+ end
301
+
302
+ def other
303
+ @other = self.answers.where('response_class = ? and is_exclusive = ? and is_comment = ?',"string",false, false).size > 0
304
+ end
305
+
306
+ def other_text
307
+ answer = self.answers.where('response_class = ? and is_exclusive = ? and is_comment = ?',"string", false, false).first
308
+ @other_text = (answer ? answer.text : "Other")
309
+ end
310
+
311
+ def comments
312
+ if self.part_of_group?
313
+ @comments = self.question_group.questions.where('is_comment=?',true).size > 0
314
+ else
315
+ @comments = self.answers.where('is_comment=?',true).size > 0
316
+ end
317
+ end
318
+
319
+ def comments_text
320
+ if self.part_of_group?
321
+ @comments_text = is_comment ? self.answers.first.text : "Comments"
322
+ else
323
+ answer = self.answers.where('is_comment=?',true).first
324
+ @comments_text = (answer ? answer.text : "Comments")
325
+ end
326
+ end
327
+
328
+ def dropdown_column_count
329
+ @dropdown_column_count = @dropdown_column_count || (self.question_group ? self.question_group.columns.size : 1)
330
+ end
331
+
332
+ def grid_columns_textbox
333
+ self.answers.where('response_class != ? and is_exclusive = ?',"string",false).order('display_order asc').collect(&:text).join("\n")
334
+ end
335
+
336
+ def grid_rows_textbox
337
+ if self.question_group && self.question_group.questions
338
+ self.question_group.questions.where('is_comment=?',false).order('display_order asc').collect(&:text).join("\n")
339
+ else
340
+ nil
341
+ end
342
+ end
343
+
344
+ def question_group_attributes=(params)
345
+ if question_group
346
+ question_group.update_attributes(params.except(:id))
347
+ @question_group_attributes=params
348
+ else
349
+ QuestionGroup.create!(params)
350
+ end
351
+ end
352
+
353
+ def text=(txt)
354
+ write_attribute(:text, txt)
355
+ if part_of_group? && question_group.display_type != "inline"
356
+ question_group.update_attributes(text: txt)
357
+ end
358
+ @text = txt
359
+ end
360
+
361
+ def grid_rows_textbox=(textbox)
362
+ write_attribute(:text, textbox.match(/.*\r*/).to_s.strip)
363
+ @grid_rows_textbox = textbox.gsub(/\r/,"")
364
+ end
365
+
366
+ def build_complex_questions
367
+ if (@answers_textbox && self.pick!="none") || @grid_columns_textbox || @grid_rows_textbox
368
+ self.question_type.build_complex_question_structure(
369
+ self,
370
+ answers_textbox: @answers_textbox,
371
+ omit_text: @omit_text,
372
+ is_exclusive: @omit=="1",
373
+ other_text: @other_text,
374
+ other: @other=="1",
375
+ comments_text: @comments_text,
376
+ comments: @comments=="1",
377
+ grid_columns_textbox: @grid_columns_textbox,
378
+ grid_rows_textbox: @grid_rows_textbox)
379
+ end
380
+ end
381
+
382
+ def next_display_order
383
+ if part_of_group?
384
+ self.question_group.questions.last.display_order + 1
385
+ else
386
+ display_order + 1
387
+ end
388
+ end
389
+
390
+ def make_room_for_question
391
+ if new_record?
392
+ if Question.where('survey_section_id = ? and display_order = ?',survey_section_id, display_order).size > 0
393
+ Question.where(:survey_section_id => survey_section_id)
394
+ .where("display_order >= ?", display_order)
395
+ .update_all("display_order = display_order+1")
396
+ end
397
+ end
398
+ end
399
+
400
+ def repeater?
401
+ part_of_group? ? (question_group.display_type=="repeater" ? true : false ) : false
402
+ end
403
+
404
+ private
405
+
406
+ def _update_group_id
407
+ @question_group = @question_group || self.question_group ||
408
+ QuestionGroup.create!(text: @text, display_type: :grid)
409
+ self.question_group_id = @question_group.id
410
+ end
411
+
412
+ def _remove_group
413
+ if part_of_group?
414
+ question_group.questions.map{|q| q.destroy if q.id != id}
415
+ write_attribute(:question_group_id, nil)
416
+ end
417
+ end
418
+
419
+ def _preceding_questions_numbered
420
+ _preceding_questions.to_a.delete_if{|p| !p.is_numbered?}
421
+ end
422
+
423
+ def _preceding_questions
424
+ ##all questions from previous sections, plus all questions with a lower display order than this one
425
+ Question.joins(:survey_section).where(
426
+ '(survey_id = ? and survey_sections.display_order < ?) or (survey_section_id = ? and questions.display_order <= ?)',
427
+ survey_section.survey_id,
428
+ survey_section.display_order,
429
+ survey_section.id,
430
+ display_order
431
+ )
432
+ end
433
+ end
434
+ end
435
+ end