asker-tool 2.5.9 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -7
  3. data/lib/asker/ai/ai.rb +17 -22
  4. data/lib/asker/ai/ai_calculate.rb +10 -17
  5. data/lib/asker/ai/code/base_code_ai.rb +5 -30
  6. data/lib/asker/ai/code/code_ai_factory.rb +6 -12
  7. data/lib/asker/ai/code/javascript_code_ai.rb +33 -34
  8. data/lib/asker/ai/code/python_code_ai.rb +35 -36
  9. data/lib/asker/ai/code/ruby_code_ai.rb +33 -33
  10. data/lib/asker/ai/code/sql_code_ai.rb +20 -21
  11. data/lib/asker/ai/concept_ai.rb +12 -22
  12. data/lib/asker/ai/problem/problem_ai.rb +226 -0
  13. data/lib/asker/ai/question.rb +34 -45
  14. data/lib/asker/ai/stages/base_stage.rb +7 -7
  15. data/lib/asker/ai/stages/main.rb +6 -7
  16. data/lib/asker/ai/stages/stage_b.rb +62 -28
  17. data/lib/asker/ai/stages/stage_d.rb +10 -10
  18. data/lib/asker/ai/stages/stage_f.rb +17 -17
  19. data/lib/asker/ai/stages/stage_i.rb +8 -18
  20. data/lib/asker/ai/stages/stage_s.rb +28 -26
  21. data/lib/asker/ai/stages/stage_t.rb +54 -76
  22. data/lib/asker/application.rb +15 -14
  23. data/lib/asker/check_input/check_haml_data.rb +57 -51
  24. data/lib/asker/check_input/check_table.rb +26 -19
  25. data/lib/asker/check_input.rb +10 -22
  26. data/lib/asker/cli.rb +43 -24
  27. data/lib/asker/data/code.rb +10 -9
  28. data/lib/asker/data/column.rb +21 -17
  29. data/lib/asker/data/concept.rb +24 -37
  30. data/lib/asker/data/data_field.rb +2 -2
  31. data/lib/asker/data/problem.rb +112 -0
  32. data/lib/asker/data/project_data.rb +11 -15
  33. data/lib/asker/data/row.rb +25 -23
  34. data/lib/asker/data/table.rb +25 -46
  35. data/lib/asker/data/template.rb +7 -7
  36. data/lib/asker/data/world.rb +3 -3
  37. data/lib/asker/{formatter → deprecated}/question_moodlexml_formatter.rb +19 -21
  38. data/lib/asker/displayer/code_displayer.rb +10 -10
  39. data/lib/asker/displayer/concept_ai_displayer.erb +1 -1
  40. data/lib/asker/displayer/concept_ai_displayer.rb +22 -34
  41. data/lib/asker/displayer/concept_displayer.rb +9 -11
  42. data/lib/asker/displayer/problem_displayer.rb +45 -0
  43. data/lib/asker/displayer/stats_displayer.rb +7 -12
  44. data/lib/asker/exporter/code_gift_exporter.rb +2 -2
  45. data/lib/asker/exporter/concept_ai_gift_exporter.rb +4 -4
  46. data/lib/asker/exporter/concept_ai_yaml_exporter.rb +7 -7
  47. data/lib/asker/exporter/concept_doc_exporter.rb +5 -5
  48. data/lib/asker/exporter/data_gift_exporter.rb +14 -15
  49. data/lib/asker/exporter/data_moodle_exporter.rb +51 -20
  50. data/lib/asker/exporter/output_file_exporter.rb +9 -8
  51. data/lib/asker/exporter/problem_gift_exporter.rb +30 -0
  52. data/lib/asker/files/language/ca/templates.yaml +6 -0
  53. data/lib/asker/files/language/du/templates.yaml +6 -0
  54. data/lib/asker/files/language/en/templates.yaml +7 -1
  55. data/lib/asker/files/language/es/templates.yaml +6 -0
  56. data/lib/asker/files/language/fr/templates.yaml +6 -0
  57. data/lib/asker/formatter/code_string_formatter.rb +5 -5
  58. data/lib/asker/formatter/concept_doc_formatter.rb +3 -3
  59. data/lib/asker/formatter/concept_string_formatter.rb +27 -24
  60. data/lib/asker/formatter/moodle/ddmatch.erb +40 -0
  61. data/lib/asker/formatter/moodle/gapfill.erb +57 -0
  62. data/lib/asker/formatter/moodle/ordering.erb +41 -0
  63. data/lib/asker/formatter/question_gift_formatter.rb +41 -14
  64. data/lib/asker/formatter/question_hash_formatter.rb +5 -6
  65. data/lib/asker/formatter/question_moodle_formatter.rb +14 -7
  66. data/lib/asker/formatter/rb2haml_formatter.rb +8 -7
  67. data/lib/asker/lang/lang.rb +16 -16
  68. data/lib/asker/lang/lang_factory.rb +13 -16
  69. data/lib/asker/lang/text_actions.rb +20 -18
  70. data/lib/asker/loader/code_loader.rb +10 -22
  71. data/lib/asker/loader/content_loader.rb +42 -49
  72. data/lib/asker/loader/directory_loader.rb +13 -16
  73. data/lib/asker/loader/embedded_file.rb +29 -24
  74. data/lib/asker/loader/file_loader.rb +7 -6
  75. data/lib/asker/loader/haml_loader.rb +6 -5
  76. data/lib/asker/loader/image_url_loader.rb +21 -18
  77. data/lib/asker/loader/input_loader.rb +27 -14
  78. data/lib/asker/loader/problem_loader.rb +88 -0
  79. data/lib/asker/loader/project_loader.rb +11 -28
  80. data/lib/asker/logger.rb +22 -13
  81. data/lib/asker/skeleton.rb +27 -43
  82. data/lib/asker/start.rb +44 -0
  83. data/lib/asker/version.rb +1 -1
  84. data/lib/asker.rb +7 -53
  85. metadata +12 -20
  86. data/lib/asker/ai/code/problem_code_ai.rb +0 -176
  87. data/lib/asker/exporter/code_moodle_exporter.rb +0 -15
  88. data/lib/asker/exporter/concept_ai_moodle_exporter.rb +0 -15
