quby 4.0.4 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (182) hide show
  1. checksums.yaml +4 -4
  2. data/README.markdown +0 -1
  3. data/lib/quby.rb +2 -10
  4. data/lib/quby/answers/entities/answer.rb +4 -2
  5. data/lib/quby/answers/services/score_calculator.rb +1 -3
  6. data/lib/quby/engine.rb +0 -1
  7. data/lib/quby/questionnaires.rb +1 -0
  8. data/lib/quby/questionnaires/api.rb +5 -1
  9. data/lib/quby/questionnaires/deserializer.rb +441 -0
  10. data/lib/quby/questionnaires/dsl.rb +12 -15
  11. data/lib/quby/questionnaires/entities.rb +1 -0
  12. data/lib/quby/questionnaires/entities/charting/line_chart.rb +23 -0
  13. data/lib/quby/questionnaires/entities/charting/overview_chart.rb +3 -1
  14. data/lib/quby/questionnaires/entities/definition.rb +3 -5
  15. data/lib/quby/questionnaires/entities/fields.rb +0 -15
  16. data/lib/quby/questionnaires/entities/question.rb +9 -32
  17. data/lib/quby/questionnaires/entities/questionnaire.rb +4 -15
  18. data/lib/quby/questionnaires/entities/questions/checkbox_question.rb +0 -24
  19. data/lib/quby/questionnaires/entities/questions/date_question.rb +0 -8
  20. data/lib/quby/questionnaires/entities/score_calculation.rb +40 -3
  21. data/lib/quby/questionnaires/repos.rb +1 -0
  22. data/lib/quby/questionnaires/repos/bundle_disk_repo.rb +51 -0
  23. data/lib/quby/table_backend/range_tree.rb +0 -51
  24. data/lib/quby/version.rb +1 -1
  25. data/spec/features/tables_spec.rb +40 -0
  26. data/spec/fixtures/score_test.rb +4 -0
  27. data/spec/internal/log/test-events.log +183 -0
  28. data/spec/internal/log/test.log +5522 -0
  29. data/spec/internal/tmp/cache/assets/sprockets/v3.0/00/00nglkSepN01iGiDa7URmJtWQP-xSWffZb9wJgBqUr4.cache +2 -0
  30. data/spec/internal/tmp/cache/assets/sprockets/v3.0/1V/1VQnRlqKBL0MybpMmC3EVtA2s6-ZX-GSGs2Cza3g3kI.cache +1 -0
  31. data/spec/internal/tmp/cache/assets/sprockets/v3.0/1_/1_J_3havMjJDCz3iAQXAXXBh6tBQRJ3OpdWm3W46ss8.cache +1 -0
  32. data/spec/internal/tmp/cache/assets/sprockets/v3.0/4G/4gcYvPHmPX3fZw9xr6plMl5Hfbo2xTWwcD7T7lIlD4Y.cache +2 -0
  33. data/spec/internal/tmp/cache/assets/sprockets/v3.0/4_/4_FK6PMcF1eyIQx7RtVQ5t8c6X7smPQmJWNN5YTImVc.cache +1 -0
  34. data/spec/internal/tmp/cache/assets/sprockets/v3.0/4d/4Dd15Upcg8_kiEsx_5wpQNrjM1sCk0tGTtWGhtmCKpg.cache +1 -0
  35. data/spec/internal/tmp/cache/assets/sprockets/v3.0/4h/4hd8RzwlWb2IEeLLZN0s-3_sdJXtw6nu2k5baLb9vNs.cache +0 -0
  36. data/spec/internal/tmp/cache/assets/sprockets/v3.0/5X/5xFJleOz2sGyE9gbs8KPkToDiv3s9bwFzroBRcjPELY.cache +0 -0
  37. data/spec/internal/tmp/cache/assets/sprockets/v3.0/6Y/6y1tV_fKJP9YIQn-dF847aZ9kza1O7iyTEUht42D7nc.cache +0 -0
  38. data/spec/internal/tmp/cache/assets/sprockets/v3.0/6d/6dfYZY_DkQe892FZVDUbBbrINslzVUkLZePdnVxO3Ps.cache +1 -0
  39. data/spec/internal/tmp/cache/assets/sprockets/v3.0/6w/6WcYsWQCgw0z_URAWGhOsbiKXcqVxZ--ImazY5S-2Gk.cache +2 -0
  40. data/spec/internal/tmp/cache/assets/sprockets/v3.0/77/77EJjFGVCRzLbhJy6KZZUYe5w0LxsqZQcmFcHAbxKeQ.cache +1 -0
  41. data/spec/internal/tmp/cache/assets/sprockets/v3.0/7f/7fvhJnfiJknq6sA-MPtumXlL5qB-rfH__Heye13NyHc.cache +1 -0
  42. data/spec/internal/tmp/cache/assets/sprockets/v3.0/8W/8W6AwDwFqo6egmWyt6Cs0vf_XJbT2YxPs2sqbo0Q9kk.cache +2 -0
  43. data/spec/internal/tmp/cache/assets/sprockets/v3.0/8c/8cQ8uBhUhrouKwor0Z87dDCxyPbqbLvK8KEFEgjHxqM.cache +0 -0
  44. data/spec/internal/tmp/cache/assets/sprockets/v3.0/9L/9LJo1U9wQdfWrrn3HkNZTgIZc6jeHvWbGk8tJ4m_Qec.cache +1 -0
  45. data/spec/internal/tmp/cache/assets/sprockets/v3.0/9o/9oXJXZnA6F49i4EflydD0Ivx6dhSxGGDD69G-N5NSCg.cache +0 -0
  46. data/spec/internal/tmp/cache/assets/sprockets/v3.0/A1/a1jgtYtDHIb04Og07H6_XNmhXppaAnFiO88kgq80edQ.cache +1 -0
  47. data/spec/internal/tmp/cache/assets/sprockets/v3.0/AC/ACV6t4unw39Kpn2DnbBtHeJYLoAHyyBN1DTRXs6a2ik.cache +1 -0
  48. data/spec/internal/tmp/cache/assets/sprockets/v3.0/BO/BoVO8e1GTTGHfnJTOByDxMlvVi9fNGyOdSubqCbBITI.cache +2 -0
  49. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Ba/babjk3aW-jBoEw_ERhmT5ZQprEqilJCWmImrvVskWk8.cache +0 -0
  50. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Cg/cgb1vvOogMD0V1lqQ5k_mny7Z38rbqBdIumlGh5sZ_E.cache +0 -0
  51. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Cw/CwjNk_RYiC93RW8vRs7fWsHcwKTg7uE9OLifRm3Y6WA.cache +2 -0
  52. data/spec/internal/tmp/cache/assets/sprockets/v3.0/DY/Dy1qsY0W-eyFR6RVKSCCli-XsitpaXD4DwTjXL86t2M.cache +1 -0
  53. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Dk/DkGOwkJNeDdljv4b8QbwtPVrsghwYuA6EOxx1goEQg0.cache +1 -0
  54. data/spec/internal/tmp/cache/assets/sprockets/v3.0/FG/fGmVSU-v7aZV9-CLG5U2OmteGgWTuAotlFcwFyx4Eqs.cache +1 -0
  55. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Fd/fDidWZjSZsPN4uAEyVYABV2WFCSR7AZE0l8U-9XOw7U.cache +0 -0
  56. data/spec/internal/tmp/cache/assets/sprockets/v3.0/G1/G1RRwpldS5MGQ5x-WHyPWzXJWDOd2qyWMMFumuU4-Ms.cache +1 -0
  57. data/spec/internal/tmp/cache/assets/sprockets/v3.0/GG/ggGfy_gZryUPmYwgenN3A4jv0q-buxyjCGdLB6wfHxQ.cache +1 -0
  58. data/spec/internal/tmp/cache/assets/sprockets/v3.0/GY/gYwYoJrifknCG-a0FXlM1NU6tUj1JXxm27s-a0uP_IE.cache +1 -0
  59. data/spec/internal/tmp/cache/assets/sprockets/v3.0/I6/i64Ysik0cyoTrYF0-O63B7sN3AdIgeoJ7zLh9pMC2u4.cache +0 -0
  60. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Ic/ICKeOd4yOff3JGsfU1XliLgXfJtTPKSbXsqNVZAb1-Y.cache +1 -0
  61. data/spec/internal/tmp/cache/assets/sprockets/v3.0/J7/J75M1l--IMcCn-Wy7uWv17SWaj0DFaGiiwac63pENPQ.cache +1 -0
  62. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Jh/JHPqsKZsrkvQg23ukphinYoViiIw3askwCMP8VrHTMk.cache +1 -0
  63. data/spec/internal/tmp/cache/assets/sprockets/v3.0/K9/k9tOCRdX5tsxa5LImq7MZk0WzC692t8tx5LzLln0l4Q.cache +0 -0
  64. data/spec/internal/tmp/cache/assets/sprockets/v3.0/LY/ly4TNAIbW9ghgXz8mxfmskR_6AkuiMiG-RIFGhewgQ0.cache +1 -0
  65. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Lq/LQ9fP0wIqVc5s-AtP3ESWJr0qpkDe-VpTxpgYl-9FfU.cache +1 -0
  66. data/spec/internal/tmp/cache/assets/sprockets/v3.0/MO/MOmnpcaRW98Ke-zTOOCSZ0ODnnQntKhQr0vFc_un1_A.cache +1 -0
  67. data/spec/internal/tmp/cache/assets/sprockets/v3.0/OX/OxOBsUA5EpScoZgEt_UhsHzhu-GGmxKnHpCwnhbTwiM.cache +1 -0
  68. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Ot/oTVAc3-iuSlg7XAM-YIAhw3cdc5yhgL2-tK3N7sfOfU.cache +0 -0
  69. data/spec/internal/tmp/cache/assets/sprockets/v3.0/PK/pKNcBOgAbCyIwsWIvnlmiPiFxrw6nUo_nTz7wWFDr80.cache +1 -0
  70. data/spec/internal/tmp/cache/assets/sprockets/v3.0/QB/QBMvSW7SYbYcm5lSjybgO7fhHDFIQjJWOvefH0MQC-A.cache +0 -0
  71. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Qs/QSbtm1y8JKPV0MQkpiC0hEisaSjKZ3tp2AWHSrhO30Y.cache +1 -0
  72. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Sp/SpyGDguFy3tp0sim1miZOehEDpE97oueO7wl3oUFYcE.cache +1 -0
  73. data/spec/internal/tmp/cache/assets/sprockets/v3.0/TW/TwtJPRSE9lmwTuSP0lezWSRSBp2u43zuF196FQcHY-c.cache +1 -0
  74. data/spec/internal/tmp/cache/assets/sprockets/v3.0/VX/Vxn9zv60MCKUBfRhrjefo-MEzFej81PPEutxTPQ4VLQ.cache +0 -0
  75. data/spec/internal/tmp/cache/assets/sprockets/v3.0/Vr/vrqPEzlsuORocHY0EbtGdFomRmrcoAa6wWSDMx642-Q.cache +2 -0
  76. data/spec/internal/tmp/cache/assets/sprockets/v3.0/WH/WHVQ0GBe51hbYqv2g7YYo7EIw9PSzt9-RqU7hKP2dc4.cache +1 -0
  77. data/spec/internal/tmp/cache/assets/sprockets/v3.0/_B/_B796ezRHfk7H8YKj7zr3-gRy6Mw2IVn2NbHu-k20ug.cache +1 -0
  78. data/spec/internal/tmp/cache/assets/sprockets/v3.0/_W/_Wh-7b1t-Xm19wg3KPtkXq7DOpohaL5OhnLggzJLpj4.cache +0 -0
  79. data/spec/internal/tmp/cache/assets/sprockets/v3.0/b2/B2y-2qTbQYKRm6_sCQ6qfUeVfl9rKXUvotTDm56w4bs.cache +1 -0
  80. data/spec/internal/tmp/cache/assets/sprockets/v3.0/bw/bwFR2MLpvJvPSAPJzSLWk6TUDCw5TlBFUUuq7z_Y9vg.cache +1 -0
  81. data/spec/internal/tmp/cache/assets/sprockets/v3.0/c7/c7ubvrHhbhAYh-K4srvM9EqwWdbWTLrsLMgjG5XQDbY.cache +1 -0
  82. data/spec/internal/tmp/cache/assets/sprockets/v3.0/cX/CXpMzZYfJy6HIOVXN0IyYilvy6J4CDgFZIwqKm8OQEU.cache +1 -0
  83. data/spec/internal/tmp/cache/assets/sprockets/v3.0/eN/EN0enu5dkKEnIz-DVCpbhkfeph7wImeXnlqb7SXMrjE.cache +1 -0
  84. data/spec/internal/tmp/cache/assets/sprockets/v3.0/fu/fuP9zP6C6rj650HLkypoTfYN2KD1ecB9HdtlbhbA2cg.cache +1 -0
  85. data/spec/internal/tmp/cache/assets/sprockets/v3.0/gr/gRPXI3EsVPwysy9YKW_6yDxaqRRr4Idxtexf7zCHECw.cache +1 -0
  86. data/spec/internal/tmp/cache/assets/sprockets/v3.0/h0/h0P63ZZdlSbtztDOe0V9r7jtCoy0L2bAf_iDNKLJ0nk.cache +1 -0
  87. data/spec/internal/tmp/cache/assets/sprockets/v3.0/i-/i-Or0Xuxbbm3-HdE_xGIf6sAx2vhUa3eVMpbT37lyEo.cache +1 -0
  88. data/spec/internal/tmp/cache/assets/sprockets/v3.0/iF/iFP67If98izMEAAMKnAFi2dWAb8TNlHNayZKNjXdAMs.cache +1 -0
  89. data/spec/internal/tmp/cache/assets/sprockets/v3.0/iH/Ih2svq5ul-xibfRUfd1fxigouli6U19Rf80FrTSoMak.cache +1 -0
  90. data/spec/internal/tmp/cache/assets/sprockets/v3.0/ka/Ka_YEbZNQxpS8XAA7xZturgPr2vLYQq60KK1uwou4Lo.cache +1 -0
  91. data/spec/internal/tmp/cache/assets/sprockets/v3.0/kq/KqkIGfAdJjTcr53lFl1aDAMUeqhhKdXGyTlHVlCz0R4.cache +1 -0
  92. data/spec/internal/tmp/cache/assets/sprockets/v3.0/mR/mRDPkwomGSfjpIWEKtPPBSx5cBHssvH8v0gCSI33P3Y.cache +1 -0
  93. data/spec/internal/tmp/cache/assets/sprockets/v3.0/n4/n4qaNjjNvKB0VAbVJlP_BjFlcLf70TsBzcIMc7VO9dw.cache +2 -0
  94. data/spec/internal/tmp/cache/assets/sprockets/v3.0/nY/nykt5oy2ExUYa8g1phLCDRqwb55BuUot5ZBbRdNuCCw.cache +2 -0
  95. data/spec/internal/tmp/cache/assets/sprockets/v3.0/nv/nvmgm4bOtxCbD6EA14ZIwDcYO8fxXOoEk_s7V-uRMMM.cache +1 -0
  96. data/spec/internal/tmp/cache/assets/sprockets/v3.0/oG/og5ypFjbFAX_nH7oxK6Bn5Ejh81BMDBSMxbGcL5ZbQs.cache +1 -0
  97. data/spec/internal/tmp/cache/assets/sprockets/v3.0/oO/oO4iBwOm5E7uLH39zeR8zun4eBM-WxZKvEMpIYKx6SU.cache +1 -0
  98. data/spec/internal/tmp/cache/assets/sprockets/v3.0/pN/PnQ-dEclV6jTDE4kkqw4G9dBpHArAgUnATOpRrQAsGc.cache +1 -0
  99. data/spec/internal/tmp/cache/assets/sprockets/v3.0/pe/pet4fFbc2Cm2SD_ZBO_s83Gey29EXE26u-7lwn2O8gM.cache +1 -0
  100. data/spec/internal/tmp/cache/assets/sprockets/v3.0/qo/Qoqb-JjTe1BiY4rICKHm5e6bsPt-DHXZc4ADXESjw2I.cache +1 -0
  101. data/spec/internal/tmp/cache/assets/sprockets/v3.0/qx/QXICLj9xx1EI9rBeXLXpKOeHGll_LBWHRWlpb1DK424.cache +1 -0
  102. data/spec/internal/tmp/cache/assets/sprockets/v3.0/rM/rMF4Y2UGkmBABLlC1eSSqb6y6g_Z3uUCLBtFvz_LoyI.cache +1 -0
  103. data/spec/internal/tmp/cache/assets/sprockets/v3.0/ty/ty1qAnpL07anYdhIJoiPYLcsWKejuzy6JaxU9mI7egw.cache +1 -0
  104. data/spec/internal/tmp/cache/assets/sprockets/v3.0/uH/UHs9nE45XS7v4QlMjSjhwHbqYIKC06FsEjMSMIfKanU.cache +1 -0
  105. data/spec/internal/tmp/cache/assets/sprockets/v3.0/v8/v8VXJ3AQom-JqxBYAB7tOjV-tt4IdDb6YyFEzlsWV8c.cache +1 -0
  106. data/spec/internal/tmp/cache/assets/sprockets/v3.0/vD/VDI2CxkTg7-DuMcLmofLvch0yukSl88oI_AjGgN-hQ8.cache +1 -0
  107. data/spec/internal/tmp/cache/assets/sprockets/v3.0/wq/wqhk46aZc1Bbb8gTCOZKxTvwTV0ANLSz8VdQYACo03E.cache +1 -0
  108. data/spec/internal/tmp/cache/assets/sprockets/v3.0/xy/Xy8Yk--iYZNupHMjUtYX1AQ0BppsvThCh_ZAizblCb0.cache +1 -0
  109. data/spec/internal/tmp/cache/assets/sprockets/v3.0/zu/zUCfHRTHprn06F5WxH6wp_BLs9-yhs-foc0j0Y7BYbg.cache +1 -0
  110. data/spec/internal/tmp/cache/assets/sprockets/v3.0/zx/ZX2IY3eKQjBy93tlH7_Xg7zGZKYuaQ-E8YPvtPT_4vM.cache +1 -0
  111. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-35.705.html +1 -0
  112. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-35.705.png +0 -0
  113. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.005.html +1 -0
  114. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.005.png +0 -0
  115. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.276.html +1 -0
  116. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.276.png +0 -0
  117. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.556.html +1 -0
  118. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.556.png +0 -0
  119. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.818.html +1 -0
  120. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-36.818.png +0 -0
  121. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.083.html +1 -0
  122. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.083.png +0 -0
  123. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.350.html +1 -0
  124. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.350.png +0 -0
  125. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.620.html +1 -0
  126. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.620.png +0 -0
  127. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.885.html +1 -0
  128. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-37.885.png +0 -0
  129. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-38.152.html +1 -0
  130. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-27-38.152.png +0 -0
  131. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-28-14.063.html +207 -0
  132. data/spec/internal/tmp/capybara/screenshot_2020-11-17-14-28-14.063.png +0 -0
  133. data/spec/quby/answers/entities/score_spec.rb +4 -0
  134. data/spec/quby/answers/services/answer_validations_spec.rb +8 -8
  135. data/spec/quby/answers/services/score_calculator_spec.rb +4 -14
  136. data/spec/quby/questionnaires/deserializer/questionnaire_spec.rb +237 -0
  137. data/spec/quby/questionnaires/dsl_spec.rb +0 -9
  138. data/spec/quby/questionnaires/entities/fields_spec.rb +3 -3
  139. data/spec/quby/questionnaires/entities/question_spec.rb +0 -8
  140. data/spec/quby/questionnaires/entities/questionnaire_spec.rb +2 -26
  141. data/spec/quby/table_backend/range_tree_spec.rb +46 -13
  142. data/spec/spec_helper.rb +1 -1
  143. metadata +230 -53
  144. data/lib/quby/lookup_table.rb +0 -29
  145. data/lib/quby/lookup_table_repo.rb +0 -24
  146. data/lib/quby/questionnaires/dsl/base.rb +0 -20
  147. data/lib/quby/questionnaires/dsl/calls_custom_methods.rb +0 -29
  148. data/lib/quby/questionnaires/dsl/charting/bar_chart_builder.rb +0 -18
  149. data/lib/quby/questionnaires/dsl/charting/chart_builder.rb +0 -91
  150. data/lib/quby/questionnaires/dsl/charting/line_chart_builder.rb +0 -57
  151. data/lib/quby/questionnaires/dsl/charting/overview_chart_builder.rb +0 -31
  152. data/lib/quby/questionnaires/dsl/charting/radar_chart_builder.rb +0 -18
  153. data/lib/quby/questionnaires/dsl/helpers.rb +0 -51
  154. data/lib/quby/questionnaires/dsl/panel_builder.rb +0 -80
  155. data/lib/quby/questionnaires/dsl/question_builder.rb +0 -40
  156. data/lib/quby/questionnaires/dsl/questionnaire_builder.rb +0 -260
  157. data/lib/quby/questionnaires/dsl/questions/base.rb +0 -179
  158. data/lib/quby/questionnaires/dsl/questions/checkbox_question_builder.rb +0 -20
  159. data/lib/quby/questionnaires/dsl/questions/date_question_builder.rb +0 -18
  160. data/lib/quby/questionnaires/dsl/questions/deprecated_question_builder.rb +0 -18
  161. data/lib/quby/questionnaires/dsl/questions/float_question_builder.rb +0 -21
  162. data/lib/quby/questionnaires/dsl/questions/integer_question_builder.rb +0 -21
  163. data/lib/quby/questionnaires/dsl/questions/radio_question_builder.rb +0 -20
  164. data/lib/quby/questionnaires/dsl/questions/select_question_builder.rb +0 -18
  165. data/lib/quby/questionnaires/dsl/questions/string_question_builder.rb +0 -20
  166. data/lib/quby/questionnaires/dsl/questions/text_question_builder.rb +0 -22
  167. data/lib/quby/questionnaires/dsl/score_builder.rb +0 -22
  168. data/lib/quby/questionnaires/dsl/standardized_panel_generators.rb +0 -33
  169. data/lib/quby/questionnaires/dsl/table_builder.rb +0 -48
  170. data/lib/quby/questionnaires/services/definition_validator.rb +0 -298
  171. data/spec/benchmarks/load_normscore_csv.rb +0 -18
  172. data/spec/quby/lookup_table_repo_spec.rb +0 -20
  173. data/spec/quby/lookup_table_spec.rb +0 -38
  174. data/spec/quby/questionnaires/dsl/calls_custom_methods_spec.rb +0 -38
  175. data/spec/quby/questionnaires/dsl/charting/bar_chart_builder_spec.rb +0 -41
  176. data/spec/quby/questionnaires/dsl/charting/chart_builder_spec.rb +0 -127
  177. data/spec/quby/questionnaires/dsl/charting/line_chart_builder_spec.rb +0 -66
  178. data/spec/quby/questionnaires/dsl/charting/radar_chart_builder_spec.rb +0 -41
  179. data/spec/quby/questionnaires/dsl/helpers_spec.rb +0 -80
  180. data/spec/quby/questionnaires/dsl/questionnaire_builder_spec.rb +0 -480
  181. data/spec/quby/questionnaires/services/definition_validator_spec.rb +0 -793
  182. data/spec/support/examples_for_chart_builders.rb +0 -59
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c276c32cea7ff4fe777bdb9b2728d7016602b8749dcda735fb5dbab803c884bc
4
- data.tar.gz: d89ef17383b734f36969c597afacd7d8f9c6939548eb200a5957413f5b9f07b9
3
+ metadata.gz: 4a0b61b70b821b00c262a58f7f7fb7ec04a33d4b26df7719be157d8f4015f823
4
+ data.tar.gz: 6b897f4f1e7867999c9dda5b0bbd39f6b5493945715edce8194d45beab809926
5
5
  SHA512:
