quby-compiler 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.gitlab-ci.yml +5 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Dockerfile +11 -0
  8. data/Gemfile +8 -0
  9. data/Gemfile.lock +133 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +44 -0
  12. data/Rakefile +6 -0
  13. data/bin/console +14 -0
  14. data/bin/rspec +29 -0
  15. data/bin/setup +8 -0
  16. data/config/locales/de.yml +58 -0
  17. data/config/locales/en.yml +57 -0
  18. data/config/locales/nl.yml +57 -0
  19. data/config/locales/rails-i18n/README.md +4 -0
  20. data/config/locales/rails-i18n/de.yml +223 -0
  21. data/config/locales/rails-i18n/en.yml +216 -0
  22. data/config/locales/rails-i18n/nl.yml +214 -0
  23. data/exe/quby-compile +56 -0
  24. data/lib/quby/array_attribute_valid_validator.rb +15 -0
  25. data/lib/quby/attribute_valid_validator.rb +14 -0
  26. data/lib/quby/compiler.rb +50 -0
  27. data/lib/quby/compiler/dsl.rb +29 -0
  28. data/lib/quby/compiler/dsl/base.rb +20 -0
  29. data/lib/quby/compiler/dsl/calls_custom_methods.rb +29 -0
  30. data/lib/quby/compiler/dsl/charting/bar_chart_builder.rb +14 -0
  31. data/lib/quby/compiler/dsl/charting/chart_builder.rb +95 -0
  32. data/lib/quby/compiler/dsl/charting/line_chart_builder.rb +34 -0
  33. data/lib/quby/compiler/dsl/charting/overview_chart_builder.rb +31 -0
  34. data/lib/quby/compiler/dsl/charting/radar_chart_builder.rb +14 -0
  35. data/lib/quby/compiler/dsl/helpers.rb +53 -0
  36. data/lib/quby/compiler/dsl/panel_builder.rb +80 -0
  37. data/lib/quby/compiler/dsl/question_builder.rb +40 -0
  38. data/lib/quby/compiler/dsl/questionnaire_builder.rb +279 -0
  39. data/lib/quby/compiler/dsl/questions/base.rb +180 -0
  40. data/lib/quby/compiler/dsl/questions/checkbox_question_builder.rb +20 -0
  41. data/lib/quby/compiler/dsl/questions/date_question_builder.rb +18 -0
  42. data/lib/quby/compiler/dsl/questions/deprecated_question_builder.rb +18 -0
  43. data/lib/quby/compiler/dsl/questions/float_question_builder.rb +21 -0
  44. data/lib/quby/compiler/dsl/questions/integer_question_builder.rb +21 -0
  45. data/lib/quby/compiler/dsl/questions/radio_question_builder.rb +20 -0
  46. data/lib/quby/compiler/dsl/questions/select_question_builder.rb +18 -0
  47. data/lib/quby/compiler/dsl/questions/string_question_builder.rb +20 -0
  48. data/lib/quby/compiler/dsl/questions/text_question_builder.rb +22 -0
  49. data/lib/quby/compiler/dsl/score_builder.rb +22 -0
  50. data/lib/quby/compiler/dsl/score_schema_builder.rb +53 -0
  51. data/lib/quby/compiler/dsl/standardized_panel_generators.rb +33 -0
  52. data/lib/quby/compiler/dsl/table_builder.rb +48 -0
  53. data/lib/quby/compiler/entities.rb +38 -0
  54. data/lib/quby/compiler/entities/charting/bar_chart.rb +17 -0
  55. data/lib/quby/compiler/entities/charting/chart.rb +101 -0
  56. data/lib/quby/compiler/entities/charting/charts.rb +42 -0
  57. data/lib/quby/compiler/entities/charting/line_chart.rb +38 -0
  58. data/lib/quby/compiler/entities/charting/overview_chart.rb +20 -0
  59. data/lib/quby/compiler/entities/charting/plottable.rb +20 -0
  60. data/lib/quby/compiler/entities/charting/radar_chart.rb +17 -0
  61. data/lib/quby/compiler/entities/definition.rb +26 -0
  62. data/lib/quby/compiler/entities/fields.rb +119 -0
  63. data/lib/quby/compiler/entities/flag.rb +55 -0
  64. data/lib/quby/compiler/entities/item.rb +40 -0
  65. data/lib/quby/compiler/entities/lookup_tables.rb +71 -0
  66. data/lib/quby/compiler/entities/outcome_table.rb +31 -0
  67. data/lib/quby/compiler/entities/panel.rb +82 -0
  68. data/lib/quby/compiler/entities/question.rb +365 -0
  69. data/lib/quby/compiler/entities/question_option.rb +96 -0
  70. data/lib/quby/compiler/entities/questionnaire.rb +440 -0
  71. data/lib/quby/compiler/entities/questions/checkbox_question.rb +82 -0
  72. data/lib/quby/compiler/entities/questions/date_question.rb +84 -0
  73. data/lib/quby/compiler/entities/questions/deprecated_question.rb +19 -0
  74. data/lib/quby/compiler/entities/questions/float_question.rb +15 -0
  75. data/lib/quby/compiler/entities/questions/integer_question.rb +15 -0
  76. data/lib/quby/compiler/entities/questions/radio_question.rb +19 -0
  77. data/lib/quby/compiler/entities/questions/select_question.rb +19 -0
  78. data/lib/quby/compiler/entities/questions/string_question.rb +15 -0
  79. data/lib/quby/compiler/entities/questions/text_question.rb +15 -0
  80. data/lib/quby/compiler/entities/score_calculation.rb +35 -0
  81. data/lib/quby/compiler/entities/score_schema.rb +25 -0
  82. data/lib/quby/compiler/entities/subscore_schema.rb +23 -0
  83. data/lib/quby/compiler/entities/table.rb +143 -0
  84. data/lib/quby/compiler/entities/text.rb +71 -0
  85. data/lib/quby/compiler/entities/textvar.rb +23 -0
  86. data/lib/quby/compiler/entities/validation.rb +17 -0
  87. data/lib/quby/compiler/entities/version.rb +23 -0
  88. data/lib/quby/compiler/entities/visibility_rule.rb +71 -0
  89. data/lib/quby/compiler/instance.rb +72 -0
  90. data/lib/quby/compiler/output.rb +13 -0
  91. data/lib/quby/compiler/outputs.rb +4 -0
  92. data/lib/quby/compiler/outputs/quby_frontend_v1_serializer.rb +362 -0
  93. data/lib/quby/compiler/outputs/quby_frontend_v2_serializer.rb +15 -0
  94. data/lib/quby/compiler/outputs/roqua_serializer.rb +108 -0
  95. data/lib/quby/compiler/outputs/seed_serializer.rb +34 -0
  96. data/lib/quby/compiler/services/definition_validator.rb +330 -0
  97. data/lib/quby/compiler/services/quby_proxy.rb +405 -0
  98. data/lib/quby/compiler/services/seed_diff.rb +116 -0
  99. data/lib/quby/compiler/services/text_transformation.rb +30 -0
  100. data/lib/quby/compiler/version.rb +5 -0
  101. data/lib/quby/markdown_parser.rb +38 -0
  102. data/lib/quby/range_categories.rb +38 -0
  103. data/lib/quby/settings.rb +86 -0
  104. data/lib/quby/text_transformation.rb +26 -0
  105. data/lib/quby/type_validator.rb +12 -0
  106. data/quby-compiler.gemspec +39 -0
  107. metadata +277 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable LineLength
