asker-tool 2.7.2 → 2.9.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/asker/ai/problem/customizer.rb +36 -0
  3. data/lib/asker/ai/problem/problem_ai.rb +9 -239
  4. data/lib/asker/ai/problem/stage_answers.rb +104 -0
  5. data/lib/asker/ai/problem/stage_base.rb +34 -0
  6. data/lib/asker/ai/problem/stage_steps.rb +121 -0
  7. data/lib/asker/application.rb +1 -0
  8. data/lib/asker/check_input/check_haml_data.rb +2 -2
  9. data/lib/asker/cli.rb +2 -0
  10. data/lib/asker/data/concept.rb +5 -5
  11. data/lib/asker/data/problem.rb +10 -2
  12. data/lib/asker/data/project_data.rb +3 -2
  13. data/lib/asker/displayer/code_displayer.rb +2 -2
  14. data/lib/asker/displayer/concept_ai_displayer.rb +5 -3
  15. data/lib/asker/displayer/concept_displayer.rb +2 -2
  16. data/lib/asker/displayer/problem_displayer.rb +14 -7
  17. data/lib/asker/displayer/stats_displayer.rb +3 -3
  18. data/lib/asker/exporter/doc/format_code2doc.rb +17 -0
  19. data/lib/asker/exporter/doc/format_concept2doc.rb +30 -0
  20. data/lib/asker/exporter/doc/format_problem2doc.rb +41 -0
  21. data/lib/asker/exporter/export2doc.rb +41 -0
  22. data/lib/asker/exporter/{data_gift_exporter.rb → export2gift.rb} +8 -8
  23. data/lib/asker/exporter/{data_moodle_exporter.rb → export2moodle_xml.rb} +13 -11
  24. data/lib/asker/exporter/export2yaml.rb +70 -0
  25. data/lib/asker/exporter/export_action.rb +18 -0
  26. data/lib/asker/exporter/{code_gift_exporter.rb → gift/export_code2gift.rb} +7 -6
  27. data/lib/asker/exporter/{concept_ai_gift_exporter.rb → gift/export_concept2gift.rb} +7 -8
  28. data/lib/asker/exporter/{problem_gift_exporter.rb → gift/export_problem2gift.rb} +2 -2
  29. data/lib/asker/{formatter → exporter/gift}/question_gift_formatter.rb +0 -3
  30. data/lib/asker/formatter/concept_string_formatter.rb +0 -1
  31. data/lib/asker/formatter/{question_hash_formatter.rb → question2hash.rb} +15 -7
  32. data/lib/asker/formatter/{question_moodle_formatter.rb → question2moodle_xml.rb} +3 -5
  33. data/lib/asker/loader/code_loader.rb +1 -1
  34. data/lib/asker/loader/content_loader.rb +1 -0
  35. data/lib/asker/loader/embedded_file/loader.rb +114 -0
  36. data/lib/asker/loader/embedded_file/type.rb +51 -0
  37. data/lib/asker/loader/problem_loader.rb +2 -0
  38. data/lib/asker/logger.rb +1 -1
  39. data/lib/asker/start.rb +3 -3
  40. data/lib/asker/version.rb +1 -1
  41. metadata +22 -16
  42. data/lib/asker/deprecated/question_moodlexml_formatter.rb +0 -69
  43. data/lib/asker/exporter/concept_ai_yaml_exporter.rb +0 -36
  44. data/lib/asker/exporter/concept_doc_exporter.rb +0 -24
  45. data/lib/asker/exporter/output_file_exporter.rb +0 -18
  46. data/lib/asker/formatter/concept_doc_formatter.rb +0 -33
  47. data/lib/asker/loader/embedded_file.rb +0 -133
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04fea4738c097db67a060121841ec075ce611ceceec3c31fb49819cd03924db0
4
- data.tar.gz: 89e0185e69dac3e5f66dec08331de9c64dfe49801a33cf587293d93f03163263
3
+ metadata.gz: cb6924c54cad0b4b8a04231d21f5b4e5e419f2150f6457b080eaa56ec45fc5bb
4
+ data.tar.gz: d6fac1e84e3e4c7f1e836acac9a050fd36b300960e122e993f4af676905b8aaa
5
5
  SHA512:
