fe 1.0.0 → 2.0.0

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 (402) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -4
  3. data/app/assets/config/fe/manifest.js +3 -0
  4. data/app/assets/config/manifest.js +3 -0
  5. data/app/assets/javascripts/application.js.erb +1 -1
  6. data/app/assets/javascripts/fe/admin.js +0 -1
  7. data/app/assets/javascripts/fe/fe.admin.js +2 -6
  8. data/app/assets/javascripts/fe/{fe.common.js → fe.common.js.erb} +14 -7
  9. data/app/assets/javascripts/fe/fe.public.js +1 -2
  10. data/app/assets/javascripts/fe/{fe.public.nojquery.js → fe.public.nojquery.js.erb} +152 -76
  11. data/app/assets/javascripts/fe/jquery.html5_upload.js +258 -0
  12. data/app/assets/stylesheets/fe/fe.screen.css.scss.erb +22 -1
  13. data/app/assets/stylesheets/fe/validation.css +5 -1
  14. data/app/controllers/{fe/concerns → concerns/fe}/admin/question_sheets_controller_concern.rb +15 -16
  15. data/app/controllers/concerns/fe/answer_pages_controller_concern.rb +131 -0
  16. data/app/controllers/{fe/concerns → concerns/fe}/answer_sheets_controller_concern.rb +14 -6
  17. data/app/controllers/{fe/concerns → concerns/fe}/application_controller_concern.rb +3 -3
  18. data/app/controllers/fe/admin/elements_controller.rb +46 -24
  19. data/app/controllers/fe/admin/email_templates_controller.rb +5 -5
  20. data/app/controllers/fe/admin/question_pages_controller.rb +8 -8
  21. data/app/controllers/fe/reference_pages_controller.rb +11 -11
  22. data/app/controllers/fe/reference_sheets_controller.rb +4 -4
  23. data/app/controllers/fe/references_controller.rb +19 -19
  24. data/app/controllers/fe/submit_pages_controller.rb +5 -5
  25. data/app/helpers/fe/answer_pages_helper.rb +1 -1
  26. data/app/helpers/fe/application_helper.rb +3 -3
  27. data/app/jobs/fe/update_reference_sheet_visibility_job.rb +11 -0
  28. data/app/mailers/fe/notifier.rb +4 -4
  29. data/app/models/answer_sheet.rb +1 -1
  30. data/app/models/application_record.rb +3 -0
  31. data/app/models/{fe/concerns → concerns/fe}/answer_concern.rb +2 -2
  32. data/app/models/{fe/concerns → concerns/fe}/answer_pages_presenter_concern.rb +6 -5
  33. data/app/models/concerns/fe/answer_sheet_concern.rb +125 -0
  34. data/app/models/{fe/concerns → concerns/fe}/choice_field_concern.rb +41 -23
  35. data/app/models/fe/address.rb +2 -2
  36. data/app/models/fe/answer.rb +1 -1
  37. data/app/models/fe/answer_sheet.rb +1 -1
  38. data/app/models/fe/answer_sheet_question_sheet.rb +2 -2
  39. data/app/models/fe/application.rb +14 -10
  40. data/app/models/fe/condition.rb +4 -4
  41. data/app/models/fe/date_field.rb +1 -1
  42. data/app/models/fe/element.rb +126 -44
  43. data/app/models/fe/email_address.rb +2 -2
  44. data/app/models/fe/email_template.rb +1 -1
  45. data/app/models/fe/page.rb +94 -19
  46. data/app/models/fe/page_element.rb +2 -2
  47. data/app/models/fe/paragraph.rb +1 -1
  48. data/app/models/fe/person.rb +5 -5
  49. data/app/models/fe/phone_number.rb +1 -1
  50. data/app/models/fe/question.rb +62 -38
  51. data/app/models/fe/question_grid.rb +15 -3
  52. data/app/models/fe/question_set.rb +53 -13
  53. data/app/models/fe/question_sheet.rb +49 -16
  54. data/app/models/fe/reference_question.rb +9 -3
  55. data/app/models/fe/reference_sheet.rb +102 -47
  56. data/app/models/fe/state_chooser.rb +2 -2
  57. data/app/models/fe/text_field.rb +2 -2
  58. data/app/models/fe/user.rb +1 -1
  59. data/app/models/staff.rb +6 -6
  60. data/app/views/fe/admin/elements/create.js.erb +3 -3
  61. data/app/views/fe/admin/elements/destroy.js.erb +1 -1
  62. data/app/views/fe/admin/elements/drop.js.erb +1 -1
  63. data/app/views/fe/admin/elements/duplicate.js.erb +1 -1
  64. data/app/views/fe/admin/elements/edit.js.erb +1 -1
  65. data/app/views/fe/admin/elements/error.js.erb +1 -1
  66. data/app/views/fe/admin/elements/new.js.erb +13 -6
  67. data/app/views/fe/admin/elements/update.js.erb +1 -1
  68. data/app/views/fe/admin/email_templates/_form.html.erb +3 -3
  69. data/app/views/fe/admin/email_templates/edit.html.erb +3 -3
  70. data/app/views/fe/admin/email_templates/index.html.erb +3 -3
  71. data/app/views/fe/admin/email_templates/new.html.erb +3 -3
  72. data/app/views/fe/admin/panels/_advanced_options.html.erb +8 -8
  73. data/app/views/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  74. data/app/views/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  75. data/app/views/fe/admin/panels/_common_fields.html.erb +2 -17
  76. data/app/views/fe/admin/panels/_condition.html.erb +1 -1
  77. data/app/views/fe/admin/panels/_insert.html.erb +25 -25
  78. data/app/views/fe/admin/panels/_nav_controls.html.erb +4 -4
  79. data/app/views/fe/admin/panels/_page.html.erb +1 -1
  80. data/app/views/fe/admin/panels/_pages_list.html.erb +3 -3
  81. data/app/views/fe/admin/panels/_prop_attachment_field.html.erb +2 -2
  82. data/app/views/fe/admin/panels/_prop_choice_field.html.erb +36 -17
  83. data/app/views/fe/admin/panels/_prop_date_field.html.erb +1 -1
  84. data/app/views/fe/admin/panels/_prop_element.html.erb +5 -5
  85. data/app/views/fe/admin/panels/_prop_page.html.erb +3 -3
  86. data/app/views/fe/admin/panels/_prop_paragraph.html.erb +8 -8
  87. data/app/views/fe/admin/panels/_prop_question_grid.html.erb +7 -2
  88. data/app/views/fe/admin/panels/_prop_question_grid_with_total.html.erb +2 -2
  89. data/app/views/fe/admin/panels/_prop_reference_question.html.erb +3 -3
  90. data/app/views/fe/admin/panels/_prop_section.html.erb +1 -1
  91. data/app/views/fe/admin/panels/_prop_sheet.html.erb +3 -3
  92. data/app/views/fe/admin/panels/_prop_text_field.html.erb +12 -12
  93. data/app/views/fe/admin/question_pages/_element.html.erb +7 -7
  94. data/app/views/fe/admin/question_pages/_element_show.html.erb +2 -2
  95. data/app/views/fe/admin/question_pages/_question_page.html.erb +4 -4
  96. data/app/views/fe/admin/question_pages/create.js.erb +3 -3
  97. data/app/views/fe/admin/question_pages/destroy.js.erb +3 -3
  98. data/app/views/fe/admin/question_pages/edit.js.erb +1 -1
  99. data/app/views/fe/admin/question_pages/error.js.erb +1 -1
  100. data/app/views/fe/admin/question_pages/show.js.erb +2 -2
  101. data/app/views/fe/admin/question_pages/show_panel.js.erb +1 -1
  102. data/app/views/fe/admin/question_pages/update.js.erb +1 -1
  103. data/app/views/fe/admin/question_sheets/edit.js.erb +1 -1
  104. data/app/views/fe/admin/question_sheets/error.js.erb +1 -1
  105. data/app/views/fe/admin/question_sheets/index.html.erb +8 -8
  106. data/app/views/fe/admin/question_sheets/new.html.erb +3 -3
  107. data/app/views/fe/admin/question_sheets/show.html.erb +5 -5
  108. data/app/views/fe/admin/question_sheets/update.js.erb +1 -1
  109. data/app/views/fe/answer_pages/_answer_page.html.erb +13 -14
  110. data/app/views/fe/answer_pages/_element.html.erb +22 -2
  111. data/app/views/fe/answer_pages/_page_name.html.erb +1 -1
  112. data/app/views/fe/answer_pages/show.html.erb +39 -0
  113. data/app/views/fe/answer_pages/update.js.erb +10 -2
  114. data/app/views/fe/answer_sheets/_answer_sheet.html.erb +4 -4
  115. data/app/views/fe/answer_sheets/_element.html.erb +9 -7
  116. data/app/views/fe/answer_sheets/_incomplete.html.erb +1 -1
  117. data/app/views/fe/answer_sheets/_page_link.html.erb +9 -7
  118. data/app/views/fe/answer_sheets/_pages_list.html.erb +3 -3
  119. data/app/views/fe/answer_sheets/_submit_to.html.erb +1 -1
  120. data/app/views/fe/answer_sheets/_title.html.erb +1 -1
  121. data/app/views/fe/answer_sheets/edit.html.erb +18 -18
  122. data/app/views/fe/answer_sheets/incomplete.js.erb +7 -4
  123. data/app/views/fe/answer_sheets/index.html.erb +1 -1
  124. data/app/views/fe/answer_sheets/show.html.erb +1 -1
  125. data/app/views/fe/applications/_logout.html.erb +1 -1
  126. data/app/views/fe/questions/fe/_acceptance.html.erb +10 -9
  127. data/app/views/fe/questions/fe/_attachment_field.html.erb +129 -10
  128. data/app/views/fe/questions/fe/_checkbox_field.html.erb +18 -16
  129. data/app/views/fe/questions/fe/_country.html.erb +6 -6
  130. data/app/views/fe/questions/fe/_date_field.html.erb +5 -4
  131. data/app/views/fe/questions/fe/_date_field_mmyy.html.erb +8 -8
  132. data/app/views/fe/questions/fe/_drop_down_field.html.erb +5 -5
  133. data/app/views/fe/questions/fe/_question_grid.html.erb +10 -10
  134. data/app/views/fe/questions/fe/_question_grid_with_total.html.erb +8 -8
  135. data/app/views/fe/questions/fe/_questions.html.erb +11 -5
  136. data/app/views/fe/questions/fe/_radio_button_field.html.erb +35 -19
  137. data/app/views/fe/questions/fe/_rating.html.erb +56 -18
  138. data/app/views/fe/questions/fe/_reference_discipler.html.erb +1 -1
  139. data/app/views/fe/questions/fe/_reference_friend.html.erb +1 -1
  140. data/app/views/fe/questions/fe/_reference_parent.html.erb +1 -1
  141. data/app/views/fe/questions/fe/_reference_peer.html.erb +1 -1
  142. data/app/views/fe/questions/fe/_reference_question.html.erb +40 -24
  143. data/app/views/fe/questions/fe/_reference_roommate.html.erb +1 -1
  144. data/app/views/fe/questions/fe/_reference_spiritual.html.erb +1 -1
  145. data/app/views/fe/questions/fe/_reference_staff.html.erb +1 -1
  146. data/app/views/fe/questions/fe/_state_chooser.html.erb +6 -6
  147. data/app/views/fe/questions/fe/_text_area_field.html.erb +14 -10
  148. data/app/views/fe/questions/fe/_text_field.html.erb +7 -6
  149. data/app/views/fe/questions/fe/_yes_no.html.erb +8 -8
  150. data/app/views/fe/questions/fe/_yes_no_field.erb +11 -8
  151. data/app/views/fe/reference_pages/_reference.html.erb +12 -12
  152. data/app/views/fe/reference_pages/edit.html.erb +4 -4
  153. data/app/views/fe/reference_sheets/done.html.erb +2 -2
  154. data/app/views/fe/reference_sheets/not_found.html.erb +3 -3
  155. data/app/views/fe/references/edit.html.erb +5 -5
  156. data/app/views/fe/references/show.html.erb +3 -3
  157. data/app/views/fe/references/submit.js.erb +3 -3
  158. data/app/views/fe/submit_pages/_thankyou.html.erb +1 -1
  159. data/app/views/fe/submit_pages/edit.html.erb +9 -9
  160. data/app/views/fe/submit_pages/error.js.erb +1 -1
  161. data/app/views/fe/submit_pages/submit.js.erb +2 -2
  162. data/app/views/layouts/fe/_error_messages_for.html.erb +1 -1
  163. data/app/views/layouts/fe/application.html.erb +3 -4
  164. data/app/views/layouts/fe/fe_admin.html.erb +30 -0
  165. data/app/views.current/fe/admin/elements/_errors.html.erb +11 -0
  166. data/app/views.current/fe/admin/elements/create.js.erb +12 -0
  167. data/app/views.current/fe/admin/elements/destroy.js.erb +4 -0
  168. data/app/views.current/fe/admin/elements/drop.js.erb +3 -0
  169. data/app/views.current/fe/admin/elements/duplicate.js.erb +3 -0
  170. data/app/views.current/fe/admin/elements/edit.js.erb +4 -0
  171. data/app/views.current/fe/admin/elements/error.js.erb +4 -0
  172. data/app/views.current/fe/admin/elements/new.js.erb +17 -0
  173. data/app/views.current/fe/admin/elements/update.js.erb +9 -0
  174. data/app/views.current/fe/admin/email_templates/_form.html.erb +8 -0
  175. data/app/views.current/fe/admin/email_templates/edit.html.erb +13 -0
  176. data/app/views.current/fe/admin/email_templates/index.html.erb +20 -0
  177. data/app/views.current/fe/admin/email_templates/new.html.erb +11 -0
  178. data/app/views.current/fe/admin/panels/_advanced_options.html.erb +49 -0
  179. data/app/views.current/fe/admin/panels/_common_boolean_fields.html.erb +1 -0
  180. data/app/views.current/fe/admin/panels/_common_boolean_fields_default.html.erb +11 -0
  181. data/app/views.current/fe/admin/panels/_common_fields.html.erb +23 -0
  182. data/app/views.current/fe/admin/panels/_condition.html.erb +6 -0
  183. data/app/views.current/fe/admin/panels/_insert.html.erb +39 -0
  184. data/app/views.current/fe/admin/panels/_nav_controls.html.erb +6 -0
  185. data/app/views.current/fe/admin/panels/_page.html.erb +3 -0
  186. data/app/views.current/fe/admin/panels/_pages_list.html.erb +16 -0
  187. data/app/views.current/fe/admin/panels/_prop_attachment_field.html.erb +2 -0
  188. data/app/views.current/fe/admin/panels/_prop_choice_field.html.erb +74 -0
  189. data/app/views.current/fe/admin/panels/_prop_date_field.html.erb +7 -0
  190. data/app/views.current/fe/admin/panels/_prop_element.html.erb +23 -0
  191. data/app/views.current/fe/admin/panels/_prop_page.html.erb +26 -0
  192. data/app/views.current/fe/admin/panels/_prop_paragraph.html.erb +46 -0
  193. data/app/views.current/fe/admin/panels/_prop_question_grid.html.erb +28 -0
  194. data/app/views.current/fe/admin/panels/_prop_question_grid_with_total.html.erb +14 -0
  195. data/app/views.current/fe/admin/panels/_prop_reference_question.html.erb +12 -0
  196. data/app/views.current/fe/admin/panels/_prop_section.html.erb +8 -0
  197. data/app/views.current/fe/admin/panels/_prop_sheet.html.erb +20 -0
  198. data/app/views.current/fe/admin/panels/_prop_text_field.html.erb +20 -0
  199. data/app/views.current/fe/admin/question_pages/_element.html.erb +28 -0
  200. data/app/views.current/fe/admin/question_pages/_element_show.html.erb +10 -0
  201. data/app/views.current/fe/admin/question_pages/_errors.html.erb +10 -0
  202. data/app/views.current/fe/admin/question_pages/_question_page.html.erb +13 -0
  203. data/app/views.current/fe/admin/question_pages/create.js.erb +11 -0
  204. data/app/views.current/fe/admin/question_pages/destroy.js.erb +5 -0
  205. data/app/views.current/fe/admin/question_pages/edit.js.erb +3 -0
  206. data/app/views.current/fe/admin/question_pages/error.js.erb +4 -0
  207. data/app/views.current/fe/admin/question_pages/show.js.erb +9 -0
  208. data/app/views.current/fe/admin/question_pages/show_panel.js.erb +3 -0
  209. data/app/views.current/fe/admin/question_pages/update.js.erb +2 -0
  210. data/app/views.current/fe/admin/question_sheets/_errors.html.erb +11 -0
  211. data/app/views.current/fe/admin/question_sheets/edit.js.erb +3 -0
  212. data/app/views.current/fe/admin/question_sheets/error.js.erb +5 -0
  213. data/app/views.current/fe/admin/question_sheets/index.html.erb +41 -0
  214. data/app/views.current/fe/admin/question_sheets/new.html.erb +15 -0
  215. data/app/views.current/fe/admin/question_sheets/show.html.erb +27 -0
  216. data/app/views.current/fe/admin/question_sheets/update.js.erb +2 -0
  217. data/app/views.current/fe/answer_pages/_answer_page.html.erb +53 -0
  218. data/app/views.current/fe/answer_pages/_element.html.erb +32 -0
  219. data/app/views.current/fe/answer_pages/_page_name.html.erb +1 -0
  220. data/app/views.current/fe/answer_pages/show.html.erb +39 -0
  221. data/app/views.current/fe/answer_pages/update.js.erb +13 -0
  222. data/app/views.current/fe/answer_sheets/_answer_sheet.html.erb +26 -0
  223. data/app/views.current/fe/answer_sheets/_element.html.erb +74 -0
  224. data/app/views.current/fe/answer_sheets/_incomplete.html.erb +10 -0
  225. data/app/views.current/fe/answer_sheets/_page_link.html.erb +9 -0
  226. data/app/views.current/fe/answer_sheets/_pages_list.html.erb +11 -0
  227. data/app/views.current/fe/answer_sheets/_submit_to.html.erb +1 -0
  228. data/app/views.current/fe/answer_sheets/_title.html.erb +1 -0
  229. data/app/views.current/fe/answer_sheets/edit.html.erb +66 -0
  230. data/app/views.current/fe/answer_sheets/incomplete.js.erb +11 -0
  231. data/app/views.current/fe/answer_sheets/index.html.erb +18 -0
  232. data/app/views.current/fe/answer_sheets/send_reference_invite.js.erb +8 -0
  233. data/app/views.current/fe/answer_sheets/show.html.erb +13 -0
  234. data/app/views.current/fe/applications/_logout.html.erb +1 -0
  235. data/app/views.current/fe/applications/show.html.erb +1 -0
  236. data/app/views.current/fe/help/builder.html +33 -0
  237. data/app/views.current/fe/help/question_grid.html +18 -0
  238. data/app/views.current/fe/questions/fe/_acceptance.html.erb +14 -0
  239. data/app/views.current/fe/questions/fe/_attachment_field.html.erb +165 -0
  240. data/app/views.current/fe/questions/fe/_checkbox_field.html.erb +53 -0
  241. data/app/views.current/fe/questions/fe/_country.html.erb +7 -0
  242. data/app/views.current/fe/questions/fe/_date_field.html.erb +7 -0
  243. data/app/views.current/fe/questions/fe/_date_field_mmyy.html.erb +9 -0
  244. data/app/views.current/fe/questions/fe/_drop_down_field.html.erb +8 -0
  245. data/app/views.current/fe/questions/fe/_paragraph.html.erb +1 -0
  246. data/app/views.current/fe/questions/fe/_question_grid.html.erb +70 -0
  247. data/app/views.current/fe/questions/fe/_question_grid_with_total.html.erb +64 -0
  248. data/app/views.current/fe/questions/fe/_questions.html.erb +15 -0
  249. data/app/views.current/fe/questions/fe/_radio_button_field.html.erb +60 -0
  250. data/app/views.current/fe/questions/fe/_rating.html.erb +64 -0
  251. data/app/views.current/fe/questions/fe/_reference_discipler.html.erb +1 -0
  252. data/app/views.current/fe/questions/fe/_reference_friend.html.erb +1 -0
  253. data/app/views.current/fe/questions/fe/_reference_parent.html.erb +1 -0
  254. data/app/views.current/fe/questions/fe/_reference_peer.html.erb +1 -0
  255. data/app/views.current/fe/questions/fe/_reference_question.html.erb +61 -0
  256. data/app/views.current/fe/questions/fe/_reference_roommate.html.erb +1 -0
  257. data/app/views.current/fe/questions/fe/_reference_spiritual.html.erb +1 -0
  258. data/app/views.current/fe/questions/fe/_reference_staff.html.erb +1 -0
  259. data/app/views.current/fe/questions/fe/_section.html.erb +1 -0
  260. data/app/views.current/fe/questions/fe/_state_chooser.html.erb +7 -0
  261. data/app/views.current/fe/questions/fe/_text_area_field.html.erb +17 -0
  262. data/app/views.current/fe/questions/fe/_text_field.html.erb +9 -0
  263. data/app/views.current/fe/questions/fe/_yes_no.html.erb +17 -0
  264. data/app/views.current/fe/questions/fe/_yes_no_field.erb +20 -0
  265. data/app/views.current/fe/reference_pages/_reference.html.erb +31 -0
  266. data/app/views.current/fe/reference_pages/edit.html.erb +24 -0
  267. data/app/views.current/fe/reference_sheets/done.html.erb +2 -0
  268. data/app/views.current/fe/reference_sheets/not_found.html.erb +5 -0
  269. data/app/views.current/fe/reference_sheets/submitted.js.erb +1 -0
  270. data/app/views.current/fe/references/edit.html.erb +8 -0
  271. data/app/views.current/fe/references/send_invite.js.erb +7 -0
  272. data/app/views.current/fe/references/show.html.erb +18 -0
  273. data/app/views.current/fe/references/submit.js.erb +3 -0
  274. data/app/views.current/fe/submit_pages/_errors.html.erb +1 -0
  275. data/app/views.current/fe/submit_pages/_thankyou.html.erb +2 -0
  276. data/app/views.current/fe/submit_pages/edit.html.erb +36 -0
  277. data/app/views.current/fe/submit_pages/error.js.erb +1 -0
  278. data/app/views.current/fe/submit_pages/submit.js.erb +3 -0
  279. data/app/views.current/layouts/fe/_error_messages_for.html.erb +7 -0
  280. data/app/views.current/layouts/fe/application.html.erb +47 -0
  281. data/app/{views/layouts/fe/fe.admin.html.erb → views.current/layouts/fe/fe_admin.html.erb} +4 -4
  282. data/config/initializers/paper_trail.rb +3 -0
  283. data/config/routes.rb +3 -1
  284. data/db/migrate/20131003041856_core.rb +23 -23
  285. data/db/migrate/20131003044250_create_reference_sheets.rb +1 -1
  286. data/db/migrate/20131003044436_add_element_and_answer_fields.rb +3 -3
  287. data/db/migrate/20131003044518_create_email_templates.rb +2 -2
  288. data/db/migrate/20131003044621_add_max_lengths.rb +1 -1
  289. data/db/migrate/20131003044714_create_join_table.rb +1 -1
  290. data/db/migrate/20131016162128_remove_question_id_from_element.rb +1 -1
  291. data/db/migrate/20140623153424_create_fe_people.rb +1 -1
  292. data/db/migrate/20140624180246_create_fe_addresses.rb +1 -1
  293. data/db/migrate/20140624182216_create_fe_phone_numbers.rb +1 -1
  294. data/db/migrate/20140625160545_create_fe_users.rb +1 -1
  295. data/db/migrate/20140808202507_add_conditional_type_to_elements.rb +1 -1
  296. data/db/migrate/20140808203609_add_conditional_answer_to_elements.rb +1 -1
  297. data/db/migrate/20141103204704_remove_short_value_column.rb +1 -1
  298. data/db/migrate/20141109154522_move_conditional_ids_used_for_choice_field_to_their_own_column.rb +1 -1
  299. data/db/migrate/20150504221439_add_all_element_ids_to_pages.rb +1 -1
  300. data/db/migrate/20150713022326_add_locale_columns.rb +1 -1
  301. data/db/migrate/20150714220730_add_locale_to_answer_sheet.rb +1 -1
  302. data/db/migrate/20150925181652_add_share_to_elements.rb +5 -0
  303. data/db/migrate/20150928085325_change_pages_all_element_ids_to_text.rb +1 -1
  304. data/db/migrate/20150930191538_add_locale_to_reference_sheets.rb +1 -1
  305. data/db/migrate/20151021181928_switch_conditional_answer_separator_to_semicolon.rb +7 -0
  306. data/db/migrate/20151021184250_add_question_sheet_id_in_refs.rb +12 -0
  307. data/db/migrate/20160201185838_add_visible_and_visibility_cache_key_to_reference_sheets.rb +6 -0
  308. data/db/migrate/20160805221415_add_rating_extra_labels.rb +10 -0
  309. data/db/migrate/20181108201746_create_versions.rb +80 -0
  310. data/db/migrate/20181218201130_increase_slug_length.rb +5 -0
  311. data/lib/fe/engine.rb +10 -10
  312. data/lib/fe/version.rb +1 -1
  313. data/lib/fe.rb +7 -1
  314. data/spec/controllers/fe/admin/elements_controller_spec.rb +30 -20
  315. data/spec/controllers/fe/admin/email_templates_controller_spec.rb +2 -2
  316. data/spec/controllers/fe/admin/question_pages_controller_spec.rb +1 -1
  317. data/spec/controllers/fe/admin/question_sheets_controller_spec.rb +4 -4
  318. data/spec/controllers/fe/answer_pages_controller_spec.rb +86 -30
  319. data/spec/controllers/fe/answer_sheets_controller_spec.rb +65 -9
  320. data/spec/dummy/app/assets/config/manifest.js +0 -0
  321. data/spec/dummy/app/models/user.rb +1 -1
  322. data/spec/dummy/app/views/layouts/application.html.erb +2 -2
  323. data/spec/dummy/config/application.rb +1 -0
  324. data/spec/dummy/config/environments/test.rb +3 -3
  325. data/spec/dummy/config/initializers/assets.rb +5 -2
  326. data/spec/dummy/config/initializers/fast_gettext.rb +1 -1
  327. data/spec/dummy/config/initializers/to_unsafe_h.rb +5 -0
  328. data/spec/dummy/config/initializers/to_unsafe_h.rb.new +5 -0
  329. data/spec/dummy/db/migrate/20141203214017_core.fe_engine.rb +23 -23
  330. data/spec/dummy/db/migrate/20141203214018_create_reference_sheets.fe_engine.rb +1 -1
  331. data/spec/dummy/db/migrate/20141203214019_add_element_and_answer_fields.fe_engine.rb +3 -3
  332. data/spec/dummy/db/migrate/20141203214020_create_email_templates.fe_engine.rb +2 -2
  333. data/spec/dummy/db/migrate/20141203214021_add_max_lengths.fe_engine.rb +1 -1
  334. data/spec/dummy/db/migrate/20141203214022_create_join_table.fe_engine.rb +1 -1
  335. data/spec/dummy/db/migrate/20141203214023_remove_question_id_from_element.fe_engine.rb +1 -1
  336. data/spec/dummy/db/migrate/20141203214024_create_fe_people.fe_engine.rb +1 -1
  337. data/spec/dummy/db/migrate/20141203214025_create_fe_addresses.fe_engine.rb +1 -1
  338. data/spec/dummy/db/migrate/20141203214027_create_fe_users.fe_engine.rb +1 -1
  339. data/spec/dummy/db/migrate/20141203214028_add_conditional_type_to_elements.fe_engine.rb +1 -1
  340. data/spec/dummy/db/migrate/20141203214029_add_conditional_answer_to_elements.fe_engine.rb +1 -1
  341. data/spec/dummy/db/migrate/20141203214030_remove_short_value_column.fe_engine.rb +1 -1
  342. data/spec/dummy/db/migrate/20141203214031_move_conditional_ids_used_for_choice_field_to_their_own_column.fe_engine.rb +1 -1
  343. data/spec/dummy/db/migrate/20150123215803_create_users.rb +1 -1
  344. data/spec/dummy/db/migrate/20150504222619_add_all_element_ids_to_pages.fe_engine.rb +1 -1
  345. data/spec/dummy/db/migrate/20150925192557_add_share_to_elements.fe_engine.rb +6 -0
  346. data/spec/dummy/db/migrate/20150930190001_create_fe_phone_numbers.fe_engine.rb +1 -1
  347. data/spec/dummy/db/migrate/20150930190002_add_locale_columns.fe_engine.rb +1 -1
  348. data/spec/dummy/db/migrate/20150930190003_add_locale_to_answer_sheet.fe_engine.rb +1 -1
  349. data/spec/dummy/db/migrate/20150930190004_change_pages_all_element_ids_to_text.fe_engine.rb +1 -1
  350. data/spec/dummy/db/migrate/20150930191756_add_locale_to_reference_sheets.fe_engine.rb +1 -1
  351. data/spec/dummy/db/migrate/20151021190027_add_question_sheet_id_in_refs.fe_engine.rb +13 -0
  352. data/spec/dummy/db/migrate/20160204164612_switch_conditional_answer_separator_to_semicolon.fe_engine.rb +8 -0
  353. data/spec/dummy/db/migrate/20160204164613_add_visible_and_visibility_cache_key_to_reference_sheets.fe_engine.rb +7 -0
  354. data/spec/dummy/db/migrate/20181108201746_create_versions.rb +80 -0
  355. data/spec/dummy/db/schema.rb +71 -79
  356. data/spec/dummy/log/test.log +101278 -419
  357. data/spec/factories/answer_sheet_question_sheets.rb +1 -1
  358. data/spec/factories/answer_sheets.rb +2 -2
  359. data/spec/factories/answers.rb +1 -1
  360. data/spec/factories/applications.rb +3 -3
  361. data/spec/factories/dummy_applications.rb +3 -3
  362. data/spec/factories/dummy_people.rb +3 -3
  363. data/spec/factories/dummy_users.rb +3 -3
  364. data/spec/factories/elements.rb +21 -21
  365. data/spec/factories/email_templates.rb +3 -3
  366. data/spec/factories/fe_addresses.rb +10 -10
  367. data/spec/factories/fe_email_addresses.rb +3 -3
  368. data/spec/factories/fe_email_templates.rb +4 -4
  369. data/spec/factories/fe_people.rb +5 -5
  370. data/spec/factories/fe_phone_numbers.rb +3 -3
  371. data/spec/factories/fe_user.rb +3 -3
  372. data/spec/factories/page.rb +1 -1
  373. data/spec/factories/page_elements.rb +1 -1
  374. data/spec/factories/paragraphs.rb +1 -1
  375. data/spec/factories/question_sheet.rb +2 -2
  376. data/spec/factories/reference_questions.rb +1 -1
  377. data/spec/factories/reference_sheets.rb +1 -1
  378. data/spec/jobs/fe/update_reference_sheet_visibility_job_spec.rb +40 -0
  379. data/spec/models/fe/answer_sheet_question_sheet_spec.rb +1 -1
  380. data/spec/models/fe/answer_spec.rb +2 -2
  381. data/spec/models/fe/application_spec.rb +94 -1
  382. data/spec/models/fe/choice_field_spec.rb +47 -1
  383. data/spec/models/fe/condition_spec.rb +2 -2
  384. data/spec/models/fe/element_spec.rb +252 -60
  385. data/spec/models/fe/email_template_spec.rb +1 -1
  386. data/spec/models/fe/page_element_spec.rb +1 -1
  387. data/spec/models/fe/page_spec.rb +57 -12
  388. data/spec/models/fe/person_spec.rb +1 -1
  389. data/spec/models/fe/question_set_spec.rb +91 -0
  390. data/spec/models/fe/question_sheet_spec.rb +11 -15
  391. data/spec/models/fe/question_spec.rb +73 -6
  392. data/spec/models/fe/reference_question_spec.rb +20 -0
  393. data/spec/models/fe/reference_sheet_spec.rb +287 -4
  394. data/spec/models/fe/text_field_spec.rb +22 -0
  395. data/spec/rails_helper.rb +79 -76
  396. metadata +195 -57
  397. data/app/assets/javascripts/fe/jquery.scrollTo-min.js +0 -7
  398. data/app/controllers/fe/concerns/answer_pages_controller_concern.rb +0 -84
  399. data/app/models/fe/concerns/answer_sheet_concern.rb +0 -85
  400. data/spec/dummy/db/migrate/20141203214026_create_create_fe_phone_numbers.fe_engine.rb +0 -17
  401. data/spec/dummy/log/development.log +0 -5
  402. /data/app/{assets/stylesheets/360front.css → views.current/fe/admin/elements/reorder.js.erb} +0 -0
