asker-tool 2.5.9 → 2.7.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.
- checksums.yaml +4 -4
- data/README.md +10 -7
- data/lib/asker/ai/ai.rb +17 -22
- data/lib/asker/ai/ai_calculate.rb +10 -17
- data/lib/asker/ai/code/base_code_ai.rb +5 -30
- data/lib/asker/ai/code/code_ai_factory.rb +6 -12
- data/lib/asker/ai/code/javascript_code_ai.rb +33 -34
- data/lib/asker/ai/code/python_code_ai.rb +35 -36
- data/lib/asker/ai/code/ruby_code_ai.rb +33 -33
- data/lib/asker/ai/code/sql_code_ai.rb +20 -21
- data/lib/asker/ai/concept_ai.rb +12 -22
- data/lib/asker/ai/problem/problem_ai.rb +226 -0
- data/lib/asker/ai/question.rb +34 -45
- data/lib/asker/ai/stages/base_stage.rb +7 -7
- data/lib/asker/ai/stages/main.rb +6 -7
- data/lib/asker/ai/stages/stage_b.rb +62 -28
- data/lib/asker/ai/stages/stage_d.rb +10 -10
- data/lib/asker/ai/stages/stage_f.rb +17 -17
- data/lib/asker/ai/stages/stage_i.rb +8 -18
- data/lib/asker/ai/stages/stage_s.rb +28 -26
- data/lib/asker/ai/stages/stage_t.rb +54 -76
- data/lib/asker/application.rb +15 -14
- data/lib/asker/check_input/check_haml_data.rb +57 -51
- data/lib/asker/check_input/check_table.rb +26 -19
- data/lib/asker/check_input.rb +10 -22
- data/lib/asker/cli.rb +43 -24
- data/lib/asker/data/code.rb +10 -9
- data/lib/asker/data/column.rb +21 -17
- data/lib/asker/data/concept.rb +24 -37
- data/lib/asker/data/data_field.rb +2 -2
- data/lib/asker/data/problem.rb +112 -0
- data/lib/asker/data/project_data.rb +11 -15
- data/lib/asker/data/row.rb +25 -23
- data/lib/asker/data/table.rb +25 -46
- data/lib/asker/data/template.rb +7 -7
- data/lib/asker/data/world.rb +3 -3
- data/lib/asker/{formatter → deprecated}/question_moodlexml_formatter.rb +19 -21
- data/lib/asker/displayer/code_displayer.rb +10 -10
- data/lib/asker/displayer/concept_ai_displayer.erb +1 -1
- data/lib/asker/displayer/concept_ai_displayer.rb +22 -34
- data/lib/asker/displayer/concept_displayer.rb +9 -11
- data/lib/asker/displayer/problem_displayer.rb +45 -0
- data/lib/asker/displayer/stats_displayer.rb +7 -12
- data/lib/asker/exporter/code_gift_exporter.rb +2 -2
- data/lib/asker/exporter/concept_ai_gift_exporter.rb +4 -4
- data/lib/asker/exporter/concept_ai_yaml_exporter.rb +7 -7
- data/lib/asker/exporter/concept_doc_exporter.rb +5 -5
- data/lib/asker/exporter/data_gift_exporter.rb +14 -15
- data/lib/asker/exporter/data_moodle_exporter.rb +51 -20
- data/lib/asker/exporter/output_file_exporter.rb +9 -8
- data/lib/asker/exporter/problem_gift_exporter.rb +30 -0
- data/lib/asker/files/language/ca/templates.yaml +6 -0
- data/lib/asker/files/language/du/templates.yaml +6 -0
- data/lib/asker/files/language/en/templates.yaml +7 -1
- data/lib/asker/files/language/es/templates.yaml +6 -0
- data/lib/asker/files/language/fr/templates.yaml +6 -0
- data/lib/asker/formatter/code_string_formatter.rb +5 -5
- data/lib/asker/formatter/concept_doc_formatter.rb +3 -3
- data/lib/asker/formatter/concept_string_formatter.rb +27 -24
- data/lib/asker/formatter/moodle/ddmatch.erb +40 -0
- data/lib/asker/formatter/moodle/gapfill.erb +57 -0
- data/lib/asker/formatter/moodle/ordering.erb +41 -0
- data/lib/asker/formatter/question_gift_formatter.rb +41 -14
- data/lib/asker/formatter/question_hash_formatter.rb +5 -6
- data/lib/asker/formatter/question_moodle_formatter.rb +14 -7
- data/lib/asker/formatter/rb2haml_formatter.rb +8 -7
- data/lib/asker/lang/lang.rb +16 -16
- data/lib/asker/lang/lang_factory.rb +13 -16
- data/lib/asker/lang/text_actions.rb +20 -18
- data/lib/asker/loader/code_loader.rb +10 -22
- data/lib/asker/loader/content_loader.rb +42 -49
- data/lib/asker/loader/directory_loader.rb +13 -16
- data/lib/asker/loader/embedded_file.rb +29 -24
- data/lib/asker/loader/file_loader.rb +7 -6
- data/lib/asker/loader/haml_loader.rb +6 -5
- data/lib/asker/loader/image_url_loader.rb +21 -18
- data/lib/asker/loader/input_loader.rb +27 -14
- data/lib/asker/loader/problem_loader.rb +88 -0
- data/lib/asker/loader/project_loader.rb +11 -28
- data/lib/asker/logger.rb +22 -13
- data/lib/asker/skeleton.rb +27 -43
- data/lib/asker/start.rb +44 -0
- data/lib/asker/version.rb +1 -1
- data/lib/asker.rb +7 -53
- metadata +12 -20
- data/lib/asker/ai/code/problem_code_ai.rb +0 -176
- data/lib/asker/exporter/code_moodle_exporter.rb +0 -15
- data/lib/asker/exporter/concept_ai_moodle_exporter.rb +0 -15
|
@@ -1,51 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative '../data/code'
|
|
3
|
+
require "rexml/document"
|
|
4
|
+
require_relative "../logger"
|
|
5
|
+
require_relative "../data/code"
|
|
7
6
|
|
|
8
|
-
# Read XML info about Code input data
|
|
9
7
|
module CodeLoader
|
|
10
8
|
##
|
|
11
9
|
# Load XML data about Code object
|
|
12
10
|
# @param xmldata (XML Object)
|
|
13
11
|
# @param filepath (String)
|
|
14
12
|
# @return Code object
|
|
15
|
-
def self.
|
|
13
|
+
def self.call(xmldata, filepath)
|
|
16
14
|
data = read_codedata_from_xml(xmldata, File.basename(filepath))
|
|
17
15
|
code = Code.new(File.dirname(filepath), data[:path], data[:type])
|
|
18
16
|
code.features << data[:features]
|
|
19
17
|
code
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
##
|
|
23
|
-
# Read Code data from XML content
|
|
24
|
-
# @param xmldata (XML Object)
|
|
25
|
-
# @param filename (String) File name that contains data
|
|
26
|
-
# @return Code object
|
|
27
20
|
private_class_method def self.read_codedata_from_xml(xmldata, filename)
|
|
28
|
-
data = {
|
|
21
|
+
data = {path: "?", type: "?", features: []}
|
|
29
22
|
xmldata.elements.each do |i|
|
|
30
|
-
data[:path] = i.text if i.name ==
|
|
31
|
-
data[:type] = i.text.to_sym if i.name ==
|
|
32
|
-
data[:features] << read_features(i, filename) if i.name ==
|
|
23
|
+
data[:path] = i.text if i.name == "path"
|
|
24
|
+
data[:type] = i.text.to_sym if i.name == "type"
|
|
25
|
+
data[:features] << read_features(i, filename) if i.name == "features"
|
|
33
26
|
end
|
|
34
27
|
data
|
|
35
28
|
end
|
|
36
29
|
|
|
37
|
-
##
|
|
38
|
-
# Read features data from XML input
|
|
39
|
-
# @param xmldata (XML object)
|
|
40
|
-
# @return Array with features (Strings)
|
|
41
30
|
private_class_method def self.read_features(xmldata, filename)
|
|
42
31
|
features = []
|
|
43
32
|
xmldata.elements.each do |i|
|
|
44
|
-
if i.name ==
|
|
33
|
+
if i.name == "row"
|
|
45
34
|
features << i.text
|
|
46
35
|
else
|
|
47
|
-
|
|
48
|
-
Logger.verboseln msg
|
|
36
|
+
Logger.error "CodeLoader: features/#{i.name} from #{filename}"
|
|
49
37
|
end
|
|
50
38
|
end
|
|
51
39
|
features
|
|
@@ -1,100 +1,93 @@
|
|
|
1
|
-
require "rainbow"
|
|
2
1
|
require "rexml/document"
|
|
3
2
|
require_relative "code_loader"
|
|
3
|
+
require_relative "problem_loader"
|
|
4
4
|
require_relative "../data/concept"
|
|
5
5
|
require_relative "../data/project_data"
|
|
6
|
+
require_relative "../lang/lang_factory"
|
|
7
|
+
require_relative "../logger"
|
|
6
8
|
|
|
7
|
-
# Define methods that load data from XML contents
|
|
8
9
|
module ContentLoader
|
|
9
10
|
##
|
|
10
11
|
# Load XML content into Asker data objects
|
|
11
12
|
# @param filepath (String) File path
|
|
12
13
|
# @param content (String) XML plane text content
|
|
13
|
-
def self.
|
|
14
|
-
concepts = []
|
|
15
|
-
codes = []
|
|
14
|
+
def self.call(filepath, content)
|
|
16
15
|
begin
|
|
17
16
|
xmlcontent = REXML::Document.new(content)
|
|
18
17
|
rescue REXML::ParseException
|
|
19
18
|
raise_error_with(filepath, content)
|
|
20
19
|
end
|
|
20
|
+
codes = []
|
|
21
|
+
concepts = []
|
|
22
|
+
problems = []
|
|
21
23
|
lang = read_lang_attribute(xmlcontent)
|
|
22
24
|
context = read_context_attribute(xmlcontent)
|
|
23
25
|
|
|
24
26
|
xmlcontent.root.elements.each do |xmldata|
|
|
25
27
|
case xmldata.name
|
|
26
|
-
when "concept"
|
|
27
|
-
concepts << read_concept(xmldata, filepath, lang, context)
|
|
28
28
|
when "code"
|
|
29
29
|
codes << read_code(xmldata, filepath)
|
|
30
|
+
when "concept"
|
|
31
|
+
concepts << read_concept(xmldata, filepath, lang, context)
|
|
32
|
+
when "problem"
|
|
33
|
+
problems << read_problem(xmldata, filepath, lang, context)
|
|
30
34
|
else
|
|
31
|
-
|
|
32
|
-
puts Rainbow("[INFO ] Only 'concept' and 'code' are available at this level").red
|
|
35
|
+
Logger.warn "ContentLoader: Unkown tag (#{xmldata.name}) Use concept, code or problem."
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
|
|
36
|
-
{
|
|
39
|
+
{concepts: concepts, codes: codes, problems: problems}
|
|
37
40
|
end
|
|
38
41
|
|
|
39
|
-
##
|
|
40
|
-
# Read lang attr from input XML data
|
|
41
|
-
# @param xmldata (XML Object)
|
|
42
42
|
private_class_method def self.read_lang_attribute(xmldata)
|
|
43
43
|
begin
|
|
44
|
-
|
|
45
|
-
rescue
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
lang_code = xmldata.root.attributes["lang"]
|
|
45
|
+
rescue itself
|
|
46
|
+
lang_code = ProjectData.instance.lang
|
|
47
|
+
Logger.warn "ContentLoader: Applying default lang (#{lang_code})"
|
|
48
48
|
end
|
|
49
|
-
|
|
49
|
+
LangFactory.instance.get(lang_code)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
##
|
|
53
|
-
# Read context attr from input XML data
|
|
54
|
-
# @param xmldata (XML Object)
|
|
55
52
|
private_class_method def self.read_context_attribute(xmldata)
|
|
56
53
|
begin
|
|
57
|
-
context = xmldata.root.attributes["context"]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
context = xmldata.root.attributes["context"].split(",")
|
|
55
|
+
context.collect!(&:strip)
|
|
56
|
+
rescue itself
|
|
57
|
+
context = ["unknown"]
|
|
58
|
+
Logger.warn "ContentLoader: Context is empty!"
|
|
61
59
|
end
|
|
62
60
|
context
|
|
63
61
|
end
|
|
64
62
|
|
|
65
|
-
##
|
|
66
|
-
# Read concept from input XML data
|
|
67
|
-
# @param xmldata (XML Object)
|
|
68
|
-
# @param filepath (String)
|
|
69
|
-
# @param lang
|
|
70
|
-
# @param context
|
|
71
63
|
private_class_method def self.read_concept(xmldata, filepath, lang, context)
|
|
72
64
|
project = ProjectData.instance
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
65
|
+
concept = Concept.new(xmldata, filepath, lang, context)
|
|
66
|
+
cond = [File.basename(filepath), :default].include? project.get(:process_file)
|
|
67
|
+
concept.process = true if cond
|
|
68
|
+
concept
|
|
76
69
|
end
|
|
77
70
|
|
|
78
|
-
##
|
|
79
|
-
# Read code from input XML data
|
|
80
|
-
# @param xmldata (XML Object)
|
|
81
|
-
# @param filepath (String)
|
|
82
71
|
private_class_method def self.read_code(xmldata, filepath)
|
|
83
72
|
project = ProjectData.instance
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
73
|
+
code = CodeLoader.call(xmldata, filepath)
|
|
74
|
+
cond = [File.basename(filepath), :default].include? project.get(:process_file)
|
|
75
|
+
code.process = true if cond
|
|
76
|
+
code
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private_class_method def self.read_problem(xmldata, filepath, lang, context)
|
|
80
|
+
project = ProjectData.instance
|
|
81
|
+
problem = ProblemLoader.new(lang, context).call(xmldata, filepath)
|
|
82
|
+
cond = [File.basename(filepath), :default].include? project.get(:process_file)
|
|
83
|
+
problem.process = true if cond
|
|
84
|
+
problem
|
|
87
85
|
end
|
|
88
86
|
|
|
89
|
-
##
|
|
90
|
-
# Raise error and save content into error.file
|
|
91
|
-
# @param filepath (String)
|
|
92
|
-
# @param content (String)
|
|
93
87
|
private_class_method def self.raise_error_with(filepath, content)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
f = File.open('output/error.xml', 'w')
|
|
88
|
+
Logger.error "ContentLoader: Format error (#{filepath})"
|
|
89
|
+
Logger.error " : Revise output file (ouput/error.xml)"
|
|
90
|
+
f = File.open("output/error.xml", "w")
|
|
98
91
|
f.write(content)
|
|
99
92
|
f.close
|
|
100
93
|
raise msg
|
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
3
|
+
require_relative "file_loader"
|
|
4
|
+
require_relative "../logger"
|
|
4
5
|
|
|
5
|
-
# Load input data from one directory
|
|
6
6
|
module DirectoryLoader
|
|
7
7
|
##
|
|
8
|
-
# Load input data from directory
|
|
8
|
+
# Load input data from one directory
|
|
9
9
|
# @param dirname (String) Directory name
|
|
10
|
-
def self.
|
|
10
|
+
def self.call(dirname)
|
|
11
11
|
DirectoryLoader.check_dir(dirname)
|
|
12
|
-
files = (Dir.new(dirname).entries - [
|
|
12
|
+
files = (Dir.new(dirname).entries - [".", ".."]).sort
|
|
13
13
|
# Accept only HAML or XML files
|
|
14
14
|
accepted = files.select { |f| %w[.xml .haml].include? File.extname(f) }
|
|
15
15
|
DirectoryLoader.load_files(accepted, dirname)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
##
|
|
19
|
-
# Check directory
|
|
20
|
-
# @param dirname (String) Directory name
|
|
21
18
|
def self.check_dir(dirname)
|
|
22
19
|
return if Dir.exist? dirname
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
raise msg
|
|
21
|
+
Logger.error "DirectoryLoader: #{dirname} directory dosn't exist!"
|
|
22
|
+
exit 1
|
|
27
23
|
end
|
|
28
24
|
|
|
29
25
|
##
|
|
@@ -31,13 +27,14 @@ module DirectoryLoader
|
|
|
31
27
|
# @param filenames (Array) File name list
|
|
32
28
|
# @param dirname (String) Base directory
|
|
33
29
|
def self.load_files(filenames, dirname)
|
|
34
|
-
|
|
30
|
+
data = {concepts: [], codes: [], problems: []}
|
|
35
31
|
filenames.each do |filename|
|
|
36
32
|
filepath = File.join(dirname, filename)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
loaded = FileLoader.call(filepath)
|
|
34
|
+
data[:concepts] += loaded[:concepts]
|
|
35
|
+
data[:codes] += loaded[:codes]
|
|
36
|
+
data[:problems] += loaded[:problems]
|
|
40
37
|
end
|
|
41
|
-
|
|
38
|
+
data
|
|
42
39
|
end
|
|
43
40
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "base64"
|
|
2
|
+
require_relative "../logger"
|
|
2
3
|
|
|
3
4
|
# Methods to load embedded files defined into asker input data file
|
|
4
5
|
# Example:
|
|
@@ -15,46 +16,46 @@ module EmbeddedFile
|
|
|
15
16
|
return load_video(value, localdir) if is_video? value
|
|
16
17
|
|
|
17
18
|
if is_url? value
|
|
18
|
-
Logger.
|
|
19
|
+
Logger.error "EmbebbedFile: Unkown URL (#{value})"
|
|
19
20
|
exit 1
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
filepath = File.join(localdir, value)
|
|
23
24
|
unless File.exist?(filepath)
|
|
24
|
-
Logger.
|
|
25
|
-
# return { text: "URI error", file: :none, type: :unkown }
|
|
25
|
+
Logger.error "EmbeddedFile: File does not exist (#{filepath})"
|
|
26
26
|
exit 1
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Suposse that filename is TEXT file
|
|
30
|
-
|
|
30
|
+
{text: "<pre>#{File.read(filepath)}</pre>", file: :none, type: :text}
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def self.is_audio?(filename)
|
|
34
|
-
extens = [
|
|
35
|
-
extens.each {|ext| return true if filename.downcase.end_with?(ext) }
|
|
34
|
+
extens = [".mp3", ".ogg", ".wav"]
|
|
35
|
+
extens.each { |ext| return true if filename.downcase.end_with?(ext) }
|
|
36
36
|
false
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def self.is_image?(filename)
|
|
40
|
-
extens = [
|
|
41
|
-
extens.each {|ext| return true if filename.downcase.end_with?(ext) }
|
|
40
|
+
extens = [".jpg", ".jpeg", ".png"]
|
|
41
|
+
extens.each { |ext| return true if filename.downcase.end_with?(ext) }
|
|
42
42
|
false
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def self.is_video?(filename)
|
|
46
|
-
extens = [
|
|
47
|
-
extens.each {|ext| return true if filename.downcase.end_with?(ext) }
|
|
46
|
+
extens = [".mp4", ".ogv"]
|
|
47
|
+
extens.each { |ext| return true if filename.downcase.end_with?(ext) }
|
|
48
48
|
false
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def self.is_url?(value)
|
|
52
|
-
return true if value.start_with?(
|
|
52
|
+
return true if value.start_with?("https://", "http://")
|
|
53
|
+
|
|
53
54
|
false
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def self.load_audio(value, localdir)
|
|
57
|
-
output = {
|
|
58
|
+
output = {text: :error, file: :none, type: :audio}
|
|
58
59
|
|
|
59
60
|
if is_url? value
|
|
60
61
|
output[:text] = "<audio src=\"#{value}\" controls></audio>"
|
|
@@ -65,20 +66,21 @@ module EmbeddedFile
|
|
|
65
66
|
|
|
66
67
|
filepath = File.join(localdir, value)
|
|
67
68
|
unless File.exist?(filepath)
|
|
68
|
-
Logger.
|
|
69
|
+
Logger.error "EmbebbedFile: Audio file no exists (#{filepath})"
|
|
69
70
|
exit 1
|
|
70
71
|
end
|
|
71
72
|
output[:text] = '<audio controls><source src="@@PLUGINFILE@@/' + File.basename(filepath) \
|
|
72
73
|
+ '">Your browser does not support the audio tag.</audio>'
|
|
73
74
|
output[:file] = '<file name="' + File.basename(filepath) \
|
|
74
75
|
+ '" path="/" encoding="base64">' \
|
|
75
|
-
+ Base64.strict_encode64(File.open(filepath,
|
|
76
|
+
+ Base64.strict_encode64(File.open(filepath, "rb").read) \
|
|
77
|
+
+ "</file>"
|
|
76
78
|
output[:type] = :audio
|
|
77
79
|
output
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
def self.load_image(value, localdir)
|
|
81
|
-
output = {
|
|
83
|
+
output = {text: :error, file: :none, type: :image}
|
|
82
84
|
|
|
83
85
|
if is_url? value
|
|
84
86
|
output[:text] = "<img src=\"#{value}\" alt=\"image\" width=\"400\" height=\"300\">"
|
|
@@ -89,21 +91,21 @@ module EmbeddedFile
|
|
|
89
91
|
|
|
90
92
|
filepath = File.join(localdir, value)
|
|
91
93
|
unless File.exist?(filepath)
|
|
92
|
-
Logger.
|
|
94
|
+
Logger.error "EmbeddedFile: Unknown file (#{filepath})"
|
|
93
95
|
exit 1
|
|
94
96
|
end
|
|
95
97
|
output[:text] = '<img src="@@PLUGINFILE@@/' + File.basename(filepath) \
|
|
96
98
|
+ '" alt="imagen" class="img-responsive atto_image_button_text-bottom">'
|
|
97
99
|
output[:file] = '<file name="' + File.basename(filepath) \
|
|
98
100
|
+ '" path="/" encoding="base64">' \
|
|
99
|
-
+ Base64.strict_encode64(File.open(filepath,
|
|
101
|
+
+ Base64.strict_encode64(File.open(filepath, "rb").read) \
|
|
102
|
+
+ "</file>"
|
|
100
103
|
output[:type] = :image
|
|
101
104
|
output
|
|
102
105
|
end
|
|
103
106
|
|
|
104
107
|
def self.load_video(value, localdir)
|
|
105
|
-
output = {
|
|
106
|
-
|
|
108
|
+
output = {text: :error, file: :none, type: :video}
|
|
107
109
|
if is_url? value
|
|
108
110
|
output[:text] = "<video controls width=\"400\" height=\"300\">" \
|
|
109
111
|
+ "<source src=\"#{value}\"/></video>"
|
|
@@ -114,14 +116,17 @@ module EmbeddedFile
|
|
|
114
116
|
|
|
115
117
|
filepath = File.join(localdir, value)
|
|
116
118
|
unless File.exist?(filepath)
|
|
117
|
-
Logger.
|
|
119
|
+
Logger.error "Unknown file (#{filepath})"
|
|
118
120
|
exit 1
|
|
119
121
|
end
|
|
120
|
-
output[:text] = '<video controls><source src="@@PLUGINFILE@@/'
|
|
122
|
+
output[:text] = '<video controls><source src="@@PLUGINFILE@@/' \
|
|
123
|
+
+ File.basename(filepath) \
|
|
121
124
|
+ '"/>Your browser does not support the video tag.</video>'
|
|
122
|
-
output[:file] = '<file name="'
|
|
125
|
+
output[:file] = '<file name="' \
|
|
126
|
+
+ File.basename(filepath) \
|
|
123
127
|
+ '" path="/" encoding="base64">' \
|
|
124
|
-
+ Base64.strict_encode64(File.open(filepath,
|
|
128
|
+
+ Base64.strict_encode64(File.open(filepath, "rb").read) \
|
|
129
|
+
+ "</file>"
|
|
125
130
|
output[:type] = :video
|
|
126
131
|
output
|
|
127
132
|
end
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
require_relative "content_loader"
|
|
2
2
|
require_relative "haml_loader"
|
|
3
|
+
require_relative "../logger"
|
|
3
4
|
|
|
4
5
|
##
|
|
5
6
|
# Load a filename and return a Hash with concepts list and code list
|
|
6
7
|
# return { concepts: [], codes: [] }
|
|
7
8
|
module FileLoader
|
|
8
|
-
def self.
|
|
9
|
-
if File.extname(filename).casecmp(
|
|
9
|
+
def self.call(filename)
|
|
10
|
+
if File.extname(filename).casecmp(".haml").zero?
|
|
10
11
|
file_content = HamlLoader.load filename
|
|
11
|
-
elsif File.extname(filename).casecmp(
|
|
12
|
+
elsif File.extname(filename).casecmp(".xml").zero?
|
|
12
13
|
file_content = File.read(filename)
|
|
13
14
|
else
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
Logger.error "FileLoader: HAML or XML required (#{filename})"
|
|
16
|
+
exit 1
|
|
16
17
|
end
|
|
17
|
-
ContentLoader.
|
|
18
|
+
ContentLoader.call(filename, file_content)
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "haml"
|
|
2
|
+
require_relative "../logger"
|
|
2
3
|
|
|
3
4
|
module HamlLoader
|
|
4
5
|
def self.load(filename)
|
|
@@ -6,13 +7,13 @@ module HamlLoader
|
|
|
6
7
|
begin
|
|
7
8
|
# INFO <haml 5.1> 20221223
|
|
8
9
|
haml_engine = Haml::Engine.new(template)
|
|
9
|
-
return haml_engine.render
|
|
10
10
|
# INFO <haml 6.1> 20221226
|
|
11
11
|
# return Haml::Template.new { template }.render
|
|
12
|
-
rescue
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
exit
|
|
12
|
+
rescue => e
|
|
13
|
+
Logger.error "HamlLoader: Can't load file (#{filename})"
|
|
14
|
+
Logger.error " : #{e}"
|
|
15
|
+
exit 1
|
|
16
16
|
end
|
|
17
|
+
haml_engine.render
|
|
17
18
|
end
|
|
18
19
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
require
|
|
3
|
-
|
|
1
|
+
require "net/http"
|
|
2
|
+
require "uri"
|
|
3
|
+
require_relative "../logger"
|
|
4
4
|
|
|
5
5
|
# Search URL images on Internet
|
|
6
6
|
# Methods:
|
|
@@ -11,44 +11,47 @@ module ImageUrlLoader
|
|
|
11
11
|
# Search "input" images on Google and return URL
|
|
12
12
|
def self.load(input = [])
|
|
13
13
|
filters = []
|
|
14
|
-
if input.
|
|
14
|
+
if input.instance_of? String
|
|
15
15
|
filters += sanitize_string(input.clone)
|
|
16
|
-
elsif input.
|
|
16
|
+
elsif input.instance_of? Array
|
|
17
17
|
filters = sanitize_array(input.clone)
|
|
18
18
|
else
|
|
19
|
-
|
|
19
|
+
Logger.error "ImageUrlLoader: Unkown type (#{input.class})"
|
|
20
|
+
exit 1
|
|
20
21
|
end
|
|
21
22
|
# Search Image URLs from Google site, selected by <filters>
|
|
22
|
-
search_url =
|
|
23
|
-
search_url << filters.flatten.join(
|
|
24
|
-
search_url <<
|
|
23
|
+
search_url = "https://www.google.es/search?q="
|
|
24
|
+
search_url << filters.flatten.join("+").to_s
|
|
25
|
+
search_url << "&source=lnms&tbm=isch&sa=X&ved=2ahUKEwj_g8Wfst7nAhWpzoUKHf_wDbsQ_AUoAnoECBMQBA&biw=1280&bih=591"
|
|
25
26
|
image_urls = []
|
|
26
27
|
begin
|
|
27
28
|
uri = URI.parse(search_url)
|
|
28
29
|
response = Net::HTTP.get_response(uri)
|
|
29
30
|
|
|
30
|
-
r = response.body.split(
|
|
31
|
+
r = response.body.split(" ")
|
|
31
32
|
r.each do |line|
|
|
32
33
|
if line.include? 'src="https'
|
|
33
34
|
image_urls << line.delete('"')[4, line.size]
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
rescue
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
puts ' => Ensure URL is well formed'
|
|
38
|
+
Logger.warn "ImageUrlLoader: Problems with URL (#{search_url})"
|
|
39
|
+
Logger.warn " (a) Check Internet connections"
|
|
40
|
+
Logger.warn " (b) Ensure URL is well formed"
|
|
41
41
|
end
|
|
42
42
|
image_urls
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def self.sanitize_string(input)
|
|
46
46
|
text = input.dup
|
|
47
|
-
r = [
|
|
47
|
+
r = [
|
|
48
|
+
%w[á a], %w[é e], %w[í i], %w[ó o], %w[ú u], %w[ñ n],
|
|
49
|
+
%w[Á A], %w[É E], %w[Í I], %w[Ó O], %w[Ú U], %w[Ñ N]
|
|
50
|
+
]
|
|
48
51
|
r.each { |item| text.gsub!(item[0], item[1]) }
|
|
49
|
-
r = [
|
|
50
|
-
r.each { |item| text.gsub!(item,
|
|
51
|
-
text.split(
|
|
52
|
+
r = %w[- _ , "]
|
|
53
|
+
r.each { |item| text.gsub!(item, " ") }
|
|
54
|
+
text.split(" ")
|
|
52
55
|
end
|
|
53
56
|
|
|
54
57
|
def self.sanitize_array(input)
|
|
@@ -1,35 +1,48 @@
|
|
|
1
|
-
require_relative
|
|
2
|
-
require_relative
|
|
3
|
-
require_relative
|
|
1
|
+
require_relative "directory_loader"
|
|
2
|
+
require_relative "../ai/concept_ai"
|
|
3
|
+
require_relative "../ai/code/code_ai_factory"
|
|
4
|
+
require_relative "../ai/problem/problem_ai"
|
|
5
|
+
require_relative "../data/world"
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
class InputLoader
|
|
6
8
|
##
|
|
7
9
|
# Load input data from every input directory
|
|
8
10
|
# @param inputdirs (Array)
|
|
9
|
-
def
|
|
10
|
-
data = {
|
|
11
|
-
|
|
11
|
+
def call(inputdirs, internet = true)
|
|
12
|
+
data = {
|
|
13
|
+
world: nil,
|
|
14
|
+
concepts: [], codes: [], problems: [],
|
|
15
|
+
concepts_ai: [], codes_ai: []
|
|
16
|
+
}
|
|
12
17
|
inputdirs.each do |dirname|
|
|
13
|
-
|
|
14
|
-
data[:concepts] +=
|
|
15
|
-
data[:codes] +=
|
|
18
|
+
loaded = DirectoryLoader.call(dirname)
|
|
19
|
+
data[:concepts] += loaded[:concepts]
|
|
20
|
+
data[:codes] += loaded[:codes]
|
|
21
|
+
data[:problems] += loaded[:problems]
|
|
16
22
|
end
|
|
17
23
|
create_questions(data, internet)
|
|
18
24
|
end
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
#
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def create_questions(data, internet)
|
|
29
|
+
# Create World data. Calculate concept neighbours
|
|
30
|
+
# TODO: Calculate code and problem neighbours
|
|
24
31
|
data[:world] = World.new(data[:concepts], internet)
|
|
32
|
+
|
|
25
33
|
# Create ConceptAI data (ConceptAI = concept + questions)
|
|
26
34
|
data[:concepts].each do |concept|
|
|
27
35
|
data[:concepts_ai] << ConceptAI.new(concept, data[:world])
|
|
28
36
|
end
|
|
37
|
+
|
|
29
38
|
# Create CodeAI data (CodeAI = code + questions)
|
|
30
39
|
data[:codes].each do |code|
|
|
31
40
|
data[:codes_ai] << CodeAIFactory.get(code)
|
|
32
41
|
end
|
|
42
|
+
|
|
43
|
+
# Fill problem with questions
|
|
44
|
+
data[:problems].each { |problem| ProblemAI.new.call(problem) }
|
|
45
|
+
|
|
33
46
|
data
|
|
34
47
|
end
|
|
35
48
|
end
|