6
- metadata.gz: 13c0dbaceeb204e6b8263f15ba5ccd240a8c3ec7909575a6ace921d8bf8f74554c673d350bf02dc14aca22edd935d5e1f820d74714fddaaf4a33fce93c38a26a
7
- data.tar.gz: 9698ad8f849a346b0e9abb86a6be4e64c17922c0e22a35daa423dda2c2b08d679c6ae5d8edc2d3edbb7f64c26026c2f7dd1d66e0538187244d0e2f8d66f8fdee
6
+ metadata.gz: ee6ac3134f616e98e63f3ad2160325defc99b80e15082c62e8da378e401e3768bb6f75696e96d79f9e48ce1f652b0eeef8767796b8f61d53d717c1eaa405bebc
7
+ data.tar.gz: 766d487ea2ca668819b6dd0c27459843d20be17ec4c1c54f7f114024b68b12c95e63f8aeff9c81b8016efbbd70cc74a32fb513549ad6c10255e1e9d36be4200c
@@ -0,0 +1,36 @@
1
+ require_relative "../../logger"
2
+
3
+ class Customizer
4
+ def initialize(problem)
5
+ @problem = problem
6
+ end
7
+
8
+ def call(text:, custom:, type: nil)
9
+ output = text.clone
10
+ custom.each_pair { |oldvalue, newvalue| output.gsub!(oldvalue, newvalue) }
11
+
12
+ if type.nil?
13
+ return output
14
+ elsif type == "formula"
15
+ begin
16
+ return eval(output).to_s
17
+ rescue SyntaxError => e
18
+ Logger.error "Problem.name = #{@problem.name}"
19
+ Logger.error "Customizer: Wrong formula '#{text}' or wrong values '#{output}'"
20
+ Logger.error e.to_s
21
+ exit 1
22
+ end
23
+ else
24
+ Logger.error "Customizer: Wrong answer type (#{type})"
25
+ exit 1
26
+ end
27
+ end
28
+
29
+ def min(*args)
30
+ args.min
31
+ end
32
+
33
+ def max(*args)
34
+ args.max
35
+ end
36
+ end
@@ -1,252 +1,22 @@
1
1
  require_relative "../../lang/lang_factory"
2
2
  require_relative "../question"
3
+ require_relative "stage_answers"
4
+ require_relative "stage_steps"
3
5
 
4
6
  class ProblemAI
5
7
  attr_accessor :problem
6
8
 
7
9
  def call(problem)
8
10
  @problem = problem
9
- make_questions
10
- @problem
11
- end
12
-
13
- def make_questions
14
- @counter = 0
15
- @questions = []
16
- @customs = get_customs(@problem)
17
- make_questions_with_aswers
18
- make_questions_with_steps
19
- @problem.questions = @questions
20
- end
21
-
22
- private
23
-
24
- def counter
25
- @counter += 1
26
- end
27
-
28
- def customize(text:, custom:)
29
- output = text.clone
30
- custom.each_pair { |oldvalue, newvalue| output.gsub!(oldvalue, newvalue) }
31
- output
32
- end
33
-
34
- def get_customs(problem)
35
- customs = []
36
- vars = problem.varnames
37
- problem.cases.each do |acase|
38
- custom = {}
39
- vars.each_with_index { |varname, index| custom[varname] = acase[index] }
40
- customs << custom
41
- end
42
- customs
43
- end
44
-
45
- def lines_to_s(lines)
46
- output = ""
47
- lines.each_with_index do |line, index|
48
- output << "%2d: #{line}\n" % (index + 1)
49
- end
50
- output
51
- end
52
-
53
- def make_questions_with_aswers
54
- name = @problem.name
55
- lang = @problem.lang
56
-
57
- @customs.each do |custom|
58
- desc = customize(text: @problem.desc, custom: custom)
59
-
60
- @problem.asks.each do |ask|
61
- next if ask[:text].nil?
62
- asktext = customize(text: ask[:text], custom: custom)
63
- next if ask[:answer].nil?
64
- correct_answer = customize(text: ask[:answer], custom: custom)
65
-
66
- # Question boolean => true
67
- q = Question.new(:boolean)
68
- q.name = "#{name}-#{counter}-01pa1true"
69
- q.text = lang.text_for(:pa1, desc, asktext, correct_answer)
70
- q.good = "TRUE"
71
- @questions << q
72
11
 