@@ -0,0 +1,125 @@
1
+ module Fe
2
+ module AnswerSheetConcern
3
+ extend ActiveSupport::Concern
4
+
5
+ begin
6
+ included do
7
+ has_many :answer_sheet_question_sheets, foreign_key: 'answer_sheet_id', class_name: '::Fe::AnswerSheetQuestionSheet'
8
+ has_many :question_sheets, through: :answer_sheet_question_sheets, class_name: 'Fe::QuestionSheet'
9
+ has_many :answers, ->(answer_sheet) {
10
+ question_sheet_ids = answer_sheet.question_sheet_ids
11
+
12
+ if question_sheet_ids.any?
13
+ element_ids = Fe::Page.joins(:question_sheet).where(question_sheet_id: question_sheet_ids).pluck(:all_element_ids).compact
14
+ element_ids = element_ids.collect{ |e| e.split(',') }.flatten
15
+ end
16
+
17
+ unless question_sheet_ids.any? && element_ids.any?
18
+ # an answer sheet not assigned to a question sheet, or assigned to
19
+ # a question sheet with no elements should not return any answers
20
+ return where('false')
21
+ end
22
+
23
+ where('question_id' => element_ids)
24
+
25
+ }, foreign_key: 'answer_sheet_id', class_name: '::Fe::Answer'
26
+ has_many :reference_sheets, foreign_key: 'applicant_answer_sheet_id', class_name: 'Fe::ReferenceSheet'
27
+ has_many :payments, foreign_key: 'application_id', class_name: 'Fe::Payment'
28
+ end
29
+ rescue ActiveSupport::Concern::MultipleIncludedBlocks
30
+ end
31
+
32
+ def languages
33
+ return [] unless question_sheets.first
34
+
35
+ unless @languages
36
+ @languages = question_sheets.first.languages
37
+ question_sheets[1..-1].each { |qs| @languages &= qs.languages.select(&:present?) }
38
+ end
39
+ @languages
40
+ end
41
+
42
+ def complete?
43
+ !completed_at.nil?
44
+ end
45
+
46
+ # answers for this sheet, grouped by question id
47
+ def answers_by_question
48
+ @answers_by_question ||= answers.group_by { |answer| answer.question_id }
49
+ end
50
+
51
+ # Convenience method if there is only one question sheet in your system
52
+ def question_sheet
53
+ question_sheets.first
54
+ end
55
+
56
+ def pages
57
+ Page.where(question_sheet_id: question_sheets.collect(&:id)).visible.order('number')
58
+ end
59
+
60
+ def completely_filled_out?
61
+ pages.all? {|p| p.complete?(self)}
62
+ end
63
+
64
+ def has_answer_for?(question_id)
65
+ !answers_by_question[question_id].nil?
66
+ end
67
+
68
+ def reference?
69
+ false
70
+ end
71
+
72
+ def percent_complete(required_only = true, restrict_to_pages = [])
73
+ # build an element to page lookup using page's cached all_element_ids
74
+ # this will make the hidden? calls on element faster because we can pass the page
75
+ # (the page builds a list of hidden elements for quick lookup)
76
+ elements_to_pages = {}
77
+ pages = question_sheets.collect(&:pages).flatten
78
+ pages = pages & restrict_to_pages if restrict_to_pages.present?
79
+ pages.each do |p|
80
+ p.all_element_ids_arr.each do |e_id|
81
+ elements_to_pages[e_id] = p
82
+ end
83
+ end
84
+
85
+ # determine which questions should count towards the questions total in the percent calculation
86
+ countable_questions = question_sheets.collect{ |qs| qs.all_elements.questions }.flatten
87
+ countable_questions.select!{ |e| elements_to_pages[e.id] } if restrict_to_pages.present?
88
+ countable_questions.reject!{ |e| e.hidden?(self, elements_to_pages[e.id]) }
89
+ countable_questions.select!{ |e| e.required } if required_only
90
+
91
+ # no progress if there are no questions
92
+ num_questions = countable_questions.length
93
+ return 0 if num_questions == 0
94
+
95
+ # count questions with answers in Fe::Answer
96
+ answers = self.answers.where("(question_id IN (?) AND value IS NOT NULL) AND (value != '')", countable_questions.collect(&:id))
97
+ answered_question_ids = answers.pluck(Arel.sql('distinct(question_id)'))
98
+
99
+ # need to look for answers for the remaining questions using has_response?
100
+ # so that questions with object_name/attribute_name set are counted
101
+ other_answered_questions = countable_questions.reject{ |e| answered_question_ids.include?(e.id) }
102
+ other_answered_questions.select!{ |e| e.has_response?(self) }
103
+
104
+ # count total
105
+ num_answers = answered_question_ids.count + other_answered_questions.count
106
+ [ [ (num_answers.to_f / num_questions.to_f * 100.0).to_i, 100 ].min, 0 ].max
107
+ end
108
+
109
+ def collat_title() "" end
110
+
111
+ def question_sheet_ids
112
+ question_sheets.collect(&:id)
113
+ end
114
+
115
+ def question_sheets_all_reference_elements
116
+ # forms are generally not changed often so caching on the last updated elementd
117
+ # will provide a good balance of speed and cache invalidation
118
+ element_ids = Rails.cache.fetch(question_sheets + ['answer_sheet#answer_sheet_all_reference_elements', Fe::Element.order('updated_at desc, id desc').first]) do
119
+ question_sheets.compact.collect { |q| q.all_elements.reference_kinds.pluck(:id) }.flatten
120
+ end
121
+
122
+ Fe::Element.find(element_ids)
123
+ end
124
+ end
125
+ end
@@ -13,11 +13,32 @@ module Fe
13
13
 