6
- metadata.gz: 3fc841fd7e02157de4732a113cd26a2c2015606e1747397370c315558e878f07ba2f033b6126e246b1ba32ce3494106382c219d0c5db6aeb08caa63944a63dee
7
- data.tar.gz: a5602705d78127675bdb0c7418d13ee060a54b86d452778f99747b4760b8b3e9a72b66032de0bc134469243058c823a32ed46d1a9d7b7256db1e44360f6addb3
6
+ metadata.gz: 2595b285e0c4fd37dd708577bafbffc3c830194097d79ebb1c7784c6918f0a40cf4e4695e68cf0755cc7a09fb95a43beee4caebad495c141c61ac377fd90b637
7
+ data.tar.gz: b575e091c90af8581c044e6866cf5cb328bc513d0f24ba3453efffa212741012f31650bcd07f6fe1d1649fe80e85bd0ff6ebb3e5c03a8e753e7c656bead146be
@@ -27,7 +27,6 @@ and where it can store its answers.
27
27
  ```ruby
28
28
  Quby.questionnaire_repo = Quby::Questionnaires::Repos::DiskRepo.new(Rails.root.join("db/questionnaires/definitions"))
29
29
  Quby.answer_repo = Quby::Answers::Repos::MongoidRepo.new
30
- Quby.lookup_table_repo = Quby::LookupTableRepo::Disk.new(Rails.root.join("db/questionnaires/lookup_tables"))
31
30
  Quby::Settings.shared_secret = ENV["QUBY_SHARED_SECRET"]
32
31
  ```
