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
@@ -1,27 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'quby/questionnaires/dsl/base'
4
- require 'quby/questionnaires/dsl/helpers'
5
- require 'quby/questionnaires/dsl/questionnaire_builder'
6
-
7
3
  module Quby
8
4
  module Questionnaires
9
5
  module DSL
6
+ # Deprecated, precompile elsewhere and use from_json
10
7
  def self.build_from_definition(definition)
11
- Entities::Questionnaire.new(definition.key, last_update: definition.timestamp).tap do |questionnaire|
12
- builder = QuestionnaireBuilder.new(questionnaire)
13
- builder.instance_eval(definition.sourcecode, definition.path) if definition.sourcecode
14
- questionnaire.callback_after_dsl_enhance_on_questions
15
- end
8
+ compiled = Quby::Compiler.compile(definition.key, definition.sourcecode, seeds: [], lookup_tables: {}, path: definition.path, last_update: definition.timestamp)
9
+ data = JSON.parse(compiled[:outputs][:quby_frontend_v1].content)
10
+ Deserializer.from_json(data)
16
11
  end
17
12
 
13
+ # Deprecated, precompile elsewhere and use from_json
18
14
  def self.build(key, sourcecode = nil, timestamp: nil, &block)
19
- Entities::Questionnaire.new(key, last_update: timestamp).tap do |questionnaire|
20
- builder = QuestionnaireBuilder.new(questionnaire)
21
- builder.instance_eval(sourcecode, key) if sourcecode
22
- builder.instance_eval(&block) if block
23
- questionnaire.callback_after_dsl_enhance_on_questions
24
- end
15
+ compiled = Quby::Compiler.compile(key, sourcecode, seeds: [], lookup_tables: {}, last_update: timestamp, &block)
16
+ data = JSON.parse(compiled[:outputs][:quby_frontend_v1].content)
17
+ Deserializer.from_json(data)
18
+ end
19
+
20
+ def self.from_json(hash)
21
+ Deserializer.from_json(hash)
25
22
  end
26
23
  end
27
24
  end
@@ -13,6 +13,7 @@ require 'quby/questionnaires/entities/fields'
13
13
 
14
14
  require 'quby/questionnaires/entities/charting/charts'
15
15
  require 'quby/questionnaires/entities/charting/chart'
16
+ require 'quby/questionnaires/entities/charting/overview_chart'
16
17
  require 'quby/questionnaires/entities/charting/line_chart'
17
18
  require 'quby/questionnaires/entities/charting/bar_chart'
18
19
  require 'quby/questionnaires/entities/charting/radar_chart'
@@ -27,10 +27,33 @@ module Quby
27
27
  self.clinically_relevant_change = clinically_relevant_change
28
28
  end
29
29
 
30
+ def baseline
31
+ @baseline_proc ||= make_baseline_proc
32
+ end
33
+
30
34
  def tonality=(value)
31
35
  fail "Invalid tonality: #{value}" unless [:higher_is_better, :lower_is_better].include?(value)
32
36
  @tonality = value
33
37
  end
38
+
39
+ private
40
+
41
+ def make_baseline_proc
42
+ return unless @baseline
43
+
44
+ case @baseline
45
+ when Hash
46
+ ->(age, gender) {
47
+ age_match = @baseline.find { |age_range, _v| age && age_range === age }
48
+ hash_by_gender = (age_match&.last || @baseline.stringify_keys["default"])
49
+
50
+ gender_match = hash_by_gender.find {|gender_key, _v| gender && gender_key.to_s == gender.to_s }
51
+ gender_match&.last || hash_by_gender.stringify_keys['default']
52
+ }
53
+ else
54
+ ->(age, gender) { @baseline }
55
+ end
56
+ end
34
57
  end
35
58
  end
36
59
  end
@@ -11,7 +11,9 @@ module Quby
11
11
  # @return Integer
12
12
  attr_accessor :y_max
13
13
 
14
- def initialize
14
+ def initialize(subscore: nil, y_max: nil)
15
+ self.subscore = subscore
16
+ self.y_max = y_max
15
17
  end