14
14
  begin
15
15
  included do
16
- has_many :elements, :class_name => "Element", :foreign_key => "choice_field_id", :dependent => :nullify#, :order => :position
16
+ has_many :elements, class_name: "Element", foreign_key: "choice_field_id", dependent: :nullify#, order: :position
17
+ serialize :rating_before_label_translations, Hash
18
+ serialize :rating_after_label_translations, Hash
19
+ serialize :rating_na_label_translations, Hash
17
20
  end
18
21
  rescue ActiveSupport::Concern::MultipleIncludedBlocks
19
22
  end
20
23
 
24
+ def rating_before_label(locale = nil)
25
+ rating_before_label_translations &&
26
+ rating_before_label_translations[locale].present? ?
27
+ rating_before_label_translations[locale] : self[:rating_before_label]
28
+ end
29
+
30
+ def rating_after_label(locale = nil)
31
+ rating_after_label_translations &&
32
+ rating_after_label_translations[locale].present? ?
33
+ rating_after_label_translations[locale] : self[:rating_after_label]
34
+ end
35
+
36
+ def rating_na_label(locale = nil)
37
+ rating_na_label_translations &&
38
+ rating_na_label_translations[locale].present? ?
39
+ rating_na_label_translations[locale] : self[:rating_na_label]
40
+ end
41
+
21
42
  # Returns choices stored one per line in content field