@@ -16,29 +16,29 @@ class RubyCodeAI < BaseCodeAI
16
16
  questions = []
17
17
  # error_lines = []
18
18
  @lines.each_with_index do |line, index|
19
- if line.strip.start_with?('#')
19
+ if line.strip.start_with?("#")
20
20
  lines = clone_array @lines
21
- lines[index].sub!('#','').strip!
21
+ lines[index].sub!("#", "").strip!
22
22
 
23
23
  q = Question.new(:short)
24
24
  q.name = "#{name}-#{num}-uncomment"
25
- q.text = @lang.text_for(:code1,lines_to_html(lines))
26
- q.shorts << (index+1)
27
- q.feedback = 'Comment symbol removed'
25
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
26
+ q.shorts << (index + 1)
27
+ q.feedback = "Comment symbol removed"
28
28
  questions << q
29
- elsif line.strip.size>0
29
+ elsif line.strip.size > 0
30
30
  lines = clone_array @lines
31
- lines[index]='# ' + lines[index]
31
+ lines[index] = "# " + lines[index]
32
32
 
33
33
  q = Question.new(:short)
34
34
  q.name = "#{name}-#{num}-comment"
35
- q.text = @lang.text_for(:code1,lines_to_html(lines))
36
- q.shorts << (index+1)
37
- q.feedback = 'Comment symbol added'
35
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
36
+ q.shorts << (index + 1)
37
+ q.feedback = "Comment symbol added"
38
38
  questions << q
39
39
  end
40
40
  end