4
+
5
+ module Quby
6
+ module Compiler
7
+ module DSL
8
+ module StandardizedPanelGenerators
9
+ def start_panel
10
+ panel do
11
+ text "Welkom bij deze vragenlijst"
12
+ end
13
+ end
14
+
15
+ def end_panel
16
+ panel do
17
+ text "*Bedankt voor het invullen van deze vragenlijst.*<br>
18
+ * U kunt uw antwoorden downloaden door op de knop &#39;Download als PDF&#39; te klikken. Dit bestand is ook geschikt om af te drukken.<br>
19
+ * Sla de antwoorden op door op de &#39;Klaar&#39;-knop onderaan te klikken. Daarna kunt u uw antwoorden niet meer wijzigen."
20
+ end
21
+ end
22
+
23
+ def informal_end_panel
24
+ panel do
25
+ text "*Bedankt voor het invullen van deze vragenlijst.*<br>
26
+ * Je kan je antwoorden downloaden door op de knop &#39;Download als PDF&#39; te klikken. Dit bestand is ook geschikt om af te drukken.<br>
27
+ * Sla de antwoorden op door op de &#39;Klaar&#39;-knop onderaan te klikken. Daarna kun je je antwoorden niet meer wijzigen."
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quby/compiler/entities'
4
+
5
+ module Quby
6
+ module Compiler
7
+ module DSL
8
+ class TableBuilder
9
+ prepend CallsCustomMethods
10
+ include Helpers
11
+
12
+ def initialize(panel, options = {})
13
+ @panel = panel
14
+ @table = Entities::Table.new(options)
15
+ @default_question_options = options[:default_question_options] || {}
16
+ @panel.items << @table
17
+ end
18
+
19
+ def title(value)
20
+ @table.title = value
21
+ end
22
+
23
+ def description(value)
24
+ @table.description = value
25
+ end
26
+
27
+ def text(value, options = {})
28
+ @table.items << Entities::Text.new(value.to_s, options)
29
+ end
30
+
31
+ def question(key, options = {}, &block)
32
+ options = @default_question_options.merge(options)
33
+ .merge(table: @table,
34
+ questionnaire: @panel.questionnaire)
35
+
36
+ check_question_keys_uniqueness key, options, @panel.questionnaire
37
+ fail "You can't create a slider in a table at the moment" if options[:as] == :slider
38
+
39
+ question = QuestionBuilder.build(key, options, &block)
40
+
41
+ @panel.questionnaire.register_question(question)
42
+ @table.items << question
43
+ @panel.items << question
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require 'quby/compiler/entities/lookup_tables'
3
+
4
+ require 'quby/compiler/entities/definition'
5
+ require 'quby/compiler/entities/questionnaire'
6
+ require 'quby/compiler/entities/version'
7
+ require 'quby/compiler/entities/question_option'
8
+ require 'quby/compiler/entities/item'
9
+ require 'quby/compiler/entities/score_calculation'
10
+
11
+ require 'quby/compiler/entities/subscore_schema'
12
+ require 'quby/compiler/entities/score_schema'
13
+
14
+ require 'quby/compiler/entities/fields'
15
+
16
+ require 'quby/compiler/entities/charting/charts'
17
+ require 'quby/compiler/entities/charting/chart'
18
+ require 'quby/compiler/entities/charting/line_chart'
19
+ require 'quby/compiler/entities/charting/bar_chart'
20
+ require 'quby/compiler/entities/charting/radar_chart'
21
+ require 'quby/compiler/entities/charting/plottable'
22
+
23
+ require 'quby/compiler/entities/panel'
24
+ require 'quby/compiler/entities/text'
25
+ require 'quby/compiler/entities/table'
26
+
27
+ require 'quby/compiler/entities/outcome_table'
28
+
29
+ require 'quby/compiler/entities/question'
30
+ require 'quby/compiler/entities/questions/checkbox_question'
31
+ require 'quby/compiler/entities/questions/date_question'
32
+ require 'quby/compiler/entities/questions/deprecated_question'
33
+ require 'quby/compiler/entities/questions/float_question'
34
+ require 'quby/compiler/entities/questions/integer_question'
35
+ require 'quby/compiler/entities/questions/radio_question'
36
+ require 'quby/compiler/entities/questions/select_question'
37
+ require 'quby/compiler/entities/questions/string_question'
38
+ require 'quby/compiler/entities/questions/text_question'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quby/compiler/entities/charting/chart'
4
+
5
+ module Quby
6
+ module Compiler
7
+ module Entities
8
+ module Charting
9
+ class BarChart < Chart
10
+ def initialize(key, **kwargs)
11
+ super(key, **kwargs)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quby
4
+ module Compiler
5
+ module Entities
6
+ module Charting
7
+ class Chart
8
+ # @return [Symbol]
9
+ attr_accessor :key
10
+
11
+ # @return [String]
12
+ attr_accessor :title
13
+
14
+ # @return [Array]
15
+ attr_accessor :plottables
16
+
17
+ # If y_categories are defined, plottable values should correspond to
18
+ # values from this array and the graph will be plotted with
19
+ # corresponding y axis categories example (icl_r):
20
+ #
21
+ # ["Zeer laag", "Laag", "Gemiddeld", "Hoog", "Zeer Hoog"]
22
+ #
23
+ # (caution, capitalization oddity)
24
+ #
25
+ # NB: only implemented for bar charts on the roqua side
26
+ #
27
+ # @return [Array]
28
+ attr_accessor :y_categories
29
+
30
+ # If y_range_categories are defined, plottable values should fall in
31
+ # the ranges that compose the keys of this hash. The chart will label
32
+ # these ranges of y_values with the corresponding value in the hash
33
+ # on the y axis. For example:
34
+ #
35
+ # {
36
+ # (0.0...30.0) => "Zeer laag",
37
+ # (30.0...40.0) => "Laag",
38
+ # (40.0...60.0) => "Gemiddeld",
39
+ # (60.0...70.0) => "Hoog",
40
+ # (70.0..100.0) => "Zeer hoog"
41
+ # }
42
+ #
43
+ # NB: .. is inclusive the last value in the range, ... is exclusive.
44
+ #
45
+ # ChartBuilder#y_range_categories massages its parameters into this
46
+ # format. Only implemented for line charts on the RoQua side.
47
+ #
48
+ # @return [Hash<Range, String>]
49
+ attr_accessor :y_range_categories
50
+
51
+ # @return [Symbol]
52
+ attr_accessor :chart_type
53
+
54
+ # @return [Range]
55
+ attr_accessor :y_range
56
+
57
+ # @return [Float]
58
+ attr_accessor :tick_interval
59
+
60
+ # @return [Array]
61
+ attr_accessor :plotbands
62
+
63
+ # @return [Array]
64
+ attr_accessor :plotlines
65
+
66
+ def initialize(key, title: nil, plottables: nil, y_categories: nil, y_range_categories: nil, chart_type: nil, y_range: nil, tick_interval: nil, plotbands: nil, plotlines: nil)
67
+ self.key = key.to_sym
68
+ self.title = title
69
+ self.plottables = plottables || []
70
+ self.y_categories = y_categories
71
+ self.y_range_categories = y_range_categories
72
+ self.chart_type = chart_type
73
+ self.y_range = y_range
74
+ self.tick_interval = tick_interval
75
+ self.plotbands = plotbands || []
76
+ self.plotlines = plotlines || []
77
+ end
78
+
79
+ def type
80
+ self.class.name.to_s.demodulize.underscore
81
+ end
82
+
83
+ def y_range
84
+ @y_range || @y_range = default_y_range
85
+ end
86
+
87
+ def chart_type=(value)
88
+ @chart_type = value&.to_sym
89
+ end
90
+
91
+ def default_y_range
92
+ # when there are y_categories, the y_range should match the
93
+ # number of categories (validated in chart_builder#validate!)
94
+ (0..(y_categories.count - 1)) if y_categories.present?
95
+ # otherwise, nil is allowed as a y_range
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quby
4
+ module Compiler
5
+ module Entities
6
+ module Charting
7
+ class Charts
8
+ include Enumerable
9
+
10
+ def initialize
11
+ @charts = []
12
+ end
13
+
14
+ def overview
15
+ @overview_chart
16
+ end
17
+
18
+ def overview=(chart)
19
+ @overview_chart = chart
20
+ end
21
+
22
+ def add(chart)
23
+ fail "Duplicate chart: #{chart.key} already exists!" if find(chart.key)
24
+ @charts << chart
25
+ end
26
+
27
+ def find(key)
28
+ @charts.find { |i| i.key == key }
29
+ end
30
+
31
+ def each(*args, &block)
32
+ @charts.each(*args, &block)
33
+ end
34
+
35
+ def size
36
+ @charts.size
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quby/compiler/entities/charting/chart'
4
+
5
+ module Quby
6
+ module Compiler
7
+ module Entities
8
+ module Charting
9
+ class LineChart < Chart
10
+ # @return [String]
11
+ attr_accessor :y_label
12
+
13
+ # @return [Symbol]
14
+ attr_accessor :tonality
15
+
16
+ # @return [Proc]
17
+ attr_accessor :baseline
18
+
19
+ # @return [Float]
20
+ attr_accessor :clinically_relevant_change
21
+
22
+ def initialize(key, y_label: nil, tonality: :lower_is_better, baseline: nil, clinically_relevant_change: nil, **kwargs)
23
+ super(key, **kwargs)
24
+ self.y_label = y_label
25
+ self.tonality = tonality
26
+ self.baseline = baseline
27
+ self.clinically_relevant_change = clinically_relevant_change
28
+ end
29
+
30
+ def tonality=(value)
31
+ fail "Invalid tonality: #{value}" unless [:higher_is_better, :lower_is_better].include?(value)
32
+ @tonality = value
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'chart'
2
+
3
+ module Quby
4
+ module Compiler
5
+ module Entities
6
+ module Charting
7
+ class OverviewChart < Chart
8
+ # @return Symbol
9
+ attr_accessor :subscore
10
+
11
+ # @return Integer
12
+ attr_accessor :y_max
13
+
14
+ def initialize
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Quby
4
+ module Compiler
5
+ module Entities
6
+ module Charting
7
+ class Plottable < Struct.new(:key, :label, :plotted_key, :questionnaire_key, :global)
8
+ def initialize(key, options = {})
9
+ key = key
10
+ label = options[:label]
11
+ plotted_key = options.fetch(:plotted_key) { :value }
12
+ global = options[:global]
13
+ questionnaire_key = options[:questionnaire_key]
14
+ super(key, label, plotted_key, questionnaire_key, global)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'quby/compiler/entities/charting/chart'
4
+
5
+ module Quby
6
+ module Compiler
7
+ module Entities
8
+ module Charting
9
+ class RadarChart < Chart
10
+ def initialize(key, **kwargs)
11
+ super(key, **kwargs)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model'
4
+ require 'quby/compiler/services/definition_validator'
5
+
6
+ module Quby
7
+ module Compiler
8
+ module Entities
9
+ class Definition
10
+ extend ActiveModel::Naming
11
+ include ActiveModel::Validations
12
+
13
+ attr_accessor :key, :sourcecode, :timestamp, :path
14
+
15
+ def initialize(key:, path:, sourcecode: "", timestamp: nil)
16
+ @path = path
17
+ @key = key
18
+ @sourcecode = sourcecode
19
+ @timestamp = timestamp
20
+ end
21
+
22
+ validates_with Services::DefinitionValidator
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ module Quby
6
+ module Compiler
7
+ module Entities
8
+ class Fields
9
+ attr_reader :question_hash
10
+
11
+ # hash of all options from input_key to the QuestionOption.
12
+ # Used by including applications to lookup the definition of e.g. a check_box question.
13
+ attr_reader :option_hash
14
+
15
+ # An +answer_key+ is a key that will exist in the values hash of an answer. This means that answer keys for
16
+ # radio's will be just the question key, and answer keys for checkboxes will be the keys of all the options.
17
+ # These are the POST parameters when submitting the form, and so they must be globally unique or we won't know
18
+ # which question the received data belongs to.
19
+ attr_reader :answer_keys
20
+
21
+ # An +input_key+ is a key that uniquely identifies a single <input> tag. For radios, every radio option will
22
+ # have its own input key. This is needed because option keys must be globally unique so that they can be
23
+ # targeted by :depends_on relations.
24
+ attr_reader :input_keys
25
+
26
+ def initialize(questionnaire)
27
+ @question_hash = HashWithIndifferentAccess.new
28
+ @option_hash = HashWithIndifferentAccess.new
29
+ @answer_keys = Set.new
30
+ @input_keys = Set.new
31
+ @questionnaire = questionnaire
32
+ end
33
+
34
+ def add(question)
35
+ new_answer_keys = Set.new(question.answer_keys)
36
+ new_input_keys = Set.new(question.input_keys)
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
+ @question_hash[question.key] = question
44
+ @input_keys.merge(new_input_keys)
45
+ @answer_keys.merge(new_answer_keys)
46
+ question.options.each do |option|
47
+ @option_hash[option.input_key] = option
48
+ end
49
+ end
50
+
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
+ def key_in_use?(key)
62
+ @question_hash.key?(key) || input_keys.include?(key.to_sym)
63
+ end
64
+
65
+ # Given a list of question and option keys returns a list of input-keys. If a given key is a question-key,
66
+ # adds the question.input_keys If a given key is an option-input-key it adds the given key. Raises an error
67
+ # if a key is not defined.
68
+ def expand_input_keys(keys)
69
+ keys.reduce([]) do |ikeys, key|
70
+ if question_hash.key?(key)
71
+ ikeys += question_hash[key].input_keys
72
+ elsif input_keys.include?(key.to_sym)
73
+ ikeys << key
74
+ else
75
+ fail Entities::Questionnaire::UnknownInputKey, "Unknown input key #{key}"
76
+ end
77
+ end
78
+ end
79
+
80
+ # returns a human readable string description given a key of a question,
81
+ # question component (date components, checkbox options), score, flag or textvar
82
+ def description_for_variable(key)
83
+ # for questionnaires where we do not check_key_clashes we cannot reliably retrace the variable keys,
84
+ # since they contain conflicts between option keys and question keys
85
+ # in order to be safe we return a string explaining the issue
86
+ return "No description due to question/option key clash" if option_hash.key?(key) && question_hash.key?(key)
87
+
88
+ variable_description(key)
89
+ end
90
+
91
+ def as_json
92
+ question_hash
93
+ end
94
+
95
+ private
96
+
97
+ # warning, will contain a result even if option/answer key clashes exist for a given key
98
+ def variable_description(key)
99
+ @question_variable_descriptions ||= @questionnaire.questions
100
+ .map(&:variable_descriptions)
101
+ .reduce({}, &:merge!)
102
+ @question_variable_descriptions[key] ||
103
+ score_descriptions[key] ||
104
+ @questionnaire.flags[key]&.variable_description ||
105
+ @questionnaire.textvars[key]&.description
106
+ end
107
+
108
+ def score_descriptions
109
+ @score_variable_descriptions ||=
110
+ @questionnaire.score_schemas.values.map do |score_schema|
111
+ score_schema.subscore_schemas.map do |subschema|
112
+ [subschema.export_key, "#{score_schema.label} #{subschema.label}"]
113
+ end
114
+ end.flatten(1).to_h.with_indifferent_access
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end