73
- # Locate incorrect answers
74
- incorrect_answers = []
75
- @customs.each do |aux|
76
- next if aux == custom
77
- incorrect = customize(text: ask[:answer], custom: aux)
78
- incorrect_answers << incorrect if incorrect != correct_answer
79
- end
12
+ questions = StageAnswers.new(@problem).make_questions
13
+ @problem.stats[:answer] = questions.size
14
+ @problem.questions = questions
80
15
 
81
- # Question boolean => true
82
- if incorrect_answers.size > 0
83
- q = Question.new(:boolean)
84
- q.name = "#{name}-#{counter}-02pa1false"
85
- q.text = lang.text_for(:pa1, desc, asktext, incorrect_answers.first)
86
- q.good = "FALSE"
87
- @questions << q
88
- end
16
+ questions = StageSteps.new(@problem).make_questions
17
+ @problem.stats[:steps] = questions.size
18
+ @problem.questions += questions
89
19
 
90
- # Question choice NONE
91
- if incorrect_answers.size > 2
92
- q = Question.new(:choice)
93
- q.name = "#{name}-#{counter}-03pa2-choice-none"
94
- q.text = lang.text_for(:pa2, desc, asktext)
95
- q.good = lang.text_for(:none)
96
- incorrect_answers.shuffle!
97
- q.bads << incorrect_answers[0]
98
- q.bads << incorrect_answers[1]
99
- q.bads << incorrect_answers[2]
100
- q.feedback = "Correct answer is #{correct_answer}."
101
- @questions << q
102
- end
103
-
104
- # Question choice OK
105
- if incorrect_answers.size > 2
106
- q = Question.new(:choice)
107
- q.name = "#{name}-#{counter}-04pa2choice"
108
- q.text = lang.text_for(:pa2, desc, asktext)
109
- q.good = correct_answer
110
- incorrect_answers.shuffle!
111
- q.bads << incorrect_answers[0]
112
- q.bads << incorrect_answers[1]
113
- q.bads << incorrect_answers[2]
114
- q.feedback = "Correct answer is #{correct_answer}."
115
- @questions << q
116
- end
117
-
118
- if incorrect_answers.size > 1
119
- q = Question.new(:choice)
120
- q.name = "#{name}-#{counter}-05pa2choice"
121
- q.text = lang.text_for(:pa2, desc, asktext)
122
- q.good = correct_answer
123
- incorrect_answers.shuffle!
124
- q.bads << incorrect_answers[0]
125
- q.bads << incorrect_answers[1]
126
- q.bads << lang.text_for(:none)
127
- q.feedback = "Correct answer is #{correct_answer}."
128
- @questions << q
129
- end
130
-
131
- # Question short
132
- q = Question.new(:short)
133
- q.name = "#{name}-#{counter}-06pa2short"
134
- q.text = lang.text_for(:pa2, desc, asktext)
135
- q.shorts << correct_answer
136
- q.feedback = "Correct answer is #{correct_answer}."
137
- @questions << q
138
- end
139
- end
140
-
141
- def make_questions_with_steps
142
- name = @problem.name
143
- lang = @problem.lang
144
-
145
- @customs.each do |custom|
146
- desc = customize(text: @problem.desc, custom: custom)
147
-
148
- @problem.asks.each do |ask|
149
- next if ask[:text].nil?
150
- asktext = customize(text: ask[:text], custom: custom)
151
- next if ask[:steps].nil? || ask[:steps].empty?
152
- steps = ask[:steps].map { |step| customize(text: step, custom: custom) }
153
-
154
- # Question steps ok
155
- q = Question.new(:short)
156
- q.name = "#{name}-#{counter}-07ps3short-ok"
157
- q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(steps))
158
- q.shorts << 0
159
- @questions << q
160
-
161
- # Question steps ordering
162
- if steps.size > 3
163
- q = Question.new(:ordering)
164
- q.name = "#{name}-#{counter}-08ps6ordering"
165
- q.text = lang.text_for(:ps6, desc, asktext, lines_to_s(steps))
166
- steps.each { |step| q.ordering << step }
167
- @questions << q
168
- end
169
-
170
- # Question steps hide
171
- if steps.size > 3
172
- (0...(steps.size)).each do |index|
173
- q = Question.new(:short)
174
- q.name = "#{name}-#{counter}-09ps7short-hide"
175
- hide_steps = steps.clone
176
- hide_steps[index] = hide(steps[index])
177
- q.text = lang.text_for(:ps7, desc, asktext, lines_to_s(hide_steps))
178
- q.shorts << steps[index]
179
- @questions << q
180
- end
181
- end
182
-
183
- # Using diferents wrong steps sequences
184
- max = steps.size - 1
185
- (0..max).each do |index|
186
- change = rand(max + 1)
187
- bads = steps.clone
188
-
189
- minor = index
190
- major = change
191
- if minor > major
192
- minor, major = major, minor
193
- elsif minor == major
194
- next
195
- end
196
- bads[minor], bads[major] = bads[major], bads[minor]
197
-
198
- # Question steps error
199
- q = Question.new(:short)
200
- q.name = "#{name}-#{counter}-10ps3short-error"
201
- q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(bads))
202
- q.shorts << minor + 1
203
- q.feedback = lang.text_for(:ps4, minor + 1, major + 1)
204
- @questions << q
205
- end
206
-
207
- # Match questions
208
- indexes = (0..(steps.size - 1)).to_a.shuffle
209
- (0..(steps.size - 4)).each do |first|
210
- incomplete_steps = steps.clone
211
- incomplete_steps[indexes[first]] = "?"
212
- incomplete_steps[indexes[first + 1]] = "?"
213
- incomplete_steps[indexes[first + 2]] = "?"
214
- incomplete_steps[indexes[first + 3]] = "?"
215
-
216
- q = Question.new(:match)
217
- q.name = "#{name}-#{counter}-11ps5match"
218
- q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
219
- q.matching << [steps[indexes[first]], (indexes[first] + 1).to_s]
220
- q.matching << [steps[indexes[first + 1]], (indexes[first + 1] + 1).to_s]
221
- q.matching << [steps[indexes[first + 2]], (indexes[first + 2] + 1).to_s]
222
- q.matching << [steps[indexes[first + 3]], (indexes[first + 3] + 1).to_s]
223
- q.matching << ["", lang.text_for(:error)]
224
- @questions << q
225
-
226
- q = Question.new(:ddmatch)
227
- q.name = "#{name}-#{counter}-12ps5ddmatch"
228
- q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
229
- q.matching << [(indexes[first] + 1).to_s, steps[indexes[first]]]
230
- q.matching << [(indexes[first + 1] + 1).to_s, steps[indexes[first + 1]]]
231
- q.matching << [(indexes[first + 2] + 1).to_s, steps[indexes[first + 2]]]
232
- q.matching << [(indexes[first + 3] + 1).to_s, steps[indexes[first + 3]]]
233
- q.matching << ["", lang.text_for(:error)]
234
- @questions << q
235
- end
236
- end
237
- end
238
- end
239
-
240
- def hide(text)
241
- output = []
242
- text.chars do |c|
243
- output << if c == " "
244
- c
245
- else
246
- "?"
247
- end
248
- end
249
- output.join
250
- end
20
+ @problem
251
21
  end