41
- questions.shuffle[0,@lines.size/@reduce]
41
+ questions.shuffle[0, @lines.size / @reduce]
42
42
  end
43
43
 
44
44
  ##
@@ -47,7 +47,7 @@ class RubyCodeAI < BaseCodeAI
47
47
  questions = []
48
48
  empty_lines = []
49
49
  used_lines = []
50
- @lines.each_with_index do |line,index|
50
+ @lines.each_with_index do |line, index|
51
51
  if line.strip.size.zero?
52
52
  empty_lines << index
53
53
  else
@@ -57,28 +57,28 @@ class RubyCodeAI < BaseCodeAI
57
57
 
58
58
  used_lines.each do |index|
59
59
  lines = clone_array(@lines)
60
- lines.insert(index, ' ' * (rand(4).to_i + 1))
60
+ lines.insert(index, " " * (rand(4).to_i + 1))
61
61
  if @lines.size < 4 || rand(2) == 0
62
62
  q = Question.new(:short)
63
63
  q.name = "#{name}-#{num}-codeok"
64
- q.text = @lang.text_for(:code1,lines_to_html(lines))
65
- q.shorts << '0'
66
- q.feedback = 'Code is OK'
64
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
65
+ q.shorts << "0"
66
+ q.feedback = "Code is OK"
67
67
  questions << q
68
68
  else
69
69
  q = Question.new(:choice)
70
70
  q.name = "#{name}-#{num}-codeok"
71
- q.text = @lang.text_for(:code2,lines_to_html(lines))
71
+ q.text = @lang.text_for(:code2, lines_to_html(lines))
72
72
  others = (1..@lines.size).to_a.shuffle!
73
- q.good = '0'
73
+ q.good = "0"
74
74
  q.bads << others[0].to_s
75
75
  q.bads << others[1].to_s
76
76
  q.bads << others[2].to_s
77
- q.feedback = 'Code is OK'
77
+ q.feedback = "Code is OK"
78
78
  end
79
79
  end
80
80
 
81
- questions.shuffle[0,@lines.size/@reduce]
81
+ questions.shuffle[0, @lines.size / @reduce]
82
82
  end
83
83
 
84
84
  ##
@@ -86,13 +86,13 @@ class RubyCodeAI < BaseCodeAI
86
86
  def make_syntax_error
87
87
  questions = []
88
88
 
89
- @lang.mistakes.each_pair do |key,values|
89
+ @lang.mistakes.each_pair do |key, values|
90
90
  error_lines = []
91
- @lines.each_with_index do |line,index|
91
+ @lines.each_with_index do |line, index|
92
92
  error_lines << index if line.include?(key.to_s)
93
93
  end
94
94
 
95
- v = values.split(',')
95
+ v = values.split(",")
96
96
  v.each do |value|
97
97
  error_lines.each do |index|
98
98
  lines = clone_array(@lines)
@@ -100,15 +100,15 @@ class RubyCodeAI < BaseCodeAI
100
100
  if @lines.size < 4 || rand(2) == 0
101
101
  q = Question.new(:short)
102
102
  q.name = "#{name}-#{num}-syntaxerror"
103
- q.text = @lang.text_for(:code1,lines_to_html(lines))
104
- q.shorts << (index+1)
103
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
104
+ q.shorts << (index + 1)
105
105
  q.feedback = "Syntax error: '#{value}' must be '#{key}'"
106
106
  else
107
107
  q = Question.new(:choice)
108
108
  q.name = "#{name}-#{num}-syntaxerror"
109
- q.text = @lang.text_for(:code2,lines_to_html(lines))
109
+ q.text = @lang.text_for(:code2, lines_to_html(lines))
110
110
  others = (1..@lines.size).to_a.shuffle!
111
- others.delete(index+1)
111
+ others.delete(index + 1)
112
112
  q.good = (index + 1).to_s
113
113
  q.bads << others[0].to_s
114
114
  q.bads << others[1].to_s
