quby 4.0.4 → 5.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/lib/quby/questionnaires.rb +1 -0
  3. data/lib/quby/questionnaires/api.rb +5 -1
  4. data/lib/quby/questionnaires/deserializer.rb +433 -0
  5. data/lib/quby/questionnaires/dsl.rb +10 -15
  6. data/lib/quby/questionnaires/entities.rb +1 -0
  7. data/lib/quby/questionnaires/entities/charting/line_chart.rb +23 -0
  8. data/lib/quby/questionnaires/entities/charting/overview_chart.rb +3 -1
  9. data/lib/quby/questionnaires/entities/definition.rb +3 -5
  10. data/lib/quby/questionnaires/entities/fields.rb +0 -15
  11. data/lib/quby/questionnaires/entities/question.rb +9 -32
  12. data/lib/quby/questionnaires/entities/questionnaire.rb +4 -15
  13. data/lib/quby/questionnaires/entities/questions/checkbox_question.rb +0 -24
  14. data/lib/quby/questionnaires/entities/questions/date_question.rb +0 -8
  15. data/lib/quby/questionnaires/entities/score_calculation.rb +36 -3
  16. data/lib/quby/questionnaires/repos.rb +1 -0
  17. data/lib/quby/questionnaires/repos/bundle_disk_repo.rb +51 -0
  18. data/lib/quby/version.rb +1 -1
  19. data/spec/internal/log/test-events.log +0 -451
  20. data/spec/internal/log/test.log +0 -18003
  21. data/spec/quby/answers/services/answer_validations_spec.rb +8 -8
  22. data/spec/quby/questionnaires/deserializer/questionnaire_spec.rb +237 -0
  23. data/spec/quby/questionnaires/dsl_spec.rb +0 -9
  24. data/spec/quby/questionnaires/entities/fields_spec.rb +3 -3
  25. data/spec/quby/questionnaires/entities/question_spec.rb +0 -8
  26. data/spec/quby/questionnaires/entities/questionnaire_spec.rb +2 -26
  27. data/spec/quby/table_backend/range_tree_spec.rb +7 -0
  28. data/spec/spec_helper.rb +1 -0
  29. metadata +22 -139
  30. data/lib/quby/questionnaires/dsl/base.rb +0 -20
  31. data/lib/quby/questionnaires/dsl/calls_custom_methods.rb +0 -29
  32. data/lib/quby/questionnaires/dsl/charting/bar_chart_builder.rb +0 -18
  33. data/lib/quby/questionnaires/dsl/charting/chart_builder.rb +0 -91
  34. data/lib/quby/questionnaires/dsl/charting/line_chart_builder.rb +0 -57
  35. data/lib/quby/questionnaires/dsl/charting/overview_chart_builder.rb +0 -31
  36. data/lib/quby/questionnaires/dsl/charting/radar_chart_builder.rb +0 -18
  37. data/lib/quby/questionnaires/dsl/helpers.rb +0 -51
  38. data/lib/quby/questionnaires/dsl/panel_builder.rb +0 -80
  39. data/lib/quby/questionnaires/dsl/question_builder.rb +0 -40
  40. data/lib/quby/questionnaires/dsl/questionnaire_builder.rb +0 -260
  41. data/lib/quby/questionnaires/dsl/questions/base.rb +0 -179
  42. data/lib/quby/questionnaires/dsl/questions/checkbox_question_builder.rb +0 -20
  43. data/lib/quby/questionnaires/dsl/questions/date_question_builder.rb +0 -18
  44. data/lib/quby/questionnaires/dsl/questions/deprecated_question_builder.rb +0 -18
  45. data/lib/quby/questionnaires/dsl/questions/float_question_builder.rb +0 -21
  46. data/lib/quby/questionnaires/dsl/questions/integer_question_builder.rb +0 -21
  47. data/lib/quby/questionnaires/dsl/questions/radio_question_builder.rb +0 -20
  48. data/lib/quby/questionnaires/dsl/questions/select_question_builder.rb +0 -18
  49. data/lib/quby/questionnaires/dsl/questions/string_question_builder.rb +0 -20
  50. data/lib/quby/questionnaires/dsl/questions/text_question_builder.rb +0 -22
  51. data/lib/quby/questionnaires/dsl/score_builder.rb +0 -22
  52. data/lib/quby/questionnaires/dsl/standardized_panel_generators.rb +0 -33
  53. data/lib/quby/questionnaires/dsl/table_builder.rb +0 -48
  54. data/lib/quby/questionnaires/services/definition_validator.rb +0 -298
  55. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-21-57.510.html +0 -1
  56. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-21-57.510.png +0 -0
  57. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-23-56.006.html +0 -1
  58. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-23-56.006.png +0 -0
  59. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-24-43.842.html +0 -12
  60. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-24-43.842.png +0 -0
  61. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-25-04.631.html +0 -12
  62. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-25-04.631.png +0 -0
  63. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-25-11.690.html +0 -12
  64. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-25-11.690.png +0 -0
  65. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-26-25.111.html +0 -12
  66. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-26-25.111.png +0 -0
  67. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-26-57.026.html +0 -12
  68. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-26-57.026.png +0 -0
  69. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-27-13.545.html +0 -12
  70. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-27-13.545.png +0 -0
  71. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-27-45.475.html +0 -12
  72. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-27-45.475.png +0 -0
  73. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-32-13.907.html +0 -1
  74. data/spec/internal/tmp/capybara/screenshot_2020-10-27-18-32-13.907.png +0 -0
  75. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-16-31.954.html +0 -207
  76. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-16-31.954.png +0 -0
  77. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-16-59.938.html +0 -323
  78. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-16-59.938.png +0 -0
  79. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-08.700.html +0 -323
  80. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-08.700.png +0 -0
  81. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-11.550.html +0 -323
  82. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-11.550.png +0 -0
  83. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-14.413.html +0 -323
  84. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-14.413.png +0 -0
  85. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-17.275.html +0 -323
  86. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-17.275.png +0 -0
  87. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-20.191.html +0 -323
  88. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-20.191.png +0 -0
  89. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-23.042.html +0 -323
  90. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-23.042.png +0 -0
  91. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-25.927.html +0 -323
  92. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-25.927.png +0 -0
  93. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-28.735.html +0 -323
  94. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-17-28.735.png +0 -0
  95. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-40-20.422.html +0 -1
  96. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-40-20.422.png +0 -0
  97. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-40-20.738.html +0 -1
  98. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-40-20.738.png +0 -0
  99. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-41-16.971.html +0 -207
  100. data/spec/internal/tmp/capybara/screenshot_2020-11-04-13-41-16.971.png +0 -0
  101. data/spec/quby/questionnaires/dsl/calls_custom_methods_spec.rb +0 -38
  102. data/spec/quby/questionnaires/dsl/charting/bar_chart_builder_spec.rb +0 -41
  103. data/spec/quby/questionnaires/dsl/charting/chart_builder_spec.rb +0 -127
  104. data/spec/quby/questionnaires/dsl/charting/line_chart_builder_spec.rb +0 -66
  105. data/spec/quby/questionnaires/dsl/charting/radar_chart_builder_spec.rb +0 -41
  106. data/spec/quby/questionnaires/dsl/helpers_spec.rb +0 -80
  107. data/spec/quby/questionnaires/dsl/questionnaire_builder_spec.rb +0 -480
  108. data/spec/quby/questionnaires/services/definition_validator_spec.rb +0 -793
  109. 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: 1d344a78d772e4e8e4453a3c698152243750e60bc7a89e2e13c6f4d1cd68b334
