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,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