22
43
  def choices(locale = nil)
23
44
  retVal = Array.new
@@ -34,7 +55,7 @@ module Fe
34
55
  retVal = [ doc.elements.collect(text_xpath){|c|c.text}, doc.elements.collect(value_xpath){|c|c.text} ].transpose
35
56
  end
36
57
  elsif content.present?
37
- choices = content_translations[locale] || content
58
+ choices = content(locale)
38
59
  choices.split("\n").each do |opt|
39
60
  pair = opt.strip.split(";").reverse!
40
61
  pair[1] ||= pair[0]
@@ -44,19 +65,19 @@ module Fe
44
65
  return retVal
45
66
  end
46
67
 
47
- def has_answer?(choice, app)
48
- responses(app).each do |r| # loop through Answers
49
- # legacy field type choices may be int or tinyint
50
- # raise r.inspect + ' - ' + choice.inspect if id == 1137 && r != 1
51
- # r = r.to_s
52
- return true if case true
53
- when is_true(r) then is_true(choice)
54
- when is_false(r) then is_false(choice)
55
- else
56
- r.to_s == choice.to_s
57
- end
68
+ # choices can be an array, in which case any match returns true
69
+ def has_answer?(choice, answer_sheet)
70
+ if choice.is_a?(Array)
71
+ return choice.any? { |c|
72
+ has_answer?(c, answer_sheet)
73
+ }
58
74
  end
