quby-compiler 0.4.7 → 0.4.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f3028be16a77f8fbf5f30b7abfb04839a2a501c4e829bda046a415723beafd4
4
- data.tar.gz: d682aa3b84a73c5b2f3e41f90c82c0e20fbcb431149be21b6136416ddfa827d8
3
+ metadata.gz: 886ce60c1b1c37de0dfdbb8d0b14b4c2ed2574f873a2230ff50e80413e50c031
4
+ data.tar.gz: ee12f0733d624ffea8061b4f9f7bcbcdea9c904eb05d9b1e426b9f646bae91da
5
5
  SHA512:
6
- metadata.gz: 91673fe9c11612f9c9dba10ab129217942d388830423550376f36972250643660eca53af578183c77d924aee9db44587b4ee4ec19e3e66d445a91367a6df7cff
7
- data.tar.gz: 19d1703844eb8a809f68b8a4634af28d97f627a42b575cdb468737dc43b87fda0351ef875edb0c21e70483aaec84d2362256b38c531840f64c9820d7ba69efa6
6
+ metadata.gz: d24325dcee35fafd2d99d65bc6761eb7fa764efe2c5b549208e5130b3b0218bdc2dbf9e932886f1f05a8ac9461236dd398e9d8d733a4a715c2a62fddeb768da0
7
+ data.tar.gz: 0d45e85ea11b5c03710011aff584bf6df363f140184519e6a5c374651e516599a7667921560b41d3870b4c3ef98a55cfc097550aba39199f4be233001453e0fc
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.4.8
2
+
3
+ * roqua.json:
4
+ * sort parent questions before it's children, to make the order more logical to just dumbly show
5
+ * parse markdown before stripping tags (questiom.title, option.description)
6
+ * context-free-titles no longer contain the title as fallback
7
+ * quby2.json
8
+ * sort visibility rules: flags first, then in question order, so we can apply them in order without issue
9
+
1
10
  # 0.4.7
2
11
 
3
12
  * seeds.yml: Fix missing title questions (broken by 0.4.6)
@@ -31,17 +31,6 @@ module Quby
31
31
  "#{description} (true - '#{description_true}', false - '#{description_false}')"
32
32
  end
33
33
 
34
- def to_codebook(_options = {})
35
- output = []
36
- output << "#{key} flag"
37
- output << "'#{description}'" if description.present?
38
- output << " 'true' - #{description_true}"
39
- output << " 'false' - #{description_false}"
40
- output << " '' (leeg) - Vlag niet ingesteld, informatie onbekend"
41
- output << ""
42
- output.join("\n")
43
- end
44
-
45
34
  private
46
35
 
47
36
  def ensure_valid_descriptions
@@ -30,10 +30,6 @@ module Quby
30
30
  class: self.class.to_s
31
31
  }
32
32
  end
33
-
34
- def to_codebook(questionnaire, options = {})
35
- ""
36
- end
37
33
  end
38
34
  end
39
35
  end
@@ -209,6 +209,10 @@ module Quby
209
209
  end
210
210
  # rubocop:enable CyclomaticComplexity, Metrics/MethodLength
211
211
 
212
+ def context_free_title_or_title
213
+ context_free_title || title
214
+ end
215
+
212
216
  # rubocop:disable AccessorMethodName
213
217
  def set_depends_on(keys)
214
218
  return if keys.blank?
@@ -217,10 +221,6 @@ module Quby
217
221
  end
218
222
  # rubocop:enable AccessorMethodName
219
223
 
220
- def context_free_title
221
- @context_free_title || @title
222
- end
223
-
224
224
  def expand_depends_on_input_keys
225
225
  return unless @depends_on
226
226
  @depends_on = questionnaire.expand_input_keys(@depends_on)
@@ -329,39 +329,8 @@ module Quby
329
329
  !parent_option_key.nil?
330
330
  end
331
331
 
