quby-compiler 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities'
|
4
|
+
|
5
|
+
module Quby
|
6
|
+
module Compiler
|
7
|
+
module DSL
|
8
|
+
class ChartBuilder
|
9
|
+
# rubocop:disable AccessorMethodName
|
10
|
+
def self.set_chart_class(chart_class)
|
11
|
+
@chart_class = chart_class
|
12
|
+
end
|
13
|
+
# rubocop:enable AccessorMethodName
|
14
|
+
|
15
|
+
def self.chart_class
|
16
|
+
@chart_class
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(questionnaire, key, options = {})
|
20
|
+
@chart = self.class.chart_class.new(key, options)
|
21
|
+
@questionnaire = questionnaire
|
22
|
+
end
|
23
|
+
|
24
|
+
def title(title)
|
25
|
+
@chart.title = title
|
26
|
+
end
|
27
|
+
|
28
|
+
def range(range)
|
29
|
+
@chart.y_range = range
|
30
|
+
end
|
31
|
+
|
32
|
+
def tick_interval(tick_interval)
|
33
|
+
@chart.tick_interval = tick_interval
|
34
|
+
end
|
35
|
+
|
36
|
+
def y_categories(y_categories)
|
37
|
+
@chart.y_categories = y_categories
|
38
|
+
end
|
39
|
+
|
40
|
+
def y_range_categories(*y_range_categories)
|
41
|
+
@chart.y_range_categories = RangeCategories.new(*y_range_categories).as_range_hash
|
42
|
+
end
|
43
|
+
|
44
|
+
def plotband(from, to, color)
|
45
|
+
@chart.plotbands << {from: from, to: to, color: color}
|
46
|
+
end
|
47
|
+
|
48
|
+
def plotline(value, color)
|
49
|
+
@chart.plotlines << {value: value, color: color}
|
50
|
+
end
|
51
|
+
|
52
|
+
def plot(key, options = {})
|
53
|
+
unless plottable = @questionnaire.find_plottable(key)
|
54
|
+
fail "Questionnaire #{@questionnaire.key} chart #{@chart.key} references unknown score or question #{key}"
|
55
|
+
end
|
56
|
+
|
57
|
+
configure_options plottable, options
|
58
|
+
|
59
|
+
@chart.plottables << Entities::Charting::Plottable.new(plottable.key, options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def chart_type(type)
|
63
|
+
@chart.chart_type = type
|
64
|
+
end
|
65
|
+
|
66
|
+
def build(&block)
|
67
|
+
instance_eval(&block)
|
68
|
+
validate!
|
69
|
+
@chart
|
70
|
+
end
|
71
|
+
|
72
|
+
def validate!
|
73
|
+
if @chart.y_categories.present? && @chart.y_range.present? &&
|
74
|
+
@chart.y_range != (0..(@chart.y_categories.count - 1))
|
75
|
+
fail ArgumentError, 'Y_categories size and range do not match'
|
76
|
+
end
|
77
|
+
|
78
|
+
true
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def configure_options(plottable, options)
|
84
|
+
case plottable
|
85
|
+
when Entities::ScoreCalculation
|
86
|
+
options.reverse_merge! plottable.options
|
87
|
+
when Entities::Question
|
88
|
+
options[:label] ||= plottable.title
|
89
|
+
end
|
90
|
+
options[:questionnaire_key] = @questionnaire.key
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities/charting/line_chart'
|
4
|
+
require_relative 'chart_builder'
|
5
|
+
|
6
|
+
module Quby
|
7
|
+
module Compiler
|
8
|
+
module DSL
|
9
|
+
class LineChartBuilder < ChartBuilder
|
10
|
+
set_chart_class(Entities::Charting::LineChart)
|
11
|
+
|
12
|
+
def y_axis_label(label)
|
13
|
+
@chart.y_label = label
|
14
|
+
end
|
15
|
+
|
16
|
+
def tonality(value)
|
17
|
+
@chart.tonality = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def baseline(value)
|
21
|
+
@chart.baseline = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def clinically_relevant_change(value)
|
25
|
+
@chart.clinically_relevant_change = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate!
|
29
|
+
fail "Chart #{@chart.key} has no range specified" unless @chart.y_range
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'quby/compiler/entities/charting/overview_chart'
|
2
|
+
require_relative 'chart_builder'
|
3
|
+
|
4
|
+
module Quby
|
5
|
+
module Compiler
|
6
|
+
module DSL
|
7
|
+
class OverviewChartBuilder < ChartBuilder
|
8
|
+
set_chart_class(Entities::Charting::OverviewChart)
|
9
|
+
|
10
|
+
def initialize(questionnaire, options = {})
|
11
|
+
@questionnaire = questionnaire
|
12
|
+
@chart = self.class.chart_class.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def subscore(key)
|
16
|
+
@chart.subscore = key
|
17
|
+
end
|
18
|
+
|
19
|
+
def y_max(value)
|
20
|
+
@chart.y_max = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate!
|
24
|
+
fail ArgumentError, "subscore not specified" unless @chart.subscore.present?
|
25
|
+
fail ArgumentError, "y_max not specified" unless @chart.y_max.present?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities/charting/radar_chart'
|
4
|
+
require_relative 'chart_builder'
|
5
|
+
|
6
|
+
module Quby
|
7
|
+
module Compiler
|
8
|
+
module DSL
|
9
|
+
class RadarChartBuilder < ChartBuilder
|
10
|
+
set_chart_class(Entities::Charting::RadarChart)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module DSL
|
6
|
+
module Helpers
|
7
|
+
include ActionView::Helpers::TagHelper
|
8
|
+
include ActionView::Helpers::OutputSafetyHelper
|
9
|
+
|
10
|
+
def image_tag(source, options = {})
|
11
|
+
tag.img(options.reverse_merge(src: source, alt: image_alt(source)))
|
12
|
+
end
|
13
|
+
|
14
|
+
# Copied from ActionController::Base.helpers.image_alt, because it will be removed from Rails 6.0, but we want
|
15
|
+
# to keep using this functionality
|
16
|
+
def image_alt(source)
|
17
|
+
File.basename(source, ".*").sub(/-[[:xdigit:]]{32,64}\z/, "").tr("-_", " ").capitalize
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_question_keys_uniqueness(key, options, questionnaire)
|
21
|
+
keys = QuestionBuilder.build(key, options).claimed_keys
|
22
|
+
if keys.any? { |k| questionnaire.key_in_use? k }
|
23
|
+
fail "#{questionnaire.key}:#{key}: A question or option with input key #{key} is already defined."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def video_tag(*urls, poster: nil, autoplay: false, loop: false)
|
28
|
+
# assume the file extension is the video format
|
29
|
+
# otherwise, the host's declared mime type is used to figure out compatibility
|
30
|
+
# which is usually wrong
|
31
|
+
sources = urls.map { |url| [url, url.match(/[^\.]*\z/)&.[](0).downcase] }
|
32
|
+
sources.each { |_url, ext| raise 'unknown video url file extension' unless %w[mp4 webm].include?(ext) }
|
33
|
+
|
34
|
+
options = {
|
35
|
+
width: '100%',
|
36
|
+
preload: 'none',
|
37
|
+
poster: poster,
|
38
|
+
loop: loop,
|
39
|
+
autoplay: autoplay,
|
40
|
+
muted: autoplay,
|
41
|
+
controls: ''
|
42
|
+
}
|
43
|
+
|
44
|
+
content = sources.map { |url, ext| tag.source src: url, type: "video/#{ext}" }
|
45
|
+
.append(I18n.t('video_not_supported'))
|
46
|
+
.yield_self(&tag.method(:safe_join))
|
47
|
+
|
48
|
+
content_tag(:video, content, options)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities'
|
4
|
+
|
5
|
+
module Quby
|
6
|
+
module Compiler
|
7
|
+
module DSL
|
8
|
+
class PanelBuilder < Base
|
9
|
+
attr_reader :title
|
10
|
+
attr_reader :questionnaire
|
11
|
+
|
12
|
+
def initialize(title, options = {})
|
13
|
+
@panel = Entities::Panel.new(options.merge(title: title, items: []))
|
14
|
+
@default_question_options = options[:default_question_options] || {}
|
15
|
+
@questionnaire = options[:questionnaire]
|
16
|
+
@custom_methods = options[:custom_methods] || {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def build
|
20
|
+
@panel
|
21
|
+
end
|
22
|
+
|
23
|
+
def title(value)
|
24
|
+
@panel.title = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def text(value, options = {})
|
28
|
+
@panel.items << Entities::Text.new(value.to_s, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def html(value)
|
32
|
+
@panel.items << Entities::Text.new('', html_content: value.to_s)
|
33
|
+
end
|
34
|
+
|
35
|
+
def raw_html(value)
|
36
|
+
@panel.items << Entities::Text.new('', raw_content: value.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def video(*urls, **options)
|
40
|
+
video_html = video_tag *urls, **options
|
41
|
+
@panel.items << Entities::Text.new('', raw_content: video_html)
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_question_options(options = {})
|
45
|
+
@default_question_options = @default_question_options.merge(options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def question(key, options = {}, &block)
|
49
|
+
options = @default_question_options.merge(options).merge(questionnaire: @panel.questionnaire)
|
50
|
+
|
51
|
+
check_question_keys_uniqueness key, options, @questionnaire
|
52
|
+
|
53
|
+
question = QuestionBuilder.build(key, options, &block)
|
54
|
+
|
55
|
+
@questionnaire.register_question(question)
|
56
|
+
@panel.items << question
|
57
|
+
end
|
58
|
+
|
59
|
+
def table(options = {}, &block)
|
60
|
+
table_builder = TableBuilder.new(@panel, options.merge(questionnaire: @panel.questionnaire,
|
61
|
+
default_question_options: @default_question_options,
|
62
|
+
custom_methods: @custom_methods))
|
63
|
+
table_builder.instance_eval(&block) if block
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing(method_sym, *args, &block)
|
67
|
+
if @custom_methods.key? method_sym
|
68
|
+
instance_exec(*args, &@custom_methods[method_sym])
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def respond_to_missing?(method_name, include_private = false)
|
75
|
+
@custom_methods.key?(method_name) || super
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities'
|
4
|
+
|
5
|
+
require 'quby/compiler/dsl/questions/base'
|
6
|
+
require 'quby/compiler/dsl/questions/checkbox_question_builder'
|
7
|
+
require 'quby/compiler/dsl/questions/date_question_builder'
|
8
|
+
require 'quby/compiler/dsl/questions/deprecated_question_builder'
|
9
|
+
require 'quby/compiler/dsl/questions/float_question_builder'
|
10
|
+
require 'quby/compiler/dsl/questions/integer_question_builder'
|
11
|
+
require 'quby/compiler/dsl/questions/radio_question_builder'
|
12
|
+
require 'quby/compiler/dsl/questions/select_question_builder'
|
13
|
+
require 'quby/compiler/dsl/questions/string_question_builder'
|
14
|
+
require 'quby/compiler/dsl/questions/text_question_builder'
|
15
|
+
|
16
|
+
module Quby
|
17
|
+
module Compiler
|
18
|
+
module DSL
|
19
|
+
module QuestionBuilder
|
20
|
+
include Helpers
|
21
|
+
BUILDERS = {
|
22
|
+
'string' => Questions::StringQuestionBuilder,
|
23
|
+
'textarea' => Questions::TextQuestionBuilder,
|
24
|
+
'integer' => Questions::IntegerQuestionBuilder,
|
25
|
+
'float' => Questions::FloatQuestionBuilder,
|
26
|
+
'radio' => Questions::RadioQuestionBuilder,
|
27
|
+
'scale' => Questions::RadioQuestionBuilder,
|
28
|
+
'select' => Questions::SelectQuestionBuilder,
|
29
|
+
'check_box' => Questions::CheckboxQuestionBuilder,
|
30
|
+
'date' => Questions::DateQuestionBuilder,
|
31
|
+
'hidden' => Questions::DeprecatedQuestionBuilder
|
32
|
+
}
|
33
|
+
|
34
|
+
def self.build(key, options = {}, &block)
|
35
|
+
BUILDERS.fetch(options.fetch(:type).to_s).build(key, options, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/dsl/panel_builder'
|
4
|
+
require 'quby/compiler/dsl/table_builder'
|
5
|
+
require 'quby/compiler/dsl/question_builder'
|
6
|
+
require 'quby/compiler/dsl/score_builder'
|
7
|
+
require 'quby/compiler/dsl/charting/line_chart_builder'
|
8
|
+
require 'quby/compiler/dsl/charting/radar_chart_builder'
|
9
|
+
require 'quby/compiler/dsl/charting/bar_chart_builder'
|
10
|
+
require 'quby/compiler/dsl/charting/overview_chart_builder'
|
11
|
+
|
12
|
+
require_relative 'standardized_panel_generators'
|
13
|
+
|
14
|
+
module Quby
|
15
|
+
module Compiler
|
16
|
+
module DSL
|
17
|
+
class QuestionnaireBuilder
|
18
|
+
prepend CallsCustomMethods
|
19
|
+
include StandardizedPanelGenerators
|
20
|
+
include Helpers
|
21
|
+
|
22
|
+
def initialize(target_instance, lookup_tables: nil)
|
23
|
+
@questionnaire = target_instance
|
24
|
+
@lookup_tables = lookup_tables
|
25
|
+
@default_question_options = {}
|
26
|
+
@custom_methods = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def leave_page_alert(text)
|
30
|
+
@questionnaire.leave_page_alert = text
|
31
|
+
end
|
32
|
+
|
33
|
+
def key(key)
|
34
|
+
# no-op, key is now passed in to Questionnaire constructor
|
35
|
+
end
|
36
|
+
|
37
|
+
def roqua_keys(*keys)
|
38
|
+
@questionnaire.roqua_keys = keys
|
39
|
+
end
|
40
|
+
|
41
|
+
def seeds_patch(value)
|
42
|
+
@questionnaire.seeds_patch = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def do_not_check_key_clashes
|
46
|
+
@questionnaire.check_key_clashes = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def do_not_check_score_keys_consistency
|
50
|
+
@questionnaire.check_score_keys_consistency = false
|
51
|
+
end
|
52
|
+
|
53
|
+
def do_not_validate_html
|
54
|
+
@questionnaire.validate_html = false
|
55
|
+
end
|
56
|
+
|
57
|
+
def title(title)
|
58
|
+
@questionnaire.title = title
|
59
|
+
end
|
60
|
+
|
61
|
+
def description(description)
|
62
|
+
@questionnaire.description = description
|
63
|
+
end
|
64
|
+
|
65
|
+
def outcome_description(description)
|
66
|
+
@questionnaire.outcome_description = description
|
67
|
+
end
|
68
|
+
|
69
|
+
def short_description(description)
|
70
|
+
@questionnaire.short_description = description
|
71
|
+
end
|
72
|
+
|
73
|
+
def sbg_key(sbg_key)
|
74
|
+
@questionnaire.sbg_key = sbg_key
|
75
|
+
end
|
76
|
+
|
77
|
+
def sbg_domain(sbg_code, outcome:, from: nil, till: nil, sbg_key: nil, primary: false)
|
78
|
+
@questionnaire.sbg_domains << {
|
79
|
+
sbg_code: sbg_code,
|
80
|
+
from: from,
|
81
|
+
till: till,
|
82
|
+
outcome: outcome,
|
83
|
+
sbg_key: sbg_key || @questionnaire.sbg_key,
|
84
|
+
primary: primary
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def abortable
|
89
|
+
@questionnaire.abortable = true
|
90
|
+
end
|
91
|
+
|
92
|
+
def allow_hotkeys(type = :all)
|
93
|
+
@questionnaire.allow_hotkeys = type
|
94
|
+
end
|
95
|
+
|
96
|
+
def license(type, licensor: nil)
|
97
|
+
@questionnaire.license = type
|
98
|
+
@questionnaire.licensor = licensor
|
99
|
+
end
|
100
|
+
|
101
|
+
def language(language)
|
102
|
+
@questionnaire.language = language
|
103
|
+
end
|
104
|
+
|
105
|
+
def respondent_types(*respondent_types)
|
106
|
+
@questionnaire.respondent_types = respondent_types
|
107
|
+
end
|
108
|
+
|
109
|
+
def tags(*tags)
|
110
|
+
@questionnaire.tags = tags
|
111
|
+
end
|
112
|
+
|
113
|
+
def version(number, release_notes:, regenerate_outcome: false, deactivate_answers: false)
|
114
|
+
@questionnaire.versions << Entities::Version.new(
|
115
|
+
number: number,
|
116
|
+
release_notes: release_notes,
|
117
|
+
regenerate_outcome: regenerate_outcome,
|
118
|
+
deactivate_answers: deactivate_answers
|
119
|
+
)
|
120
|
+
end
|
121
|
+
|
122
|
+
def outcome_regeneration_requested_at(timestamp)
|
123
|
+
@questionnaire.outcome_regeneration_requested_at = timestamp
|
124
|
+
end
|
125
|
+
|
126
|
+
def deactivate_answers_requested_at(timestamp)
|
127
|
+
@questionnaire.deactivate_answers_requested_at = timestamp
|
128
|
+
end
|
129
|
+
|
130
|
+
def enable_previous_questionnaire_button
|
131
|
+
@questionnaire.enable_previous_questionnaire_button = true
|
132
|
+
end
|
133
|
+
|
134
|
+
def renderer_version(version)
|
135
|
+
@questionnaire.renderer_version = version
|
136
|
+
end
|
137
|
+
|
138
|
+
def css(value)
|
139
|
+
@questionnaire.extra_css += value
|
140
|
+
end
|
141
|
+
|
142
|
+
def allow_switch_to_bulk(value=true)
|
143
|
+
@questionnaire.allow_switch_to_bulk = value
|
144
|
+
end
|
145
|
+
|
146
|
+
def default_answer_value(value)
|
147
|
+
@questionnaire.default_answer_value = value
|
148
|
+
end
|
149
|
+
|
150
|
+
def panel(title = nil, options = {}, &block)
|
151
|
+
panel = PanelBuilder.build(title, options.merge(default_panel_options), &block)
|
152
|
+
@questionnaire.add_panel(panel)
|
153
|
+
end
|
154
|
+
|
155
|
+
def custom_method(key, &block)
|
156
|
+
if PanelBuilder.new(nil, custom_methods: @custom_methods).respond_to? key
|
157
|
+
fail 'Custom method trying to override existing method'
|
158
|
+
end
|
159
|
+
@custom_methods[key] = block
|
160
|
+
end
|
161
|
+
|
162
|
+
def import_lookup_tree(key)
|
163
|
+
@questionnaire.lookup_tables[key] = @lookup_tables.fetch(key)
|
164
|
+
end
|
165
|
+
|
166
|
+
def add_lookup_tree(key, levels:, tree:)
|
167
|
+
@questionnaire.lookup_tables[key] = {levels: levels, tree: tree}
|
168
|
+
end
|
169
|
+
|
170
|
+
def default_question_options(options = {})
|
171
|
+
@default_question_options.merge!(options)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Short-circuit the question command to perform an implicit panel
|
175
|
+
def question(key, options = {}, &block)
|
176
|
+
panel(nil, default_panel_options) do
|
177
|
+
question(key, @default_question_options.merge(options).merge(questionnaire: @questionnaire), &block)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Short-circuit the text command to perform an implicit panel
|
182
|
+
def text(value, options = {})
|
183
|
+
panel(nil, default_panel_options) do
|
184
|
+
text(value, options)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Short-circuit the table command to perform an implicit panel
|
189
|
+
def table(options = {}, &block)
|
190
|
+
panel(nil, default_panel_options) do
|
191
|
+
table(options, &block)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
# variable :totaal do
|
196
|
+
# # Plain old Ruby code here, executed in the scope of the answer
|
197
|
+
# # variables are private to the score calculation
|
198
|
+
# q01 + q02 + q03
|
199
|
+
# end
|
200
|
+
def variable(key, options = {}, &block)
|
201
|
+
@questionnaire.add_score_calculation ScoreBuilder.new(key, options, &block).build
|
202
|
+
end
|
203
|
+
|
204
|
+
def score(key, options = {}, &block)
|
205
|
+
@questionnaire.errors.add "Score #{key}", 'misses label in score call' if options[:label].blank?
|
206
|
+
schema = options.delete(:schema)
|
207
|
+
score_schema(key, options[:label], schema) if schema.present?
|
208
|
+
variable(key, options.reverse_merge(score: true), &block)
|
209
|
+
end
|
210
|
+
|
211
|
+
def score_schema(key, label, options = nil, &block)
|
212
|
+
if block
|
213
|
+
schema, calculations = ScoreSchemaBuilder.build(key: key, label: label, &block)
|
214
|
+
@questionnaire.add_score_schema schema
|
215
|
+
calculations.each do |calculation|
|
216
|
+
@questionnaire.add_score_calculation calculation
|
217
|
+
end
|
218
|
+
else
|
219
|
+
@questionnaire.add_score_schema Entities::ScoreSchema.new key: key,
|
220
|
+
label: label,
|
221
|
+
subscore_schemas: options
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def attention(options = {}, &block)
|
226
|
+
variable(:attention, options.reverse_merge(action: true), &block)
|
227
|
+
end
|
228
|
+
|
229
|
+
def alarm(options = {}, &block)
|
230
|
+
variable(:alarm, options.reverse_merge(action: true), &block)
|
231
|
+
end
|
232
|
+
|
233
|
+
def completion(options = {}, &block)
|
234
|
+
variable(:completion, options.reverse_merge(completion: true), &block)
|
235
|
+
end
|
236
|
+
|
237
|
+
def overview_chart(*args, &block)
|
238
|
+
raise "Cannot define more than one overview chart" if @questionnaire.charts.overview.present?
|
239
|
+
builder = OverviewChartBuilder.new(@questionnaire, *args)
|
240
|
+
@questionnaire.charts.overview = builder.build(&block)
|
241
|
+
end
|
242
|
+
|
243
|
+
def line_chart(*args, &block)
|
244
|
+
builder = LineChartBuilder.new(@questionnaire, *args)
|
245
|
+
@questionnaire.add_chart(builder.build(&block))
|
246
|
+
end
|
247
|
+
|
248
|
+
def bar_chart(*args, &block)
|
249
|
+
builder = BarChartBuilder.new(@questionnaire, *args)
|
250
|
+
@questionnaire.add_chart(builder.build(&block))
|
251
|
+
end
|
252
|
+
|
253
|
+
def radar_chart(*args, &block)
|
254
|
+
builder = RadarChartBuilder.new(@questionnaire, *args)
|
255
|
+
@questionnaire.add_chart(builder.build(&block))
|
256
|
+
end
|
257
|
+
|
258
|
+
def flag(flag_options)
|
259
|
+
@questionnaire.add_flag flag_options
|
260
|
+
end
|
261
|
+
|
262
|
+
def textvar(textvar_options)
|
263
|
+
@questionnaire.add_textvar textvar_options
|
264
|
+
end
|
265
|
+
|
266
|
+
def outcome_table(table_options)
|
267
|
+
@questionnaire.add_outcome_table table_options
|
268
|
+
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
def default_panel_options
|
273
|
+
{questionnaire: @questionnaire, default_question_options: @default_question_options,
|
274
|
+
custom_methods: @custom_methods}
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|