59
- false
75
+
76
+ responses(answer_sheet).any? { |r|
77
+ is_true(r) && is_true(choice) ||
78
+ is_false(r) && is_false(choice) ||
79
+ r.to_s.strip == choice.to_s.strip
80
+ }
60
81
  end
61
82
 
62
83
  # which view to render this element?
@@ -89,8 +110,8 @@ module Fe
89
110
  end
90
111
 
91
112
  # css class names for javascript-based validation
92
- def validation_class(answer_sheet)
93
- if self.required?(answer_sheet)
113
+ def validation_class(answer_sheet, page = nil)
114
+ if self.required?(answer_sheet, page)
94
115
  if self.style == 'drop-down'
95
116
  'validate-selection required'
96
117
  elsif self.style == 'rating'
@@ -105,9 +126,8 @@ module Fe
105
126
  end
106
127
  end
107
128
 
108
- def display_response(app=nil)
129
+ def display_response(app = nil)
109
130
  r = responses(app)
110
- r.reject! {|a| a.class == Answer && a.value.blank?}
111
131
  if r.blank?
112
132
  ""
113
133
  elsif self.style == 'yes-no'
@@ -120,15 +140,13 @@ module Fe
120
140
  elsif self.style == 'acceptance'
121
141
  "Accepted" # if not blank, it's accepted
