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,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities/item'
|
4
|
+
require 'quby/markdown_parser'
|
5
|
+
|
6
|
+
module Quby
|
7
|
+
module Compiler
|
8
|
+
module Entities
|
9
|
+
class Text < Item
|
10
|
+
attr_reader :str
|
11
|
+
attr_accessor :display_in
|
12
|
+
attr_accessor :html_content
|
13
|
+
|
14
|
+
# In case of being displayed inside a table, amount of columns/rows to span
|
15
|
+
attr_accessor :col_span
|
16
|
+
attr_accessor :row_span
|
17
|
+
|
18
|
+
def initialize(str, options = {})
|
19
|
+
if options[:html_content]
|
20
|
+
options[:raw_content] = "<div class='item text'>" + options[:html_content] + "</div>"
|
21
|
+
end
|
22
|
+
super(options)
|
23
|
+
@str = str
|
24
|
+
@html_content = options[:html_content]
|
25
|
+
@display_in = options[:display_in] || [:paged]
|
26
|
+
@col_span = options[:col_span] || 1
|
27
|
+
@row_span = options[:row_span] || 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_json(options = {})
|
31
|
+
super().merge(text: text)
|
32
|
+
end
|
33
|
+
|
34
|
+
def html
|
35
|
+
@html_content || text
|
36
|
+
end
|
37
|
+
|
38
|
+
def text
|
39
|
+
@text ||= Quby::MarkdownParser.new(str).to_html
|
40
|
+
end
|
41
|
+
|
42
|
+
def key
|
43
|
+
't0'
|
44
|
+
end
|
45
|
+
|
46
|
+
def type
|
47
|
+
"text"
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_answer(answer_hash)
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other)
|
55
|
+
case other.class
|
56
|
+
when String
|
57
|
+
text == other
|
58
|
+
when self.class
|
59
|
+
text == other.text
|
60
|
+
else
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
text
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
class Textvar < Struct.new(:key, :description, :default, :depends_on_flag)
|
7
|
+
# rubocop:disable ParameterLists
|
8
|
+
def initialize(key:, description:, default: nil, depends_on_flag: nil)
|
9
|
+
default = "{{#{key}}}" unless default
|
10
|
+
super(key, description, default, depends_on_flag)
|
11
|
+
end
|
12
|
+
# rubocop:enable ParameterLists
|
13
|
+
|
14
|
+
def to_codebook(_options = {})
|
15
|
+
output = []
|
16
|
+
output << "#{key} Textvariabele"
|
17
|
+
output << description
|
18
|
+
output.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'active_model/model'
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
class Version
|
7
|
+
include ActiveModel::Model
|
8
|
+
|
9
|
+
# @return [Integer]
|
10
|
+
attr_accessor :number
|
11
|
+
|
12
|
+
# @return [String]
|
13
|
+
attr_accessor :release_notes
|
14
|
+
|
15
|
+
# @return [Boolean]
|
16
|
+
attr_accessor :regenerate_outcome
|
17
|
+
|
18
|
+
# @return [Boolean]
|
19
|
+
attr_accessor :deactivate_answers
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Quby
|
2
|
+
module Compiler
|
3
|
+
module Entities
|
4
|
+
class VisibilityRule
|
5
|
+
def self.from(question)
|
6
|
+
[].tap do |rules|
|
7
|
+
# Transform "default invisible" into just being hidden by itself,
|
8
|
+
# since any other question showing it will take precedence anyway.
|
9
|
+
if question.default_invisible
|
10
|
+
rules << new(condition: { type: :always, field_key: question.key },
|
11
|
+
action: { type: :hide_question, field_key: question.key })
|
12
|
+
end
|
13
|
+
|
14
|
+
case question.type
|
15
|
+
when :radio, :scale, :select
|
16
|
+
question.options.each do |option|
|
17
|
+
rules.concat rules_for_option(question, option, type: :equal)
|
18
|
+
end
|
19
|
+
when :check_box
|
20
|
+
question.options.each do |option|
|
21
|
+
rules.concat rules_for_option(question, option, type: :contains)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.rules_for_option(question, option, type:)
|
28
|
+
[].tap do |rules|
|
29
|
+
option.shows_questions.each do |shows_question|
|
30
|
+
rules << new(condition: { type: type, field_key: question.key, value: option.key },
|
31
|
+
action: { type: :show_question, field_key: shows_question })
|
32
|
+
end
|
33
|
+
|
34
|
+
option.hides_questions.each do |hides_question|
|
35
|
+
rules << new(condition: { type: type, field_key: question.key, value: option.key },
|
36
|
+
action: { type: :hide_question, field_key: hides_question })
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.from_flag(flag)
|
42
|
+
condition = { type: "flag_equal", flag_key: flag.key, value: flag.trigger_on }
|
43
|
+
|
44
|
+
[].tap do |rules|
|
45
|
+
flag.shows_questions.each do |question_key|
|
46
|
+
rules << VisibilityRule.new(condition: condition, action: { type: "show_question", field_key: question_key })
|
47
|
+
end
|
48
|
+
|
49
|
+
flag.hides_questions.each do |question_key|
|
50
|
+
rules << VisibilityRule.new(condition: condition, action: { type: "hide_question", field_key: question_key })
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :condition, :action
|
56
|
+
|
57
|
+
def initialize(condition:, action:)
|
58
|
+
@condition = condition
|
59
|
+
@action = action
|
60
|
+
end
|
61
|
+
|
62
|
+
def as_json
|
63
|
+
{
|
64
|
+
condition: condition.transform_keys { |key| key.to_s.camelize(:lower) },
|
65
|
+
action: action.transform_keys { |key| key.to_s.camelize(:lower) },
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Quby
|
2
|
+
module Compiler
|
3
|
+
class Instance
|
4
|
+
attr_reader :lookup_tables
|
5
|
+
|
6
|
+
def initialize(lookup_tables:)
|
7
|
+
@lookup_tables = lookup_tables
|
8
|
+
end
|
9
|
+
|
10
|
+
def compile(key:, sourcecode:, seeds:, path: nil, last_update: nil, &block)
|
11
|
+
if block # defined in block for tests
|
12
|
+
questionnaire = DSL.build(key, path: path, &block)
|
13
|
+
else # sourcecode given as string
|
14
|
+
tempfile = Tempfile.new(key)
|
15
|
+
questionnaire = Entities::Questionnaire.new(key, last_update: last_update)
|
16
|
+
Thread.current["quby-questionnaire-loading"] = Quby::Compiler::DSL::QuestionnaireBuilder.new(questionnaire, lookup_tables: lookup_tables)
|
17
|
+
|
18
|
+
tempfile.puts "Thread.current['quby-questionnaire-loading'].instance_eval do"
|
19
|
+
tempfile.puts sourcecode
|
20
|
+
tempfile.puts "end"
|
21
|
+
tempfile.flush
|
22
|
+
|
23
|
+
load tempfile.path
|
24
|
+
Thread.current['quby-questionnaire-loading'] = nil
|
25
|
+
|
26
|
+
questionnaire.callback_after_dsl_enhance_on_questions
|
27
|
+
end
|
28
|
+
|
29
|
+
{
|
30
|
+
outputs: {
|
31
|
+
definition: Output.new(
|
32
|
+
key: :definition,
|
33
|
+
filename: "definition.rb",
|
34
|
+
content: sourcecode,
|
35
|
+
),
|
36
|
+
roqua: Output.new(
|
37
|
+
key: :roqua,
|
38
|
+
filename: "roqua.json",
|
39
|
+
content: Outputs::RoquaSerializer.new(questionnaire).to_json,
|
40
|
+
),
|
41
|
+
seeds: Output.new(
|
42
|
+
key: :seeds,
|
43
|
+
filename: "seeds.yml",
|
44
|
+
content: YAML.dump(Outputs::SeedSerializer.new(questionnaire, seeds).generate),
|
45
|
+
),
|
46
|
+
quby_frontend_v1: Output.new(
|
47
|
+
key: :quby_frontend_v1,
|
48
|
+
filename: "quby-frontend-v1.json",
|
49
|
+
content: Outputs::QubyFrontendV1Serializer.new(questionnaire).to_json,
|
50
|
+
),
|
51
|
+
quby_frontend_v2: Output.new(
|
52
|
+
key: :quby_frontend_v2,
|
53
|
+
filename: "quby-frontend-v2.json",
|
54
|
+
content: Outputs::QubyFrontendV2Serializer.new(questionnaire).to_json,
|
55
|
+
),
|
56
|
+
},
|
57
|
+
}
|
58
|
+
ensure
|
59
|
+
# We can only close and remove the file once serializers have finished.
|
60
|
+
# The serializers need the file in order to grab the source for score blocks
|
61
|
+
tempfile&.close
|
62
|
+
tempfile&.unlink
|
63
|
+
end
|
64
|
+
|
65
|
+
def validate(key:, sourcecode:)
|
66
|
+
definition = Entities::Definition.new(key: key, sourcecode: sourcecode, path: "validating '#{key}'")
|
67
|
+
definition.valid?
|
68
|
+
definition
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,362 @@
|
|
1
|
+
require 'method_source'
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Outputs
|
6
|
+
class QubyFrontendV1Serializer
|
7
|
+
attr_reader :questionnaire
|
8
|
+
|
9
|
+
def initialize(questionnaire)
|
10
|
+
@questionnaire = questionnaire
|
11
|
+
end
|
12
|
+
|
13
|
+
def panels
|
14
|
+
questionnaire.panels.map do |panel|
|
15
|
+
{
|
16
|
+
key: panel.key,
|
17
|
+
title: panel.title,
|
18
|
+
items: panel.items.reject { |item| item.respond_to?(:table) && item.table }.map { |item| item_as_json(item) }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def item_as_json(item)
|
24
|
+
case item
|
25
|
+
when Quby::Compiler::Entities::Text
|
26
|
+
text_as_json(item)
|
27
|
+
when Quby::Compiler::Entities::Question
|
28
|
+
question_as_json(item)
|
29
|
+
when Quby::Compiler::Entities::Table
|
30
|
+
table_as_json(item)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def text_as_json(text)
|
35
|
+
{
|
36
|
+
type: 'text',
|
37
|
+
str: text.str,
|
38
|
+
html_content: text.html_content,
|
39
|
+
display_in: text.display_in,
|
40
|
+
col_span: text.col_span,
|
41
|
+
row_span: text.row_span,
|
42
|
+
raw_content: text.raw_content,
|
43
|
+
switch_cycle: text.switch_cycle,
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def table_as_json(table)
|
48
|
+
{
|
49
|
+
type: 'table',
|
50
|
+
columns: table.columns,
|
51
|
+
title: table.title,
|
52
|
+
description: table.description,
|
53
|
+
show_option_desc: table.show_option_desc,
|
54
|
+
items: table.items.map { |item| item_as_json(item) }
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def question_as_json(question)
|
59
|
+
base_options = {
|
60
|
+
type: 'question',
|
61
|
+
question_type: question.type,
|
62
|
+
key: question.key,
|
63
|
+
title: question.title,
|
64
|
+
context_free_title: question.context_free_title,
|
65
|
+
description: question.description,
|
66
|
+
presentation: question.presentation,
|
67
|
+
hidden: question.hidden,
|
68
|
+
depends_on: question.depends_on,
|
69
|
+
default_position: question.default_position,
|
70
|
+
col_span: question.col_span,
|
71
|
+
row_span: question.row_span,
|
72
|
+
validations: question.validations.map {|validation| validation_as_json(validation) },
|
73
|
+
raw_content: question.raw_content,
|
74
|
+
switch_cycle: question.switch_cycle,
|
75
|
+
|
76
|
+
sbg_key: question.sbg_key,
|
77
|
+
allow_duplicate_option_values: question.allow_duplicate_option_values,
|
78
|
+
allow_blank_titles: question.allow_blank_titles,
|
79
|
+
as: question.as,
|
80
|
+
display_modes: question.display_modes,
|
81
|
+
autocomplete: question.autocomplete,
|
82
|
+
show_values: question.show_values,
|
83
|
+
deselectable: question.deselectable,
|
84
|
+
disallow_bulk: question.disallow_bulk,
|
85
|
+
score_header: question.score_header,
|
86
|
+
sets_textvar: question.sets_textvar,
|
87
|
+
default_invisible: question.default_invisible,
|
88
|
+
question_group: question.question_group,
|
89
|
+
group_minimum_answered: question.group_minimum_answered,
|
90
|
+
group_maximum_answered: question.group_maximum_answered,
|
91
|
+
value_tooltip: question.input_data[:value_tooltip],
|
92
|
+
|
93
|
+
parent_option_key: question.parent_option_key,
|
94
|
+
}
|
95
|
+
|
96
|
+
case question
|
97
|
+
when Quby::Compiler::Entities::Questions::CheckboxQuestion
|
98
|
+
base_options.merge(
|
99
|
+
options: question.options.map { |option| option_as_json(option) },
|
100
|
+
check_all_option: question.check_all_option,
|
101
|
+
uncheck_all_option: question.uncheck_all_option,
|
102
|
+
maximum_checked_allowed: question.maximum_checked_allowed,
|
103
|
+
minimum_checked_required: question.minimum_checked_required
|
104
|
+
)
|
105
|
+
when Quby::Compiler::Entities::Questions::DateQuestion
|
106
|
+
base_options.merge(
|
107
|
+
components: question.components,
|
108
|
+
required_components: question.required_components,
|
109
|
+
year_key: question.year_key,
|
110
|
+
month_key: question.month_key,
|
111
|
+
day_key: question.day_key,
|
112
|
+
hour_key: question.hour_key,
|
113
|
+
minute_key: question.minute_key,
|
114
|
+
)
|
115
|
+
when Quby::Compiler::Entities::Questions::DeprecatedQuestion
|
116
|
+
base_options.merge(
|
117
|
+
options: question.options.map { |option| option_as_json(option) }
|
118
|
+
)
|
119
|
+
when Quby::Compiler::Entities::Questions::FloatQuestion
|
120
|
+
base_options.merge(
|
121
|
+
labels: question.labels,
|
122
|
+
unit: question.unit,
|
123
|
+
size: question.size,
|
124
|
+
)
|
125
|
+
when Quby::Compiler::Entities::Questions::IntegerQuestion
|
126
|
+
base_options.merge(
|
127
|
+
labels: question.labels,
|
128
|
+
unit: question.unit,
|
129
|
+
size: question.size,
|
130
|
+
)
|
131
|
+
when Quby::Compiler::Entities::Questions::RadioQuestion
|
132
|
+
base_options.merge(
|
133
|
+
options: question.options.map { |option| option_as_json(option) }
|
134
|
+
)
|
135
|
+
when Quby::Compiler::Entities::Questions::SelectQuestion
|
136
|
+
base_options.merge(
|
137
|
+
options: question.options.map { |option| option_as_json(option) }
|
138
|
+
)
|
139
|
+
when Quby::Compiler::Entities::Questions::StringQuestion
|
140
|
+
base_options.merge(
|
141
|
+
unit: question.unit,
|
142
|
+
size: question.size,
|
143
|
+
)
|
144
|
+
when Quby::Compiler::Entities::Questions::TextQuestion
|
145
|
+
base_options.merge(
|
146
|
+
lines: question.lines
|
147
|
+
)
|
148
|
+
else
|
149
|
+
raise "Unknown item type"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def validation_as_json(validation)
|
154
|
+
case validation[:type]
|
155
|
+
when :requires_answer
|
156
|
+
validation.slice(:type, :explanation)
|
157
|
+
when :answer_group_minimum, :answer_group_maximum
|
158
|
+
validation.slice(:type, :explanation, :group, :value)
|
159
|
+
when :valid_integer, :valid_float
|
160
|
+
validation.slice(:type, :explanation)
|
161
|
+
when :valid_date
|
162
|
+
validation.slice(:type, :explanation, :subtype).merge(value_type: validation[:value].class.to_s)
|
163
|
+
when :minimum, :maximum
|
164
|
+
validation.slice(:type, :explanation, :value, :subtype).merge(value_type: validation[:value].class.to_s)
|
165
|
+
when :too_many_checked
|
166
|
+
validation.slice(:type, :explanation, :uncheck_all_key)
|
167
|
+
when :minimum_checked_required
|
168
|
+
validation.slice(:type, :explanation, :minimum_checked_value)
|
169
|
+
when :maximum_checked_allowed
|
170
|
+
validation.slice(:type, :explanation, :maximum_checked_value)
|
171
|
+
when :regexp
|
172
|
+
validation.slice(:type, :explanation).merge(matcher: validation.fetch(:matcher).source)
|
173
|
+
when :not_all_checked
|
174
|
+
validation.slice(:type, :explanation, :check_all_key)
|
175
|
+
else
|
176
|
+
raise "Unknown validation type: #{validation.inspect}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def option_as_json(option)
|
181
|
+
{
|
182
|
+
key: option.key,
|
183
|
+
value: option.value,
|
184
|
+
description: option.description,
|
185
|
+
context_free_description: option.context_free_description,
|
186
|
+
questions: option.questions.map {|question| question_as_json(question)},
|
187
|
+
inner_title: option.inner_title,
|
188
|
+
hides_questions: option.hides_questions,
|
189
|
+
shows_questions: option.shows_questions,
|
190
|
+
hidden: option.hidden,
|
191
|
+
placeholder: option.placeholder,
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
def score_calculations
|
196
|
+
questionnaire.score_calculations.transform_values do |score_calculation|
|
197
|
+
{
|
198
|
+
key: score_calculation.key,
|
199
|
+
label: score_calculation.label,
|
200
|
+
sbg_key: score_calculation.sbg_key,
|
201
|
+
options: score_calculation.options,
|
202
|
+
sourcecode: score_calculation.sourcecode
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def score_schemas
|
208
|
+
questionnaire.score_schemas.transform_values do |schema|
|
209
|
+
{
|
210
|
+
key: schema.key,
|
211
|
+
label: schema.label,
|
212
|
+
subscore_schemas: schema.subscore_schemas.map do |subschema|
|
213
|
+
{
|
214
|
+
key: subschema.key,
|
215
|
+
label: subschema.label,
|
216
|
+
export_key: subschema.export_key,
|
217
|
+
only_for_export: subschema.only_for_export
|
218
|
+
}
|
219
|
+
end
|
220
|
+
}
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def charts
|
225
|
+
{
|
226
|
+
overview: questionnaire.charts.overview && {
|
227
|
+
subscore: questionnaire.charts.overview.subscore,
|
228
|
+
y_max: questionnaire.charts.overview.y_max,
|
229
|
+
},
|
230
|
+
others: questionnaire.charts.map do |chart|
|
231
|
+
case chart
|
232
|
+
when Quby::Compiler::Entities::Charting::LineChart
|
233
|
+
{
|
234
|
+
y_label: chart.y_label,
|
235
|
+
tonality: chart.tonality,
|
236
|
+
baseline: YAML.dump(chart.baseline),
|
237
|
+
clinically_relevant_change: chart.clinically_relevant_change,
|
238
|
+
}
|
239
|
+
when Quby::Compiler::Entities::Charting::OverviewChart
|
240
|
+
{
|
241
|
+
subscore: chart.subscore,
|
242
|
+
y_max: chart.y_max,
|
243
|
+
}
|
244
|
+
else
|
245
|
+
{}
|
246
|
+
end.merge(
|
247
|
+
key: chart.key,
|
248
|
+
type: chart.type,
|
249
|
+
title: chart.title,
|
250
|
+
plottables: chart.plottables,
|
251
|
+
y_categories: chart.y_categories,
|
252
|
+
y_range_categories: chart.y_range_categories,
|
253
|
+
chart_type: chart.chart_type,
|
254
|
+
y_range: range_as_json(chart.y_range),
|
255
|
+
tick_interval: chart.tick_interval,
|
256
|
+
plotbands: chart.plotbands,
|
257
|
+
plotlines: chart.plotlines
|
258
|
+
)
|
259
|
+
end
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
def flags
|
264
|
+
questionnaire.flags.transform_values do |flag|
|
265
|
+
{
|
266
|
+
key: flag.key,
|
267
|
+
description_true: flag.description_true,
|
268
|
+
description_false: flag.description_false,
|
269
|
+
description: flag.description,
|
270
|
+
internal: flag.internal,
|
271
|
+
trigger_on: flag.trigger_on,
|
272
|
+
shows_questions: flag.shows_questions,
|
273
|
+
hides_questions: flag.hides_questions,
|
274
|
+
depends_on: flag.depends_on,
|
275
|
+
default_in_interface: flag.default_in_interface,
|
276
|
+
}
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def textvars
|
281
|
+
questionnaire.textvars.transform_values do |textvar|
|
282
|
+
{
|
283
|
+
key: textvar.key,
|
284
|
+
description: textvar.description,
|
285
|
+
default: textvar.default,
|
286
|
+
depends_on_flag: textvar.depends_on_flag,
|
287
|
+
}
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def lookup_tables
|
292
|
+
# TODO: Figure out something better. For now, no interest in walking a tree and converting Range objects to something JSON-able.
|
293
|
+
YAML.dump(questionnaire.lookup_tables)
|
294
|
+
end
|
295
|
+
|
296
|
+
def outcome_tables
|
297
|
+
# TODO DOes not seem to be used in definitions?
|
298
|
+
questionnaire.outcome_tables.map do |outcome_table|
|
299
|
+
{
|
300
|
+
key: outcome_table.key,
|
301
|
+
score_keys: outcome_table.score_keys,
|
302
|
+
subscore_keys: outcome_table.subscore_keys,
|
303
|
+
name: outcome_table.name,
|
304
|
+
default_collapsed: outcome_table.default_collapsed,
|
305
|
+
}
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def as_json(options = {})
|
310
|
+
{
|
311
|
+
key: questionnaire.key,
|
312
|
+
title: questionnaire.title,
|
313
|
+
description: questionnaire.description,
|
314
|
+
outcome_description: questionnaire.outcome_description,
|
315
|
+
short_description: questionnaire.short_description,
|
316
|
+
abortable: questionnaire.abortable,
|
317
|
+
enable_previous_questionnaire_button: questionnaire.enable_previous_questionnaire_button,
|
318
|
+
default_answer_value: questionnaire.default_answer_value,
|
319
|
+
leave_page_alert: questionnaire.leave_page_alert,
|
320
|
+
allow_hotkeys: questionnaire.allow_hotkeys,
|
321
|
+
license: questionnaire.license,
|
322
|
+
licensor: questionnaire.licensor,
|
323
|
+
language: questionnaire.language,
|
324
|
+
renderer_version: questionnaire.renderer_version,
|
325
|
+
last_update: questionnaire.last_update,
|
326
|
+
last_author: questionnaire.last_author,
|
327
|
+
extra_css: questionnaire.extra_css,
|
328
|
+
allow_switch_to_bulk: questionnaire.allow_switch_to_bulk,
|
329
|
+
|
330
|
+
panels: panels,
|
331
|
+
score_calculations: score_calculations,
|
332
|
+
score_schemas: score_schemas,
|
333
|
+
flags: flags,
|
334
|
+
textvars: textvars,
|
335
|
+
lookup_tables: lookup_tables,
|
336
|
+
|
337
|
+
# belong to roqua domain, but for now they're here too:
|
338
|
+
roqua_keys: questionnaire.roqua_keys,
|
339
|
+
sbg_key: questionnaire.sbg_key,
|
340
|
+
sbg_domains: questionnaire.sbg_domains,
|
341
|
+
outcome_regeneration_requested_at: questionnaire.outcome_regeneration_requested_at,
|
342
|
+
deactivate_answers_requested_at: questionnaire.deactivate_answers_requested_at,
|
343
|
+
respondent_types: questionnaire.respondent_types,
|
344
|
+
tags: questionnaire.tags.to_h.keys,
|
345
|
+
charts: charts,
|
346
|
+
outcome_tables: outcome_tables,
|
347
|
+
}
|
348
|
+
end
|
349
|
+
|
350
|
+
def range_as_json(range)
|
351
|
+
return unless range
|
352
|
+
|
353
|
+
{
|
354
|
+
begin: range.begin,
|
355
|
+
end: range.end,
|
356
|
+
exclude_end: range.exclude_end?
|
357
|
+
}
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|