@@ -119,7 +119,7 @@ class RubyCodeAI < BaseCodeAI
119
119
  end
120
120
  end
121
121
  end
122
- questions.shuffle[0,@lines.size/@reduce]
122
+ questions.shuffle[0, @lines.size / @reduce]
123
123
  end
124
124
 
125
125
  ##
@@ -129,7 +129,7 @@ class RubyCodeAI < BaseCodeAI
129
129
  # error_lines = []
130
130
  @lines.each_with_index do |line, index|
131
131
  # Search Variable assignment
132
- m = /\s*(\w*)\s*\=\w*/.match(line)
132
+ m = /\s*(\w*)\s*=\w*/.match(line)
133
133
  i = []
134
134
  unless m.nil?
135
135
  varname = (m.values_at 1)[0]
@@ -152,22 +152,22 @@ class RubyCodeAI < BaseCodeAI
152
152
  q.name = "#{name}-#{num}-variable"
153
153
  q.text = @lang.text_for(:code1, lines_to_html(lines))
154
154
  q.shorts << (index + 1)
155
- q.feedback = "Variable error! Swapped lines #{(index+1)} with #{(k+1)}"
155
+ q.feedback = "Variable error! Swapped lines #{index + 1} with #{k + 1}"
156
156
  else
157
157
  q = Question.new(:choice)
158
158
  q.name = "#{name}-#{num}-variable"
159
159
  q.text = @lang.text_for(:code2, lines_to_html(lines))
160
160
  others = (1..@lines.size).to_a.shuffle!
161
- others.delete(index+1)
161
+ others.delete(index + 1)
162
162
  q.good = (index + 1).to_s
163
163
  q.bads << others[0].to_s
164
164
  q.bads << others[1].to_s
165
165
  q.bads << others[2].to_s
166
- q.feedback = "Variable error! Swapped lines #{(index+1)} with #{(k+1)}"
166
+ q.feedback = "Variable error! Swapped lines #{index + 1} with #{k + 1}"
167
167
  end
168
168
  questions << q
169
169
  end
170
170
  end
171
- questions.shuffle[0,@lines.size/@reduce]
171
+ questions.shuffle[0, @lines.size / @reduce]
172
172
  end
173
173
  end
@@ -1,36 +1,35 @@
1
-
2
- require_relative '../../lang/lang_factory'
3
- require_relative '../../ai/question'
4
- require_relative 'base_code_ai'
1
+ require_relative "../../lang/lang_factory"
2
+ require_relative "../../ai/question"
3
+ require_relative "base_code_ai"
5
4
 
6
5
  class SQLCodeAI < BaseCodeAI
7
6
  def initialize(code)
8
- @lang = LangFactory.instance.get('sql')
7
+ @lang = LangFactory.instance.get("sql")
9
8
  super code
10
9
  end
11
10
 
12
11
  def make_comment_error
13
12
  questions = []
14
- @lines.each_with_index do |line,index|
15
- if line.include?('//')
13
+ @lines.each_with_index do |line, index|
14
+ if line.include?("//")
16
15
  lines = clone_array @lines
17
- lines[index].sub!('//','').strip!
16
+ lines[index].sub!("//", "").strip!
18
17
 
19
18
  q = Question.new(:short)
20
19
  q.name = "#{name}-#{num}-code1uncomment"
21
- q.text = @lang.text_for(:code1,lines_to_html(lines))
22
- q.shorts << (index+1)
23
- q.feedback = 'Comment symbol removed'
20
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
21
+ q.shorts << (index + 1)
22
+ q.feedback = "Comment symbol removed"
24
23
  questions << q
25
- elsif line.strip.size>0
24
+ elsif line.strip.size > 0
26
25
  lines = clone_array @lines
27
- lines[index]='// ' + lines[index]
26
+ lines[index] = "// " + lines[index]
28
27
 
29
28
  q = Question.new(:short)
30
29
  q.name = "#{name}-#{num}-code1comment"