122
142
  else
123
- r.compact.join(", ")
143
+ r.compact.join(', ')
124
144
  end
125
145
  end
126
146
 
127
147
  def conditional_match(answer_sheet)
128
- displayed_response = display_response(answer_sheet)
129
- (is_true(displayed_response) && is_true(conditional_answer)) ||
130
- (is_response_false(answer_sheet) && is_false(conditional_answer)) ||
131
- (displayed_response == conditional_answer)
148
+ has_answer?(conditional_answers, answer_sheet) ||
149
+ (responses(answer_sheet).empty? && conditional_answers.empty?)
132
150
  end
133
151
 
134
152
  def is_response_false(answer_sheet)
@@ -1,3 +1,3 @@
1
- class Fe::Address < ActiveRecord::Base
2
- belongs_to :person
1
+ class Fe::Address < ApplicationRecord
2
+ belongs_to :person, optional: true
3
3
  end
@@ -7,7 +7,7 @@
7
7
  # may want special handling for ChoiceFields to store both id/slug and text representations
8
8
 
9
9
  module Fe
10
- class Answer < ActiveRecord::Base
10
+ class Answer < ApplicationRecord
11
11
  include Fe::AnswerConcern
12
12
  end
13
13
  end
@@ -1,5 +1,5 @@
1
1
  module Fe
2
- class AnswerSheet < ActiveRecord::Base
2
+ class AnswerSheet < ApplicationRecord
3
3
  self.abstract_class = true
4
4
 
5
5
  =begin
@@ -1,7 +1,7 @@
1
1
  module Fe
2
- class AnswerSheetQuestionSheet < ActiveRecord::Base
2
+ class AnswerSheetQuestionSheet < ApplicationRecord
3
3
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
4
4
  belongs_to :answer_sheet, class_name: Fe.answer_sheet_class
5
- belongs_to :question_sheet, class_name: 'Fe::QuestionSheet'
5
+ belongs_to :question_sheet, optional: true, class_name: 'Fe::QuestionSheet'
6
6
  end
7
7
  end
@@ -5,12 +5,16 @@ class Fe::Application < Fe::AnswerSheet
5
5
 
6
6
  self.table_name = "#{Fe.table_name_prefix}applications"
7
7
 
8
- belongs_to :applicant, foreign_key: 'person_id', class_name: "Person"
9
- has_many :references, :class_name => 'ReferenceSheet', :foreign_key => :applicant_answer_sheet_id, :dependent => :destroy
10
- has_one :answer_sheet_question_sheet, :foreign_key => "answer_sheet_id"
11
- has_many :answer_sheet_question_sheets, :foreign_key => 'answer_sheet_id'
12
- has_many :question_sheets, :through => :answer_sheet_question_sheets
13
-
8
+ belongs_to :applicant, optional: true, foreign_key: 'person_id', class_name: "Person"
9
+ has_many :references, class_name: 'ReferenceSheet', foreign_key: :applicant_answer_sheet_id, dependent: :destroy
10
+ has_one :answer_sheet_question_sheet, foreign_key: "answer_sheet_id"
11
+ has_many :answer_sheet_question_sheets, foreign_key: 'answer_sheet_id'
12
+ has_many :question_sheets, through: :answer_sheet_question_sheets
13
+
14
+ has_paper_trail on: [], ignore: [:updated_at]
15
+
16
+ alias_method :all_references, :references
17
+
14
18
  # This will be overridden by the state machine defined in the enclosing app
15
19
  def completed?
16
20
  raise "completed? should be implemented by the extending class"
@@ -35,7 +39,7 @@ class Fe::Application < Fe::AnswerSheet
35
39
  end
36
40
  return Fe::ReferenceSheet.new()
37
41
  end
38
-
42
+
39
43
  def answer_sheets
40
44
  a_sheets = [self]
41
45
  references.each do |r|
@@ -43,7 +47,7 @@ class Fe::Application < Fe::AnswerSheet
43
47
  end
44
48
  a_sheets
45
49
  end
46
-
50
+
47
51
  def reference_answer_sheets
48
52
  r_sheets = Array.new()
49
53
  references.each do |r|