16
18
  end
17
19
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'active_model'
4
- require 'quby/questionnaires/services/definition_validator'
5
4
 
6
5
  module Quby
7
6
  module Questionnaires
@@ -10,16 +9,15 @@ module Quby
10
9
  extend ActiveModel::Naming
11
10
  include ActiveModel::Validations
12
11
 
13
- attr_accessor :key, :sourcecode, :timestamp, :path
12
+ attr_accessor :key, :sourcecode, :json, :timestamp, :path
14
13
 
15
- def initialize(key:, path:, sourcecode: "", timestamp: nil)
14
+ def initialize(key:, path:, sourcecode: "", json: nil, timestamp: nil)
16
15
  @path = path
17
16
  @key = key
18
17
  @sourcecode = sourcecode
18
+ @json = json
19
19
  @timestamp = timestamp
20
20
  end
21
-
22
- validates_with Services::DefinitionValidator
23
21
  end
24
22
  end
25
23
  end
@@ -35,11 +35,6 @@ module Quby
35
35
  new_answer_keys = Set.new(question.answer_keys)
36
36
  new_input_keys = Set.new(question.input_keys)
37
37
 
38
- # This is probably the best place to ensure that keys don't collide. However,our current set of questionnaires
39
- # does have a few collisions between +v_1+ option +a9+ and its subquestion +v_1_a9+, so we have excluded
40
- # those questionnaires from this check through @questionnaire.check_key_clashes.
41
- check_key_clashes(new_answer_keys, new_input_keys) if @questionnaire.check_key_clashes
42
-
43
38
  @question_hash[question.key] = question
44
39
  @input_keys.merge(new_input_keys)
45
40
  @answer_keys.merge(new_answer_keys)
@@ -48,16 +43,6 @@ module Quby
48
43
  end
49
44
  end
50
45
 
51
- def check_key_clashes(new_answer_keys, new_input_keys)
52
- if @answer_keys.intersect?(new_answer_keys)
53
- fail "Duplicate answer keys: #{@answer_keys.intersection(new_answer_keys).inspect}"
54
- end
55
-
56
- if @input_keys.intersect?(new_input_keys)
57
- fail "Duplicate input keys: #{@input_keys.intersection(new_input_keys).inspect}"
58
- end
59
- end
60
-
61
46
  def key_in_use?(key)
62
47
  @question_hash.key?(key) || input_keys.include?(key.to_sym)
63
48
  end
@@ -8,8 +8,6 @@ module Quby
8
8
  class Question < Item
9
9
  MARKDOWN_ATTRIBUTES = %w(description title).freeze
10
10
 
11
- set_callback :after_dsl_enhance, :expand_depends_on_input_keys
12
-
13
11
  # Standard attributes
14
12
  attr_accessor :key
15
13
  validates :key, presence: true, 'quby/type': {is_a: Symbol}
@@ -140,7 +138,7 @@ module Quby
140
138
  @description = options[:description]
141
139
  @display_modes = options[:display_modes]
142
140
  @presentation = options[:presentation]
143
- @validations = []
141
+ @validations = options[:validations] || []
144
142
  @parent = options[:parent]
145
143
  @hidden = options[:hidden]
146
144
  @table = options[:table]
@@ -150,12 +148,13 @@ module Quby
150
148
  @deselectable = (options[:deselectable].nil? || options[:deselectable])
151
149
  @disallow_bulk = options[:disallow_bulk]
152
150
  @score_header = options[:score_header] || :none
153
- @sets_textvar = "#{questionnaire.key}_#{options[:sets_textvar]}" if options[:sets_textvar]
151
+ @sets_textvar = options[:sets_textvar]
154
152
  @unit = options[:unit]
155
153
  @lines = options[:lines] || 6
156
154
  @cols = options[:cols] || 40
157
155
  @default_invisible = options[:default_invisible] || false
158
- @labels ||= []
156
+ @labels = options[:labels] || []
157
+ @size = options[:size]
159
158
 