252
22
  end
@@ -0,0 +1,104 @@
1
+ require_relative "../question"
2
+ require_relative "stage_base"
3
+
4
+ class StageAnswers < StageBase
5
+ def make_questions
6
+ name = @problem.name
7
+ lang = @problem.lang
8
+ questions = []
9
+
10
+ @customs.each do |custom|
11
+ desc = customize(text: @problem.desc, custom: custom)
12
+
13
+ @problem.asks.each do |ask|
14
+ next if ask[:text].nil?
15
+ asktext = customize(text: ask[:text], custom: custom)
16
+ next if ask[:answer].nil?
17
+
18
+ correct_answer = customize(
19
+ text: ask[:answer],
20
+ custom: custom,
21
+ type: ask[:answer_type]
22
+ )
23
+
24
+ # Question boolean => true
25
+ q = Question.new(:boolean)
26
+ q.name = "#{name}-#{counter}-01pa1true"
27
+ q.text = lang.text_for(:pa1, desc, asktext, correct_answer)
28
+ q.good = "TRUE"
29
+ questions << q
30
+
31
+ # Locate incorrect answers
32
+ incorrect_answers = []
33
+ @customs.each do |aux|
34
+ next if aux == custom
35
+ incorrect = customize(
36
+ text: ask[:answer],
37
+ custom: aux,
38
+ type: ask[:answer_type]
39
+ )
40
+ incorrect_answers << incorrect if incorrect != correct_answer
41
+ end
42
+
43
+ # Question boolean => true
44
+ if incorrect_answers.size > 0
45
+ q = Question.new(:boolean)
46
+ q.name = "#{name}-#{counter}-02pa1false"
47
+ q.text = lang.text_for(:pa1, desc, asktext, incorrect_answers.first)
48
+ q.good = "FALSE"
49
+ questions << q
50
+ end
51
+
52
+ # Question choice NONE
53
+ if incorrect_answers.size > 2
54
+ q = Question.new(:choice)
55
+ q.name = "#{name}-#{counter}-03pa2-choice-none"
56
+ q.text = lang.text_for(:pa2, desc, asktext)
57
+ q.good = lang.text_for(:none)
58
+ incorrect_answers.shuffle!
59
+ q.bads << incorrect_answers[0]
60
+ q.bads << incorrect_answers[1]
61
+ q.bads << incorrect_answers[2]
62
+ q.feedback = "Correct answer is #{correct_answer}."
63
+ questions << q
64
+ end
65
+
66
+ # Question choice OK
67
+ if incorrect_answers.size > 2
68
+ q = Question.new(:choice)
69
+ q.name = "#{name}-#{counter}-04pa2choice"
70
+ q.text = lang.text_for(:pa2, desc, asktext)
71
+ q.good = correct_answer
72
+ incorrect_answers.shuffle!
73
+ q.bads << incorrect_answers[0]
74
+ q.bads << incorrect_answers[1]
75
+ q.bads << incorrect_answers[2]
76
+ q.feedback = "Correct answer is #{correct_answer}."
77
+ questions << q
78
+ end
79
+
80
+ if incorrect_answers.size > 1
81
+ q = Question.new(:choice)
82
+ q.name = "#{name}-#{counter}-05pa2choice"
83
+ q.text = lang.text_for(:pa2, desc, asktext)
84
+ q.good = correct_answer
85
+ incorrect_answers.shuffle!
86
+ q.bads << incorrect_answers[0]
87
+ q.bads << incorrect_answers[1]
88
+ q.bads << lang.text_for(:none)
89
+ q.feedback = "Correct answer is #{correct_answer}."
90
+ questions << q
91
+ end
92
+
93
+ # Question short
94
+ q = Question.new(:short)
95
+ q.name = "#{name}-#{counter}-06pa2short"
96
+ q.text = lang.text_for(:pa2, desc, asktext)
97
+ q.shorts << correct_answer
98
+ q.feedback = "Correct answer is #{correct_answer}."
99
+ questions << q
100
+ end
101
+ end
102
+ questions
103
+ end
104
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "../../logger"
2
+ require_relative "customizer"
3
+
4
+ class StageBase
5
+ def initialize(problem)
6
+ @problem = problem
7
+ @customs = get_customs(@problem)
8
+ @customizer = Customizer.new(@problem)
9
+ @counter = @problem.questions.size
10
+ end
11
+
12
+ def counter
13
+ @counter += 1
14
+ end
15
+
16
+ def customize(...)
17
+ @customizer.call(...)
18
+ end
19
+
20
+ private
21
+
22
+ def get_customs(problem)
23
+ return [] if problem.cases.nil?
24
+
25
+ customs = []
26
+ vars = problem.varnames
27
+ problem.cases.each do |acase|
28
+ custom = {}
29
+ vars.each_with_index { |varname, index| custom[varname] = acase[index] }
30
+ customs << custom
31
+ end
32
+ customs
33
+ end
34
+ end
@@ -0,0 +1,121 @@
1
+ require_relative "../question"
2
+ require_relative "stage_base"
3
+
4
+ class StageSteps < StageBase
5
+ def make_questions
6
+ name = @problem.name
7
+ lang = @problem.lang
8
+ questions = []
9
+
10
+ @customs.each do |custom|
11
+ desc = customize(text: @problem.desc, custom: custom)
12
+
13
+ @problem.asks.each do |ask|
14
+ next if ask[:text].nil?
15
+ asktext = customize(text: ask[:text], custom: custom)
16
+ next if ask[:steps].nil? || ask[:steps].empty?
17
+ steps = ask[:steps].map { |step| customize(text: step, custom: custom) }
18
+
19
+ # Question steps ok
20
+ q = Question.new(:short)
21
+ q.name = "#{name}-#{counter}-07ps3short-ok"
22
+ q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(steps))
23
+ q.shorts << 0
24
+ questions << q
25
+
26
+ # Question steps ordering
27
+ if steps.size > 3
28
+ q = Question.new(:ordering)
29
+ q.name = "#{name}-#{counter}-08ps6ordering"
30
+ q.text = lang.text_for(:ps6, desc, asktext, lines_to_s(steps))
31
+ steps.each { |step| q.ordering << step }
32
+ questions << q
33
+ end
34
+
35
+ # Question steps hide
36
+ if steps.size > 3
37
+ (0...(steps.size)).each do |index|
38
+ q = Question.new(:short)
39
+ q.name = "#{name}-#{counter}-09ps7short-hide"
40
+ hide_steps = steps.clone
41
+ hide_steps[index] = hide(steps[index])
42
+ q.text = lang.text_for(:ps7, desc, asktext, lines_to_s(hide_steps))
43
+ q.shorts << steps[index]
44
+ questions << q
45
+ end
46
+ end
47
+
48
+ # Using diferents wrong steps sequences
49
+ indexes = (0...(steps.size)).to_a
50
+ combinations = indexes.combination(2).to_a
51
+
52
+ combinations.each do |minor, major|
53
+ bads = steps.clone
54
+ bads[minor], bads[major] = bads[major], bads[minor]
55
+
56
+ # Question steps error
57
+ q = Question.new(:short)
58
+ q.name = "#{name}-#{counter}-10ps3short-error"
59
+ q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(bads))
60
+ q.shorts << minor + 1
61
+ q.feedback = lang.text_for(:ps4, minor + 1, major + 1)
62
+ questions << q
63
+ end
64
+
65
+ # Match questions
66
+ indexes = (0..(steps.size - 1)).to_a.shuffle
67
+ (0..(steps.size - 4)).each do |first|
68
+ incomplete_steps = steps.clone
69
+ incomplete_steps[indexes[first]] = "?"
70
+ incomplete_steps[indexes[first + 1]] = "?"
71
+ incomplete_steps[indexes[first + 2]] = "?"
72
+ incomplete_steps[indexes[first + 3]] = "?"
73
+
74
+ q = Question.new(:match)
75
+ q.name = "#{name}-#{counter}-11ps5match"
76
+ q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
77
+ q.matching << [steps[indexes[first]], (indexes[first] + 1).to_s]
78
+ q.matching << [steps[indexes[first + 1]], (indexes[first + 1] + 1).to_s]
79
+ q.matching << [steps[indexes[first + 2]], (indexes[first + 2] + 1).to_s]
80
+ q.matching << [steps[indexes[first + 3]], (indexes[first + 3] + 1).to_s]
81
+ q.matching << ["", lang.text_for(:error)]
82
+ questions << q
83
+
84
+ q = Question.new(:ddmatch)
85
+ q.name = "#{name}-#{counter}-12ps5ddmatch"
86
+ q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
87
+ q.matching << [(indexes[first] + 1).to_s, steps[indexes[first]]]
88
+ q.matching << [(indexes[first + 1] + 1).to_s, steps[indexes[first + 1]]]
89
+ q.matching << [(indexes[first + 2] + 1).to_s, steps[indexes[first + 2]]]
90
+ q.matching << [(indexes[first + 3] + 1).to_s, steps[indexes[first + 3]]]
91
+ q.matching << ["", lang.text_for(:error)]
92
+ questions << q
93
+ end
94
+ end
95
+ end
96
+ questions
97
+ end
98
+
99
+ private
100
+
101
+ def hide(text)
102
+ output = []
103
+ text.chars do |c|
104
+ output << if c == " "
105
+ c
106
+ else
107
+ "?"
108
+ end
109
+ end
110
+ output.join
111
+ end
112
+
113
+ def lines_to_s(lines)
114
+ output = ""
115
+ lines.each_with_index do |line, index|
116
+ output << "%2d: #{line}\n" % (index + 1)
117
+ end
118
+ output
119
+ end
120
+
121
+ end
@@ -1,5 +1,6 @@
1
1
  require "singleton"