@@ -51,9 +55,9 @@ class Fe::Application < Fe::AnswerSheet
51
55
  end
52
56
  r_sheets
53
57
  end
54
-
58
+
55
59
  def has_references?
56
60
  self.references.size > 0
57
61
  end
58
-
62
+
59
63
  end
@@ -3,17 +3,17 @@
3
3
  # i.e. "answer == 'yes'"
4
4
  # a question can have more than one answer (choose many) in which case ANY answer will do (find)
5
5
  module Fe
6
- class Condition < ActiveRecord::Base
6
+ class Condition < ApplicationRecord
7
7
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
8
8
 
9
9
  belongs_to :question_sheet
10
10
 
11
11
  belongs_to :trigger,
12
- :class_name => "Question",
13
- :foreign_key => "trigger_id"
12
+ class_name: "Question",
13
+ foreign_key: "trigger_id"
14
14
 
15
15
  validates_presence_of :expression
16
- validates_length_of :expression, :maximum => 255, :allow_nil => true
16
+ validates_length_of :expression, maximum: 255, allow_nil: true
17
17
 
18
18
  # evaluate triggering element against expression and return match|nil
19
19
  def evaluate?
@@ -3,7 +3,7 @@
3
3
  module Fe
4
4
  class DateField < Question
5
5
 
6
- def validation_class(answer_sheet = nil)
6
+ def validation_class(answer_sheet = nil, page = nil)
7
7
  if self.style == 'mmyy'
8
8
  'validate-selection ' + super
9
9
  else
@@ -1,42 +1,47 @@
1
1
  # Element represents a section, question or content element on the question sheet
2
2
  module Fe
3
- class Element < ActiveRecord::Base
3
+ class Element < ApplicationRecord
4
4
  self.table_name = self.table_name.sub('fe_', Fe.table_name_prefix)
5
5
 
6
+ attr_accessor :old_id
7
+
6
8
  belongs_to :question_grid,
7
- class_name: "Fe::QuestionGrid"
9
+ optional: true, class_name: "Fe::QuestionGrid"
8
10
 
9
11
  belongs_to :question_grid_with_total,
10
- class_name: "Fe::QuestionGridWithTotal",
12
+ optional: true, class_name: "Fe::QuestionGridWithTotal",
11
13
  foreign_key: "question_grid_id"
12
14
 
13
15
  belongs_to :choice_field,
14
- class_name: "Fe::ChoiceField"
16
+ optional: true, class_name: "Fe::ChoiceField"
15
17
 
16
18
  has_many :choice_field_children, foreign_key: 'choice_field_id',
17
19
  class_name: 'Fe::Element'
18
20
 
19
- belongs_to :question_sheet, :foreign_key => "related_question_sheet_id"
21
+ belongs_to :question_sheet, optional: true, foreign_key: "related_question_sheet_id"
20
22
 
21
- belongs_to :conditional, polymorphic: true
23
+ belongs_to :conditional, optional: true, polymorphic: true
22
24
 
23
25
  self.inheritance_column = :kind
24
26
 
25
27
  has_many :page_elements, dependent: :destroy
26
28
  has_many :pages, through: :page_elements
27
29
 
28
- scope :active, -> { select("distinct(#{Fe::Element.table_name}.id), #{Fe::Element.table_name}.*").where(Fe::QuestionSheet.table_name + '.archived' => false).joins({:pages => :question_sheet}) }
30
+ scope :active, -> { select("distinct(#{Fe::Element.table_name}.id), #{Fe::Element.table_name}.*").where(Fe::QuestionSheet.table_name + '.archived' => false).joins({pages: :question_sheet}) }
29
31
  scope :questions, -> { where("kind NOT IN('Fe::Paragraph', 'Fe::Section', 'Fe::QuestionGrid', 'Fe::QuestionGridWithTotal')") }
32
+ scope :shared, -> { where(share: true) }
33
+ scope :grid_kinds, -> { where(kind: ['Fe::QuestionGrid', 'Fe::QuestionGridWithTotal']) }
34
+ scope :reference_kinds, -> { where(kind: 'Fe::ReferenceQuestion') }
30
35
 
31
36
  validates_presence_of :kind
32
37
  validates_presence_of :style
33
- # validates_presence_of :label, :style, :on => :update
38
+ # validates_presence_of :label, :style, on: :update
34
39
 
35
- validates_length_of :kind, :maximum => 40, :allow_nil => true
36
- validates_length_of :style, :maximum => 40, :allow_nil => true
37
- # validates_length_of :label, :maximum => 255, :allow_nil => true
40
+ validates_length_of :kind, maximum: 40, allow_nil: true
41
+ validates_length_of :style, maximum: 40, allow_nil: true
42
+ # validates_length_of :label, maximum: 255, allow_nil: true
38
43
 
39
- before_validation :set_defaults, :on => :create
44
+ before_validation :set_defaults, on: :create
40
45
  before_save :set_conditional_element
41
46
  after_save :update_page_all_element_ids
42
47
  after_save :update_any_previous_conditional_elements
@@ -46,22 +51,22 @@ module Fe
46
51
  serialize :content_translations, Hash
47
52
 
48
53
  # HUMANIZED_ATTRIBUTES = {
49
- # :slug => "Variable"
54
+ # slug: "Variable"
50
55
  # }changed.include?('address1')
51
56
  #
52
57
  # def self.human_attrib_name(attr)
53
58
  # HUMANIZED_ATTRIBUTES[attr.to_sym] || super
54
59
  # end
55
60
  def label(locale = nil)
56
- label_translations[locale] || self[:label]
61
+ label_translations[locale].present? ? label_translations[locale] : self[:label]
57
62
  end
58
63
 
59
64
  def content(locale = nil)
60
- content_translations[locale] || self[:content]
65
+ content_translations[locale].present? ? content_translations[locale] : self[:content]
61
66
  end
62
67
 
63
68
  def tooltip(locale = nil)
64
- tip_translations[locale] || self[:tooltip]
69
+ tip_translations[locale].present? ? tip_translations[locale] : self[:tooltip]
65
70
  end
66
71
 
67
72
  # returns all pages this element is on, whether that be directly, through a grid, or as a choice field conditional option
@@ -89,12 +94,10 @@ module Fe
89
94
  end
90
95
 
91
96
  # assume each element is on a question sheet only once to make things simpler. if not, just take the first one
97
+ # NOTE: getting the previous_element isn't an expensive operation any more because of the all_elements_id cache
92
98
  def previous_element(question_sheet, page = nil)
93
- unless page
94
- page_element = page_elements.joins(page: :question_sheet).where("#{Fe::QuestionSheet.table_name}.id" => question_sheet.id).first
95
- return unless page_element
96
- page = page_element.page
97
- end
99
+ return false unless question_sheet
100
+ page ||= pages_on.detect{ |p| p.question_sheet == question_sheet }
98
101
 
99
102
  index = page.all_element_ids_arr.index(self.id)
100
103
  unless index
@@ -105,13 +108,46 @@ module Fe
105
108
  index = page.all_element_ids_arr.index(self.id)
106
109
  end
107
110
  if index && index > 0 && prev_el_id = page.all_element_ids_arr[index-1]