160
159
  @col_span = options[:col_span] || 1
161
160
  @row_span = options[:row_span] || 1
@@ -169,16 +168,6 @@ module Quby
169
168
  @input_data = {}
170
169
  @input_data[:value_tooltip] = true if options[:value_tooltip]
171
170
 
172
- # Require subquestions of required questions by default
173
- options[:required] = true if @parent&.validations&.first&.fetch(:type, nil) == :requires_answer
174
- @validations << {type: :requires_answer, explanation: options[:error_explanation]} if options[:required]
175
-
176
- if @type == :float
177
- @validations << {type: :valid_float, explanation: options[:error_explanation]}
178
- elsif @type == :integer
179
- @validations << {type: :valid_integer, explanation: options[:error_explanation]}
180
- end
181
-
182
171
  if options[:minimum] and (@type == :integer || @type == :float)
183
172
  fail "deprecated" # pretty sure this is not used anywhere
184
173
  end
@@ -186,17 +175,6 @@ module Quby
186
175
  fail "deprecated" # pretty sure this is not used anywhere
187
176
  end
188
177
  @default_position = options[:default_position]
189
-
190
- if @question_group
191
- if @group_minimum_answered
192
- @validations << {type: :answer_group_minimum, group: @question_group, value: @group_minimum_answered,
193
- explanation: options[:error_explanation]}
194
- end
195
- if @group_maximum_answered
196
- @validations << {type: :answer_group_maximum, group: @question_group, value: @group_maximum_answered,
197
- explanation: options[:error_explanation]}
198
- end
199
- end
200
178
  end
201
179
  # rubocop:enable CyclomaticComplexity, Metrics/MethodLength
202
180
 
@@ -212,12 +190,11 @@ module Quby
212
190
  @context_free_title || @title
213
191
  end
214
192
 
215
- def expand_depends_on_input_keys
216
- return unless @depends_on
217
- @depends_on = questionnaire.expand_input_keys(@depends_on)
218
- @extra_data[:"depends-on"] = @depends_on.to_json
219
- rescue => e
220
- raise e.class, "Question #{key} depends_on contains an error: #{e.message}"
193
+ def extra_data
194
+ result = @extra_data
195
+ result = result.merge(:"depends-on" => @depends_on.to_json) if @depends_on
196
+ result = result.merge(:placeholder => @options.find { |option| option.placeholder }&.key)
197
+ result
221
198
  end
222
199
 
223
200
  def col_span
@@ -29,8 +29,9 @@ module Quby
29
29
 
30
30
  RESPONDENT_TYPES = %i( profess patient parent second_parent teacher caregiver )
31
31
 
32
- def initialize(key, last_update: Time.now)
32
+ def initialize(key, attributes = {}, last_update: Time.now)
33
33
  @key = key
34
+ @attributes = attributes
34
35
  @sbg_domains = []
35
36
  @last_update = Time.at(last_update.to_i)
36
37
  @score_calculations = {}.with_indifferent_access
@@ -54,6 +55,8 @@ module Quby
54
55
  @lookup_tables = {}
55
56
  end
56
57
 
58
+ attr_reader :attributes
59
+
57
60
  attr_accessor :key
58
61
  attr_accessor :title
59
62
  attr_accessor :description
@@ -136,20 +139,6 @@ module Quby
136
139
  end
137
140
  end
138
141
 
139
- def callback_after_dsl_enhance_on_questions
140
- question_hash.each_value do |q|
141
- q.run_callbacks :after_dsl_enhance
142
- end
143
- ensure_scores_have_schemas if Quby::Settings.require_score_schemas
144
- end
145
-
146
- def ensure_scores_have_schemas
147
- missing_schemas = scores.map(&:key).map(&:to_s) - score_schemas.keys
148
- missing_schemas.each do |key|
149
- errors.add "Score #{key}", 'is missing a score schema'
150
- end
151
- end
152
-
153
142
  def validate_questions
154
143
  question_hash.each_value do |q|
155
144
  unless q.valid?
