surveillance 0.1.1

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 (276) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +42 -0
  4. data/Rakefile +23 -0
  5. data/app/assets/javascripts/surveillance.js +6 -0
  6. data/app/assets/javascripts/surveillance/admin.coffee +4 -0
  7. data/app/assets/javascripts/surveillance/admin/matrix-questions.coffee +18 -0
  8. data/app/assets/javascripts/surveillance/application.js +17 -0
  9. data/app/assets/javascripts/surveillance/collections/branch_rule_collection.coffee +2 -0
  10. data/app/assets/javascripts/surveillance/form.coffee +7 -0
  11. data/app/assets/javascripts/surveillance/manifest.coffee +7 -0
  12. data/app/assets/javascripts/surveillance/models/branch_rule.coffee +21 -0
  13. data/app/assets/javascripts/surveillance/models/question.coffee +19 -0
  14. data/app/assets/javascripts/surveillance/models/questions/choosable_question.coffee +15 -0
  15. data/app/assets/javascripts/surveillance/models/questions/matrix.coffee +19 -0
  16. data/app/assets/javascripts/surveillance/models/questions/matrix_question.coffee +7 -0
  17. data/app/assets/javascripts/surveillance/models/questions/number.coffee +17 -0
  18. data/app/assets/javascripts/surveillance/vendor/backbone.min.js +1 -0
  19. data/app/assets/javascripts/surveillance/vendor/bootstrap.js +2118 -0
  20. data/app/assets/javascripts/surveillance/vendor/jquery-ui-sortable.js +2256 -0
  21. data/app/assets/javascripts/surveillance/vendor/jquery.scrollto.js +210 -0
  22. data/app/assets/javascripts/surveillance/vendor/underscore.min.js +5 -0
  23. data/app/assets/javascripts/surveillance/views/attempt_view.coffee +74 -0
  24. data/app/assets/javascripts/surveillance/views/question_view.coffee +53 -0
  25. data/app/assets/javascripts/surveillance/views/questions/choice_view.coffee +52 -0
  26. data/app/assets/javascripts/surveillance/views/questions/matrix_question_view.coffee +26 -0
  27. data/app/assets/javascripts/surveillance/views/questions/matrix_view.coffee +21 -0
  28. data/app/assets/javascripts/surveillance/views/questions/number_view.coffee +19 -0
  29. data/app/assets/javascripts/surveillance/views/questions/order.coffee +9 -0
  30. data/app/assets/javascripts/surveillance/views/questions/select_matrix_view.coffee +31 -0
  31. data/app/assets/javascripts/surveillance/views/section_view.coffee +54 -0
  32. data/app/assets/stylesheets/surveillance/admin.sass +1 -0
  33. data/app/assets/stylesheets/surveillance/application.css +14 -0
  34. data/app/assets/stylesheets/surveillance/fonts/glyphicons-halflings-regular.eot +0 -0
  35. data/app/assets/stylesheets/surveillance/fonts/glyphicons-halflings-regular.svg +229 -0
  36. data/app/assets/stylesheets/surveillance/fonts/glyphicons-halflings-regular.ttf +0 -0
  37. data/app/assets/stylesheets/surveillance/fonts/glyphicons-halflings-regular.woff +0 -0
  38. data/app/assets/stylesheets/surveillance/overrides.sass +2 -0
  39. data/app/assets/stylesheets/surveillance/vendor/bootstrap.css +6175 -0
  40. data/app/controllers/concerns/surveillance/attempts_management_concern.rb +51 -0
  41. data/app/controllers/concerns/surveillance/base_controller_concern.rb +37 -0
  42. data/app/controllers/surveillance/admin/attempts_controller.rb +38 -0
  43. data/app/controllers/surveillance/admin/base_controller.rb +15 -0
  44. data/app/controllers/surveillance/admin/exports_controller.rb +24 -0
  45. data/app/controllers/surveillance/admin/options_controller.rb +44 -0
  46. data/app/controllers/surveillance/admin/questions_controller.rb +59 -0
  47. data/app/controllers/surveillance/admin/sections_controller.rb +46 -0
  48. data/app/controllers/surveillance/admin/surveys_controller.rb +43 -0
  49. data/app/controllers/surveillance/application_controller.rb +7 -0
  50. data/app/controllers/surveillance/response/attempts_controller.rb +50 -0
  51. data/app/controllers/surveillance/response/base_controller.rb +38 -0
  52. data/app/controllers/surveillance/response/partial_attempts_controller.rb +15 -0
  53. data/app/controllers/surveillance/surveys_controller.rb +6 -0
  54. data/app/helpers/surveillance/admin/exports_helper.rb +37 -0
  55. data/app/helpers/surveillance/application_helper.rb +84 -0
  56. data/app/models/surveillance/answer.rb +56 -0
  57. data/app/models/surveillance/answer_content.rb +7 -0
  58. data/app/models/surveillance/attempt.rb +146 -0
  59. data/app/models/surveillance/branch_rule.rb +52 -0
  60. data/app/models/surveillance/field_setting.rb +6 -0
  61. data/app/models/surveillance/option.rb +18 -0
  62. data/app/models/surveillance/question.rb +50 -0
  63. data/app/models/surveillance/section.rb +16 -0
  64. data/app/models/surveillance/survey.rb +38 -0
  65. data/app/views/layouts/surveillance/application.html.haml +11 -0
  66. data/app/views/surveillance/admin/attempts/index.html.haml +47 -0
  67. data/app/views/surveillance/admin/attempts/list.html.haml +11 -0
  68. data/app/views/surveillance/admin/attempts/overview.html.haml +6 -0
  69. data/app/views/surveillance/admin/attempts/show.html.haml +62 -0
  70. data/app/views/surveillance/admin/exports/show.rxls +39 -0
  71. data/app/views/surveillance/admin/options/_field.html.haml +11 -0
  72. data/app/views/surveillance/admin/options/_form.html.haml +8 -0
  73. data/app/views/surveillance/admin/options/_list.html.haml +10 -0
  74. data/app/views/surveillance/admin/options/_option.html.haml +13 -0
  75. data/app/views/surveillance/admin/options/edit.html.haml +7 -0
  76. data/app/views/surveillance/admin/options/new.html.haml +7 -0
  77. data/app/views/surveillance/admin/questions/_form.html.haml +35 -0
  78. data/app/views/surveillance/admin/questions/_list.html.haml +17 -0
  79. data/app/views/surveillance/admin/questions/_question.html.haml +27 -0
  80. data/app/views/surveillance/admin/questions/edit.html.haml +7 -0
  81. data/app/views/surveillance/admin/questions/new.html.haml +4 -0
  82. data/app/views/surveillance/admin/sections/_form.html.haml +16 -0
  83. data/app/views/surveillance/admin/sections/_list.html.haml +13 -0
  84. data/app/views/surveillance/admin/sections/_section.html.haml +16 -0
  85. data/app/views/surveillance/admin/sections/edit.html.haml +7 -0
  86. data/app/views/surveillance/admin/sections/new.html.haml +4 -0
  87. data/app/views/surveillance/admin/surveys/_form.html.haml +22 -0
  88. data/app/views/surveillance/admin/surveys/_list.html.haml +13 -0
  89. data/app/views/surveillance/admin/surveys/_survey.html.haml +32 -0
  90. data/app/views/surveillance/admin/surveys/edit.html.haml +7 -0
  91. data/app/views/surveillance/admin/surveys/index.html.haml +1 -0
  92. data/app/views/surveillance/admin/surveys/new.html.haml +4 -0
  93. data/app/views/surveillance/field/date/_form.html.haml +5 -0
  94. data/app/views/surveillance/field/date/_overview.html.haml +2 -0
  95. data/app/views/surveillance/field/date/_show.html.haml +10 -0
  96. data/app/views/surveillance/field/matrix/_form.html.haml +40 -0
  97. data/app/views/surveillance/field/matrix/_overview.html.haml +19 -0
  98. data/app/views/surveillance/field/matrix/_questions_fields.html.haml +11 -0
  99. data/app/views/surveillance/field/matrix/_settings.html.haml +18 -0
  100. data/app/views/surveillance/field/matrix/_show.html.haml +22 -0
  101. data/app/views/surveillance/field/multiple_choices/_form.html.haml +18 -0
  102. data/app/views/surveillance/field/multiple_choices/_settings.html.haml +21 -0
  103. data/app/views/surveillance/field/multiple_choices/_show.html.haml +21 -0
  104. data/app/views/surveillance/field/multiple_choices_matrix/_form.html.haml +33 -0
  105. data/app/views/surveillance/field/multiple_choices_matrix/_show.html.haml +1 -0
  106. data/app/views/surveillance/field/number/_form.html.haml +5 -0
  107. data/app/views/surveillance/field/number/_overview.html.haml +2 -0
  108. data/app/views/surveillance/field/number/_settings.html.haml +2 -0
  109. data/app/views/surveillance/field/number/_show.html.haml +10 -0
  110. data/app/views/surveillance/field/order/_form.html.haml +17 -0
  111. data/app/views/surveillance/field/order/_overview.html.haml +22 -0
  112. data/app/views/surveillance/field/order/_settings.html.haml +11 -0
  113. data/app/views/surveillance/field/order/_show.html.haml +8 -0
  114. data/app/views/surveillance/field/select_matrix/_form.html.haml +17 -0
  115. data/app/views/surveillance/field/shared/_rules_form.html.haml +30 -0
  116. data/app/views/surveillance/field/shared/settings/_boolean.html.haml +9 -0
  117. data/app/views/surveillance/field/shared/settings/_number.html.haml +5 -0
  118. data/app/views/surveillance/field/single_choice/_form.html.haml +16 -0
  119. data/app/views/surveillance/field/single_choice/_overview.html.haml +14 -0
  120. data/app/views/surveillance/field/single_choice/_settings.html.haml +2 -0
  121. data/app/views/surveillance/field/single_choice/_show.html.haml +21 -0
  122. data/app/views/surveillance/field/single_select/_form.html.haml +4 -0
  123. data/app/views/surveillance/field/single_select/_overview.html.haml +14 -0
  124. data/app/views/surveillance/field/single_select/_show.html.haml +13 -0
  125. data/app/views/surveillance/field/text/_form.html.haml +5 -0
  126. data/app/views/surveillance/field/text/_overview.html.haml +2 -0
  127. data/app/views/surveillance/field/text/_show.html.haml +10 -0
  128. data/app/views/surveillance/field/text_area/_form.html.haml +5 -0
  129. data/app/views/surveillance/field/text_area/_overview.html.haml +1 -0
  130. data/app/views/surveillance/field/text_area/_show.html.haml +10 -0
  131. data/app/views/surveillance/response/attempts/complete.html.haml +11 -0
  132. data/app/views/surveillance/response/attempts/edit.html.haml +15 -0
  133. data/app/views/surveillance/sections/_section.html.haml +24 -0
  134. data/app/views/surveillance/surveys/_survey.html.haml +4 -0
  135. data/app/views/surveillance/surveys/index.html.haml +8 -0
  136. data/app/views/surveillance/surveys/show.html.haml +15 -0
  137. data/config/locales/surveillance.fr.yml +165 -0
  138. data/config/routes.rb +30 -0
  139. data/db/migrate/20130626115136_create_surveillance_surveys.rb +13 -0
  140. data/db/migrate/20130626115805_create_surveillance_sections.rb +12 -0
  141. data/db/migrate/20130626130351_create_surveillance_questions.rb +14 -0
  142. data/db/migrate/20130702111125_create_surveillance_options.rb +11 -0
  143. data/db/migrate/20130703113456_create_surveillance_attempts.rb +11 -0
  144. data/db/migrate/20130703115649_create_surveillance_answers.rb +11 -0
  145. data/db/migrate/20130703115723_create_surveillance_answer_contents.rb +10 -0
  146. data/db/migrate/20130710151627_create_surveillance_field_settings.rb +11 -0
  147. data/db/migrate/20130711100032_create_surveillance_answers_options_join_table.rb +7 -0
  148. data/db/migrate/20130729143002_create_surveillance_branch_rules.rb +14 -0
  149. data/db/migrate/20140212153506_add_state_to_surveillance_attempts.rb +6 -0
  150. data/db/migrate/20140213101147_add_access_token_to_surveillance_attempts.rb +5 -0
  151. data/db/migrate/20140213133036_add_partial_to_surveillance_questions.rb +5 -0
  152. data/lib/generators/surveillance/install/install_generator.rb +34 -0
  153. data/lib/generators/surveillance/install/templates/initializer.rb +51 -0
  154. data/lib/surveillance.rb +61 -0
  155. data/lib/surveillance/branch_action.rb +5 -0
  156. data/lib/surveillance/branch_condition.rb +5 -0
  157. data/lib/surveillance/engine.rb +13 -0
  158. data/lib/surveillance/field.rb +58 -0
  159. data/lib/surveillance/field/base.rb +149 -0
  160. data/lib/surveillance/field/date.rb +17 -0
  161. data/lib/surveillance/field/matrix.rb +66 -0
  162. data/lib/surveillance/field/matrix_question.rb +13 -0
  163. data/lib/surveillance/field/multiple_choices.rb +29 -0
  164. data/lib/surveillance/field/multiple_choices_matrix.rb +13 -0
  165. data/lib/surveillance/field/number.rb +34 -0
  166. data/lib/surveillance/field/order.rb +40 -0
  167. data/lib/surveillance/field/order_question.rb +22 -0
  168. data/lib/surveillance/field/other_choice.rb +28 -0
  169. data/lib/surveillance/field/select_matrix.rb +13 -0
  170. data/lib/surveillance/field/single_choice.rb +36 -0
  171. data/lib/surveillance/field/single_select.rb +31 -0
  172. data/lib/surveillance/field/text.rb +13 -0
  173. data/lib/surveillance/field/text_area.rb +6 -0
  174. data/lib/surveillance/partials_collection.rb +7 -0
  175. data/lib/surveillance/setting.rb +24 -0
  176. data/lib/surveillance/settings_collection.rb +17 -0
  177. data/lib/surveillance/validators.rb +7 -0
  178. data/lib/surveillance/validators/answer_validator.rb +9 -0
  179. data/lib/surveillance/version.rb +3 -0
  180. data/lib/tasks/surveillance_tasks.rake +4 -0
  181. data/spec/dummy/README.rdoc +28 -0
  182. data/spec/dummy/Rakefile +6 -0
  183. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  184. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  185. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  186. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  187. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  188. data/spec/dummy/bin/bundle +3 -0
  189. data/spec/dummy/bin/rails +4 -0
  190. data/spec/dummy/bin/rake +4 -0
  191. data/spec/dummy/config.ru +4 -0
  192. data/spec/dummy/config/application.rb +28 -0
  193. data/spec/dummy/config/boot.rb +5 -0
  194. data/spec/dummy/config/database.yml +25 -0
  195. data/spec/dummy/config/environment.rb +5 -0
  196. data/spec/dummy/config/environments/development.rb +29 -0
  197. data/spec/dummy/config/environments/production.rb +80 -0
  198. data/spec/dummy/config/environments/test.rb +36 -0
  199. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  200. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  201. data/spec/dummy/config/initializers/inflections.rb +16 -0
  202. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  203. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  204. data/spec/dummy/config/initializers/session_store.rb +3 -0
  205. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  206. data/spec/dummy/config/locales/en.yml +23 -0
  207. data/spec/dummy/config/locales/rails.fr.yml +226 -0
  208. data/spec/dummy/config/routes.rb +3 -0
  209. data/spec/dummy/db/development.sqlite3 +0 -0
  210. data/spec/dummy/db/migrate/20140113224541_create_surveillance_surveys.surveillance.rb +14 -0
  211. data/spec/dummy/db/migrate/20140113224542_create_surveillance_sections.surveillance.rb +13 -0
  212. data/spec/dummy/db/migrate/20140113224543_create_surveillance_questions.surveillance.rb +15 -0
  213. data/spec/dummy/db/migrate/20140113224544_create_surveillance_options.surveillance.rb +12 -0
  214. data/spec/dummy/db/migrate/20140113224545_create_surveillance_attempts.surveillance.rb +12 -0
  215. data/spec/dummy/db/migrate/20140113224546_create_surveillance_answers.surveillance.rb +12 -0
  216. data/spec/dummy/db/migrate/20140113224547_create_surveillance_answer_contents.surveillance.rb +11 -0
  217. data/spec/dummy/db/migrate/20140113224548_create_surveillance_field_settings.surveillance.rb +12 -0
  218. data/spec/dummy/db/migrate/20140113224549_create_surveillance_answers_options_join_table.surveillance.rb +8 -0
  219. data/spec/dummy/db/migrate/20140113224550_create_surveillance_branch_rules.surveillance.rb +15 -0
  220. data/spec/dummy/db/schema.rb +103 -0
  221. data/spec/dummy/log/development.log +38066 -0
  222. data/spec/dummy/public/404.html +58 -0
  223. data/spec/dummy/public/422.html +58 -0
  224. data/spec/dummy/public/500.html +57 -0
  225. data/spec/dummy/public/favicon.ico +0 -0
  226. data/spec/dummy/spec +1 -0
  227. data/spec/dummy/tmp/cache/assets/development/sprockets/0018421aaf29044fbd13363bbdfd8f44 +0 -0
  228. data/spec/dummy/tmp/cache/assets/development/sprockets/017d7a30ec05c0fb0265fb5167c73ee9 +0 -0
  229. data/spec/dummy/tmp/cache/assets/development/sprockets/034b76adeb89d6ca63cd9eea649331f5 +0 -0
  230. data/spec/dummy/tmp/cache/assets/development/sprockets/05de8fbfacab66a0efe4f9790b2ac723 +0 -0
  231. data/spec/dummy/tmp/cache/assets/development/sprockets/0811effcff3b9107d49398bdba7488e5 +0 -0
  232. data/spec/dummy/tmp/cache/assets/development/sprockets/0d45beae32861dc508fc2db09b9d95b3 +0 -0
  233. data/spec/dummy/tmp/cache/assets/development/sprockets/0d4961f3da5a0d5234736a6abbf42226 +0 -0
  234. data/spec/dummy/tmp/cache/assets/development/sprockets/0f491ed240e1a27bbc05e5213afd48bb +0 -0
  235. data/spec/dummy/tmp/cache/assets/development/sprockets/10e2a0dbdbb68c055e8b7ccd6f2906d8 +0 -0
  236. data/spec/dummy/tmp/cache/assets/development/sprockets/1173cdef6c662a55ef590489fe26e8fe +0 -0
  237. data/spec/dummy/tmp/cache/assets/development/sprockets/1b3e93127523ec0f3c93bcd91d4390dc +0 -0
  238. data/spec/dummy/tmp/cache/assets/development/sprockets/1bb56fbf17ccbb14dcbaa4d6d639e620 +0 -0
  239. data/spec/dummy/tmp/cache/assets/development/sprockets/2bd0ba6a1598c9fa67081d8f906db6f8 +0 -0
  240. data/spec/dummy/tmp/cache/assets/development/sprockets/3fbe053f103de46cc1b320f059b99c1f +0 -0
  241. data/spec/dummy/tmp/cache/assets/development/sprockets/4144b095447efb810a548b99e41df9eb +0 -0
  242. data/spec/dummy/tmp/cache/assets/development/sprockets/421d097cb8d88f975d8ccf75c9e5e306 +0 -0
  243. data/spec/dummy/tmp/cache/assets/development/sprockets/474bc1c9055df6cc1cdff58817a6cb41 +0 -0
  244. data/spec/dummy/tmp/cache/assets/development/sprockets/4829d251eeb22cdbd05b27b6ba809538 +0 -0
  245. data/spec/dummy/tmp/cache/assets/development/sprockets/4f584ca3f0c8ec62906e6f03bc5963c2 +0 -0
  246. data/spec/dummy/tmp/cache/assets/development/sprockets/55d647d88e01b91b2a146e7db0481c09 +0 -0
  247. data/spec/dummy/tmp/cache/assets/development/sprockets/62db8b5885b79c5dc0667fab0fbf46bb +0 -0
  248. data/spec/dummy/tmp/cache/assets/development/sprockets/7080a4d265fe784c9704db71f81da832 +0 -0
  249. data/spec/dummy/tmp/cache/assets/development/sprockets/7220af243fd39e3dffd233252e4a8c17 +0 -0
  250. data/spec/dummy/tmp/cache/assets/development/sprockets/7cd0e5bfe12b86ed494d02e8e5b62bbf +0 -0
  251. data/spec/dummy/tmp/cache/assets/development/sprockets/7e37a8ae2a50653b388d57c573d12548 +0 -0
  252. data/spec/dummy/tmp/cache/assets/development/sprockets/8113790dad1da8858c82cfc3eb14ef87 +0 -0
  253. data/spec/dummy/tmp/cache/assets/development/sprockets/83997102fc04f275cd6cd8c1ddb55835 +0 -0
  254. data/spec/dummy/tmp/cache/assets/development/sprockets/8a7b047a16adbc640e03eaabe4a4fe93 +0 -0
  255. data/spec/dummy/tmp/cache/assets/development/sprockets/90b48fb4bfa22ddcd063734065d27086 +0 -0
  256. data/spec/dummy/tmp/cache/assets/development/sprockets/935886b24c90d344752d2a80e3657801 +0 -0
  257. data/spec/dummy/tmp/cache/assets/development/sprockets/9606a6d70af63ce3fea26ded0a69d5c2 +0 -0
  258. data/spec/dummy/tmp/cache/assets/development/sprockets/97db73fdf0bbab24fb92892c61ec7a9b +0 -0
  259. data/spec/dummy/tmp/cache/assets/development/sprockets/a23bb60554e715c228ae3cdfb5d81807 +0 -0
  260. data/spec/dummy/tmp/cache/assets/development/sprockets/ad17e102b9f9288a9e919b0b0a2ab05b +0 -0
  261. data/spec/dummy/tmp/cache/assets/development/sprockets/c26ae1f6640b52c232bd392a6e2f7110 +0 -0
  262. data/spec/dummy/tmp/cache/assets/development/sprockets/c6cb3deca0f08bd9569d00fca09272da +0 -0
  263. data/spec/dummy/tmp/cache/assets/development/sprockets/d39b4eebbd0042342e10497a34fd6378 +0 -0
  264. data/spec/dummy/tmp/cache/assets/development/sprockets/d5ee573b2abdcdc245a830bde2dd76f3 +0 -0
  265. data/spec/dummy/tmp/cache/assets/development/sprockets/d6487e59842ff206d84f6de5e3a7400c +0 -0
  266. data/spec/dummy/tmp/cache/assets/development/sprockets/dafa124ddd0e828d7c5c0a5c4db455ba +0 -0
  267. data/spec/dummy/tmp/cache/assets/development/sprockets/e5bbd03518f9abc3e9bc1373c74fd3f1 +0 -0
  268. data/spec/dummy/tmp/cache/assets/development/sprockets/ed5aa255edff522c2e36e6f0151ff747 +0 -0
  269. data/spec/dummy/tmp/cache/assets/development/sprockets/ee263a426f6530013f86aa860448fa91 +0 -0
  270. data/spec/dummy/tmp/cache/assets/development/sprockets/efa0c4d855d425fdf4b63ab26c45e94c +0 -0
  271. data/spec/dummy/tmp/cache/assets/development/sprockets/f2881259fd5bad1c0a5b1ab5dff97c47 +0 -0
  272. data/spec/dummy/tmp/cache/assets/development/sprockets/f34108e540cff2fbc32036da452ca6ae +0 -0
  273. data/spec/dummy/tmp/cache/assets/development/sprockets/f7e7e00ef8efc13c6a2628e453344440 +0 -0
  274. data/spec/dummy/tmp/cache/assets/development/sprockets/f9a44009076e46b0f35af5e7031a1871 +0 -0
  275. data/spec/spec_helper.rb +87 -0
  276. metadata +598 -0