108
- return Fe::Element.find(prev_el_id)
111
+ # occasionally the all_elements_ids_arr can get out of sync here, resulting in no element found
112
+ el = Fe::Element.find_by(id: prev_el_id)
113
+ unless el
114
+ page.rebuild_all_element_ids
115
+ index = page.all_element_ids_arr.index(self.id)
116
+ prev_el_id = page.all_element_ids_arr[index-1]
117
+ el = Fe::Element.find(prev_el_id) # give an error at this point if it's not found
118
+ end
119
+
120
+ return el
121
+ end
122
+ end
123
+
124
+ # return an array of all elements whose answers or visibility might affect
125
+ # the visibility of this element
126
+ def visibility_affecting_element_ids
127
+ return @visibility_affecting_element_ids if @visibility_affecting_element_ids
128
+
129
+ # the form doesn't change much so caching on the last updated element will
130
+ # provide a good balance of speed and cache invalidation
131
+ Rails.cache.fetch([self, 'element#visibility_affecting_element_ids', Fe::Element.order('updated_at desc, id desc').first]) do
132
+ elements = []
133
+
134
+ elements << question_grid if question_grid
135
+ elements << choice_field if choice_field
136
+ elements += Fe::Element.where(conditional_type: 'Fe::Element', conditional_id: id)
137
+ element_ids = elements.collect(&:id) +
138
+ elements.collect { |e| e.visibility_affecting_element_ids }.flatten
139
+ element_ids.uniq
109
140
  end
110
141
  end
111
142
 
112
- def hidden_by_conditional?(answer_sheet, page, prev_el)
113
- (prev_el ||= previous_element(answer_sheet.question_sheet, page)) &&
114
- prev_el.is_a?(Fe::Question) &&
143
+ def visibility_affecting_questions
144
+ Fe::Question.where(id: visibility_affecting_element_ids)
145
+ end
146
+
147
+ def hidden_by_conditional?(answer_sheet, page)
148
+ return false unless answer_sheet.question_sheets.include?(page.question_sheet)
149
+ prev_el = previous_element(page.question_sheet, page)
150
+ prev_el.is_a?(Fe::Question) &&
115
151
  prev_el.conditional == self &&
116
152
  !prev_el.conditional_match(answer_sheet)
117
153
  end
@@ -122,21 +158,21 @@ module Fe
122
158
  choice_field.is_response_false(answer_sheet)
123
159
  end
124
160
 
125
- # use prev_el directly if it's passed in; otherwise, pass page to previous_element to find the prev_el faster
126
- def visible?(answer_sheet = nil, page = nil, prev_el = nil)
127
- !hidden?(answer_sheet, page, prev_el)
161
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
162
+ def visible?(answer_sheet = nil, page = nil)
163
+ !hidden?(answer_sheet, page)
128
164
  end
129
165
 
130
- # use prev_el directly if it's passed in; otherwise, pass page to previous_element to find the prev_el faster
131
- def hidden?(answer_sheet = nil, page = nil, prev_el = nil)
132
- @hidden ||= answer_sheet &&
133
- (hidden_by_choice_field?(answer_sheet) ||
134
- hidden_by_conditional?(answer_sheet, page, prev_el))
166
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
167
+ def hidden?(answer_sheet = nil, page = nil)
168
+ page ||= pages_on.detect{ |p| answer_sheet.question_sheets.include?(p.question_sheet) }
169
+ return true if !page || page.hidden?(answer_sheet)
170
+ return page.all_hidden_elements(answer_sheet).include?(self)
135
171
  end
136
172
 
137
- # use prev_el directly if it's passed in; otherwise, pass page to previous_element to find the prev_el faster
138
- def required?(answer_sheet = nil, page = nil, prev_el = nil)
139
- if hidden?(answer_sheet, page, prev_el)
173
+ # use page if it's passed in, otherwise it will revert to the first page in answer_sheet
174
+ def required?(answer_sheet = nil, page = nil)
175
+ if answer_sheet && hidden?(answer_sheet, page)
140
176
  return false
141
177
  else
142
178
  required == true
@@ -145,7 +181,7 @@ module Fe
145
181
 
146
182
  def position(page = nil)
147
183
  if page
148
- page_elements.where(:page_id => page.id).first.try(:position)
184
+ page_elements.where(page_id: page.id).first.try(:position)
149
185
  else
150
186
  self[:position]
151
187
  end
@@ -153,7 +189,7 @@ module Fe
153
189
 
154
190
  def set_position(position, page = nil)
155
191
  if page
156
- pe = page_elements.where(:page_id => page.id).first
192
+ pe = page_elements.where(page_id: page.id).first
157
193
  pe.update_attribute(:position, position) if pe
158
194
  else
159
195
  self[:position] = position
@@ -189,8 +225,8 @@ module Fe
189
225
  new_element.choice_field_id = parent.id
190
226
  end
191
227
  new_element.position = parent.elements.maximum(:position).to_i + 1 if parent
192
- new_element.save!(:validate => false)
193
- Fe::PageElement.create(:element => new_element, :page => page) unless parent
228
+ new_element.save!(validate: false)
229
+ Fe::PageElement.create(element: new_element, page: page) unless parent
194
230
 
195
231
  # duplicate children
196
232
  if respond_to?(:elements) && elements.present?
@@ -204,7 +240,10 @@ module Fe
204
240
  def all_elements
205
241
  if respond_to?(:elements)
206
242
  elements.reload
207
- (elements + elements.collect(&:all_elements)).flatten
243
+ #(elements + elements.collect(&:all_elements)).flatten
244
+ elements.collect{ |el|
245
+ [el, el.all_elements]
246
+ }.flatten
208
247
  else
209
248
  []
210
249
  end
@@ -215,10 +254,14 @@ module Fe
215
254
  (self.is_a?(Fe::Question) || self.is_a?(Fe::QuestionGrid) || self.is_a?(Fe::QuestionGridWithTotal))
216
255
  end
217
256
 
257
+ def conditional_answers
258
+ conditional_answer.split(';').collect(&:strip)
259
+ end
260
+
218
261
  def conditional_match(answer_sheet)
219
262
  displayed_response = display_response(answer_sheet)
220
263
  return false unless displayed_response && conditional_answer
221
- (displayed_response.split(',') & conditional_answer.split(',')).length > 0
264
+ conditional_answers.include?(displayed_response)
222
265
  end
223
266
 
224
267
  def self.max_label_length
@@ -232,7 +275,7 @@ module Fe
232
275
 
233
276
  if index = page.all_element_ids_arr.index(self.id)
234
277
  self.conditional_id = page.all_element_ids_arr[index+1]
235
- else
278
+ else
236
279
  self.conditional_id = nil
237
280
  end
238
281
  end
@@ -255,13 +298,52 @@ module Fe
255
298
  end
256
299
 
257
300
  def update_page_all_element_ids
258
- [question_grid, question_grid_with_total].compact.each do |field|
301
+ [question_grid, question_grid_with_total, choice_field].compact.each do |field|
259
302
  field.update_page_all_element_ids
260
303
  end
261
304
 
262
305
  pages.reload.each do |p| p.rebuild_all_element_ids end
263
306
  end
264
307
 
308
+ # matches in an AND method; if requested we can add a second filter method later
309
+ # to match on an OR basis
310
+ def matches_filter(filter)
311
+ filter.all? { |method| self.send(method) }
312
+ end
313
+
314
+ def css_classes
315
+ css_class.to_s.split(' ').collect(&:strip)
316
+ end
317
+
318
+ def self.create_from_import(element_data, page, question_sheet)
319
+ element_data[:old_id] = element_data.delete('id')
320
+ children = element_data.delete(:children)
321
+ element = element_data['kind'].constantize.create!(element_data)
322
+ question_sheet.element_id_mappings[element.old_id] = element.id
323
+ children.each do |child|
324
+ byebug unless child.class == Hash
325
+ child_element = create_from_import(child, page, question_sheet)
326
+ if child['choice_field_id'].present?
327
+ child_element.choice_field_id = element.id
328
+ end
329
+ byebug if child_element.label == 'Your Name:'
330
+ if child['question_grid_id'].present?
331
+ child_element.question_grid_id = element.id
332
+ end
333
+ child_element.save!
334
+ end
335
+ element
336
+ end
337
+
338
+ def export_hash
339
+ children = choice_field_children.collect(&:export_hash)
340
+ self.attributes.to_hash.merge(children: children)
341
+ end
342
+
343
+ def export_to_yaml
344
+ export_hash.to_yaml
345
+ end
346
+
265
347
  protected
266
348
 
267
349
  def set_defaults