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,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class CheckboxQuestion < Question
|
8
|
+
# checkbox option that checks all other options on check
|
9
|
+
attr_accessor :check_all_option
|
10
|
+
|
11
|
+
# checkbox option that unchecks all other options on check
|
12
|
+
attr_accessor :uncheck_all_option
|
13
|
+
|
14
|
+
# checkbox option that allows to select a maximum amount of checkboxes
|
15
|
+
attr_accessor :maximum_checked_allowed
|
16
|
+
|
17
|
+
# checkbox option that forces to select a minimum amount of checkboxes
|
18
|
+
attr_accessor :minimum_checked_required
|
19
|
+
|
20
|
+
def initialize(key, options = {})
|
21
|
+
super
|
22
|
+
|
23
|
+
@check_all_option = options[:check_all_option]
|
24
|
+
@uncheck_all_option = options[:uncheck_all_option]
|
25
|
+
@maximum_checked_allowed = options[:maximum_checked_allowed]
|
26
|
+
@minimum_checked_required = options[:minimum_checked_required]
|
27
|
+
|
28
|
+
if @check_all_option
|
29
|
+
@validations << {type: :not_all_checked,
|
30
|
+
check_all_key: @check_all_option,
|
31
|
+
explanation: options[:error_explanation]}
|
32
|
+
end
|
33
|
+
|
34
|
+
if @uncheck_all_option
|
35
|
+
@validations << {type: :too_many_checked,
|
36
|
+
uncheck_all_key: @uncheck_all_option,
|
37
|
+
explanation: options[:error_explanation]}
|
38
|
+
end
|
39
|
+
|
40
|
+
if @maximum_checked_allowed
|
41
|
+
@validations << {type: :maximum_checked_allowed,
|
42
|
+
maximum_checked_value: @maximum_checked_allowed,
|
43
|
+
explanation: options[:error_explanation]}
|
44
|
+
end
|
45
|
+
|
46
|
+
if @minimum_checked_required
|
47
|
+
@validations << {type: :minimum_checked_required,
|
48
|
+
minimum_checked_value: @minimum_checked_required,
|
49
|
+
explanation: options[:error_explanation]}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def variable_descriptions
|
54
|
+
options.each_with_object(key => context_free_title) do |option, hash|
|
55
|
+
next if option.input_key.blank?
|
56
|
+
hash[option.input_key] = "#{context_free_title} - #{option.description}"
|
57
|
+
end.with_indifferent_access
|
58
|
+
end
|
59
|
+
|
60
|
+
def claimed_keys
|
61
|
+
[key]
|
62
|
+
end
|
63
|
+
|
64
|
+
def answer_keys
|
65
|
+
# Some options don't have a key (inner_title), they are stripped.
|
66
|
+
options.map { |opt| opt.input_key }.compact
|
67
|
+
end
|
68
|
+
|
69
|
+
def as_json(options = {})
|
70
|
+
super.merge(options: @options.as_json)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_codebook(questionnaire, opts = {})
|
74
|
+
options.map do |option|
|
75
|
+
option.to_codebook(questionnaire, opts)
|
76
|
+
end.compact.join("\n\n")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class DateQuestion < Question
|
8
|
+
POSSIBLE_COMPONENTS = %i( day month year hour minute )
|
9
|
+
COMPONENT_KEYS = Hash[POSSIBLE_COMPONENTS.zip %w( dd mm yyyy hh ii)]
|
10
|
+
COMPONENT_PLACEHOLDERS = Hash[POSSIBLE_COMPONENTS.zip %w( DD MM YYYY hh mm)]
|
11
|
+
DEFAULT_COMPONENTS = %i( day month year )
|
12
|
+
|
13
|
+
# For optionally giving year, month and day fields of dates their own keys
|
14
|
+
POSSIBLE_COMPONENTS.each do |component|
|
15
|
+
attr_accessor "#{component}_key".to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
# @!attribute [r] components
|
19
|
+
# @return [Array<Symbol>] date parts to show
|
20
|
+
# @!attribute [r] required_components
|
21
|
+
# @return [Array<Symbol>] date parts that are required if the question is required or partly filled out.
|
22
|
+
attr_accessor :components, :required_components, :optional_components
|
23
|
+
|
24
|
+
def initialize(key, options = {})
|
25
|
+
super
|
26
|
+
|
27
|
+
@components = options[:components] || DEFAULT_COMPONENTS
|
28
|
+
@required_components = options[:required_components] || @components
|
29
|
+
@optional_components = @components - @required_components
|
30
|
+
|
31
|
+
components.each do |component|
|
32
|
+
component_key = options[:"#{component}_key"] || "#{key}_#{COMPONENT_KEYS[component]}"
|
33
|
+
instance_variable_set("@#{component}_key", component_key.to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
add_date_validation(options[:error_explanation])
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_date_validation(explanation)
|
40
|
+
@validations << {type: :valid_date,
|
41
|
+
subtype: :"valid_date_#{components.sort.join('_')}",
|
42
|
+
explanation: explanation}
|
43
|
+
end
|
44
|
+
|
45
|
+
def claimed_keys
|
46
|
+
[key] + answer_keys
|
47
|
+
end
|
48
|
+
|
49
|
+
def answer_keys
|
50
|
+
components.map do |component|
|
51
|
+
send("#{component}_key").to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def variable_descriptions
|
56
|
+
components.each_with_object(key => context_free_title) do |component, hash|
|
57
|
+
key = send("#{component}_key")
|
58
|
+
hash[key] = "#{context_free_title} (#{I18n.t component})"
|
59
|
+
end.with_indifferent_access
|
60
|
+
end
|
61
|
+
|
62
|
+
def as_json(options = {})
|
63
|
+
component_keys = components.each_with_object({}) do |component, hash|
|
64
|
+
hash["#{component}Key"] = send("#{component}_key")
|
65
|
+
end
|
66
|
+
super.merge(components: components).merge(component_keys)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_codebook(questionnaire, opts = {})
|
70
|
+
output = []
|
71
|
+
components.each do |component|
|
72
|
+
output << "#{codebook_key(send("#{component}_key"), questionnaire, opts)} " \
|
73
|
+
"#{type}_#{component} #{codebook_output_range}"
|
74
|
+
output << "\"#{title}\"" unless title.blank?
|
75
|
+
output << options.map(&:to_codebook).join("\n") unless options.blank?
|
76
|
+
output << ""
|
77
|
+
end
|
78
|
+
output.join("\n")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class DeprecatedQuestion < Question
|
8
|
+
def hidden?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json(options = {})
|
13
|
+
super.merge(options: @options.as_json)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class RadioQuestion < Question
|
8
|
+
def as_json(options = {})
|
9
|
+
super.merge(options: @options.as_json)
|
10
|
+
end
|
11
|
+
|
12
|
+
def codebook_output_type
|
13
|
+
:radio
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class SelectQuestion < Question
|
8
|
+
def as_json(options = {})
|
9
|
+
super.merge(options: @options.as_json)
|
10
|
+
end
|
11
|
+
|
12
|
+
def codebook_output_type
|
13
|
+
:radio
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class StringQuestion < Question
|
8
|
+
def as_json(options = {})
|
9
|
+
super.merge(autocomplete: @autocomplete, size: @size || 30)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
module Questions
|
7
|
+
class TextQuestion < Question
|
8
|
+
def as_json(options = {})
|
9
|
+
super.merge(autocomplete: @autocomplete, lines: lines, cols: cols)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Quby
|
4
|
+
module Compiler
|
5
|
+
module Entities
|
6
|
+
class ScoreCalculation
|
7
|
+
attr_accessor :key, :label, :sbg_key, :options, :calculation
|
8
|
+
|
9
|
+
def initialize(key, options, &block)
|
10
|
+
@key = key
|
11
|
+
@label = options[:label]
|
12
|
+
@sbg_key = options[:sbg_key]
|
13
|
+
@options = options
|
14
|
+
@calculation = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def score
|
18
|
+
@options[:score]
|
19
|
+
end
|
20
|
+
|
21
|
+
def completion
|
22
|
+
@options[:completion]
|
23
|
+
end
|
24
|
+
|
25
|
+
def action
|
26
|
+
@options[:action]
|
27
|
+
end
|
28
|
+
|
29
|
+
def sourcecode
|
30
|
+
options[:ruby_string] || calculation&.to_proc&.source
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Quby
|
2
|
+
module Compiler
|
3
|
+
module Entities
|
4
|
+
# ScoreSchema instances describe score definitions.
|
5
|
+
#
|
6
|
+
# Score definitions are blocks of ruby code that return a hash of score results based on a questionnaire
|
7
|
+
# response (answer). These schemas describe the purpose and form of the scores. Each key-value pair of the result
|
8
|
+
# hash is called a subscore.
|
9
|
+
# The :value subscore is treated as the main score result. Subscores are usually identified by their 'export_key'.
|
10
|
+
# The score value's export_key is usually set to a shortened version of the main score key.
|
11
|
+
class ScoreSchema < Dry::Struct
|
12
|
+
# The key of the corresponding score in the questionnaire definition
|
13
|
+
attribute :key, Types::Symbol
|
14
|
+
# A label describing the general purpose of the score
|
15
|
+
attribute :label, Types::String
|
16
|
+
# An array of SubscoreSchemas describing each key that can be returned in the result hash of a score.
|
17
|
+
attribute :subscore_schemas, Types::Array(Entities::SubscoreSchema)
|
18
|
+
|
19
|
+
def export_key_labels
|
20
|
+
subscore_schemas.map { |schema| [schema.export_key, schema.label] }.to_h.with_indifferent_access
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Quby
|
2
|
+
module Compiler
|
3
|
+
module Entities
|
4
|
+
# SubscoreSchema instances describe each key that could be returned in a score result hash
|
5
|
+
class SubscoreSchema < Dry::Struct
|
6
|
+
# The key this subscore has in the hash returned by the score
|
7
|
+
attribute :key, Types::Symbol
|
8
|
+
# The description of this subscore in the context of the score, like 'Mean', 'T-Score' or 'Interpretation'
|
9
|
+
attribute :label, Types::String
|
10
|
+
# The shortened key that will used as the field/column name for csv and oru exports,
|
11
|
+
# excluding the questionnaire key part
|
12
|
+
attribute :export_key, Types::Symbol
|
13
|
+
# [Optional] Whether this score will only be exported through oru/api/data exports, but not shown in interfaces
|
14
|
+
attribute :only_for_export?, Types::Bool
|
15
|
+
# [Optional] The key of the variable definition used to calculate the subscore result.
|
16
|
+
attribute :calculation_key?, Types::Symbol
|
17
|
+
# [Optional argument] The name of the outcome table where this subscore should be shown. Used for cases where scores
|
18
|
+
# differ in subscores too much to be shown as one table. By default, all scores end up in the `:main` table.
|
19
|
+
attribute :outcome_table, Types::Symbol.default(:main).meta(omittable: true)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities/item'
|
4
|
+
|
5
|
+
module Quby
|
6
|
+
module Compiler
|
7
|
+
module Entities
|
8
|
+
class Table < Item
|
9
|
+
attr_accessor :columns
|
10
|
+
attr_accessor :items
|
11
|
+
|
12
|
+
attr_accessor :title
|
13
|
+
attr_accessor :description
|
14
|
+
|
15
|
+
# Whether the question options in this table should show their descriptions
|
16
|
+
attr_accessor :show_option_desc
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@columns = options[:columns]
|
20
|
+
@title = options[:title]
|
21
|
+
@description = options[:description]
|
22
|
+
@show_option_desc = options[:show_option_desc] || false
|
23
|
+
@items = []
|
24
|
+
end
|
25
|
+
|
26
|
+
def item_table
|
27
|
+
rows
|
28
|
+
@item_table
|
29
|
+
end
|
30
|
+
|
31
|
+
# FIXME: code to be ashamed of
|
32
|
+
# rubocop:disable CyclomaticComplexity, Metrics/MethodLength
|
33
|
+
def rows
|
34
|
+
return @rows if @rows
|
35
|
+
@item_table = [[]]
|
36
|
+
@rows = [[[]]]
|
37
|
+
filled_columns = 0
|
38
|
+
filled_rows = 0
|
39
|
+
row_items = 0
|
40
|
+
|
41
|
+
skips = []
|
42
|
+
items.each do |item|
|
43
|
+
|
44
|
+
skips.delete_if do |row_span, skip_cols_at, skip_cols_length|
|
45
|
+
next true if row_span == 1
|
46
|
+
if skip_cols_length > 0 and filled_columns == skip_cols_at
|
47
|
+
filled_columns += skip_cols_length
|
48
|
+
skip_cols_length.times do
|
49
|
+
@item_table[filled_rows] << nil
|
50
|
+
@rows[filled_rows][row_items] << nil
|
51
|
+
end
|
52
|
+
if filled_columns >= columns and item != items.last
|
53
|
+
filled_rows += 1
|
54
|
+
filled_columns = 0
|
55
|
+
row_items = 0
|
56
|
+
@rows << [[]]
|
57
|
+
@item_table << []
|
58
|
+
skips.map! do |new_row_span, new_skip_cols_at, new_skip_cols_length|
|
59
|
+
[new_row_span - 1, new_skip_cols_at, new_skip_cols_length]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
if filled_columns != 0 and item != items.last
|
63
|
+
row_items += 1
|
64
|
+
@rows[filled_rows] << []
|
65
|
+
end
|
66
|
+
next row_span - 1 == 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
if item.is_a?(Text) || !([:check_box, :radio, :scale].include? item.type)
|
71
|
+
if item.row_span > 1
|
72
|
+
skips << [item.row_span, filled_columns, item.col_span]
|
73
|
+
end
|
74
|
+
|
75
|
+
@item_table[filled_rows] << item
|
76
|
+
@rows[filled_rows][row_items] << item
|
77
|
+
filled_columns += item.col_span
|
78
|
+
|
79
|
+
if filled_columns >= columns and item != items.last
|
80
|
+
filled_rows += 1
|
81
|
+
filled_columns = 0
|
82
|
+
row_items = 0
|
83
|
+
@rows << [[]]
|
84
|
+
@item_table << []
|
85
|
+
end
|
86
|
+
if filled_columns != 0 and item != items.last
|
87
|
+
row_items += 1
|
88
|
+
@rows[filled_rows] << []
|
89
|
+
end
|
90
|
+
|
91
|
+
else # is :check_box, :radio or :scale question
|
92
|
+
if item.row_span > 1
|
93
|
+
skips << [item.row_span, filled_columns, item.options.length]
|
94
|
+
end
|
95
|
+
|
96
|
+
@item_table[filled_rows] << item
|
97
|
+
if item.options.length <= columns # multiple questions on one row
|
98
|
+
item.options.each do |opt|
|
99
|
+
@rows[filled_rows][row_items] << opt
|
100
|
+
filled_columns += 1
|
101
|
+
|
102
|
+
if filled_columns >= columns and item != items.last
|
103
|
+
filled_rows += 1
|
104
|
+
filled_columns = 0
|
105
|
+
row_items = 0
|
106
|
+
@rows << [[]]
|
107
|
+
@item_table << []
|
108
|
+
end
|
109
|
+
end
|
110
|
+
if filled_columns != 0 and item != items.last
|
111
|
+
row_items += 1
|
112
|
+
@rows[filled_rows] << []
|
113
|
+
end
|
114
|
+
else # one question's options split over multiple rows, ordered row wise
|
115
|
+
opt_len = item.options.length
|
116
|
+
col_len = (opt_len / columns.to_f).ceil
|
117
|
+
(0...col_len).each do |j|
|
118
|
+
(0...columns).each do |i|
|
119
|
+
break if j + i * col_len >= opt_len
|
120
|
+
@rows[filled_rows][row_items] << item.options[j + i * col_len]
|
121
|
+
filled_columns += 1
|
122
|
+
if filled_columns == columns
|
123
|
+
filled_rows += 1
|
124
|
+
filled_columns = 0
|
125
|
+
@rows << [[]]
|
126
|
+
@item_table << [item]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
@rows
|
134
|
+
end
|
135
|
+
# rubocop:enable CyclomaticComplexity, Metrics/MethodLength
|
136
|
+
|
137
|
+
def type
|
138
|
+
"table"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|