2
2
  require "inifile"
3
+ require "rainbow"
3
4
  require_relative "logger"
4
5
  require_relative "version"
5
6
 
@@ -359,9 +359,9 @@ class CheckHamlData
359
359
  if find_parent(index) != :ask
360
360
  @outputs[index][:state] = :err
361
361
  @outputs[index][:msg] = "Parent(ask) not found!"
362
- elsif !line.match(/^\s\s\s\s\s\s%answer\s/)
362
+ elsif !line.match(/^\s\s\s\s\s\s%answer[\s|\{type:]/)
363
363
  @outputs[index][:state] = :err
364
- @outputs[index][:msg] = "Write 6 spaces before %answer"
364
+ @outputs[index][:msg] = "Write 6 spaces before %answer, then space or {type:"
365
365
  end
366
366
  end
367
367
 
data/lib/asker/cli.rb CHANGED
@@ -45,6 +45,7 @@ class CLI < Thor
45
45
  end
46
46
 
47
47
  map ["--check"] => "check"
48
+ option :color, type: :boolean
48
49
  desc "check FILEPATH", "Check HAML input file syntax"
49
50
  long_desc <<-LONGDESC
50
51
 
@@ -64,6 +65,7 @@ class CLI < Thor
64
65
  end
65
66
 
66
67
  map ["f", "-f", "--file"] => "file"
68
+ option :color, type: :boolean
67
69
  desc "[file] FILEPATH", "Build output files, from HAML/XML input file."
68
70
  long_desc <<-LONGDESC
69
71
 
@@ -1,7 +1,7 @@
1
1
  require "rexml/document"
2
2
 
3
3
  require_relative "../lang/lang_factory"
4
- require_relative "../loader/embedded_file"
4
+ require_relative "../loader/embedded_file/loader"
5
5
  require_relative "../logger"
6
6
  require_relative "table"
7
7
  require_relative "data_field"
@@ -37,8 +37,8 @@ class Concept
37
37
 
38
38
  @data = {}
39
39
  @data[:tags] = []
40
- @data[:texts] = [] # Used by standard def inputs
41
- @data[:images] = [] # Used by [type => file and type => image_url] def inputs
40
+ @data[:texts] = [] # Used by standard def inputs
41
+ @data[:images] = [] # Used by [type => file and type => image_url] def inputs
42
42
  @data[:tables] = []
43
43
  @data[:neighbors] = []
44
44
  @data[:reference_to] = []
@@ -173,10 +173,10 @@ class Concept
173
173
  case value.attributes["type"]
174
174
  when "image_url"
175
175
  # Link with remote image
176
- @data[:images] << EmbeddedFile.load(value.text.strip, File.dirname(@filename))
176
+ @data[:images] << EmbeddedFile::Loader.new.call(value.text.strip, File.dirname(@filename))
177
177
  when "file"
178
178
  # Load local images and text files
179
- @data[:images] << EmbeddedFile.load(value.text.strip, File.dirname(@filename))
179
+ @data[:images] << EmbeddedFile::Loader.new.call(value.text.strip, File.dirname(@filename))
180
180
  when nil
181
181
  if value.text.nil?
182
182
  Logger.warn "Concept: def/text empty!"
@@ -1,4 +1,5 @@
1
1
  require_relative "../logger"
2
+ require_relative "../lang/lang_factory"
2
3
 
3
4
  class Problem
4
5
  attr_accessor :lang
@@ -10,12 +11,13 @@ class Problem
10
11
  attr_accessor :descs
11
12
  attr_accessor :asks
12
13
  attr_accessor :questions
14
+ attr_accessor :stats
13
15
 
14
16
  @@id = 0
15
17
  def initialize
16
18
  @@id += 1
17
19
  @id = @@id
18
- @lang = nil
20
+ @lang = LangFactory.instance.get("en")
19
21
  @context = nil
20
22
  @process = false
21
23
  @filename = "?"
@@ -24,6 +26,7 @@ class Problem
24
26
  @descs = []
25
27
  @asks = []
26
28
  @questions = []
29
+ @stats = { answer: 0, steps: 0}
27
30
  end
28
31
 
29
32
  def self.from(values)
@@ -46,7 +49,8 @@ class Problem
46
49
  end
47
50
 
48
51
  def name
49
- "problem#{@id}"
52
+ firstword = @descs[0]&.strip&.split(" ")&.first&.downcase || "problem"
53
+ "#{firstword}#{@id}"
50
54
  end
51
55
 
52
56
  def validate
@@ -59,6 +63,8 @@ class Problem
59
63
  private
60
64
 
61
65
  def validate_varnames
66
+ return if @varnames.nil?
67
+
62
68
  if !@varnames.size.zero? && @cases.size.zero?
63
69
  Logger.warn "Problem: No problem/varnames defined with no problem/case"
64
70
  end
@@ -75,6 +81,8 @@ class Problem
75
81
  end
76
82
 
77
83
  def validate_cases
84
+ return if @cases.nil?
85
+
78
86
  @cases.each do |acase|
79
87
  if acase.size != @varnames.size
80
88
  Logger.error "Problem: problem/cases size not equal to problem/varnames size"