@@ -0,0 +1,15 @@
1
+ module Surveillance
2
+ module Response
3
+ class PartialAttemptsController < Surveillance::Response::BaseController
4
+ def update
5
+ if attempt.save
6
+ head 200
7
+ else
8
+ render status: 422, json: {
9
+ errors: attempt.errors.messages
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Surveillance
2
+ class SurveysController < Surveillance::ApplicationController
3
+ expose(:surveys) { Surveillance::Survey.published }
4
+ expose(:survey, model: Surveillance::Survey)
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ module Surveillance
2
+ module Admin
3
+ module ExportsHelper
4
+ def initialize_formats
5
+ @formats = {
6
+ black_background: Spreadsheet::Format.new(
7
+ color: :white,
8
+ size: 10,
9
+ align: :left,
10
+ border: :thin,
11
+ pattern: 1,
12
+ pattern_fg_color: :black
13
+ ),
14
+ gray: Spreadsheet::Format.new(
15
+ color: :white,
16
+ size: 10,
17
+ align: :left,
18
+ border: :thin,
19
+ pattern: 1,
20
+ pattern_fg_color: :gray
21
+ ),
22
+ border: Spreadsheet::Format.new(border: :thin),
23
+ bold: Spreadsheet::Format.new(weight: :bold)
24
+ }
25
+ end
26
+
27
+ # Format helper
28
+ def set_format row, range, format
29
+ @formats || initialize_formats
30
+ # Assume int if `range` is not a Range
31
+ range = (range..range) unless range.instance_of? Range
32
+ # Set format to specified cells
33
+ range.each { |n| @sheet.row(row).set_format(n, @formats[format]) }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,84 @@
1
+ module Surveillance
2
+ module ApplicationHelper
3
+ # Renders a form field for the given question
4
+ def survey_field_for builder, question, index
5
+ question.field.attempt = builder.object
6
+
7
+ field = capture do
8
+ render partial: question.field.form_path, locals: {
9
+ f: builder, question: question, question_index: index
10
+ }
11
+ end
12
+
13
+ content_tag :div, field, class: "surveillance-question", data: {
14
+ "branch-rules" => branch_rules_for(question),
15
+ "view" => question.field.view_name,
16
+ "question-id" => question.id,
17
+ "required" => question.mandatory
18
+ }
19
+ end
20
+
21
+ def branch_rules_for question
22
+ question.branch_rules.to_json
23
+ end
24
+
25
+ # Renders an answer result
26
+ def survey_answer_for attempt, question
27
+ question.field(attempt: attempt, answer: attempt.answer_to(question))
28
+
29
+ render partial: question.field.show_path, locals: { question: question }
30
+ end
31
+
32
+ def settings_fields_for builder, question
33
+ render partial: question.field.settings_path, locals: {
34
+ f: builder, question: question
35
+ } if question.field.settings?
36
+ end
37
+
38
+ def survey_overview_for question
39
+ render partial: question.field.overview_path, locals: {
40
+ question: question
41
+ }
42
+ end
43
+
44
+ def link_to_add_fields label, target, options = {}
45
+ builder = options.fetch(:builder, nil)
46
+ relation = options.fetch(:relation, nil)
47
+ # Get class name from builder's object class relations list
48
+ class_name = builder.object.class.reflections[relation].class_name
49
+ # Build new associated object
50
+ object = class_name.constantize.new
51
+ id = object.object_id
52
+
53
+ partial_path = options.delete(:partial) || "#{ relation }_fields"
54
+
55
+ fields = builder.fields_for(relation, object, child_index: id) do |f|
56
+ locals = { f: f }
57
+ locals.reverse_merge!(options[:locals]) if options[:locals]
58
+
59
+ render partial: partial_path, locals: locals
60
+ end
61
+
62
+ link_to label.html_safe, target, class: "btn btn-primary", data: {
63
+ id: id, fields: fields.gsub("\n", ""), toggle: "new-field"
64
+ }
65
+ end
66
+
67
+ def question_options_for_select question
68
+ options_for_select(
69
+ question.options.map { |option| [option.title, option.id] }
70
+ )
71
+ end
72
+
73
+ def setting_field_for question, key, type, builder
74
+ setting = question.field.settings[key]
75
+
76
+ render(
77
+ partial: "surveillance/field/shared/settings/#{ type.to_s }",
78
+ locals: {
79
+ question: question, key: key, f: builder, setting: setting
80
+ }
81
+ )
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,56 @@
1
+ module Surveillance
2
+ class Answer < ActiveRecord::Base
3
+ belongs_to :attempt, class_name: "Surveillance::Attempt", inverse_of: :answers
4
+ belongs_to :question, class_name: "Surveillance::Question"
5
+
6
+ has_and_belongs_to_many :options, class_name: "Surveillance::Option",
7
+ join_table: "surveillance_answers_options"
8
+
9
+ has_one :content, class_name: "Surveillance::AnswerContent",
10
+ foreign_key: :answer_id
11
+ accepts_nested_attributes_for :content,
12
+ reject_if: :optional_content_not_filled?
13
+
14
+ validates_presence_of :question_id
15
+ validates_with Surveillance::Validators::AnswerValidator
16
+
17
+ def option
18
+ options.first
19
+ end
20
+
21
+ def option_id
22
+ option_ids.first
23
+ end
24
+
25
+ def option_id=(id)
26
+ self.option_ids = [id]
27
+ end
28
+
29
+ def option_ids=(ids)
30
+ ids = ids.reduce([]) do |ids_list, id|
31
+ case id
32
+ when "other" then self.other_choosed = true
33
+ when /^\d+$/ then ids_list << id
34
+ end
35
+ ids_list
36
+ end
37
+
38
+ options.clear if options.length > 0
39
+ super(ids)
40
+ end
41
+
42
+ def content_or_build
43
+ content || build_content
44
+ end
45
+
46
+ def optional_content_not_filled? attributes
47
+ question.field.answer = self
48
+
49
+ if question.field.mandatory_content?
50
+ (!question.mandatory && (!attributes["value"].presence))
51
+ else
52
+ !attributes["value"].presence
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,7 @@
1
+ module Surveillance
2
+ class AnswerContent < ActiveRecord::Base
3
+ belongs_to :answer, class_name: "Surveillance::Answer"
4
+
5
+ validates_presence_of :value
6
+ end
7
+ end
@@ -0,0 +1,146 @@
1
+ module Surveillance
2
+ class Attempt < ActiveRecord::Base
3
+ include AASM
4
+
5
+ has_many :answers, class_name: "Surveillance::Answer",
6
+ foreign_key: 'attempt_id', dependent: :destroy, inverse_of: :attempt,
7
+ before_add: :set_attempt_on_answers
8
+ accepts_nested_attributes_for :answers
9
+
10
+ belongs_to :survey, class_name: "Surveillance::Survey",
11
+ inverse_of: :attempts
12
+
13
+ belongs_to :user, polymorphic: true
14
+
15
+ scope :includes_all, -> {
16
+ includes(answers: [:question, :options, :content])
17
+ }
18
+
19
+ before_validation :ensure_access_token
20
+ before_validation :filter_required_answers
21
+ before_save :fill, if: :answers_filled_and_empty?
22
+
23
+ aasm column: :state do
24
+ state :empty, initial: true
25
+ state :partially_filled
26
+ state :completed
27
+
28
+ event :fill do
29
+ transitions from: [:empty, :partially_filled], to: :partially_filled
30
+ end
31
+
32
+ event :complete do
33
+ transitions from: [:empty, :partially_filled, :completed], to: :completed
34
+ end
35
+ end
36
+
37
+ def state_label
38
+ if partially_filled?
39
+ [
40
+ I18n.t("surveillance.attempts.states.#{ state_name }"),
41
+ I18n.t("surveillance.attempts.step", step: last_answered_section + 1),
42
+ ].join(" - ")
43
+ else
44
+ I18n.t("surveillance.attempts.states.#{ state_name }")
45
+ end
46
+ end
47
+
48
+ def fill_section(index, attributes)
49
+ self.last_answered_section = index.to_i
50
+ update_attributes(attributes)
51
+ end
52
+
53
+ def sections_to_answer
54
+ @sections_to_answer ||= build_sections_to_answer_hash
55
+ end
56
+
57
+ def answer_to(question)
58
+ id = question.kind_of?(Integer) ? question : question.id
59
+ questions_answers[id]
60
+ end
61
+
62
+ def questions_answers
63
+ @questions_answers ||= Hash[answers.map { |a| [a.question_id, a] }]
64
+ end
65
+
66
+ def to_param
67
+ access_token
68
+ end
69
+
70
+ def answers_attributes=(attributes)
71
+ processed = attributes.each_with_object({}) do |(key, val), hash|
72
+ if val[:id].blank? && (answer = answer_to(val[:question_id].to_i))
73
+ val[:id] = answer.id
74
+ end
75
+
76
+ hash[key] = val
77
+ end
78
+
79
+ # Clear `@questions_answers` cache so next time we call questions_answers
80
+ # hash it will be fetched with latest data
81
+ @questions_answers = nil
82
+
83
+ assign_nested_attributes_for_collection_association(:answers, processed)
84
+ end
85
+
86
+ private
87
+
88
+ def set_attempt_on_answers(answer)
89
+ answer.attempt = self
90
+ end
91
+
92
+ def answers_filled_and_empty?
93
+ empty? && answers.length > 0
94
+ end
95
+
96
+ def ensure_access_token
97
+ self.access_token ||= Surveillance.unique_token
98
+ end
99
+
100
+ def filter_required_answers
101
+ answers_to_delete = answers.reduce([]) do |delete, answer|
102
+ delete << answer unless sections_to_answer[answer.question.section.id]
103
+ delete
104
+ end
105
+
106
+ answers_to_delete.each { |answer| answers.delete(answer) }
107
+ end
108
+
109
+ def build_sections_to_answer_hash
110
+ @sections = survey.sections.includes(questions: [:questions, :branch_rules])
111
+ @sections.limit(last_answered_section + 1) if last_answered_section
112
+
113
+ # Prepare sections to answer hash, with each section needing to be
114
+ # answered by default
115
+ sections_hash = Hash[@sections.map { |section| [section.id, true] }]
116
+ # Iterate on each section and check if a matching branching rule is found.
117
+ # If one is found, update the sections_hash according to the rule
118
+ @sections.each do |section|
119
+ next if sections_hash[section.id] == false
120
+
121
+ matching_rule = section.questions.reduce(nil) do |rule, question|
122
+ if !rule && (answer = answer_to(question))
123
+ rule = question.matching_rule_for(answer)
124
+ end
125
+
126
+ rule
127
+ end
128
+
129
+ if matching_rule
130
+ sections_hash.reduce(true) do |continue, (section_id, _)|
131
+ if continue && section.id == section_id
132
+ continue = false
133
+ elsif section_id == matching_rule.section_id
134
+ break
135
+ elsif !continue
136
+ sections_hash[section_id] = false
137
+ end
138
+ continue
139
+ end
140
+ end
141
+ end
142
+
143
+ sections_hash
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,52 @@
1
+ module Surveillance
2
+ class BranchRule < ActiveRecord::Base
3
+ belongs_to :question
4
+ belongs_to :sub_question, class_name: "Surveillance::Question"
5
+ belongs_to :section
6
+ belongs_to :option
7
+
8
+ validates_presence_of :action, :condition
9
+
10
+ def finalize_survey?
11
+ action == "finalize_survey"
12
+ end
13
+
14
+ def goto_section?
15
+ action == "goto_section"
16
+ end
17
+
18
+ def as_json options = {}
19
+ attributes = [:condition, :action]
20
+ attributes += [:sub_question_id, :section_id, :option_id].select do |id|
21
+ send(id).presence
22
+ end
23
+ super(only: attributes)
24
+ end
25
+
26
+ def matches? answer
27
+ send(condition, answer)
28
+ end
29
+
30
+ def answers answer
31
+ field = answer.question.field
32
+ field.answer = answer
33
+ field.present?
34
+ end
35
+
36
+ def doesnt_answer answer
37
+ !answers(answer)
38
+ end
39
+
40
+ def answers_option answer
41
+ qid = sub_question_id || question_id
42
+
43
+ answer.options.any? do |option|
44
+ option.id == option_id && option.question_id == qid
45
+ end
46
+ end
47
+
48
+ def doesnt_answer_option answer
49
+ !answers_option(answer)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ module Surveillance
2
+ class FieldSetting < ActiveRecord::Base
3
+ belongs_to :question
4
+ validates_presence_of :key, :value, :question
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ module Surveillance
2
+ class Option < ActiveRecord::Base
3
+ has_and_belongs_to_many :answers, class_name: "Surveillance::Answer",
4
+ join_table: "surveillance_answers_options"
5
+
6
+ belongs_to :question, class_name: "Surveillance::Question"
7
+
8
+ validates_presence_of :title
9
+
10
+ scope :ordered, -> { order("surveillance_options.id ASC") }
11
+
12
+ def answers_for question
13
+ answers.select do |answer|
14
+ answer.question_id == question.id
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,50 @@
1
+ module Surveillance
2
+ class Question < ActiveRecord::Base
3
+ include Surveillance::Field
4
+ include ActionView::Helpers::SanitizeHelper
5
+
6
+ has_many :questions, as: :parent, class_name: "Surveillance::Question",
7
+ inverse_of: :parent
8
+ accepts_nested_attributes_for :questions, allow_destroy: true
9
+
10
+ belongs_to :parent, polymorphic: true, inverse_of: :questions
11
+
12
+ has_many :options, -> { ordered }, class_name: "Surveillance::Option",
13
+ dependent: :destroy
14
+ accepts_nested_attributes_for :options, allow_destroy: :true
15
+
16
+ has_many :answers, class_name: "Surveillance::Answer", dependent: :destroy
17
+
18
+ has_many :settings, class_name: "Surveillance::FieldSetting",
19
+ dependent: :destroy, inverse_of: :question
20
+ accepts_nested_attributes_for :settings
21
+
22
+ has_many :branch_rules, class_name: "Surveillance::BranchRule",
23
+ dependent: :destroy, inverse_of: :question
24
+ accepts_nested_attributes_for :branch_rules, allow_destroy: :true
25
+
26
+ validates_presence_of :title
27
+
28
+ delegate :survey, to: :parent
29
+
30
+ scope :ordered, -> { order("surveillance_questions.id ASC") }
31
+
32
+ def matching_rule_for answer
33
+ branch_rules.find { |rule| rule.matches?(answer) }
34
+ end
35
+
36
+ def section
37
+ parent_type == "Surveillance::Question" ? parent.parent : parent
38
+ end
39
+
40
+ def column_header
41
+ header = if field.matrix? && field.display_other_field?(self)
42
+ title + " - #{ field.settings.other.label }"
43
+ else
44
+ title
45
+ end
46
+
47
+ sanitize(header, tags: [])
48
+ end
49
+ end
50
+ end