332
- def to_codebook(questionnaire, opts = {})
333
- output = []
334
- question_key = codebook_key(key, questionnaire, opts)
335
- output << "#{question_key} #{codebook_output_type} #{codebook_output_range}#{' deprecated' if hidden}"
336
- output << "\"#{context_free_title}\"" unless context_free_title.blank?
337
- options_string = options.map do |option|
338
- option.to_codebook(questionnaire, opts)
339
- end.compact.join("\n")
340
- output << options_string unless options.blank?
341
- output.join("\n")
342
- end
343
-
344
- def codebook_key(key, questionnaire, opts = {})
345
- key.to_s.gsub(/^v_/, "#{opts[:roqua_key] || questionnaire.key.to_s}_")
346
- end
347
-
348
- def codebook_output_type
349
- type
350
- end
351
-
352
- def codebook_output_range
353
- range_min = validations.find { |i| i[:type] == :minimum }&.fetch(:value, nil)
354
- range_max = validations.find { |i| i[:type] == :maximum }&.fetch(:value, nil)
355
-
356
- if range_min || range_max
357
- "(#{[range_min, "value", range_max].compact.join(" &lt;= ")})"
358
- else
359
- ""
360
- end
361
- end
362
-
363
332
  def variable_descriptions
364
- {key => context_free_title}.with_indifferent_access
333
+ {key => context_free_title_or_title}.with_indifferent_access
365
334
  end
366
335
  end
367
336
  end
@@ -10,7 +10,7 @@ module Quby
10
10
 
11
11
  attr_reader :key
12
12
  attr_reader :value
13
- attr_reader :description
13
+ attr_reader :description, :context_free_description
14
14
  attr_reader :questions
15
15
  attr_reader :inner_title
16
16
  validates :inner_title, inclusion: {in: [true, false, nil], message: "must be boolean"}
@@ -52,10 +52,6 @@ module Quby
52
52
  false
53
53
  end
54
54
 
55
- def context_free_description
56
- @context_free_description || @description
57
- end
58
-
59
55
  def as_json(options = {})
60
56
  {
61
57
  key: key,
@@ -75,28 +71,6 @@ module Quby
75
71
  viewId: view_id
76
72
  }
77
73
  end
78
-
79
- def to_codebook(questionnaire, opts)
80
- return nil if inner_title
81
- output = []
82
-
83
- if question.type == :check_box
84
- option_key = question.codebook_key(key, questionnaire, opts)
85
- output << "#{option_key} #{question.codebook_output_type}#{' deprecated' if hidden || question.hidden }"
86
- output << "\"#{question.title} -- #{description}\"" unless question.title.blank? and description.blank?
87
- output << "1\tChecked"
88
- output << "0\tUnchecked"
89
- output << "empty\tUnchecked"
90
- else
91
- output << "#{value || key}\t\"#{description}\"#{' deprecated' if hidden}"
92
- end
93
-
94
- questions.each do |subquestion|
95
- output << "\t#{subquestion.to_codebook(questionnaire, opts).gsub("\n", "\n\t")}"
96
- end
97
-
98
- output.join("\n")
99
- end
100
74
  end
101
75
  end
102
76
  end
@@ -172,6 +172,19 @@ module Quby
172
172
  end)
173
173
  end
174
174
 
175
+ # sorts parents before children, so showing makes more sense and visiblity rules are ordered correctly
176
+ def sorted_questions
177
+ return @sorted_questions if @sorted_questions
178
+
179
+ key_to_order_by = questions.map.with_index.to_h { [_1.key, [_2]] }
180
+ questions.each do |question|
181
+ if question.parent
182
+ key_to_order_by[question.key].unshift(key_to_order_by[question.parent.key].first)
183
+ end
184
+ end
185
+ @sorted_questions = questions.sort_by { key_to_order_by[_1.key] }
186
+ end
187
+
175
188
  def questions
176
189
  question_hash.values
177
190
  end
@@ -201,43 +214,6 @@ module Quby
201
214
  }
202
215
  end
203
216
 
