quby-compiler 0.5.15 → 0.5.16
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 +4 -4
- data/CHANGELOG.md +10 -1
- data/lib/quby/compiler/dsl/questionnaire_builder.rb +5 -0
- data/lib/quby/compiler/dsl/questions/base.rb +1 -0
- data/lib/quby/compiler/dsl/questions/integer_question_builder.rb +10 -0
- data/lib/quby/compiler/dsl/sexp_variable_builder.rb +57 -0
- data/lib/quby/compiler/entities/question.rb +4 -0
- data/lib/quby/compiler/entities/question_option.rb +1 -0
- data/lib/quby/compiler/entities/questionnaire.rb +6 -0
- data/lib/quby/compiler/entities/questions/concerns/split_to_units.rb +58 -0
- data/lib/quby/compiler/entities/questions/integer_question.rb +2 -0
- data/lib/quby/compiler/entities/sexp_variable.rb +50 -0
- data/lib/quby/compiler/entities/sexp_variables.rb +41 -0
- data/lib/quby/compiler/outputs/quby_frontend_v2_serializer.rb +19 -4
- data/lib/quby/compiler/services/definition_validator.rb +7 -0
- data/lib/quby/compiler/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fbd126d732f2ab8201f325d09041cb564138354150fbedc83f3f7e9f68a755f
|
4
|
+
data.tar.gz: df750a8516c8596596cf11a03f3ce758bdb745de607068d4b8b753ae62a95e17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddd7e5767209991dd94f35b086294903cc2616d76104cbdaf776181e661e0cefe501393033abc41674aed88f14a7849a3f275c81330c72c68384dd42cf9052a3
|
7
|
+
data.tar.gz: 1ba99e0f79496a7d64d95952739bd5fcc1f0ef6f1927c2cd5ce211c6bddd134dd37f617400dd3201344d35a58503e7b14503856c274404f41d6e90391b07ace3
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
+
# 0.5.16
|
2
|
+
|
3
|
+
* add integer as split_to_units option, with units and conversions as attributes
|
4
|
+
* add sexp_variables to do calculations using s expressions, for now in quby2, but later in the backend.
|
5
|
+
* test that select/radio options are always numeric
|
6
|
+
* quby2.json
|
7
|
+
* add split_to_units question, that saves a integer.
|
8
|
+
* Calculate sexpr variables and allow strings to interpolate them {{calculation.some_var}}
|
9
|
+
|
1
10
|
# 0.5.15
|
2
11
|
|
3
12
|
* Add context_description to questions, to have a text item that is hidden together with the question.
|
4
|
-
*
|
13
|
+
* quby2.json
|
5
14
|
* Moved quby2 serialization to the serializer, compact everything, no markdown for v2 title/descriptions.
|
6
15
|
* Sanitize Quby2 html in v2.
|
7
16
|
* Add contextDescription to questions.
|
@@ -8,6 +8,7 @@ require 'quby/compiler/dsl/charting/line_chart_builder'
|
|
8
8
|
require 'quby/compiler/dsl/charting/radar_chart_builder'
|
9
9
|
require 'quby/compiler/dsl/charting/bar_chart_builder'
|
10
10
|
require 'quby/compiler/dsl/charting/overview_chart_builder'
|
11
|
+
require 'quby/compiler/dsl/sexp_variable_builder'
|
11
12
|
|
12
13
|
require_relative 'standardized_panel_generators'
|
13
14
|
|
@@ -200,6 +201,10 @@ module Quby
|
|
200
201
|
end
|
201
202
|
end
|
202
203
|
|
204
|
+
def sexp_variable(key, &block)
|
205
|
+
@questionnaire.add_sexp_variable(key, SexpVariableBuilder.new(key, &block).build)
|
206
|
+
end
|
207
|
+
|
203
208
|
# variable :totaal do
|
204
209
|
# # Plain old Ruby code here, executed in the scope of the answer
|
205
210
|
# # variables are private to the score calculation
|
@@ -14,6 +14,16 @@ module Quby
|
|
14
14
|
super
|
15
15
|
@question = Entities::Questions::IntegerQuestion.new(key, options)
|
16
16
|
end
|
17
|
+
|
18
|
+
# as split_to_units
|
19
|
+
def units(*values)
|
20
|
+
@question.units = values
|
21
|
+
end
|
22
|
+
|
23
|
+
# as split_to_units
|
24
|
+
def conversions(value)
|
25
|
+
@question.conversions = value
|
26
|
+
end
|
17
27
|
end
|
18
28
|
end
|
19
29
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'quby/compiler/entities/sexp_variable'
|
4
|
+
module Quby::Compiler
|
5
|
+
module DSL
|
6
|
+
# sexp_variable :myvar do
|
7
|
+
# sum(number_values(:v_1, :v_2))
|
8
|
+
# end
|
9
|
+
class ::SexpVariableBuilder
|
10
|
+
attr_reader :calculation, :key
|
11
|
+
|
12
|
+
def initialize(key, &block)
|
13
|
+
@key = key
|
14
|
+
@calculation = instance_eval(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def string_value(key)
|
18
|
+
Entities::SexpVariables::StringValue.new(op: :string_value, key: key)
|
19
|
+
end
|
20
|
+
|
21
|
+
def number_value(key)
|
22
|
+
Entities::SexpVariables::NumberValue.new(op: :number_value, key: key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def number_values(*keys)
|
26
|
+
keys.map { |key| number_value(key) }
|
27
|
+
end
|
28
|
+
|
29
|
+
%i[sum subtract multiply divide].each do |op|
|
30
|
+
define_method(op) do |*values|
|
31
|
+
Entities::SexpVariables::NumberReducer.new(op:, values: wrap_and_flatten(values))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def round(value)
|
36
|
+
Entities::SexpVariables::NumberMethod.new(op: :round, value: value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def build
|
40
|
+
Entities::SexpVariable.new(key:, calculation:)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def wrap_and_flatten(values)
|
46
|
+
values.flat_map { |value|
|
47
|
+
case value
|
48
|
+
when Numeric
|
49
|
+
Entities::SexpVariables::Number.new(op: :number, value: value)
|
50
|
+
else
|
51
|
+
value
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -208,6 +208,10 @@ module Quby
|
|
208
208
|
end
|
209
209
|
# rubocop:enable CyclomaticComplexity, Metrics/MethodLength
|
210
210
|
|
211
|
+
# called after DSL has instance_evalled everything within the question block.
|
212
|
+
def after_build
|
213
|
+
end
|
214
|
+
|
211
215
|
def context_free_title_or_title
|
212
216
|
context_free_title || title
|
213
217
|
end
|
@@ -11,6 +11,7 @@ module Quby
|
|
11
11
|
|
12
12
|
attr_reader :key
|
13
13
|
attr_reader :value
|
14
|
+
validates :value, numericality: {allow_nil: true} # nil for checkbox questions.
|
14
15
|
attr_reader :description, :context_free_description
|
15
16
|
attr_reader :questions
|
16
17
|
# for scale/radio/checbox questions, piece of of html that is rendered between the options
|
@@ -53,6 +53,7 @@ module Quby
|
|
53
53
|
@outcome_tables = []
|
54
54
|
@check_score_keys_consistency = true
|
55
55
|
@lookup_tables = {}
|
56
|
+
@sexp_variables = {}
|
56
57
|
@versions = []
|
57
58
|
@seeds_patch = {}
|
58
59
|
@anonymous_conditions = Entities::AnonymousConditions.new
|
@@ -102,6 +103,7 @@ module Quby
|
|
102
103
|
|
103
104
|
attr_accessor :outcome_tables
|
104
105
|
attr_accessor :score_schemas
|
106
|
+
attr_accessor :sexp_variables
|
105
107
|
attr_accessor :lookup_tables
|
106
108
|
attr_accessor :anonymous_conditions
|
107
109
|
|
@@ -362,6 +364,10 @@ module Quby
|
|
362
364
|
end
|
363
365
|
end
|
364
366
|
|
367
|
+
def add_sexp_variable(key, sexp_variable)
|
368
|
+
sexp_variables[key] = sexp_variable
|
369
|
+
end
|
370
|
+
|
365
371
|
def add_outcome_table(outcome_table_options)
|
366
372
|
outcome_tables << OutcomeTable.new(**outcome_table_options, questionnaire: self)
|
367
373
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Quby::Compiler::Entities::Questions::Concerns
|
2
|
+
module SplitToUnits
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
DEFAULT_SPLIT_TO_UNIT_CONVERSIONS = {
|
5
|
+
minutes: {
|
6
|
+
hours: 60,
|
7
|
+
days: 1440,
|
8
|
+
weeks: 10080
|
9
|
+
},
|
10
|
+
seconds: {
|
11
|
+
minutes: 60,
|
12
|
+
hours: 3600,
|
13
|
+
days: 86400
|
14
|
+
},
|
15
|
+
m: {
|
16
|
+
km: 1000
|
17
|
+
},
|
18
|
+
cm: {
|
19
|
+
m: 100
|
20
|
+
},
|
21
|
+
mm: {
|
22
|
+
cm: 10,
|
23
|
+
m: 1000
|
24
|
+
},
|
25
|
+
g: {
|
26
|
+
kg: 1000
|
27
|
+
}
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
included do
|
31
|
+
attr_accessor :units, :conversions
|
32
|
+
|
33
|
+
validates :units, :conversions, presence: true, if: -> { as == :split_to_units }
|
34
|
+
validate :validate_split_to_units, if: -> { as == :split_to_units }
|
35
|
+
end
|
36
|
+
|
37
|
+
def after_build
|
38
|
+
super
|
39
|
+
return unless as == :split_to_units
|
40
|
+
@unit = units&.last
|
41
|
+
(@conversions ||= {}).reverse_merge!(default_split_to_units_conversions)
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_split_to_units_conversions
|
45
|
+
(DEFAULT_SPLIT_TO_UNIT_CONVERSIONS[unit] || {}).slice(*units)
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_split_to_units
|
49
|
+
return unless units.present? && conversions.present?
|
50
|
+
|
51
|
+
(units - [unit]).each do |unit_to_convert|
|
52
|
+
if conversions[unit_to_convert].nil?
|
53
|
+
errors.add(:conversions, "should contain a conversion for unit #{unit_to_convert}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'concerns/slider'
|
4
|
+
require_relative 'concerns/split_to_units'
|
4
5
|
|
5
6
|
module Quby
|
6
7
|
module Compiler
|
@@ -8,6 +9,7 @@ module Quby
|
|
8
9
|
module Questions
|
9
10
|
class IntegerQuestion < Question
|
10
11
|
include Concerns::Slider
|
12
|
+
include Concerns::SplitToUnits
|
11
13
|
|
12
14
|
def size
|
13
15
|
@size || 30
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'quby/compiler/entities/sexp_variables'
|
2
|
+
|
3
|
+
module Quby::Compiler::Entities
|
4
|
+
class SexpVariable
|
5
|
+
attr_reader :calculation, :key
|
6
|
+
|
7
|
+
def initialize(key:, calculation:)
|
8
|
+
@key = key
|
9
|
+
@calculation = calculation
|
10
|
+
end
|
11
|
+
|
12
|
+
# Called by DefinitionValidator.
|
13
|
+
def validate(questionnaire)
|
14
|
+
case calculation
|
15
|
+
when SexpVariables::NumberValue
|
16
|
+
validate_question_exist(questionnaire, calculation)
|
17
|
+
validate_value_is_number(questionnaire, calculation)
|
18
|
+
when SexpVariables::StringValue
|
19
|
+
validate_question_exist(questionnaire, calculation)
|
20
|
+
validate_value_is_string(questionnaire, calculation)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_question_exist(questionnaire, sexp)
|
25
|
+
return if questionnaire.question_hash.key?(sexp.key)
|
26
|
+
|
27
|
+
fail "sexp_variable #{key} uses nonexistent question #{sexp.key}."
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate_value_is_number(questionnaire, sexp)
|
31
|
+
question = questionnaire.question_hash[sexp.key]
|
32
|
+
case question
|
33
|
+
when Questions::IntegerQuestion, Questions::FloatQuestion, Questions::SelectQuestion, Questions::RadioQuestion
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
fail "sexp_variable #{key} uses non-numeric question #{sexp.key} for number_value."
|
38
|
+
end
|
39
|
+
|
40
|
+
def validate_value_is_string(questionnaire, sexp)
|
41
|
+
question = questionnaire.question_hash[sexp.key]
|
42
|
+
case question
|
43
|
+
when Questions::StringQuestion, Questions::TextareaQuestion
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
fail "sexp_variable #{key} uses non-string question #{sexp.key} for string_value."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Quby::Compiler::Entities
|
2
|
+
module SexpVariables
|
3
|
+
class Base < Dry::Struct
|
4
|
+
end
|
5
|
+
|
6
|
+
# recursive dependencies, we so we define the classes first.
|
7
|
+
class NumberValue < Base; end
|
8
|
+
class Number < Base; end
|
9
|
+
class NumberReducer < Base; end
|
10
|
+
class NumberMethod < Base; end
|
11
|
+
|
12
|
+
NumericType = Number | NumberReducer | NumberMethod | NumberValue
|
13
|
+
|
14
|
+
class NumberValue
|
15
|
+
attribute :op, Quby::Types::Symbol.enum(:number_value)
|
16
|
+
attribute :key, Quby::Types::Symbol
|
17
|
+
end
|
18
|
+
|
19
|
+
class StringValue < Base
|
20
|
+
attribute :op, Quby::Types::Symbol.enum(:string_value)
|
21
|
+
attribute :key, Quby::Types::Symbol
|
22
|
+
end
|
23
|
+
|
24
|
+
class Number
|
25
|
+
attribute :op, Quby::Types::Symbol.enum(:value)
|
26
|
+
attribute :value, Quby::Types::Integer | Quby::Types::Float
|
27
|
+
end
|
28
|
+
|
29
|
+
# returning a Number
|
30
|
+
class NumberReducer
|
31
|
+
attribute :op, Quby::Types::Symbol.enum(:sum, :subtract, :multiply, :divide)
|
32
|
+
attribute :values, Quby::Types::Array.of(NumericType)
|
33
|
+
end
|
34
|
+
|
35
|
+
# returning a Number
|
36
|
+
class NumberMethod
|
37
|
+
attribute :op, Quby::Types::Symbol.enum(:round)
|
38
|
+
attribute :value, NumericType
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -23,7 +23,8 @@ module Quby
|
|
23
23
|
questions: questions,
|
24
24
|
textvars: textvars,
|
25
25
|
validations: validations,
|
26
|
-
visibilityRules: visibility_rules.as_json
|
26
|
+
visibilityRules: visibility_rules.as_json,
|
27
|
+
sexpVariables: sexp_variables,
|
27
28
|
}
|
28
29
|
end
|
29
30
|
|
@@ -83,10 +84,17 @@ module Quby
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def float_question(question)
|
86
|
-
|
87
|
+
number_question(question)
|
87
88
|
end
|
88
89
|
|
89
90
|
def integer_question(question)
|
91
|
+
{
|
92
|
+
**number_question(question),
|
93
|
+
**split_to_units_question(question),
|
94
|
+
}.compact
|
95
|
+
end
|
96
|
+
|
97
|
+
def number_question(question)
|
90
98
|
{
|
91
99
|
**base_question(question),
|
92
100
|
**slider_question(question),
|
@@ -94,8 +102,8 @@ module Quby
|
|
94
102
|
maximum: question.maximum,
|
95
103
|
size: size(question),
|
96
104
|
unit: question.as != :slider && question.unit,
|
97
|
-
|
98
|
-
end
|
105
|
+
}.compact
|
106
|
+
end
|
99
107
|
|
100
108
|
def radio_question(question)
|
101
109
|
{
|
@@ -165,6 +173,13 @@ module Quby
|
|
165
173
|
}
|
166
174
|
end
|
167
175
|
|
176
|
+
def split_to_units_question(question)
|
177
|
+
{
|
178
|
+
units: question.units,
|
179
|
+
conversions: question.conversions,
|
180
|
+
}
|
181
|
+
end
|
182
|
+
|
168
183
|
def question_type(question)
|
169
184
|
{
|
170
185
|
date: 'date_parts',
|
@@ -26,6 +26,7 @@ module Quby
|
|
26
26
|
validate_outcome_tables(questionnaire)
|
27
27
|
validate_markdown_fields(questionnaire) if questionnaire.validate_html
|
28
28
|
validate_raw_content_items(questionnaire) if questionnaire.validate_html
|
29
|
+
validate_sexp_variables(questionnaire)
|
29
30
|
# Some compilation errors are Exceptions (pure syntax errors) and some StandardErrors (NameErrors)
|
30
31
|
rescue Exception => exception # rubocop:disable Lint/RescueException
|
31
32
|
definition.errors.add(:sourcecode, message: "Questionnaire error: #{definition.key}\n" \
|
@@ -364,6 +365,12 @@ scores_schema tables to the resulting seed."
|
|
364
365
|
fail "#{key || html} contains invalid html: #{fragment.errors.map(&:to_s).join(', ')}."
|
365
366
|
end
|
366
367
|
|
368
|
+
def validate_sexp_variables(questionnaire)
|
369
|
+
questionnaire.sexp_variables.each_value do |sexp_variable|
|
370
|
+
sexp_variable.validate(questionnaire)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
367
374
|
def delete_prefix(key, questionnaire)
|
368
375
|
key.delete_prefix("#{questionnaire.key}_")
|
369
376
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quby-compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marten Veldthuis
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-03-23 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activemodel
|
@@ -165,6 +165,7 @@ files:
|
|
165
165
|
- lib/quby/compiler/dsl/questions/text_question_builder.rb
|
166
166
|
- lib/quby/compiler/dsl/score_builder.rb
|
167
167
|
- lib/quby/compiler/dsl/score_schema_builder.rb
|
168
|
+
- lib/quby/compiler/dsl/sexp_variable_builder.rb
|
168
169
|
- lib/quby/compiler/dsl/standardized_panel_generators.rb
|
169
170
|
- lib/quby/compiler/dsl/table_builder.rb
|
170
171
|
- lib/quby/compiler/entities.rb
|
@@ -188,6 +189,7 @@ files:
|
|
188
189
|
- lib/quby/compiler/entities/questionnaire.rb
|
189
190
|
- lib/quby/compiler/entities/questions/checkbox_question.rb
|
190
191
|
- lib/quby/compiler/entities/questions/concerns/slider.rb
|
192
|
+
- lib/quby/compiler/entities/questions/concerns/split_to_units.rb
|
191
193
|
- lib/quby/compiler/entities/questions/date_question.rb
|
192
194
|
- lib/quby/compiler/entities/questions/deprecated_question.rb
|
193
195
|
- lib/quby/compiler/entities/questions/float_question.rb
|
@@ -198,6 +200,8 @@ files:
|
|
198
200
|
- lib/quby/compiler/entities/questions/text_question.rb
|
199
201
|
- lib/quby/compiler/entities/score_calculation.rb
|
200
202
|
- lib/quby/compiler/entities/score_schema.rb
|
203
|
+
- lib/quby/compiler/entities/sexp_variable.rb
|
204
|
+
- lib/quby/compiler/entities/sexp_variables.rb
|
201
205
|
- lib/quby/compiler/entities/subscore_schema.rb
|
202
206
|
- lib/quby/compiler/entities/table.rb
|
203
207
|
- lib/quby/compiler/entities/text.rb
|