@@ -24,30 +24,6 @@ module Quby
24
24
  @uncheck_all_option = options[:uncheck_all_option]
25
25
  @maximum_checked_allowed = options[:maximum_checked_allowed]
26
26
  @minimum_checked_required = options[:minimum_checked_required]
27
-
28
- if @check_all_option
29
- @validations << {type: :not_all_checked,
30
- check_all_key: @check_all_option,
31
- explanation: options[:error_explanation]}
32
- end
33
-
34
- if @uncheck_all_option
35
- @validations << {type: :too_many_checked,
36
- uncheck_all_key: @uncheck_all_option,
37
- explanation: options[:error_explanation]}
38
- end
39
-
40
- if @maximum_checked_allowed
41
- @validations << {type: :maximum_checked_allowed,
42
- maximum_checked_value: @maximum_checked_allowed,
43
- explanation: options[:error_explanation]}
44
- end
45
-
46
- if @minimum_checked_required
47
- @validations << {type: :minimum_checked_required,
48
- minimum_checked_value: @minimum_checked_required,
49
- explanation: options[:error_explanation]}
50
- end
51
27
  end
52
28
 
53
29
  def variable_descriptions
@@ -32,14 +32,6 @@ module Quby
32
32
  component_key = options[:"#{component}_key"] || "#{key}_#{COMPONENT_KEYS[component]}"
33
33
  instance_variable_set("@#{component}_key", component_key.to_sym)
34
34
  end
35
-
36
- add_date_validation(options[:error_explanation])
37
- end
38
-
39
- def add_date_validation(explanation)
40
- @validations << {type: :valid_date,
41
- subtype: :"valid_date_#{components.sort.join('_')}",
42
- explanation: explanation}
43
35
  end
44
36
 
45
37
  def claimed_keys
@@ -3,15 +3,52 @@
3
3
  module Quby
4
4
  module Questionnaires
5
5
  module Entities
6
+ # method_source gem gives us the full score source code including the initial `score() do` DSL call.
7
+ # This module helps strip off that outer DSL call.
8
+ module StripOuterScoreCall
9
+ def self.score(*args, &block)
10
+ block
11
+ end
12
+
13
+ def self.variable(*args, &block)
14
+ block
15
+ end
16
+
17
+ def self.attention(*args, &block)
18
+ block
19
+ end
20
+
21
+ def self.alarm(*args, &block)
22
+ block
23
+ end
24
+
25
+ def self.completion(*args, &block)
26
+ block
27
+ end
28
+
29
+ def self.subscore(*args, &block)
30
+ block
31
+ end
32
+ end
33
+
6
34
  class ScoreCalculation
7
- attr_accessor :key, :label, :sbg_key, :options, :calculation
35
+ attr_accessor :key, :label, :sbg_key, :options
8
36
 
9
37
  def initialize(key, options, &block)
10
38
  @key = key
11
39
  @label = options[:label]
12
40
  @sbg_key = options[:sbg_key]
13
- @options = options
14
- @calculation = block
41
+ @options = options[:options] || options # TODO remove `|| options`
42
+ @sourcecode = options[:sourcecode]
43
+ @block = block
44
+ end
45
+
46
+ def calculation
47
+ if @block
48
+ @block
49
+ else
50
+ StripOuterScoreCall.instance_eval(@sourcecode)
51
+ end
15
52
  end
16
53
 
17
54
  def score
@@ -6,6 +6,7 @@ module Quby
6
6
  autoload :Base, 'quby/questionnaires/repos/base'
7
7
  autoload :MemoryRepo, 'quby/questionnaires/repos/memory_repo'
8
8
  autoload :DiskRepo, 'quby/questionnaires/repos/disk_repo'
9
+ autoload :BundleDiskRepo, 'quby/questionnaires/repos/bundle_disk_repo'
9
10
 
10
11
  QuestionnaireNotFound = Class.new(StandardError)