204
- # rubocop:disable Metrics/MethodLength
205
- def to_codebook(options = {})
206
- output = []
207
- output << title
208
- output << "Date unknown"
209
- output << ""
210
-
211
- options[:extra_vars]&.each do |var|
212
- output << "#{var[:key]} #{var[:type]}"
213
- output << "\"#{var[:description]}\""
214
- output << ""
215
- end
216
-
217
- top_questions = panels.map do |panel|
218
- panel.items.select { |item| item.is_a? Question }
219
- end.flatten.compact
220
-
221
- top_questions.each do |question|
222
- output << question.to_codebook(self, options)
223
- output << ""
224
- end
225
-
226
- flags.each_value do |flag|
227
- output << flag.to_codebook(options)
228
- output << ""
229
- end
230
-
231
- textvars.each_value do |textvar|
232
- output << textvar.to_codebook(options)
233
- output << ""
234
- end
235
-
236
- output = output.join("\n")
237
- strip_tags(output.gsub(/\<([ 1-9])/, '&lt;\1')).gsub("&lt;", "<")
238
- end
239
- # rubocop:enable Metrics/MethodLength
240
-
241
217
  def key_in_use?(key)
242
218
  fields.key_in_use?(key) || score_calculations.key?(key)
243
219
  end
@@ -415,9 +391,14 @@ module Quby
415
391
  end.uniq(&:config)
416
392
  end
417
393
 
394
+ # Order is important
395
+ # quby2 follows them one by one and ignores rules by hidden question, so needs to be in order of interface.
396
+ # Flags don't have this issue, so as long as they are first, their order doesn't matter.
418
397
  def visibility_rules
419
- @visibility_rules ||= fields.question_hash.values.flat_map { |question| VisibilityRule.from(question) } \
420
- + flags.values.flat_map { |flag| VisibilityRule.from_flag(flag) }
398
+ @visibility_rules ||= [
399
+ *flags.values.flat_map { |flag| VisibilityRule.from_flag(flag) },
400
+ *sorted_questions.flat_map { |question| VisibilityRule.from(question) }
401
+ ]
421
402
  end
422
403
 
423
404
  private
@@ -51,9 +51,9 @@ module Quby
51
51
  end
52
52
 
53
53
  def variable_descriptions
54
- options.each_with_object(key => context_free_title) do |option, hash|
54
+ options.each_with_object(key => context_free_title_or_title) do |option, hash|
55
55
  next if option.input_key.blank?
56
- hash[option.input_key] = "#{context_free_title} - #{option.description}"
56
+ hash[option.input_key] = "#{context_free_title_or_title} - #{option.description}"
57
57
  end.with_indifferent_access
58
58
  end
59
59
 
@@ -69,12 +69,6 @@ module Quby
69
69
  def as_json(options = {})
70
70
  super.merge(options: @options.as_json)
71
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
72
  end
79
73
  end
80
74
  end
@@ -53,9 +53,9 @@ module Quby
53
53
  end
54
54
 
55
55
  def variable_descriptions
56
- components.each_with_object(key => context_free_title) do |component, hash|
56
+ components.each_with_object(key => context_free_title_or_title) do |component, hash|
57
57
  key = send("#{component}_key")
58
- hash[key] = "#{context_free_title} (#{I18n.t component})"
58
+ hash[key] = "#{context_free_title_or_title} (#{I18n.t component})"
59
59
  end.with_indifferent_access
60
60
  end
61
61
 
@@ -65,18 +65,6 @@ module Quby
65
65
  end
66
66
  super.merge(components: components).merge(component_keys)
67
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
68
  end
81
69
  end
82
70
  end
@@ -8,10 +8,6 @@ module Quby
8
8
  def as_json(options = {})
9
9
  super.merge(options: @options.as_json)
10
10
  end
11
-
12
- def codebook_output_type
13
- :radio
14
- end
15
11
  end
16
12
  end
17
13
  end
@@ -8,10 +8,6 @@ module Quby
8
8
  def as_json(options = {})
9
9
  super.merge(options: @options.as_json)
10
10
  end
11
-
12
- def codebook_output_type
13
- :radio
14
- end
15
11
  end
16
12
  end
17
13
  end
@@ -10,13 +10,6 @@ module Quby
10
10
  super(key, description, default, depends_on_flag)
11
11
  end
12
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
13
  end
21
14
  end
22
15
  end
@@ -160,7 +160,7 @@ module Quby
160
160
  # sbg_key [nil/string] _not_ defaulted to key
161
161
  # type [string]
162
162
  # title [nil/string] no html
163
- # context_free_title [nil/string] no html, defaulted to title
163
+ # context_free_title [nil/string] no html, not defaulted to title!
164
164
  # unit [nil/string] no html (not in quby either)
165
165
  # deprecated [nil/true] only show if filled for old answers