31
- q.text = @lang.text_for(:code1,lines_to_html(lines))
32
- q.shorts << (index+1)
33
- q.feedback = 'Comment symbol added'
30
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
31
+ q.shorts << (index + 1)
32
+ q.feedback = "Comment symbol added"
34
33
  questions << q
35
34
  end
36
35
  end
@@ -41,10 +40,10 @@ class SQLCodeAI < BaseCodeAI
41
40
  error_lines = []
42
41
  questions = []
43
42
 
44
- @lang.mistakes.each_pair do |key,values|
45
- v = values.split(',')
43
+ @lang.mistakes.each_pair do |key, values|
44
+ v = values.split(",")
46
45
  v.each do |value|
47
- @lines.each_with_index do |line,index|
46
+ @lines.each_with_index do |line, index|
48
47
  error_lines << index if line.include?(key.to_s)
49
48
  end
50
49
 
@@ -53,8 +52,8 @@ class SQLCodeAI < BaseCodeAI
53
52
  lines[index].sub!(key.to_s, value)
54
53
  q = Question.new(:short)
55
54
  q.name = "#{name}-#{num}-code1keyword"
56
- q.text = @lang.text_for(:code1,lines_to_html(lines))
57
- q.shorts << (index+1)
55
+ q.text = @lang.text_for(:code1, lines_to_html(lines))
56
+ q.shorts << (index + 1)
58
57
  q.feedback = "Keyword error: '#{value}' must be '#{key}'"
59
58
  questions << q
60
59
  end
@@ -1,15 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../lang/lang'
4
- require_relative 'ai'
5
- require_relative 'question'
6
-
7
- # ConceptAI: Add more info to every Concept instance.
8
- # Encapsulating AI data => questions
9
- # * concept
10
- # * questions
11
- # * num
12
- # * random_image_for
3
+ require_relative "../lang/lang"
4
+ require_relative "ai"
5
+ require_relative "question"
6
+
7
+ # Add more info to every Concept instance.
8
+ # Encapsulating AI data => questions
13
9
  class ConceptAI
14
10
  include AI
15
11
 
@@ -17,22 +13,17 @@ class ConceptAI
17
13
  attr_reader :questions
18
14
  attr_reader :excluded_questions
19
15
 
20
- ##
21
- # Initialize ConcepAI
22
- # @param concept (Concept)
23
- # @param world (World)
24
16
  def initialize(concept, world)
25
17
  @concept = concept
26
18
  @world = world
27
- @questions = { d: [], b: [], f: [], i: [], s: [], t: [] }
28
- @excluded_questions = { d: [], b: [], f: [], i: [], s: [], t: [] }
19
+ @questions = {d: [], b: [], f: [], i: [], s: [], t: []}
20
+ @excluded_questions = {d: [], b: [], f: [], i: [], s: [], t: []}
29
21
  @num = 0 # Add a unique number to every question
30
22
  make_questions
31
23
  end
32
24
 
33
- ##
34
- # Generates and return new "num" value
35
25
  def num
26
+ # Generates and return new "num" value
36
27
  @num += 1
37
28
  @num.to_s
38
29
  end
@@ -43,15 +34,14 @@ class ConceptAI
43
34
  @concept.send(method, *args, &block)
44
35
  end
45
36
 
46
- ##
47
- # Generates random image URL
48
37
  def random_image_for(_conceptname)
49
- return '' if rand <= ProjectData.instance.get(:threshold)
38
+ # Generates random image URL
39
+ return "" if rand <= ProjectData.instance.get(:threshold)
50
40
 
51
41
  keys = @world.image_urls.keys
52
42
  keys.shuffle!
53
43
  values = @world.image_urls[keys[0]] # keys[0] could be conceptname
54
- return '' if values.nil?
44
+ return "" if values.nil?
55
45
 
56
46
  values.shuffle!
57
47
  "<img src=\"#{values[0]}\" alt=\"image\"><br/>"
