asker-tool 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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