166
166
  # default_invisible [nil/true] only show if filled or when all questions are shown
@@ -170,15 +170,15 @@ module Quby
170
170
  # date_parts [nil/{..}]
171
171
  # options: [nil/{..}]
172
172
  def questions
173
- questionnaire.questions.reject{_1.type == :hidden}.to_h { |question|
173
+ questionnaire.sorted_questions.reject{_1.type == :hidden}.to_h { |question|
174
174
  [
175
175
  question.key,
176
176
  {
177
177
  key: question.key,
178
178
  sbg_key: question.sbg_key,
179
179
  type: QUBY_TYPE_TO_ROQUA_TYPE.fetch(question.type),
180
- title: strip_tags_without_html_encode(question.title),
181
- context_free_title: strip_tags_without_html_encode(question.context_free_title),
180
+ title: parse_markdown_and_strip_tags(question.title),
181
+ context_free_title: question.context_free_title,
182
182
  unit: question.unit,
183
183
  deprecated: question.hidden.presence,
184
184
  default_invisible: question.default_invisible.presence,
@@ -210,7 +210,7 @@ module Quby
210
210
  # {a1: {..}}
211
211
  # key [string]
212
212
  # value [nil/string] nil for check_box, string otherwise
213
- # description [nil/string] context_free_description, no html
213
+ # description [nil/string] context_free_description or parsed/stripped description, no html
214
214
  # child_question_keys [nil/[string]]
215
215
  def options_for(question)
216
216
  return nil if question.options.empty?
@@ -222,7 +222,7 @@ module Quby
222
222
  {
223
223
  key: option.key,
224
224
  value: (option.value.to_s unless question.type == :check_box),
225
- description: strip_tags_without_html_encode(option.context_free_description),
225
+ description: option.context_free_description || parse_markdown_and_strip_tags(option.description),
226
226
  child_question_keys: option.questions.map(&:key).presence
227
227
  }.compact
228
228
  ]
@@ -255,8 +255,17 @@ module Quby
255
255
  }
256
256
  end
257
257
 
258
+ def parse_markdown_and_strip_tags(markdown)
259
+ strip_tags_without_html_encode(Quby::MarkdownParser.new(markdown).to_html)
260
+ end
261
+
258
262
  def strip_tags_without_html_encode(html)
259
- Nokogiri::HTML(html).text.presence
263
+ Nokogiri::HTML(html).tap do |doc|
264
+ doc.css('br, hr, img').each { |node| node.replace(' ') }
265
+ end \
266
+ .text
267
+ .gsub!(/\s+/, ' ')
268
+ .presence
260
269
  end
261
270
  end
262
271
  end
@@ -40,7 +40,7 @@ module Quby
40
40
  when :select
41
41
  d_qtypes[question.key.to_s] = { type: :discrete }
42
42
  for option in question.options
43
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "") unless option.placeholder
43
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "") unless option.placeholder
44
44
  end
45
45
  update_hidden_questions_for(question)
46
46
  when :check_box
@@ -49,11 +49,11 @@ module Quby
49
49
  next if option.inner_title
50
50
  vars << option.key.to_s
51
51
  if question.hidden
52
- question_titles[option.key.to_s] = strip_tags question.context_free_title
52
+ question_titles[option.key.to_s] = strip_tags(question.context_free_title_or_title)
53
53
  end
54
54
  value = 1
55
55
  option_type = { type: :discrete }
56
- option_type[value.to_s] = (option.context_free_description || "")
56
+ option_type[value.to_s] = (option.context_free_description || option.description || "")
57
57
  option_type[:depends] = { values: [value, value.to_s].uniq, variable: option.key.to_s } unless options[:without_depends]
58
58
  d_qtypes[option.key.to_s] = option_type
59
59
  values = [value, value.to_s].uniq
@@ -75,7 +75,7 @@ module Quby
75
75
  end
76
76
  when :hidden
77
77
  if question.options.blank? # string
78
- question_titles[question.key.to_s] = strip_tags question.context_free_title
78
+ question_titles[question.key.to_s] = strip_tags(question.context_free_title_or_title)
79
79
  vars << question.key.to_s unless vars.include? question.key.to_s
80
80
  d_qtypes[question.key.to_s] = { type: :text }