11
12
  DuplicateQuestionnaire = Class.new(StandardError)
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quby/questionnaires/repos'
4
+ require 'quby/questionnaires'
5
+
6
+ module Quby
7
+ module Questionnaires
8
+ module Repos
9
+ class BundleDiskRepo < Base
10
+ attr_reader :path
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ @questionnaire_cache = {}
15
+ end
16
+
17
+ def keys
18
+ Dir[File.join(path, "*.rb")].map do |filename|
19
+ File.basename(filename, '.rb')
20
+ end
21
+ end
22
+
23
+ def find(key)
24
+ fail(QuestionnaireNotFound, key) unless exists?(key)
25
+ json = read(key)
26
+ timestamp = Time.zone.parse(json["last_update"])
27
+ Entities::Definition.new(key: key, path: questionnaire_path(key), json: json, timestamp: timestamp)
28
+ end
29
+
30
+ def exists?(key)
31
+ questionnaire_path = questionnaire_path(key)
32
+ File.exist?(questionnaire_path)
33
+ end
34
+
35
+ def timestamp(key)
36
+ Time.zone.parse(read(key)["last_update"])
37
+ end
38
+
39
+ private
40
+
41
+ def read(key)
42
+ JSON.parse(File.read(questionnaire_path(key)))
43
+ end
44
+
45
+ def questionnaire_path(key)
46
+ File.join(path,key, "quby-frontend-v1.json")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -2,8 +2,6 @@
2
2
 
3
3
  # A lookup tree to find values by multiple arguments.
4
4
  #
5
- # Created by LookupTable from a csv file or by add_lookup_tree from dsl.
6
- #
7
5
  # Example tree:
8
6
  # Inhibitie:
9
7
  # male:
@@ -36,27 +34,6 @@ module Quby::TableBackend
36
34
  @tree = tree
37
35
  end
38
36
 
39
- # load csv data into a tree.
40
- # each row is a path through the tree.
41
- # String and float types are used to make an exact match.
42
- # A range is always a range between two floats where the range is between
43
- # the low value (inclusive) and the high value (exclusive),
44
- # written as 4:5 (low:high). These boundaries can be given as floats or
45
- # integers, but internally they are always treated as a floats.
46
- # The low and high values of a range cannot be equal.
47
- # Use minfinity or infinity to create infinite ranges.
48
- #
49
- # @params levels [Array<String>] An array of column names
50
- # @param compare [Array<String>]An array of lookup types (string, float or range) for each column
51
- # @param data [Array<Array<>>] The rows describing a path through the tree.
52
- def self.from_csv(levels:, compare:, data:)
53
- tree = data.each_with_object({}) do |row, tree|
54
- add_to_tree(tree, row, levels, compare)
55
- end
56
- new(levels: levels, tree: tree)
57
- end
58
-
59
-
60
37
  # Given a parameters hash that contains a value or range for every
61
38
  # level in the tree, find and return the normscore.
62
39
  # ie. `lookup({age: 10, raw: 5, scale: 'Inhibitie', gender: 'male'})` => 39
@@ -67,34 +44,6 @@ module Quby::TableBackend
67
44
 
68
45
  private
69
46
 
70
- def self.add_to_tree(tree, (value, *path), (level, *levels), (compare, *compares))
71
- key = case compare
72
- when 'string' then value.to_s
73
- when 'float' then parse_float(value)
74
- when 'range' then create_range(value)
75
- end
76
-
77
- if levels.empty?
78
- return key
79
- end
80
-
81
- tree.merge! key => add_to_tree(tree[key] || {}, path, levels, compares)
82
- end
83
-
84
- def self.create_range(value)
85
- min, max = value.split(':').map { |val| parse_float(val) }
86
- fail 'Cannot create range between two equal values' if min == max
87
- (min...max)
88
- end
89
-
90
- def self.parse_float(value)
91
- case value
92
- when 'infinity' then Float::INFINITY
93
- when 'minfinity' then -Float::INFINITY
94
- else Float(value)
95
- end
96
- end
97
-
98
47
  # All parameters must be present to do a lookup but the order does not matter.
99
48
  def validate_parameters(parameters)
100
49
  if @levels[0...-1].sort != parameters.keys.map(&:to_s).sort