33
32
 
@@ -38,14 +38,6 @@ module Quby
38
38
  @questionnaires_api = nil
39
39
  end
40
40
 
41
- def lookup_table_repo=(repo)
42
- @lookup_table_repo = repo
43
- end
44
-
45
- def lookup_table_repo
46
- @lookup_table_repo || fail("Quby does not have its lookup table repo (Quby.lookup_table_repo) configured.")
47
- end
48
-
49
41
  def fixtures_path
50
42
  File.expand_path File.join('..', '..', 'spec', 'fixtures'), __FILE__
51
43
  end
@@ -79,7 +71,7 @@ module Quby
79
71
  end
80
72
 
81
73
  require 'quby/settings'
74
+ require 'quby/table_backend/range_tree'
82
75
  require 'quby/questionnaires'
83
76
  require 'quby/answers'
84
- require 'quby/engine'
85
- require 'quby/lookup_table_repo'
77
+ require 'quby/engine'
@@ -82,7 +82,7 @@ module Quby
82
82
  attr_accessor :extra_question_values
83
83
  attr_accessor :extra_failed_validations
84
84
 
85
- def initialize(_id: nil, questionnaire_id: nil, questionnaire_key: nil,
85
+ def initialize(_id: nil, questionnaire_id: nil, questionnaire_key: nil, questionnaire: nil,
86
86
  raw_params: nil, value: nil, patient_id: nil, patient: nil,
87
87
  token: nil, active: true, test: false, created_at: nil, updated_at: nil,
88
88
  started_at: nil, completed_at: nil, outcome: nil, outcome_generated_at: nil,
@@ -111,6 +111,8 @@ module Quby
111
111
  self.import_notes = import_notes || {}
112
112
  self.flags = flags
113
113
  self.textvars = textvars
114
+
115
+ @questionnaire = questionnaire
114
116
  end
115
117
 
116
118
  def id
@@ -162,7 +164,7 @@ module Quby
162
164
 
163
165
  # Faux belongs_to :questionnaire
164
166
  def questionnaire
165
- Quby.questionnaires.find(questionnaire_key)
167
+ @questionnaire ||= Quby.questionnaires.find(questionnaire_key)
166
168
  end
167
169
 
168
170
  def mark_completed(start_time)
@@ -231,9 +231,7 @@ module Quby
231
231
  end
232
232
 
233
233
  def table_lookup(table_key, parameters)
234
- @questionnaire.lookup_tables[table_key] ||= Quby::LookupTable.new table_key
235
- @questionnaire.lookup_tables[table_key] \
236
- .lookup(parameters)
234
+ @questionnaire.lookup_tables.fetch(table_key).lookup(parameters)
237
235
  end
238
236
 
239
237
  # Public: Ensure given question_keys have answers. Strings with nothing but whitespace are
@@ -15,7 +15,6 @@ require 'jquery-ui-rails'
15
15
  require 'compass-blueprint'
16
16
 
17
17
  require 'susy'
18
- require 'quby/lookup_table'
19
18
  require 'quby/range_categories'
20
19
  require 'quby/pdf_renderer'
21
20
 
@@ -3,3 +3,4 @@
3
3
  require 'quby/questionnaires/dsl'
4
4
  require 'quby/questionnaires/repos'
5
5
  require 'quby/questionnaires/api'
6
+ require 'quby/questionnaires/deserializer'
@@ -39,7 +39,11 @@ module Quby
39
39
 
40
40
  def build_from_definition(definition)
41
41
  ActiveSupport::Notifications.instrument('quby.questionaire.build') do
42
- DSL.build_from_definition(definition)
42
+ if definition.json
43
+ DSL.from_json(definition.json)
44
+ else
45
+ DSL.build_from_definition(definition)
46
+ end
43
47
  end
44
48
  end
45
49
 
@@ -0,0 +1,441 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quby
4
+ module Questionnaires
5
+ module Deserializer
6
+ # This symbolizes various things. Do not run on arbitrary JSON.
7
+ def self.from_json(json)
8
+ # TODO: last_update
9
+ Entities::Questionnaire.new(json.fetch("key"), json).tap do |questionnaire|
10
+ questionnaire.title = json.fetch("title")
11
+ questionnaire.description = json.fetch("description")
12
+ questionnaire.outcome_description = json.fetch("outcome_description")
13
+ questionnaire.short_description = json.fetch("short_description")
14
+ questionnaire.abortable = json.fetch("abortable")
15
+ questionnaire.enable_previous_questionnaire_button = json.fetch("enable_previous_questionnaire_button")
16
+ questionnaire.default_answer_value = json.fetch("default_answer_value")
17
+ questionnaire.leave_page_alert = json.fetch("leave_page_alert")
18
+ questionnaire.allow_hotkeys = json.fetch("allow_hotkeys")
19
+ questionnaire.license = json.fetch("license")&.to_sym
20
+ questionnaire.licensor = json.fetch("licensor")
21
+ questionnaire.language = json.fetch("language").try(:to_sym)
22
+ questionnaire.renderer_version = json.fetch("renderer_version")
23
+ questionnaire.last_update = Time.zone.parse(json.fetch("last_update"))
24
+ questionnaire.last_author = json.fetch("last_author")
25
+ questionnaire.extra_css = json.fetch("extra_css")
26
+ questionnaire.allow_switch_to_bulk = json.fetch("allow_switch_to_bulk")
27
+
28
+ questionnaire.flags = json.fetch("flags").with_indifferent_access.transform_values do |attrs|
29
+ build_flag(attrs)
30
+ end
31
+
32
+ questionnaire.textvars = json.fetch("textvars").with_indifferent_access.transform_values do |attrs|
33
+ build_textvar(attrs)
34
+ end
35
+
36
+ questionnaire.lookup_tables = YAML.load(json.fetch("lookup_tables")).transform_values do |attrs|
37
+ Quby::TableBackend::RangeTree.new(levels: attrs[:levels], tree: attrs[:tree])
38
+ end
39
+
40
+ questionnaire.score_calculations = json.fetch("score_calculations").with_indifferent_access.transform_values do |attrs|
41
+ build_score_calculation(attrs)
42
+ end
43
+
44
+ questionnaire.score_schemas = json.fetch("score_schemas").with_indifferent_access.transform_values do |schema|
45
+ build_score_schema(schema)
46
+ end
47
+
48
+ json.fetch("panels").each do |panel_json|
49
+ load_panel(questionnaire, panel_json)
50
+ end
51
+
52
+ # roqua domain
53
+ questionnaire.roqua_keys = json.fetch("roqua_keys")
54
+ questionnaire.sbg_key = json.fetch("sbg_key")
55
+ questionnaire.sbg_domains = json.fetch("sbg_domains").map(&:symbolize_keys)
56
+ questionnaire.outcome_regeneration_requested_at = json.fetch("outcome_regeneration_requested_at")&.yield_self { |str| Time.zone.parse(str) }
57
+ questionnaire.deactivate_answers_requested_at = json.fetch("deactivate_answers_requested_at").try { |str| Time.zone.parse(str) }
58
+ questionnaire.respondent_types = json.fetch("respondent_types").map(&:to_sym)
59
+ questionnaire.tags = json.fetch("tags")
60
+
61
+ json.fetch("charts").fetch("overview")&.tap do |overview_json|
62
+ questionnaire.charts.overview = Quby::Questionnaires::Entities::Charting::OverviewChart.new(
63
+ subscore: overview_json.fetch("subscore").to_sym,
64
+ y_max: overview_json.fetch("y_max"),
65
+ )
66
+ end
67
+
68
+ json.fetch("charts").fetch("others").each do |chart_json|
69
+ questionnaire.add_chart(build_chart(questionnaire, chart_json))
70
+ end
71
+
72
+ questionnaire.outcome_tables = json.fetch("outcome_tables").map do |attributes|
73
+ build_outcome_table(questionnaire, attributes)
74
+ end
75
+ end
76
+ end
77
+
78
+ def self.load_panel(questionnaire, panel_json)
79
+ panel = Entities::Panel.new(
80
+ questionnaire: questionnaire,
81
+ key: panel_json.fetch("key"),
82
+ title: panel_json.fetch("title"),
83
+ items: []
84
+ )
85
+
86
+ panel_json.fetch("items").each do |item_json|
87
+ load_item(questionnaire, item_json, panel: panel)
88
+ end
89
+
90
+ questionnaire.add_panel(panel)
91
+ end
92
+
93
+ def self.load_item(questionnaire, item_json, panel: nil)
94
+ case item_json.fetch("type")
95
+ when "text"
96
+ panel.items << build_text(item_json)
97
+ when "question"
98
+ question = build_question(questionnaire, item_json)
99
+ questionnaire.register_question(question)
100
+ panel.items << question
101
+ when "table"
102
+ table = Entities::Table.new(
103
+ title: item_json.fetch("title"),
104
+ description: item_json.fetch("description"),
105
+ columns: item_json.fetch("columns"),
106
+ show_option_desc: item_json.fetch("show_option_desc"),
107
+ )
108
+ panel.items << table
109
+
110
+ item_json.fetch("items").each do |table_item_json|
111
+ case table_item_json.fetch("type")
112
+ when "text"
113
+ table.items << build_text(table_item_json)
114
+ when "question"
115
+ question = build_question(questionnaire, table_item_json, table: table)
116
+ questionnaire.register_question(question)
117
+ table.items << question
118
+ panel.items << question
119
+ else
120
+ raise "Unknown table item: #{table_item_json}"
121
+ end
122
+ end
123
+ else
124
+ raise "Unknown item: #{item_json}"
125
+ end
126
+ end
127
+
128
+ def self.build_text(item_json)
129
+ Entities::Text.new(item_json.fetch("str"), {
130
+ html_content: item_json.fetch("html_content"),
131
+ display_in: item_json.fetch("display_in").map(&:to_sym),
132
+ col_span: item_json.fetch("col_span"),
133
+ row_span: item_json.fetch("row_span"),
134
+ raw_content: item_json.fetch("raw_content"),
135
+ switch_cycle: item_json.fetch("switch_cycle")
136
+ })
137
+ end
138
+
139
+ def self.build_question(questionnaire, item_json, parent: nil, table: nil)
140
+ key = item_json.fetch("key").to_sym
141
+ attributes = {
142
+ questionnaire: questionnaire,
143
+ parent: parent,
144
+ type: item_json.fetch("question_type").to_sym,
145
+ title: item_json.fetch("title"),
146
+ context_free_title: item_json.fetch("context_free_title"),
147
+ description: item_json.fetch("description"),
148
+ presentation: item_json.fetch("presentation").to_sym,
149
+ hidden: item_json.fetch("hidden"),
150
+ depends_on: item_json.fetch("depends_on")&.map(&:to_sym),
151
+ default_position: item_json.fetch("default_position"),
152
+ validations: item_json.fetch("validations").map {|attrs| build_question_validation(attrs)},
153
+ table: table,
154
+ col_span: item_json.fetch("col_span"),
155
+ row_span: item_json.fetch("row_span"),
156
+
157
+ # only selectable via options passed in DSL, not via DSL methods
158
+ # many apply only to certain types of questions
159
+ sbg_key: item_json.fetch("sbg_key"),
160
+ allow_duplicate_option_values: item_json.fetch("allow_duplicate_option_values"),
161
+ allow_blank_titles: item_json.fetch("allow_blank_titles"),
162
+ as: item_json.fetch("as")&.to_sym,
163
+ display_modes: item_json.fetch("display_modes")&.map(&:to_sym),
164
+ autocomplete: item_json.fetch("autocomplete"),
165
+ show_values: item_json.fetch("show_values").to_sym,
166
+ deselectable: item_json.fetch("deselectable"),
167
+ disallow_bulk: item_json.fetch("disallow_bulk"),
168
+ score_header: item_json.fetch("score_header").to_sym,
169
+ sets_textvar: item_json.fetch("sets_textvar"),
170
+ default_invisible: item_json.fetch("default_invisible"),
171
+ question_group: item_json.fetch("question_group"), # sometimes string, sometimes a symbol in the DSL. Just have to hope this works
172
+ group_minimum_answered: item_json.fetch("group_minimum_answered"),
173
+ group_maximum_answered: item_json.fetch("group_maximum_answered"),
174
+ value_tooltip: item_json.fetch("value_tooltip"),
175
+
176
+ # might be able to deduce from tree structure
177
+ parent_option_key: item_json.fetch("parent_option_key")&.to_sym
178
+ }
179
+
180
+ case item_json.fetch("question_type")
181
+ when "check_box"
182
+ Entities::Questions::CheckboxQuestion.new(key, attributes.merge(
183
+ check_all_option: item_json.fetch("check_all_option")&.to_sym,
184
+ uncheck_all_option: item_json.fetch("uncheck_all_option")&.to_sym,
185
+ maximum_checked_allowed: item_json.fetch("maximum_checked_allowed"),
186
+ minimum_checked_required: item_json.fetch("minimum_checked_required"),
187
+ )).tap do |question|
188
+ item_json.fetch("options").each do |option_json|
189
+ question.options << build_option(questionnaire, question, option_json)
190
+ end
191
+ end
192
+ when "date"
193
+ Entities::Questions::DateQuestion.new(key, attributes.merge(
194
+ components: item_json.fetch("components").map(&:to_sym),
195
+ required_components: item_json.fetch("required_components").map(&:to_sym),
196
+ year_key: item_json.fetch("year_key")&.to_sym,
197
+ month_key: item_json.fetch("month_key")&.to_sym,
198
+ day_key: item_json.fetch("day_key")&.to_sym,
199
+ hour_key: item_json.fetch("hour_key")&.to_sym,
200
+ minute_key: item_json.fetch("minute_key")&.to_sym,
201
+ ))
202
+ when "deprecated", "hidden"
203
+ Entities::Questions::DeprecatedQuestion.new(key, attributes).tap do |question|
204
+ item_json.fetch("options").each do |option_json|
205
+ question.options << build_option(questionnaire, question, option_json)
206
+ end
207
+ end
208
+ when "float"
209
+ Entities::Questions::FloatQuestion.new(key, attributes.merge(
210
+ labels: item_json.fetch("labels"),
211
+ unit: item_json.fetch("unit"),
212
+ size: item_json.fetch("size"),
213
+ ))
214
+ when "integer"
215
+ Entities::Questions::IntegerQuestion.new(key, attributes.merge(
216
+ labels: item_json.fetch("labels"),
217
+ unit: item_json.fetch("unit"),
218
+ size: item_json.fetch("size"),
219
+ ))
220
+ when "radio", "scale"
221
+ Entities::Questions::RadioQuestion.new(key, attributes).tap do |question|
222
+ item_json.fetch("options").each do |option_json|
223
+ question.options << build_option(questionnaire, question, option_json)
224
+ end
225
+ end
226
+ when "select"
227
+ Entities::Questions::SelectQuestion.new(key, attributes).tap do |question|
228
+ item_json.fetch("options").each do |option_json|
229
+ question.options << build_option(questionnaire, question, option_json)
230
+ end
231
+ end
232
+ when "string"
233
+ Entities::Questions::StringQuestion.new(key, attributes.merge(
234
+ unit: item_json.fetch("unit"),
235
+ size: item_json.fetch("size"),
236
+ ))
237
+ when "textarea"
238
+ Entities::Questions::TextQuestion.new(key, attributes.merge(
239
+ lines: item_json.fetch("lines"),
240
+ ))
241
+ else
242
+ raise "Unknown question type: #{item_json}"
243
+ end
244
+ end
245
+
246
+ def self.build_option(questionnaire, question, option_json)
247
+ option = Entities::QuestionOption.new(option_json.fetch("key")&.to_sym, question,
248
+ value: option_json.fetch("value"),
249
+ description: option_json.fetch("description"),
250
+ context_free_description: option_json.fetch("context_free_description"),
251
+ inner_title: option_json.fetch("inner_title"),
252
+ hides_questions: option_json.fetch("hides_questions").map(&:to_sym),
253
+ shows_questions: option_json.fetch("shows_questions").map(&:to_sym),
254
+ hidden: option_json.fetch("hidden"),
255
+ placeholder: option_json.fetch("placeholder"),
256
+ )
257
+
258
+ option_json.fetch("questions").each do |question_json|
259
+ subquestion = build_question(questionnaire, question_json, parent: question)
260
+ questionnaire.register_question(subquestion)
261
+ option.questions << subquestion
262
+ end
263
+
264
+ option
265
+ end
266
+
267
+ def self.build_question_validation(attrs)
268
+ base_validation = {
269
+ type: attrs.fetch("type").to_sym,
270
+ explanation: attrs["explanation"] # not always specified for min/max validation
271
+ }
272
+
273
+ case attrs.fetch("type")
274
+ when "requires_answer"
275
+ base_validation
276
+ when "answer_group_minimum", "answer_group_maximum"
277
+ base_validation.merge(
278
+ group: attrs.fetch("group"), # TODO: sometimes a symbol, sometimes a string in the original, but I hope it doesn't matter
279
+ value: attrs.fetch("value")
280
+ )
281
+ when "valid_integer", "valid_float"
282
+ base_validation
283
+ when "valid_date"
284
+ base_validation.merge(
285
+ subtype: attrs.fetch("subtype").to_sym
286
+ )
287
+ when "minimum", "maximum"
288
+ value = case attrs.fetch("value_type")
289
+ when "Date"
290
+ Date.parse(attrs.fetch("value"))
291
+ when "DateTime"
292
+ DateTime.parse(attrs.fetch("value"))
293
+ when "Time", "ActiveSuport::TimeWithZone"
294
+ Time.zone.parse(attrs.fetch("value"))
295
+ else
296
+ attrs.fetch("value")
297
+ end
298
+
299
+ base_validation.merge(
300
+ value: value,
301
+ subtype: attrs.fetch("subtype").to_sym,
302
+ )
303
+ when "too_many_checked"
304
+ base_validation.merge(
305
+ uncheck_all_key: attrs.fetch("uncheck_all_key").to_sym
306
+ )
307
+ when "minimum_checked_required"
308
+ base_validation.merge(
309
+ minimum_checked_value: attrs.fetch("minimum_checked_value")
310
+ )
311
+ when "maximum_checked_allowed"
312
+ base_validation.merge(
313
+ maximum_checked_value: attrs.fetch("maximum_checked_value")
314
+ )
315
+ when "regexp"
316
+ base_validation.merge(
317
+ matcher: Regexp.new(attrs.fetch("matcher"))
318
+ )
319
+ when "not_all_checked"
320
+ base_validation.merge(
321
+ check_all_key: attrs.fetch("check_all_key").to_sym
322
+ )
323
+ else
324
+ raise "Unknown validation type: #{attrs.inspect}"
325
+ end
326
+ end
327
+
328
+ def self.build_score_calculation(attrs)
329
+ Entities::ScoreCalculation.new(attrs.fetch("key").to_sym,
330
+ label: attrs.fetch("label"),
331
+ sbg_key: attrs.fetch("sbg_key"),
332
+ options: attrs.fetch("options").symbolize_keys,
333
+ sourcecode: attrs.fetch("sourcecode"),
334
+ )
335
+ end
336
+
337
+ def self.build_flag(attrs)
338
+ Entities::Flag.new(
339
+ key: attrs.fetch("key").to_sym,
340
+ description_true: attrs.fetch("description_true"),
341
+ description_false: attrs.fetch("description_false"),
342
+ description: attrs.fetch("description"),
343
+ internal: attrs.fetch("internal"),
344
+ trigger_on: attrs.fetch("trigger_on"),
345
+ shows_questions: attrs.fetch("shows_questions").map(&:to_sym),
346
+ hides_questions: attrs.fetch("hides_questions").map(&:to_sym),
347
+ depends_on: attrs.fetch("depends_on"), # TODO: emperically determined to be a string in DSL, is that right?
348
+ default_in_interface: attrs.fetch("default_in_interface"),
349
+ )
350
+ end
351
+
352
+ def self.build_textvar(attrs)
353
+ Entities::Textvar.new(
354
+ key: attrs.fetch("key").to_sym,
355
+ description: attrs.fetch("description"),
356
+ default: attrs.fetch("default"),
357
+ depends_on_flag: attrs.fetch("depends_on_flag")&.to_sym
358
+ )
359
+ end
360
+
361
+ def self.build_chart(questionnaire, chart_json)
362
+ base_args = {
363
+ title: chart_json.fetch("title"),
364
+ plottables: chart_json.fetch("plottables").map do |plottable_json|
365
+ Quby::Questionnaires::Entities::Charting::Plottable.new(
366
+ plottable_json.fetch("key").to_sym,
367
+ label: plottable_json.fetch("label"),
368
+ plotted_key: plottable_json.fetch("plotted_key").to_sym,
369
+ global: plottable_json.fetch("global"),
370
+ questionnaire_key: plottable_json.fetch("questionnaire_key")
371
+ )
372
+ end,
373
+ y_categories: chart_json.fetch("y_categories"),
374
+ y_range_categories: chart_json.fetch("y_range_categories"),
375
+ chart_type: chart_json.fetch("chart_type"),
376
+ y_range: deserialize_range(chart_json.fetch("y_range")),
377
+ tick_interval: chart_json.fetch("tick_interval"),
378
+ plotbands: chart_json.fetch("plotbands").map do |plotband_json|
379
+ {
380
+ color: plotband_json.fetch("color").to_sym,
381
+ from: plotband_json.fetch("from"),
382
+ to: plotband_json.fetch("to")
383
+ }
384
+ end,
385
+ }
386
+
387
+ case chart_json.fetch("type")
388
+ when "bar_chart"
389
+ Quby::Questionnaires::Entities::Charting::BarChart.new(chart_json.fetch("key").to_sym,
390
+ plotlines: chart_json.fetch("plotlines"),
391
+ **base_args
392
+ )
393
+ when "line_chart"
394
+ Quby::Questionnaires::Entities::Charting::LineChart.new(chart_json.fetch("key").to_sym,
395
+ y_label: chart_json.fetch("y_label"),
396
+ tonality: chart_json.fetch("tonality").to_sym,
397
+ baseline: YAML.load(chart_json.fetch("baseline")),
398
+ clinically_relevant_change: chart_json.fetch("clinically_relevant_change"),
399
+ **base_args
400
+ )
401
+ when "radar_chart"
402
+ Quby::Questionnaires::Entities::Charting::BarChart.new(chart_json.fetch("key").to_sym,
403
+ plotlines: chart_json.fetch("plotlines"),
404
+ **base_args
405
+ )
406
+ end
407
+ end
408
+
409
+ def self.build_score_schema(attributes)
410
+ Entities::ScoreSchema.new(
411
+ key: attributes.fetch("key").to_sym,
412
+ label: attributes.fetch("label"),
413
+ subscore_schemas: attributes.fetch("subscore_schemas").map do |subschema|
414
+ {
415
+ key: subschema.fetch("key").to_sym,
416
+ label: subschema.fetch("label"),
417
+ export_key: subschema.fetch("export_key").to_sym,
418
+ only_for_export: subschema.fetch("only_for_export")
419
+ }
420
+ end
421
+ )
422
+ end
423
+
424
+ def self.build_outcome_table(questionnaire, attributes)
425
+ Entities::OutcomeTable.new(
426
+ questionnaire: questionnaire,
427
+ key: attributes.fetch("key").to_sym,
428
+ score_keys: attributes.fetch("score_keys").map(&:to_sym),
429
+ subscore_keys: attributes.fetch("subscore_keys").map(&:to_sym),
430
+ name: attributes.fetch("name"),
431
+ default_collapsed: attributes.fetch("default_collapsed"),
432
+ )
433
+ end
434
+
435
+ def self.deserialize_range(range_attributes)
436
+ return unless range_attributes
437
+ Range.new(range_attributes.fetch("begin"), range_attributes.fetch("end"), range_attributes.fetch("exclude_end"))
438
+ end
439
+ end
440
+ end
441
+ end