asker-tool 2.1.2

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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +53 -0
  4. data/bin/asker +4 -0
  5. data/docs/changelog/v2.1.md +99 -0
  6. data/docs/commands.md +15 -0
  7. data/docs/contributions.md +18 -0
  8. data/docs/history.md +40 -0
  9. data/docs/idea.md +44 -0
  10. data/docs/inputs/README.md +39 -0
  11. data/docs/inputs/code.md +69 -0
  12. data/docs/inputs/concepts.md +142 -0
  13. data/docs/inputs/jedi.md +68 -0
  14. data/docs/inputs/tables.md +112 -0
  15. data/docs/inputs/templates.md +87 -0
  16. data/docs/install/README.md +38 -0
  17. data/docs/install/manual.md +26 -0
  18. data/docs/install/scripts.md +26 -0
  19. data/docs/revise/asker-file.md +41 -0
  20. data/docs/revise/buenas-practicas/01-convocatoria.md +30 -0
  21. data/docs/revise/buenas-practicas/02-formulario.md +35 -0
  22. data/docs/revise/buenas-practicas/03-descripcion.md +63 -0
  23. data/docs/revise/buenas-practicas/04-resultados.md +17 -0
  24. data/docs/revise/buenas-practicas/05-reproducir.md +10 -0
  25. data/docs/revise/ejemplos/01/README.md +27 -0
  26. data/docs/revise/ejemplos/02/README.md +31 -0
  27. data/docs/revise/ejemplos/03/README.md +31 -0
  28. data/docs/revise/ejemplos/04/README.md +37 -0
  29. data/docs/revise/ejemplos/05/README.md +25 -0
  30. data/docs/revise/ejemplos/06/README.md +43 -0
  31. data/docs/revise/ejemplos/README.md +11 -0
  32. data/docs/revise/projects.md +74 -0
  33. data/lib/asker.rb +103 -0
  34. data/lib/asker/ai/ai.rb +70 -0
  35. data/lib/asker/ai/ai_calculate.rb +55 -0
  36. data/lib/asker/ai/concept_ai.rb +49 -0
  37. data/lib/asker/ai/question.rb +58 -0
  38. data/lib/asker/ai/stages/base_stage.rb +16 -0
  39. data/lib/asker/ai/stages/main.rb +8 -0
  40. data/lib/asker/ai/stages/stage_b.rb +87 -0
  41. data/lib/asker/ai/stages/stage_d.rb +160 -0
  42. data/lib/asker/ai/stages/stage_f.rb +156 -0
  43. data/lib/asker/ai/stages/stage_i.rb +140 -0
  44. data/lib/asker/ai/stages/stage_s.rb +52 -0
  45. data/lib/asker/ai/stages/stage_t.rb +170 -0
  46. data/lib/asker/application.rb +30 -0
  47. data/lib/asker/checker.rb +356 -0
  48. data/lib/asker/cli.rb +85 -0
  49. data/lib/asker/code/ai/base_code_ai.rb +48 -0
  50. data/lib/asker/code/ai/code_ai_factory.rb +26 -0
  51. data/lib/asker/code/ai/javascript_code_ai.rb +167 -0
  52. data/lib/asker/code/ai/python_code_ai.rb +167 -0
  53. data/lib/asker/code/ai/ruby_code_ai.rb +169 -0
  54. data/lib/asker/code/ai/sql_code_ai.rb +69 -0
  55. data/lib/asker/code/code.rb +53 -0
  56. data/lib/asker/data/column.rb +62 -0
  57. data/lib/asker/data/concept.rb +183 -0
  58. data/lib/asker/data/data_field.rb +87 -0
  59. data/lib/asker/data/row.rb +93 -0
  60. data/lib/asker/data/table.rb +96 -0
  61. data/lib/asker/data/template.rb +65 -0
  62. data/lib/asker/data/world.rb +53 -0
  63. data/lib/asker/exporter/code_gift_exporter.rb +35 -0
  64. data/lib/asker/exporter/code_screen_exporter.rb +45 -0
  65. data/lib/asker/exporter/concept_ai_gift_exporter.rb +33 -0
  66. data/lib/asker/exporter/concept_ai_screen_exporter.rb +115 -0
  67. data/lib/asker/exporter/concept_ai_yaml_exporter.rb +33 -0
  68. data/lib/asker/exporter/concept_doc_exporter.rb +21 -0
  69. data/lib/asker/exporter/concept_screen_exporter.rb +25 -0
  70. data/lib/asker/exporter/main.rb +9 -0
  71. data/lib/asker/files/config.ini +40 -0
  72. data/lib/asker/formatter/code_string_formatter.rb +16 -0
  73. data/lib/asker/formatter/concept_doc_formatter.rb +37 -0
  74. data/lib/asker/formatter/concept_string_formatter.rb +66 -0
  75. data/lib/asker/formatter/question_gift_formatter.rb +65 -0
  76. data/lib/asker/formatter/question_hash_formatter.rb +40 -0
  77. data/lib/asker/formatter/question_moodlexml_formatter.rb +71 -0
  78. data/lib/asker/formatter/rb2haml_formatter.rb +26 -0
  79. data/lib/asker/lang/lang.rb +42 -0
  80. data/lib/asker/lang/lang_factory.rb +19 -0
  81. data/lib/asker/lang/text_actions.rb +150 -0
  82. data/lib/asker/loader/code_loader.rb +53 -0
  83. data/lib/asker/loader/content_loader.rb +101 -0
  84. data/lib/asker/loader/directory_loader.rb +58 -0
  85. data/lib/asker/loader/file_loader.rb +33 -0
  86. data/lib/asker/loader/image_url_loader.rb +61 -0
  87. data/lib/asker/loader/input_loader.rb +24 -0
  88. data/lib/asker/loader/project_loader.rb +71 -0
  89. data/lib/asker/logger.rb +21 -0
  90. data/lib/asker/project.rb +170 -0
  91. metadata +261 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'stages/main'