81
81
  d_qtypes[question.key.to_s][:depends] = :present unless options[:without_depends]
@@ -88,16 +88,16 @@ module Quby
88
88
  next if option.inner_title
89
89
  d_qtypes[question.key.to_s] ||= { type: :scale }
90
90
  values << option.value.to_s
91
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
91
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
92
92
  # TODO: missing sub-questions
93
93
  else # check_box
94
94
  d_qtypes[question.key.to_s] ||= { type: :check_box }
95
95
  no_keys = false
96
- question_titles[option.key.to_s] = strip_tags question.context_free_title
96
+ question_titles[option.key.to_s] = strip_tags(question.context_free_title_or_title)
97
97
  vars << option.key.to_s
98
98
  value = option.value || 1
99
99
  option_type = { type: :discrete }
100
- option_type[value.to_s] = (option.context_free_description || "")
100
+ option_type[value.to_s] = (option.context_free_description || option.description || "")
101
101
  option_type[:depends] = { values: [value, value.to_s].uniq, variable: option.key.to_s } unless options[:without_depends]
102
102
  d_qtypes[option.key.to_s] = option_type
103
103
  # TODO: missing sub-questions
@@ -105,7 +105,7 @@ module Quby
105
105
  end
106
106
  if no_keys # scale or radio
107
107
  d_qtypes[question.key.to_s][:depends] = { values: values, variable: question.key.to_s } unless options[:without_depends]
108
- question_titles[question.key.to_s] = strip_tags question.context_free_title
108
+ question_titles[question.key.to_s] = strip_tags(question.context_free_title_or_title)
109
109
  end
110
110
  end
111
111
  else
@@ -194,7 +194,7 @@ module Quby
194
194
 
195
195
  for question in questions_flat
196
196
  unless question.hidden && (question.type == :check_box || question.type == :hidden)
197
- title = question.context_free_title || question.description || ""
197
+ title = question.context_free_title_or_title || question.description || ""
198
198
  question_titles[question.key.to_s] = strip_tags(title)
199
199
  end
200
200
  end
@@ -240,24 +240,24 @@ module Quby
240
240
  end
241
241
  end
242
242
  d_qtypes[quest.key.to_s][:label] = quest.unit unless quest.unit.blank?
243
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
243
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
244
244
  vars << quest.key.to_s
245
245
  end
246
246
 
247
247
  def sub_textfield(question, quests, d_qtypes, vars, quest, values, key)
248
248
  d_qtypes[quest.key.to_s] = { type: :text_field }
249
249
  d_qtypes[quest.key.to_s][:depends] = { values: values, variable: key } unless options[:without_depends]
250
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
250
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
251
251
  vars << quest.key.to_s
252
252
  end
253
253
 
254
254
  def sub_radio(question, quests, d_qtypes, vars, quest, values, key)
255
255
  d_qtypes[quest.key.to_s] = { type: :scale }
256
256
  d_qtypes[quest.key.to_s][:depends] = { values: values, variable: key } unless options[:without_depends]
257
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
257
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
258
258
  for option in quest.options
259
259
  next if option.inner_title
260
- d_qtypes[quest.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
260
+ d_qtypes[quest.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
261
261
  end
262
262
  vars << quest.key.to_s
263
263
  update_hidden_questions_for(quest)
@@ -269,7 +269,7 @@ module Quby
269
269
  vars << key
270
270
  hash[component] = key.to_s
271
271
  end
272
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
272
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
273
273
  end
274
274
 
275
275
  def handle_scale(question, quests, d_qtypes, vars)
@@ -278,7 +278,7 @@ module Quby
278
278
  update_hidden_questions_for(question)
279
279
  for option in question.options
280
280
  next if option.inner_title
281
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
281
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
282
282
  values << option.value.to_s
283
283
  key = question.key.to_s
284
284
  handle_subquestions(question, quests, d_qtypes, vars, option, [option.value.to_s], key)
@@ -1,5 +1,5 @@
1
1
  module Quby
2
2
  module Compiler
3
- VERSION = "0.4.7"
3
+ VERSION = "0.4.8"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quby-compiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.7
4
+ version: 0.4.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marten Veldthuis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-02-09 00:00:00.000000000 Z
11
+ date: 2022-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel