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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.gitlab-ci.yml +5 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +11 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +133 -0
- data/LICENSE.txt +21 -0
- data/README.md +44 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/config/locales/de.yml +58 -0
- data/config/locales/en.yml +57 -0
- data/config/locales/nl.yml +57 -0
- data/config/locales/rails-i18n/README.md +4 -0
- data/config/locales/rails-i18n/de.yml +223 -0
- data/config/locales/rails-i18n/en.yml +216 -0
- data/config/locales/rails-i18n/nl.yml +214 -0
- data/exe/quby-compile +56 -0
- data/lib/quby/array_attribute_valid_validator.rb +15 -0
- data/lib/quby/attribute_valid_validator.rb +14 -0
- data/lib/quby/compiler.rb +50 -0
- data/lib/quby/compiler/dsl.rb +29 -0
- data/lib/quby/compiler/dsl/base.rb +20 -0
- data/lib/quby/compiler/dsl/calls_custom_methods.rb +29 -0
- data/lib/quby/compiler/dsl/charting/bar_chart_builder.rb +14 -0
- data/lib/quby/compiler/dsl/charting/chart_builder.rb +95 -0
- data/lib/quby/compiler/dsl/charting/line_chart_builder.rb +34 -0
- data/lib/quby/compiler/dsl/charting/overview_chart_builder.rb +31 -0
- data/lib/quby/compiler/dsl/charting/radar_chart_builder.rb +14 -0
- data/lib/quby/compiler/dsl/helpers.rb +53 -0
- data/lib/quby/compiler/dsl/panel_builder.rb +80 -0
- data/lib/quby/compiler/dsl/question_builder.rb +40 -0
- data/lib/quby/compiler/dsl/questionnaire_builder.rb +279 -0
- data/lib/quby/compiler/dsl/questions/base.rb +180 -0
- data/lib/quby/compiler/dsl/questions/checkbox_question_builder.rb +20 -0
- data/lib/quby/compiler/dsl/questions/date_question_builder.rb +18 -0
- data/lib/quby/compiler/dsl/questions/deprecated_question_builder.rb +18 -0
- data/lib/quby/compiler/dsl/questions/float_question_builder.rb +21 -0
- data/lib/quby/compiler/dsl/questions/integer_question_builder.rb +21 -0
- data/lib/quby/compiler/dsl/questions/radio_question_builder.rb +20 -0
- data/lib/quby/compiler/dsl/questions/select_question_builder.rb +18 -0
- data/lib/quby/compiler/dsl/questions/string_question_builder.rb +20 -0
- data/lib/quby/compiler/dsl/questions/text_question_builder.rb +22 -0
- data/lib/quby/compiler/dsl/score_builder.rb +22 -0
- data/lib/quby/compiler/dsl/score_schema_builder.rb +53 -0
- data/lib/quby/compiler/dsl/standardized_panel_generators.rb +33 -0
- data/lib/quby/compiler/dsl/table_builder.rb +48 -0
- data/lib/quby/compiler/entities.rb +38 -0
- data/lib/quby/compiler/entities/charting/bar_chart.rb +17 -0
- data/lib/quby/compiler/entities/charting/chart.rb +101 -0
- data/lib/quby/compiler/entities/charting/charts.rb +42 -0
- data/lib/quby/compiler/entities/charting/line_chart.rb +38 -0
- data/lib/quby/compiler/entities/charting/overview_chart.rb +20 -0
- data/lib/quby/compiler/entities/charting/plottable.rb +20 -0
- data/lib/quby/compiler/entities/charting/radar_chart.rb +17 -0
- data/lib/quby/compiler/entities/definition.rb +26 -0
- data/lib/quby/compiler/entities/fields.rb +119 -0
- data/lib/quby/compiler/entities/flag.rb +55 -0
- data/lib/quby/compiler/entities/item.rb +40 -0
- data/lib/quby/compiler/entities/lookup_tables.rb +71 -0
- data/lib/quby/compiler/entities/outcome_table.rb +31 -0
- data/lib/quby/compiler/entities/panel.rb +82 -0
- data/lib/quby/compiler/entities/question.rb +365 -0
- data/lib/quby/compiler/entities/question_option.rb +96 -0
- data/lib/quby/compiler/entities/questionnaire.rb +440 -0
- data/lib/quby/compiler/entities/questions/checkbox_question.rb +82 -0
- data/lib/quby/compiler/entities/questions/date_question.rb +84 -0
- data/lib/quby/compiler/entities/questions/deprecated_question.rb +19 -0
- data/lib/quby/compiler/entities/questions/float_question.rb +15 -0
- data/lib/quby/compiler/entities/questions/integer_question.rb +15 -0
- data/lib/quby/compiler/entities/questions/radio_question.rb +19 -0
- data/lib/quby/compiler/entities/questions/select_question.rb +19 -0
- data/lib/quby/compiler/entities/questions/string_question.rb +15 -0
- data/lib/quby/compiler/entities/questions/text_question.rb +15 -0
- data/lib/quby/compiler/entities/score_calculation.rb +35 -0
- data/lib/quby/compiler/entities/score_schema.rb +25 -0
- data/lib/quby/compiler/entities/subscore_schema.rb +23 -0
- data/lib/quby/compiler/entities/table.rb +143 -0
- data/lib/quby/compiler/entities/text.rb +71 -0
- data/lib/quby/compiler/entities/textvar.rb +23 -0
- data/lib/quby/compiler/entities/validation.rb +17 -0
- data/lib/quby/compiler/entities/version.rb +23 -0
- data/lib/quby/compiler/entities/visibility_rule.rb +71 -0
- data/lib/quby/compiler/instance.rb +72 -0
- data/lib/quby/compiler/output.rb +13 -0
- data/lib/quby/compiler/outputs.rb +4 -0
- data/lib/quby/compiler/outputs/quby_frontend_v1_serializer.rb +362 -0
- data/lib/quby/compiler/outputs/quby_frontend_v2_serializer.rb +15 -0
- data/lib/quby/compiler/outputs/roqua_serializer.rb +108 -0
- data/lib/quby/compiler/outputs/seed_serializer.rb +34 -0
- data/lib/quby/compiler/services/definition_validator.rb +330 -0
- data/lib/quby/compiler/services/quby_proxy.rb +405 -0
- data/lib/quby/compiler/services/seed_diff.rb +116 -0
- data/lib/quby/compiler/services/text_transformation.rb +30 -0
- data/lib/quby/compiler/version.rb +5 -0
- data/lib/quby/markdown_parser.rb +38 -0
- data/lib/quby/range_categories.rb +38 -0
- data/lib/quby/settings.rb +86 -0
- data/lib/quby/text_transformation.rb +26 -0
- data/lib/quby/type_validator.rb +12 -0
- data/quby-compiler.gemspec +39 -0
- 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 'Download als PDF' te klikken. Dit bestand is ook geschikt om af te drukken.<br>
|
|
19
|
+
* Sla de antwoorden op door op de 'Klaar'-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 'Download als PDF' te klikken. Dit bestand is ook geschikt om af te drukken.<br>
|
|
27
|
+
* Sla de antwoorden op door op de 'Klaar'-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
|