@@ -0,0 +1,226 @@
1
+ require_relative "../../lang/lang_factory"
2
+ require_relative "../question"
3
+
4
+ class ProblemAI
5
+ attr_accessor :problem
6
+
7
+ def call(problem)
8
+ @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}-pa1true1"
69
+ q.text = lang.text_for(:pa1, desc, asktext, correct_answer)
70
+ q.good = "TRUE"
71
+ @questions << q
72
+
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
80
+
81
+ # Question boolean => true
82
+ if incorrect_answers.size > 0
83
+ q = Question.new(:boolean)
84
+ q.name = "#{name}-#{counter}-pa2false2"
85
+ q.text = lang.text_for(:pa1, desc, asktext, incorrect_answers.first)
86
+ q.good = "FALSE"
87
+ @questions << q
88
+ end
89
+
90
+ # Question choice NONE
91
+ if incorrect_answers.size > 2
92
+ q = Question.new(:choice)
93
+ q.name = "#{name}-#{counter}-pa2-choice-none3"
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}-pa2choice4"
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}-pa2choice5"
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}-pa2short6"
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}-ps3short7"
157
+ q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(steps))
158
+ q.shorts << 0
159
+ @questions << q
160
+
161
+ if steps.size > 3
162
+ q = Question.new(:ordering)
163
+ q.name = "#{name}-#{counter}-ps6ordering8"
164
+ q.text = lang.text_for(:ps6, desc, asktext, lines_to_s(steps))
165
+ steps.each { |step| q.ordering << step }
166
+ @questions << q
167
+ end
168
+
169
+ # Using diferents wrong steps sequences
170
+ max = steps.size - 1
171
+ (0..max).each do |index|
172
+ change = rand(max + 1)
173
+ bads = steps.clone
174
+
175
+ minor = index
176
+ major = change
177
+ if minor > major
178
+ minor, major = major, minor
179
+ elsif minor == major
180
+ next
181
+ end
182
+ bads[minor], bads[major] = bads[major], bads[minor]
183
+
184
+ # Question steps error
185
+ q = Question.new(:short)
186
+ q.name = "#{name}-#{counter}-ps3short-error9"
187
+ q.text = lang.text_for(:ps3, desc, asktext, lines_to_s(bads))
188
+ q.shorts << minor + 1
189
+ q.feedback = lang.text_for(:ps4, minor + 1, major + 1)
190
+ @questions << q
191
+ end
192
+
193
+ # Match questions
194
+ indexes = (0..(steps.size - 1)).to_a.shuffle
195
+ (0..(steps.size - 4)).each do |first|
196
+ incomplete_steps = steps.clone
197
+ incomplete_steps[indexes[first]] = "?"
198
+ incomplete_steps[indexes[first + 1]] = "?"
199
+ incomplete_steps[indexes[first + 2]] = "?"
200
+ incomplete_steps[indexes[first + 3]] = "?"
201
+
202
+ q = Question.new(:match)
203
+ q.name = "#{name}-#{counter}-ps5match10"
204
+ q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
205
+ q.matching << [steps[indexes[first]], (indexes[first] + 1).to_s]
206
+ q.matching << [steps[indexes[first + 1]], (indexes[first + 1] + 1).to_s]
207
+ q.matching << [steps[indexes[first + 2]], (indexes[first + 2] + 1).to_s]
208
+ q.matching << [steps[indexes[first + 3]], (indexes[first + 3] + 1).to_s]
209
+ q.matching << ["", lang.text_for(:error)]
210
+ @questions << q
211
+
212
+ q = Question.new(:ddmatch)
213
+ q.name = "#{name}-#{counter}-ps5ddmatch11"
214
+ q.text = lang.text_for(:ps5, desc, asktext, lines_to_s(incomplete_steps))
215
+ q.matching << [(indexes[first] + 1).to_s, steps[indexes[first]]]
216
+ q.matching << [(indexes[first + 1] + 1).to_s, steps[indexes[first + 1]]]
217
+ q.matching << [(indexes[first + 2] + 1).to_s, steps[indexes[first + 2]]]
218
+ q.matching << [(indexes[first + 3] + 1).to_s, steps[indexes[first + 3]]]
219
+ q.matching << ["", lang.text_for(:error)]
220
+ @questions << q
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -1,80 +1,69 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
3
+ require "set"
4
4
 