4
+ require_relative 'ai_calculate'
5
+
6
+ # Description: Method to be included into every ConceptAI instance.
7
+ # * make_questions: use AI to fill @questions Array
8
+ module AI
9
+ include AI_calculate
10
+
11
+ def make_questions
12
+ return unless process?
13
+
14
+ make_questions_stages_di
15
+ # Process every table of this concept
16
+ tables.each do |tab|
17
+ list1, list2 = get_list1_and_list2_from(tab)
18
+ make_questions_stages_bsf(tab, list1, list2)
19
+ make_questions_stages_t(tab, list1, list2)
20
+ end
21
+ # -------------------------------------------------------
22
+ # Exclude questions as is defined into config.ini params
23
+ exclude_questions
24
+ end
25
+
26
+ def make_questions_stages_di
27
+ @questions[:d] = StageD.new(self).run # Process every def{type=text}
28
+ @questions[:i] = StageI.new(self).run # Process every def{type=image_url}
29
+ end
30
+
31
+ def make_questions_stages_bsf(tab, list1, list2)
32
+ # Stage B: process table to make match questions
33
+ @questions[:b] += StageB.new(self).run(tab, list1, list2)
34
+ # Stage S: process tables with sequences
35
+ @questions[:s] += StageS.new(self).run(tab, list1, list2)
36
+ # Stage F: process tables with only 1 field
37
+ @questions[:f] += StageF.new(self).run(tab, list1, list2)
38
+ end
39
+
40
+ def make_questions_stages_t(tab, list1, list2)
41
+ # Stage T: process_tableXfields
42
+ list3 = list1 + list2
43
+ list1.each do |row|
44
+ reorder_list_with_row(list3, row)
45
+ @questions[:t] += StageT.new(self).run(tab, row, list3)
46
+ end
47
+ end
48
+
49
+ def exclude_questions
50
+ param = Application.instance.config['questions']['exclude']
51
+ return if param.nil?
52
+
53
+ tags = param.split(',').each(&:strip!)
54
+ input = { d: [], b: [], f: [], i: [], s: [], t: [] }
55
+ output = { d: [], b: [], f: [], i: [], s: [], t: [] }
56
+
57
+ @questions.each_pair do |key, qlist|
58
+ output[key] = qlist.select { |q| string_has_this_tags?(q.name, tags) }
59
+ input[key] = @questions[key] - output[key]
60
+ end
61
+ @questions = input
62
+ @excluded_questions = output
63
+ end
64
+
65
+ def string_has_this_tags?(input, tags)
66
+ flag = false
67
+ tags.each { |e| flag = true if input.include? e }
68
+ flag
69
+ end
70
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Methods that calculate something
4
+ module AI_calculate
5
+ def get_list1_and_list2_from(ltable)
6
+ # create <list1> with all the rows from the table
7
+ list1 = []
8
+ count = 1
9
+ ltable.rows.each do |i|
10
+ list1 << { id: count, weight: 0, data: i }
11
+ count += 1
12
+ end
13
+
14
+ # create a <list2> with similar rows (same table name) from the neighbours tables
15
+ list2 = []
16
+ neighbors.each do |n|
17
+ n[:concept].tables.each do |t2|
18
+ next if t2.name != ltable.name
19
+ t2.rows.each do |i|
20
+ list2 << { id: count, weight: 0, data: i }
21
+ count += 1
22
+ end
23
+ end
24
+ end
25
+ return list1, list2
26
+ end
27
+
28
+ def calculate_nearness_between_texts(text1, text2)
29
+ return 0.0 if text2.nil? || text2.empty?
30
+
31
+ words = text1.split(' ')
32
+ count = 0
33
+ words.each { |w| count += 1 if text2.include?(w) }
34
+ (count * 100 / words.count)
35
+ end
36
+
37
+ def reorder_list_with_row(list, row)
38
+ # evaluate every row of the list2
39
+ list.each do |r|
40
+ if r[:id] == row[:id]
41
+ r[:weight] = -300
42
+ else
43
+ val = 0
44
+ s = row[:data].count
45
+ s.times do |i|
46
+ val += calculate_nearness_between_texts(row[:data][i], r[:data][i])
47
+ end
48
+ val /= s
49
+ r[:weight] = val
50
+ end
51
+ end
52
+ list.sort! { |a, b| a[:weight] <=> b[:weight] }
53
+ list.reverse!
54
+ end
55
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
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
13
+ class ConceptAI
14
+ include AI
15
+
16
+ attr_reader :concept
17
+ attr_reader :questions
18
+ attr_reader :excluded_questions
19
+
20
+ def initialize(concept, world)
21
+ @concept = concept
22
+ @world = world
23
+ @questions = { d: [], b: [], f: [], i: [], s: [], t: [] }
24
+ @excluded_questions = { d: [], b: [], f: [], i: [], s: [], t: [] }
25
+ @num = 0 # Used to add a unique number to every question
26
+ end
27
+
28
+ def num
29
+ @num += 1
30
+ @num.to_s
31
+ end
32
+
33
+ # If a method call is missing, then delegate to concept parent.
34
+ def method_missing(method, *args, &block)
35
+ @concept.send(method, *args, &block)
36
+ end
37
+
38
+ def random_image_for(_conceptname)
39
+ return '' if rand <= Project.instance.get(:threshold)
40
+
41
+ keys = @world.image_urls.keys
42
+ keys.shuffle!
43
+ values = @world.image_urls[keys[0]] # keys[0] could be conceptname
44
+ return '' if values.nil?
45
+
46
+ values.shuffle!
47
+ "<img src=\"#{values[0]}\" alt\=\"image\"><br/>"
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ require 'set'
3
+
4
+ # Define Question class
5
+ class Question
6
+ attr_accessor :name, :comment, :text
7
+ attr_accessor :good, :bads, :matching, :shorts
8
+ attr_accessor :feedback
9
+ attr_reader :type
10
+ attr_accessor :tags, :lang
11
+
12
+ def initialize(type = :choice)
13
+ reset(type)
14
+ end
15
+
16
+ def reset(type = :choice)
17
+ @name = ''
18
+ @comment = ''
19
+ @text = ''
20
+ @type = type
21
+ @good = ''
22
+ @bads = []
23
+ @matching = []
24
+ @shorts = []
25
+ @feedback = nil
26
+ shuffle_on
27
+ @tags = Set.new
28
+ @lang = nil
29
+ end
30
+
31
+ def set_choice
32
+ @type = :choice
33
+ end
34
+
35
+ def set_match
36
+ @type = :match
37
+ end
38
+
39
+ def set_boolean
40
+ @type = :boolean
41
+ end
42
+
43
+ def set_short
44
+ @type = :short
45
+ end
46
+
47
+ def shuffle_off
48
+ @shuffle = false
49
+ end
50
+
51
+ def shuffle_on
52
+ @shuffle = true
53
+ end
54
+
55
+ def shuffle?
56
+ @shuffle
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+
2
+ class BaseStage
3
+ def initialize(concept_ai)
4
+ @concept_ai = concept_ai
5
+ end
6
+
7
+ # If a method we call is missing, pass the call onto
8
+ # the object we delegate to.
9
+ def method_missing(m, *args, &block)
10
+ @concept_ai.send(m, *args, &block)
11
+ end
12
+
13
+ def run
14
+ raise 'Implement run method!'
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'stage_d'
4
+ require_relative 'stage_b'
5
+ require_relative 'stage_f'
6
+ require_relative 'stage_i'
7
+ require_relative 'stage_s'
8
+ require_relative 'stage_t'
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ require_relative 'base_stage'
6
+ require_relative '../question'
7
+
8
+ class StageB < BaseStage
9
+ # range b1
10
+
11
+ def run(pTable, pList1, pList2)
12
+ # process table match
13
+ questions = []
14
+ return questions if pTable.fields.count < 2
15
+
16
+ return questions unless type == 'text'
17
+
18
+ if pTable.fields.count>1 then
19
+ questions += process_table_match2fields(pTable, pList1, pList2, 0, 1)
20
+ elsif pTable.fields.count>2 then
21
+ questions += process_table_match2fields(pTable, pList1, pList2, 0, 2)
22
+ questions += process_table_match2fields(pTable, pList1, pList2, 1, 2)
23
+ elsif pTable.fields.count>3 then
24
+ questions += process_table_match2fields(pTable, pList1, pList2, 0, 3)
25
+ questions += process_table_match2fields(pTable, pList1, pList2, 1, 3)
26
+ questions += process_table_match2fields(pTable, pList1, pList2, 2, 3)
27
+ end
28
+
29
+ questions
30
+ end
31
+
32
+ def process_table_match2fields(pTable, pList1, pList2, pIndex1, pIndex2)
33
+ questions = []
34
+
35
+ if pList1.count>3
36
+ pList1.each_cons(4) do |e1,e2,e3,e4|
37
+ e = [ e1, e2, e3, e4 ]
38
+
39
+ #Question type <b1match>: match 4 items from the same table
40
+ e.shuffle!
41
+ q=Question.new(:match)
42
+ q.name="#{name}-#{num.to_s}-b1match4x4-#{pTable.name}"
43
+ q.text= random_image_for(name) + lang.text_for(:b1, name, pTable.fields[pIndex1].capitalize, pTable.fields[pIndex2].capitalize )
44
+ q.matching << [ e[0][:data][pIndex1], e[0][:data][pIndex2] ]
45
+ q.matching << [ e[1][:data][pIndex1], e[1][:data][pIndex2] ]
46
+ q.matching << [ e[2][:data][pIndex1], e[2][:data][pIndex2] ]
47
+ q.matching << [ e[3][:data][pIndex1], e[3][:data][pIndex2] ]
48
+ questions << q
49
+
50
+ # Question type <b1match>: match 3 items from table-A and 1 item with error
51
+ e.shuffle!
52
+ q=Question.new(:match)
53
+ q.name="#{name}-#{num.to_s}-b1match3x1misspelled-#{pTable.name}"
54
+ q.text= random_image_for(name) + lang.text_for(:b1, name, pTable.fields[pIndex1].capitalize, pTable.fields[pIndex2].capitalize )
55
+ q.matching << [ e[0][:data][pIndex1], e[0][:data][pIndex2] ]
56
+ q.matching << [ e[1][:data][pIndex1], e[1][:data][pIndex2] ]
57
+ q.matching << [ e[2][:data][pIndex1], e[2][:data][pIndex2] ]
58
+ q.matching << [ lang.do_mistake_to(e[3][:data][pIndex1]), lang.text_for(:misspelling) ]
59
+ questions << q
60
+ end
61
+ end
62
+
63
+ if pList1.count>2 and pList2.count>0
64
+ s=Set.new
65
+ pList1.each do |i|
66
+ s.add( i[:data][pIndex1]+"<=>"+i[:data][pIndex2] )
67
+ end
68
+ s.add( pList2[0][:data][pIndex1]+"<=>"+pList2[0][:data][pIndex2] )
69
+ a=s.to_a
70
+
71
+ # Question 3 items from table-A, and 1 item from table-B
72
+ if s.count > 3
73
+ q=Question.new(:match)
74
+ q.name="#{name}-#{num.to_s}-b1match3x1-#{pTable.name}"
75
+ q.text= random_image_for(name) + lang.text_for(:b1, name , pTable.fields[pIndex1].capitalize, pTable.fields[pIndex2].capitalize)
76
+ q.matching << [ pList1[0][:data][pIndex1], pList1[0][:data][pIndex2] ]
77
+ q.matching << [ pList1[1][:data][pIndex1], pList1[1][:data][pIndex2] ]
78
+ q.matching << [ pList1[2][:data][pIndex1], pList1[2][:data][pIndex2] ]
79
+ q.matching << [ pList2[0][:data][pIndex1], lang.text_for(:error) ]
80
+ questions << q
81
+ end
82
+ end
83
+
84
+ return questions
85
+ end
86
+
87
+ end
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+
3
+ require 'set'
4
+
5
+ require_relative 'base_stage'
6
+ require_relative '../question'
7
+
8
+ class StageD < BaseStage
9
+ # range d1-d4
10
+
11
+ def run
12
+ # Stage D: process every definition, I mean every <def> tag
13
+ questions = []
14
+ return questions unless type == 'text'
15
+
16
+ # for every <text> do this
17
+ texts.each do |t|
18
+ s=Set.new [name(:raw), lang.text_for(:none)]
19
+ neighbors.each { |n| s.add n[:concept].name(:decorated) }
20
+ a=s.to_a
21
+
22
+ # Question choose between 4 options
23
+ if s.count > 3
24
+ q=Question.new(:choice)
25
+ q.name="#{name(:id)}-#{num}-d1choose"
26
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d1,t)
27
+ q.good=name(:raw)
28
+ q.bads << lang.text_for(:none)
29
+ q.bads << a[2]
30
+ q.bads << a[3]
31
+ questions << q
32
+ end
33
+
34
+ #Question choose between 4 options, good none (Syntax error)
35
+ if s.count>3 and type=="text" then
36
+ q=Question.new(:choice)
37
+ q.name="#{name(:id)}-#{num}-d1none-misspelled"
38
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d1,t)
39
+ q.good = lang.text_for(:none)
40
+ q.bads << lang.do_mistake_to(name(:raw))
41
+ q.bads << a[2]
42
+ q.bads << a[3]
43
+ q.feedback="Option misspelled!: #{name(:raw)}"
44
+ questions << q
45
+ end
46
+
47
+ s.delete(name(:raw))
48
+ a=s.to_a
49
+
50
+ #Question choose between 4 options, good none
51
+ if s.count>3 then
52
+ q = Question.new(:choice)
53
+ q.name="#{name(:id)}-#{num}-d1none"
54
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d1,t)
55
+ q.good=lang.text_for(:none)
56
+ q.bads << a[1]
57
+ q.bads << a[2]
58
+ q.bads << a[3]
59
+ questions << q
60
+ end
61
+
62
+ #Question boolean => TRUE
63
+ #q = Question.new(:boolean)
64
+ #q.name="#{name}-#{num}-d2true"
65
+ #q.text=random_image_for(name) + lang.text_for(:d2,name,t)
66
+ #q.good="TRUE"
67
+ #questions << q
68
+
69
+ q = Question.new(:choice)
70
+ q.name="#{name(:id)}-#{num}-d2def-mispelled"
71
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d2,name(:decorated), lang.do_mistake_to(t) )
72
+ q.good=lang.text_for(:misspelling)
73
+ q.bads << lang.text_for(:true)
74
+ q.bads << lang.text_for(:false)
75
+ q.feedback="Definition text mispelled!: #{t}"
76
+ questions << q
77
+
78
+ if type=="text"
79
+ q = Question.new(:choice)
80
+ q.name="#{name(:id)}-#{num}-d2name-mispelled"
81
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d2, lang.do_mistake_to(name(:raw)), t)
82
+ q.good=lang.text_for(:misspelling)
83
+ q.bads << lang.text_for(:true)
84
+ q.bads << lang.text_for(:false)
85
+ q.feedback="Concept name mispelled!: #{name(:raw)}"
86
+ questions << q
87
+ end
88
+
89
+ q = Question.new(:choice)
90
+ q.name="#{name(:id)}-#{num}-d2true"
91
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d2, name(:raw), t )
92
+ q.good = lang.text_for(:true)
93
+ q.bads << lang.text_for(:misspelling)
94
+ q.bads << lang.text_for(:false)
95
+ questions << q
96
+
97
+ if a.size>1 then
98
+ q = Question.new(:choice)
99
+ q.name="#{name(:id)}-#{num}-d2false-misspelled"
100
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d2, a[1], t)
101
+ q.good = lang.text_for(:false)
102
+ q.bads << lang.text_for(:misspelling)
103
+ q.bads << lang.text_for(:true)
104
+ questions << q
105
+ end
106
+
107
+ #Question type <a4desc>: boolean => FALSE
108
+ #if neighbors.count>0 then
109
+ # q = Question.new(:boolean)
110
+ # q.name="#{name}-#{num}-d2false"
111
+ # q.text=random_image_for(name) + lang.text_for(:d2, neighbors[0][:concept].name, t)
112
+ # q.good="FALSE"
113
+ # questions << q
114
+ #end
115
+
116
+ if type=="text"
117
+ #Question hidden name questions
118
+ q = Question.new(:short)
119
+ q.name="#{name(:id)}-#{num}-d3hidden"
120
+ q.text=random_image_for(name(:raw)) + lang.text_for(:d3, lang.hide_text(name(:raw)), t )
121
+ q.shorts << name(:raw)
122
+ q.shorts << name(:raw).gsub("-"," ").gsub("_"," ")
123
+ names.each do |n|
124
+ q.shorts << n if n!=name
125
+ end
126
+ questions << q
127
+ end
128
+
129
+ # indexes = []
130
+ # exclude = ["[", "]", "(", ")", "\"" ]
131
+ # filtered[:words].each_with_index do |item,index|
132
+ # flag=true
133
+ # exclude.each { |e| flag=false if (item[:word].include?(e)) }
134
+ # indexes << index if flag
135
+ # end
136
+
137
+ #Question filtered text questions
138
+ filtered=lang.text_with_connectors(t)
139
+ indexes = filtered[:indexes]
140
+
141
+ groups = (indexes.combination(4).to_a).shuffle
142
+ max = (indexes.size/4).to_i
143
+ groups[0,max].each do |e|
144
+ e.sort!
145
+ q = Question.new(:match)
146
+ q.shuffle_off
147
+ q.name = "#{name}-#{num}-d4filtered"
148
+ s = lang.build_text_from_filtered( filtered, e)
149
+ q.text = random_image_for(name(:raw)) + lang.text_for(:d4, name(:raw) , s)
150
+ e.each_with_index do |value,index|
151
+ q.matching << [ (index+1).to_s, filtered[:words][value][:word].downcase ]
152
+ end
153
+ questions << q
154
+ end
155
+ end
156
+
157
+ return questions
158
+ end
159
+
160
+ end