4
+ data.tar.gz: acd71c1add05e591048ddd0fae1e658fb3a2e32d3b04c886bb9566c0754de09b
5
5
  SHA512:
6
- metadata.gz: 3fc841fd7e02157de4732a113cd26a2c2015606e1747397370c315558e878f07ba2f033b6126e246b1ba32ce3494106382c219d0c5db6aeb08caa63944a63dee
7
- data.tar.gz: a5602705d78127675bdb0c7418d13ee060a54b86d452778f99747b4760b8b3e9a72b66032de0bc134469243058c823a32ed46d1a9d7b7256db1e44360f6addb3
6
+ metadata.gz: 0ec1ef22216c6c83a8313cf7e98d499de62774677dc00d3b79543f5607e0e935d841fe5293f0d4babf1c45f26843c61d0fb436b6d4bb96c72ac47807061ae650
7
+ data.tar.gz: c269f585150a10d2e27dc96cb22350f54b3f1ed73222b68cb1191d437d399145acdae18cf42f07674c2fb6ac37bb97930497a24c34c7b730d4fd0f5160ad26b1
@@ -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,433 @@
1
+ module Quby
2
+ module Questionnaires
3
+ module Deserializer
4
+ # This symbolizes various things. Do not run on arbitrary JSON.
5
+ def self.from_json(json)
6
+ # TODO: last_update
7
+ Entities::Questionnaire.new(json.fetch("key"), json).tap do |questionnaire|
8
+ questionnaire.title = json.fetch("title")
9
+ questionnaire.description = json.fetch("description")
10
+ questionnaire.outcome_description = json.fetch("outcome_description")
11
+ questionnaire.short_description = json.fetch("short_description")
12
+ questionnaire.abortable = json.fetch("abortable")
13
+ questionnaire.enable_previous_questionnaire_button = json.fetch("enable_previous_questionnaire_button")
14
+ questionnaire.default_answer_value = json.fetch("default_answer_value")
15
+ questionnaire.leave_page_alert = json.fetch("leave_page_alert")
16
+ questionnaire.allow_hotkeys = json.fetch("allow_hotkeys")
17
+ questionnaire.license = json.fetch("license").try(:to_sym)
18
+ questionnaire.licensor = json.fetch("licensor")
19
+ questionnaire.language = json.fetch("language").try(:to_sym)
20
+ questionnaire.renderer_version = json.fetch("renderer_version")
21
+ questionnaire.last_update = json.fetch("last_update")
22
+ questionnaire.last_author = json.fetch("last_author")
23
+ questionnaire.extra_css = json.fetch("extra_css")
24
+ questionnaire.allow_switch_to_bulk = json.fetch("allow_switch_to_bulk")
25
+
26
+ questionnaire.flags = json.fetch("flags").with_indifferent_access.transform_values do |attrs|
27
+ build_flag(attrs)
28
+ end
29
+
30
+ questionnaire.textvars = json.fetch("textvars").with_indifferent_access.transform_values do |attrs|
31
+ build_textvar(attrs)
32
+ end
33
+
34
+ questionnaire.lookup_tables = YAML.load(json.fetch("lookup_tables")).transform_values do |attrs|
35
+ Quby::TableBackend::RangeTree.new(levels: attrs[:levels], tree: attrs[:tree])
36
+ end
37
+
38
+ questionnaire.score_calculations = json.fetch("score_calculations").with_indifferent_access.transform_values do |attrs|
39
+ build_score_calculation(attrs)
40
+ end
41
+
42
+ questionnaire.score_schemas = json.fetch("score_schemas").with_indifferent_access.transform_values do |schema|
43
+ build_score_schema(schema)
44
+ end
45
+
46
+ json.fetch("panels").each do |panel_json|
47
+ load_panel(questionnaire, panel_json)
48
+ end
49
+
50
+ # roqua domain
51
+ questionnaire.roqua_keys = json.fetch("roqua_keys")
52
+ questionnaire.sbg_key = json.fetch("sbg_key")
53
+ questionnaire.sbg_domains = json.fetch("sbg_domains").map(&:symbolize_keys)
54
+ questionnaire.outcome_regeneration_requested_at = json.fetch("outcome_regeneration_requested_at").try { |str| Time.zone.parse(str) }
55
+ questionnaire.deactivate_answers_requested_at = json.fetch("deactivate_answers_requested_at").try { |str| Time.zone.parse(str) }
56
+ questionnaire.respondent_types = json.fetch("respondent_types").map(&:to_sym)
57
+ questionnaire.tags = json.fetch("tags")
58
+
59
+ if overview_json = json.fetch("charts").fetch("overview")
60
+ questionnaire.charts.overview = Quby::Questionnaires::Entities::Charting::OverviewChart.new(
61
+ subscore: overview_json.fetch("subscore").to_sym,
62
+ y_max: overview_json.fetch("y_max"),
63
+ )
64
+ end
65
+
66
+ json.fetch("charts").fetch("others").each do |chart_json|
67
+ questionnaire.add_chart(build_chart(questionnaire, chart_json))
68
+ end
69
+
70
+ questionnaire.outcome_tables = json.fetch("outcome_tables").map do |attributes|
71
+ build_outcome_table(questionnaire, attributes)
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.load_panel(questionnaire, panel_json)
77
+ panel = Entities::Panel.new(
78
+ questionnaire: questionnaire,
79
+ key: panel_json.fetch("key"),
80
+ title: panel_json.fetch("title"),
81
+ items: []
82
+ )
83
+
84
+ panel_json.fetch("items").each do |item_json|
85
+ panel.items << load_item(questionnaire, item_json, panel: panel)
86
+ end
87
+
88
+ questionnaire.add_panel(panel)
89
+ end
90
+
91
+ def self.load_item(questionnaire, item_json, panel: nil)
92
+ case item_json.fetch("type")
93
+ when "text"
94
+ build_text(item_json)
95
+ when "question"
96
+ question = build_question(questionnaire, item_json)
97
+ questionnaire.register_question(question)
98
+ question
99
+ when "table"
100
+ table = Entities::Table.new(
101
+ title: item_json.fetch("title"),
102
+ description: item_json.fetch("description"),
103
+ columns: item_json.fetch("columns"),
104
+ show_option_desc: item_json.fetch("show_option_desc"),
105
+ )
106
+
107
+ item_json.fetch("items").each do |table_item_json|
108
+ case table_item_json.fetch("type")
109
+ when "text"
110
+ table.items << build_text(table_item_json)
111
+ when "question"
112
+ question = build_question(questionnaire, table_item_json, table: table)
113
+ questionnaire.register_question(question)
114
+ table.items << question
115
+ panel.items << question
116
+ else
117
+ raise "Unknown table item: #{table_item_json}"
118
+ end
119
+ end
120
+
121
+ table
122
+ else
123
+ raise "Unknown item: #{item_json}"
124
+ end
125
+ end
126
+
127
+ def self.build_text(item_json)
128
+ Entities::Text.new(item_json.fetch("str"), {
129
+ html_content: item_json.fetch("html_content"),
130
+ display_in: item_json.fetch("display_in").map(&:to_sym),
131
+ col_span: item_json.fetch("col_span"),
132
+ row_span: item_json.fetch("row_span"),
133
+ raw_content: item_json.fetch("raw_content"),
134
+ switch_cycle: item_json.fetch("switch_cycle")
135
+ })
136
+ end
137
+
138
+ def self.build_question(questionnaire, item_json, parent: nil, table: nil)
139
+ key = item_json.fetch("key").to_sym
140
+ attributes = {
141
+ questionnaire: questionnaire,
142
+ parent: parent,
143
+ type: item_json.fetch("question_type").to_sym,
144
+ title: item_json.fetch("title"),
145
+ context_free_title: item_json.fetch("context_free_title"),
146
+ description: item_json.fetch("description"),
147
+ presentation: item_json.fetch("presentation").to_sym,
148
+ hidden: item_json.fetch("hidden"),
149
+ depends_on: item_json.fetch("depends_on")&.map(&:to_sym),
150
+ default_position: item_json.fetch("default_position"),
151
+ validations: item_json.fetch("validations").map {|attrs| build_question_validation(attrs)},
152
+ table: table,
153
+
154
+ # only selectable via options passed in DSL, not via DSL methods
155
+ # many apply only to certain types of questions
156
+ sbg_key: item_json.fetch("sbg_key"),
157
+ allow_duplicate_option_values: item_json.fetch("allow_duplicate_option_values"),
158
+ allow_blank_titles: item_json.fetch("allow_blank_titles"),
159
+ as: item_json.fetch("as")&.to_sym,
160
+ display_modes: item_json.fetch("display_modes")&.map(&:to_sym),
161
+ autocomplete: item_json.fetch("autocomplete"),
162
+ show_values: item_json.fetch("show_values").to_sym,
163
+ deselectable: item_json.fetch("deselectable"),
164
+ disallow_bulk: item_json.fetch("disallow_bulk"),
165
+ score_header: item_json.fetch("score_header").to_sym,
166
+ sets_textvar: item_json.fetch("sets_textvar"),
167
+ default_invisible: item_json.fetch("default_invisible"),
168
+ question_group: item_json.fetch("question_group"), # sometimes string, sometimes a symbol in the DSL. Just have to hope this works
169
+ group_minimum_answered: item_json.fetch("group_minimum_answered"),
170
+ group_maximum_answered: item_json.fetch("group_maximum_answered"),
171
+ value_tooltip: item_json.fetch("value_tooltip"),
172
+
173
+ # might be able to deduce from tree structure
174
+ parent_option_key: item_json.fetch("parent_option_key")&.to_sym
175
+ }
176
+
177
+ case item_json.fetch("question_type")
178
+ when "check_box"
179
+ Entities::Questions::CheckboxQuestion.new(key, attributes.merge(
180
+ check_all_option: item_json.fetch("check_all_option")&.to_sym,
181
+ uncheck_all_option: item_json.fetch("uncheck_all_option")&.to_sym,
182
+ maximum_checked_allowed: item_json.fetch("maximum_checked_allowed"),
183
+ minimum_checked_required: item_json.fetch("minimum_checked_required"),
184
+ )).tap do |question|
185
+ item_json.fetch("options").each do |option_json|
186
+ question.options << build_option(questionnaire, question, option_json)
187
+ end
188
+ end
189
+ when "date"
190
+ Entities::Questions::DateQuestion.new(key, attributes.merge(
191
+ components: item_json.fetch("components").map(&:to_sym),
192
+ required_components: item_json.fetch("required_components").map(&:to_sym),
193
+ year_key: item_json.fetch("year_key")&.to_sym,
194
+ month_key: item_json.fetch("month_key")&.to_sym,
195
+ day_key: item_json.fetch("day_key")&.to_sym,
196
+ hour_key: item_json.fetch("hour_key")&.to_sym,
197
+ minute_key: item_json.fetch("minute_key")&.to_sym,
198
+ ))
199
+ when "deprecated", "hidden"
200
+ Entities::Questions::DeprecatedQuestion.new(key, attributes).tap do |question|
201
+ item_json.fetch("options").each do |option_json|
202
+ question.options << build_option(questionnaire, question, option_json)
203
+ end
204
+ end
205
+ when "float"
206
+ Entities::Questions::FloatQuestion.new(key, attributes.merge(
207
+ labels: item_json.fetch("labels"),
208
+ unit: item_json.fetch("unit"),
209
+ size: item_json.fetch("size"),
210
+ ))
211
+ when "integer"
212
+ Entities::Questions::IntegerQuestion.new(key, attributes.merge(
213
+ labels: item_json.fetch("labels"),
214
+ unit: item_json.fetch("unit"),
215
+ size: item_json.fetch("size"),
216
+ ))
217
+ when "radio", "scale"
218
+ Entities::Questions::RadioQuestion.new(key, attributes).tap do |question|
219
+ item_json.fetch("options").each do |option_json|
220
+ question.options << build_option(questionnaire, question, option_json)
221
+ end
222
+ end
223
+ when "select"
224
+ Entities::Questions::SelectQuestion.new(key, attributes).tap do |question|
225
+ item_json.fetch("options").each do |option_json|
226
+ question.options << build_option(questionnaire, question, option_json)
227
+ end
228
+ end
229
+ when "string"
230
+ Entities::Questions::StringQuestion.new(key, attributes.merge(
231
+ unit: item_json.fetch("unit"),
232
+ size: item_json.fetch("size"),
233
+ ))
234
+ when "textarea"
235
+ Entities::Questions::TextQuestion.new(key, attributes.merge(
236
+ lines: item_json.fetch("lines"),
237
+ ))
238
+ else
239
+ raise "Unknown question type: #{item_json}"
240
+ end
241
+ end
242
+
243
+ def self.build_option(questionnaire, question, option_json)
244
+ option = Entities::QuestionOption.new(option_json.fetch("key")&.to_sym, question,
245
+ value: option_json.fetch("value"),
246
+ description: option_json.fetch("description"),
247
+ context_free_description: option_json.fetch("context_free_description"),
248
+ inner_title: option_json.fetch("inner_title"),
249
+ hides_questions: option_json.fetch("hides_questions").map(&:to_sym),
250
+ shows_questions: option_json.fetch("shows_questions").map(&:to_sym),
251
+ hidden: option_json.fetch("hidden"),
252
+ placeholder: option_json.fetch("placeholder"),
253
+ )
254
+
255
+ option_json.fetch("questions").each do |question_json|
256
+ subquestion = build_question(questionnaire, question_json, parent: question)
257
+ questionnaire.register_question(subquestion)
258
+ option.questions << subquestion
259
+ end
260
+
261
+ option
262
+ end
263
+
264
+ def self.build_question_validation(attrs)
265
+ base_validation = {
266
+ type: attrs.fetch("type").to_sym,
267
+ explanation: attrs["explanation"] # not always specified for min/max validation
268
+ }
269
+
270
+ case attrs.fetch("type")
271
+ when "requires_answer"
272
+ base_validation
273
+ when "answer_group_minimum", "answer_group_maximum"
274
+ base_validation.merge(
275
+ group: attrs.fetch("group"), # TODO: sometimes a symbol, sometimes a string in the original, but I hope it doesn't matter
276
+ value: attrs.fetch("value")
277
+ )
278
+ when "valid_integer", "valid_float"
279
+ base_validation
280
+ when "valid_date"
281
+ base_validation.merge(
282
+ subtype: attrs.fetch("subtype").to_sym
283
+ )
284
+ when "minimum", "maximum"
285
+ value = case attrs.fetch("value_type")
286
+ when "Date"
287
+ Date.parse(attrs.fetch("value"))
288
+ when "DateTime"
289
+ DateTime.parse(attrs.fetch("value"))
290
+ when "Time", "ActiveSuport::TimeWithZone"
291
+ Time.zone.parse(attrs.fetch("value"))
292
+ else
293
+ attrs.fetch("value")
294
+ end
295
+
296
+ base_validation.merge(
297
+ value: value,
298
+ subtype: attrs.fetch("subtype").to_sym,
299
+ )
300
+ when "too_many_checked"
301
+ base_validation.merge(
302
+ uncheck_all_key: attrs.fetch("uncheck_all_key").to_sym
303
+ )
304
+ when "minimum_checked_required"
305
+ base_validation.merge(
306
+ minimum_checked_value: attrs.fetch("minimum_checked_value")
307
+ )
308
+ when "maximum_checked_allowed"
309
+ base_validation.merge(
310
+ maximum_checked_value: attrs.fetch("maximum_checked_value")
311
+ )
312
+ when "regexp"
313
+ base_validation.merge(
314
+ matcher: Regexp.new(attrs.fetch("matcher"))
315
+ )
316
+ when "not_all_checked"
317
+ base_validation.merge(
318
+ check_all_key: attrs.fetch("check_all_key").to_sym
319
+ )
320
+ else
321
+ raise "Unknown validation type: #{attrs.inspect}"
322
+ end
323
+ end
324
+
325
+ def self.build_score_calculation(attrs)
326
+ Entities::ScoreCalculation.new(attrs.fetch("key").to_sym,
327
+ label: attrs.fetch("label"),
328
+ sbg_key: attrs.fetch("sbg_key"),
329
+ options: attrs.fetch("options").symbolize_keys,
330
+ sourcecode: attrs.fetch("sourcecode"),
331
+ )
332
+ end
333
+
334
+ def self.build_flag(attrs)
335
+ Entities::Flag.new(
336
+ key: attrs.fetch("key").to_sym,
337
+ description_true: attrs.fetch("description_true"),
338
+ description_false: attrs.fetch("description_false"),
339
+ description: attrs.fetch("description"),
340
+ internal: attrs.fetch("internal"),
341
+ trigger_on: attrs.fetch("trigger_on"),
342
+ shows_questions: attrs.fetch("shows_questions").map(&:to_sym),
343
+ hides_questions: attrs.fetch("hides_questions").map(&:to_sym),
344
+ depends_on: attrs.fetch("depends_on"), # TODO: emperically determined to be a string in DSL, is that right?
345
+ default_in_interface: attrs.fetch("default_in_interface"),
346
+ )
347
+ end
348
+
349
+ def self.build_textvar(attrs)
350
+ Entities::Textvar.new(
351
+ key: attrs.fetch("key").to_sym,
352
+ description: attrs.fetch("description"),
353
+ default: attrs.fetch("default"),
354
+ depends_on_flag: attrs.fetch("depends_on_flag")&.to_sym
355
+ )
356
+ end
357
+
358
+ def self.build_chart(questionnaire, chart_json)
359
+ base_args = {
360
+ title: chart_json.fetch("title"),
361
+ plottables: chart_json.fetch("plottables").map do |plottable_json|
362
+ Quby::Questionnaires::Entities::Charting::Plottable.new(
363
+ plottable_json.fetch("key").to_sym,
364
+ label: plottable_json.fetch("label"),
365
+ plotted_key: plottable_json.fetch("plotted_key").to_sym,
366
+ global: plottable_json.fetch("global"),
367
+ questionnaire_key: plottable_json.fetch("questionnaire_key")
368
+ )
369
+ end,
370
+ y_categories: chart_json.fetch("y_categories"),
371
+ y_range_categories: chart_json.fetch("y_range_categories"),
372
+ chart_type: chart_json.fetch("chart_type"),
373
+ y_range: chart_json.fetch("y_range"),
374
+ tick_interval: chart_json.fetch("tick_interval"),
375
+ plotbands: chart_json.fetch("plotbands").map do |plotband_json|
376
+ {
377
+ color: plotband_json.fetch("color").to_sym,
378
+ from: plotband_json.fetch("from"),
379
+ to: plotband_json.fetch("to")
380
+ }
381
+ end,
382
+ }
383
+
384
+ case chart_json.fetch("type")
385
+ when "bar_chart"
386
+ Quby::Questionnaires::Entities::Charting::BarChart.new(chart_json.fetch("key").to_sym,
387
+ plotlines: chart_json.fetch("plotlines"),
388
+ **base_args
389
+ )
390
+ when "line_chart"
391
+ Quby::Questionnaires::Entities::Charting::LineChart.new(chart_json.fetch("key").to_sym,
392
+ y_label: chart_json.fetch("y_label"),
393
+ tonality: chart_json.fetch("tonality").to_sym,
394
+ baseline: YAML.load(chart_json.fetch("baseline")),
395
+ clinically_relevant_change: chart_json.fetch("clinically_relevant_change"),
396
+ **base_args
397
+ )
398
+ when "radar_chart"
399
+ Quby::Questionnaires::Entities::Charting::BarChart.new(chart_json.fetch("key").to_sym,
400
+ plotlines: chart_json.fetch("plotlines"),
401
+ **base_args
402
+ )
403
+ end
404
+ end
405
+
406
+ def self.build_score_schema(attributes)
407
+ Entities::ScoreSchema.new(
408
+ key: attributes.fetch("key").to_sym,
409
+ label: attributes.fetch("label"),
410
+ subscore_schemas: attributes.fetch("subscore_schemas").map do |subschema|
411
+ {
412
+ key: subschema.fetch("key").to_sym,
413
+ label: subschema.fetch("label"),
414
+ export_key: subschema.fetch("export_key").to_sym,
415
+ only_for_export: subschema.fetch("only_for_export")
416
+ }
417
+ end
418
+ )
419
+ end
420
+
421
+ def self.build_outcome_table(questionnaire, attributes)
422
+ Entities::OutcomeTable.new(
423
+ questionnaire: questionnaire,
424
+ key: attributes.fetch("key").to_sym,
425
+ score_keys: attributes.fetch("score_keys").map(&:to_sym),
426
+ subscore_keys: attributes.fetch("subscore_keys").map(&:to_sym),
427
+ name: attributes.fetch("name"),
428
+ default_collapsed: attributes.fetch("default_collapsed"),
429
+ )
430
+ end
431
+ end
432
+ end
433
+ end