5
- # Define Question class
6
5
  class Question
6
+ TYPES = %i[boolean choice ddmatch match ordering short]
7
+
7
8
  attr_accessor :name # Question name used as identification
8
9
  attr_accessor :comment # Comments asociated
9
- attr_accessor :text # The real text of the question
10
- attr_accessor :good # The correct answer
11
- attr_accessor :bads # Bads answers used by choice type question
12
- attr_accessor :matching # Matching answers used by match type question
13
- attr_accessor :shorts # Short answers used by short type question
14
- attr_accessor :feedback # Question feedbak
15
- attr_reader :type # Question type: :choice, :match, :boolean, :short
16
10
  attr_accessor :tags
17
11
  attr_accessor :lang # Info used when export (YAML)
18
12
  attr_accessor :encode # image base64 content used when export Moodle xml
19
13
 
20
- # Initialize object
21
- # @param type (Symbol) Question type: choice, match, boolean, short
14
+ attr_accessor :text # The real text of the question
15
+ attr_accessor :feedback # Question feedbak
16
+ attr_reader :type # Question type: ;boolean, :choice, :match, :short
17
+
18
+ attr_accessor :good # The correct answer (types: boolean, choice)
19
+ attr_accessor :bads # Bads answers (type: choice)
20
+ attr_accessor :matching # Matching answers (type: match)
21
+ attr_accessor :ordering # Steps answer (type: ordering)
22
+ attr_accessor :shorts # Short answers (type: short)
23
+
22
24
  def initialize(type = :choice)
23
25
  reset(type)
24
26
  end
25
27
 
26
- # Reset attributes
27
28
  # @param type (Symbol) Question type: choice, match, boolean, short
28
- # rubocop:disable Metrics/MethodLength
29
29
  def reset(type = :choice)
30
- @name = ''
31
- @comment = ''
32
- @text = ''
30
+ validate type
33
31
  @type = type
34
- @good = ''
35
- @bads = []
36
- @matching = []
37
- @shorts = []
38
- @feedback = nil
39
- shuffle_on
32
+
33
+ @name = ""
34
+ @comment = ""
40
35
  @tags = Set.new
41
36
  @lang = nil
42
37
  @encode = :none
43
- end
44
- # rubocop:enable Metrics/MethodLength
45
38
 
46
- # Set choice type
47
- def set_choice
48
- @type = :choice
49
- end
50
-
51
- # Set match type
52
- def set_match
53
- @type = :match
54
- end
55
-
56
- # Set boolean type
57
- def set_boolean
58
- @type = :boolean
59
- end
60
-
61
- # Set short type
62
- def set_short
63
- @type = :short
39
+ @text = ""
40
+ @feedback = nil
41
+ @good = ""
42
+ @bads = []
43
+ @matching = []
44
+ @ordering = []
45
+ @shorts = []
46
+ shuffle_on
64
47
  end
65
48
 
66
- # Set shuffle off
67
49
  def shuffle_off
68
50
  @shuffle = false
69
51
  end
70
52
 
71
- # Set shuffle on
72
53
  def shuffle_on
73
54
  @shuffle = true
74
55
  end
75
56
 
76
- # Return shuffle value
77
57
  def shuffle?
78
58
  @shuffle
79
59
  end
60
+
61
+ private
62
+
63
+ def validate(type)
64
+ unless TYPES.include? type
65
+ warn "[ERROR] Question type error (#{type})"
66
+ exit 1
67
+ end
68
+ end
80
69
  end