asker-tool 2.6.0 → 2.7.1
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 +5 -5
- data/lib/asker/ai/ai.rb +6 -6
- data/lib/asker/ai/ai_calculate.rb +3 -3
- 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/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 +40 -51
- data/lib/asker/application.rb +15 -14
- data/lib/asker/check_input/check_haml_data.rb +52 -51
- data/lib/asker/check_input/check_table.rb +17 -20
- data/lib/asker/check_input.rb +10 -23
- 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 +17 -17
- data/lib/asker/displayer/concept_displayer.rb +4 -2
- 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 +6 -6
- 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 +14 -14
- data/lib/asker/loader/file_loader.rb +5 -4
- data/lib/asker/loader/haml_loader.rb +4 -3
- data/lib/asker/loader/image_url_loader.rb +6 -5
- data/lib/asker/loader/input_loader.rb +24 -10
- data/lib/asker/loader/problem_loader.rb +88 -0
- data/lib/asker/loader/project_loader.rb +5 -12
- data/lib/asker/logger.rb +19 -10
- data/lib/asker/skeleton.rb +19 -35
- data/lib/asker/start.rb +44 -0
- data/lib/asker/version.rb +1 -1
- data/lib/asker.rb +7 -52
- metadata +12 -6
- 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,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: false
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require_relative  | 
| 3 | 
            +
            require "erb"
         | 
| 4 | 
            +
            require_relative "../application"
         | 
| 5 5 |  | 
| 6 6 | 
             
            # Transform Questions into Gift format
         | 
| 7 7 | 
             
            class QuestionMoodleFormatter
         | 
| @@ -12,20 +12,27 @@ class QuestionMoodleFormatter | |
| 12 12 | 
             
              def self.to_s(question)
         | 
| 13 13 | 
             
                case question.type
         | 
| 14 14 | 
             
                when :choice
         | 
| 15 | 
            -
                  fractions = Application.instance.config[ | 
| 15 | 
            +
                  fractions = Application.instance.config["questions"]["fractions"]
         | 
| 16 16 | 
             
                  penalties = fractions
         | 
| 17 17 | 
             
                  # penalties = ['', '-50', '-33.33333', '-25', '-20']
         | 
| 18 18 | 
             
                  # puts "[DEBUG] fractions : #{fractions}"
         | 
| 19 19 | 
             
                  # puts "[DEBUG] penalties : #{penalties}"
         | 
| 20 20 |  | 
| 21 21 | 
             
                  penalty = penalties[question.bads.size]
         | 
| 22 | 
            -
                  template = File.read(File.join(File.dirname(__FILE__),  | 
| 22 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "multichoice.erb"))
         | 
| 23 23 | 
             
                when :boolean
         | 
| 24 | 
            -
                  template = File.read(File.join(File.dirname(__FILE__),  | 
| 24 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "truefalse.erb"))
         | 
| 25 | 
            +
                when :ddmatch
         | 
| 26 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "ddmatch.erb"))
         | 
| 25 27 | 
             
                when :match
         | 
| 26 | 
            -
                  template = File.read(File.join(File.dirname(__FILE__),  | 
| 28 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "matching.erb"))
         | 
| 29 | 
            +
                when :ordering
         | 
| 30 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "ordering.erb"))
         | 
| 27 31 | 
             
                when :short
         | 
| 28 | 
            -
                  template = File.read(File.join(File.dirname(__FILE__),  | 
| 32 | 
            +
                  template = File.read(File.join(File.dirname(__FILE__), "moodle", "shortanswer.erb"))
         | 
| 33 | 
            +
                else
         | 
| 34 | 
            +
                  warn "[ERROR] QuestionMoodleFormatter: Unknown type (#{question.type})"
         | 
| 35 | 
            +
                  exit 1
         | 
| 29 36 | 
             
                end
         | 
| 30 37 | 
             
                renderer = ERB.new(template)
         | 
| 31 38 | 
             
                renderer.result(binding)
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # UNDER DEVELOPMENT!!
         | 
| 2 2 |  | 
| 3 | 
            +
            require_relative "../logger"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Rb2HamlExporter
         | 
| 4 6 | 
             
              def self.export(filename)
         | 
| 5 7 | 
             
                check_file filename
         | 
| @@ -8,19 +10,18 @@ module Rb2HamlExporter | |
| 8 10 | 
             
              end
         | 
| 9 11 |  | 
| 10 12 | 
             
              def self.check_file(filename)
         | 
| 11 | 
            -
                unless File.extname(filename).casecmp( | 
| 12 | 
            -
                   | 
| 13 | 
            -
                   | 
| 13 | 
            +
                unless File.extname(filename).casecmp(".rb").zero?
         | 
| 14 | 
            +
                  Logger.error "Rb2HamlExporter: File name error #{filename}"
         | 
| 15 | 
            +
                  exit 1
         | 
| 14 16 | 
             
                end
         | 
| 15 17 | 
             
                unless File.exist? filename
         | 
| 16 | 
            -
                   | 
| 17 | 
            -
                   | 
| 18 | 
            +
                  Logger.error "Rb2HamlExporter: File #{filename} not found!"
         | 
| 19 | 
            +
                  exit 1
         | 
| 18 20 | 
             
                end
         | 
| 19 21 | 
             
              end
         | 
| 20 22 |  | 
| 21 23 | 
             
              class Map
         | 
| 22 24 | 
             
              end
         | 
| 23 | 
            -
             | 
| 24 25 | 
             
            end
         | 
| 25 26 |  | 
| 26 | 
            -
            Rb2HamlExporter.export( | 
| 27 | 
            +
            Rb2HamlExporter.export("docs/examples/home/rb/furniture.rb")
         | 
    
        data/lib/asker/lang/lang.rb
    CHANGED
    
    | @@ -1,15 +1,15 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            require  | 
| 3 | 
            -
             | 
| 4 | 
            -
            require_relative  | 
| 5 | 
            -
            require_relative '../logger'
         | 
| 1 | 
            +
            require "erb"
         | 
| 2 | 
            +
            require "yaml"
         | 
| 3 | 
            +
            require_relative "text_actions"
         | 
| 4 | 
            +
            require_relative "../logger"
         | 
| 6 5 |  | 
| 7 6 | 
             
            class Lang
         | 
| 8 7 | 
             
              include TextActions
         | 
| 9 8 |  | 
| 10 | 
            -
              attr_reader :code | 
| 9 | 
            +
              attr_reader :code
         | 
| 10 | 
            +
              attr_reader :mistakes
         | 
| 11 11 |  | 
| 12 | 
            -
              def initialize(code =  | 
| 12 | 
            +
              def initialize(code = "en")
         | 
| 13 13 | 
             
                @code = code.to_s
         | 
| 14 14 | 
             
                load_files
         | 
| 15 15 | 
             
              end
         | 
| @@ -21,23 +21,23 @@ class Lang | |
| 21 21 | 
             
              private
         | 
| 22 22 |  | 
| 23 23 | 
             
              def load_files
         | 
| 24 | 
            -
                dirbase = File.join(File.dirname(__FILE__),  | 
| 25 | 
            -
                filepath = File.join(dirbase, @code,  | 
| 24 | 
            +
                dirbase = File.join(File.dirname(__FILE__), "..", "files", "language")
         | 
| 25 | 
            +
                filepath = File.join(dirbase, @code, "templates.yaml")
         | 
| 26 26 | 
             
                @templates = load_yaml_file(filepath)
         | 
| 27 | 
            -
                filepath = File.join(dirbase, @code,  | 
| 27 | 
            +
                filepath = File.join(dirbase, @code, "connectors.yaml")
         | 
| 28 28 | 
             
                @connectors = load_yaml_file(filepath)
         | 
| 29 | 
            -
                filepath = File.join(dirbase, @code,  | 
| 29 | 
            +
                filepath = File.join(dirbase, @code, "mistakes.yaml")
         | 
| 30 30 | 
             
                @mistakes = load_yaml_file(filepath)
         | 
| 31 31 | 
             
              end
         | 
| 32 32 |  | 
| 33 33 | 
             
              def load_yaml_file(filepath)
         | 
| 34 34 | 
             
                begin
         | 
| 35 35 | 
             
                  content = YAML.load(File.new(filepath))
         | 
| 36 | 
            -
                rescue  | 
| 37 | 
            -
                  Logger. | 
| 38 | 
            -
             | 
| 39 | 
            -
                  Logger. | 
| 40 | 
            -
                   | 
| 36 | 
            +
                rescue => e
         | 
| 37 | 
            +
                  Logger.error "Lang: YAML loading error (#{filepath})"
         | 
| 38 | 
            +
                  Logger.error "    : Revise apostrophe into string without \\ symbol"
         | 
| 39 | 
            +
                  Logger.error "    : #{e}"
         | 
| 40 | 
            +
                  exit 1
         | 
| 41 41 | 
             
                end
         | 
| 42 42 | 
             
                content
         | 
| 43 43 | 
             
              end
         | 
| @@ -1,27 +1,26 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require_relative  | 
| 5 | 
            -
            require_relative  | 
| 3 | 
            +
            require "singleton"
         | 
| 4 | 
            +
            require_relative "lang"
         | 
| 5 | 
            +
            require_relative "../application"
         | 
| 6 | 
            +
            require_relative "../logger"
         | 
| 6 7 |  | 
| 7 8 | 
             
            ##
         | 
| 8 | 
            -
            # LangFactory singleton class.
         | 
| 9 9 | 
             
            # * Read all language codes defined into configuration file
         | 
| 10 10 | 
             
            # * and load every language
         | 
| 11 11 | 
             
            # Lang objects are reused
         | 
| 12 12 | 
             
            class LangFactory
         | 
| 13 13 | 
             
              include Singleton
         | 
| 14 14 |  | 
| 15 | 
            -
              ##
         | 
| 16 | 
            -
              # Read all language codes from configuration file and load every language
         | 
| 17 15 | 
             
              def initialize
         | 
| 18 | 
            -
                 | 
| 16 | 
            +
                # Read all language codes from configuration file and load every language
         | 
| 17 | 
            +
                @default = Application.instance.config["languages"]["default"].downcase
         | 
| 19 18 | 
             
                @langs = {}
         | 
| 20 | 
            -
                Application.instance.config[ | 
| 19 | 
            +
                Application.instance.config["languages"].each_pair do |key, value|
         | 
| 21 20 | 
             
                  code = key.downcase
         | 
| 22 | 
            -
                  next if code ==  | 
| 21 | 
            +
                  next if code == "default"
         | 
| 23 22 |  | 
| 24 | 
            -
                  @langs[code] = Lang.new(code) if value.downcase ==  | 
| 23 | 
            +
                  @langs[code] = Lang.new(code) if value.downcase == "yes"
         | 
| 25 24 | 
             
                end
         | 
| 26 25 | 
             
              end
         | 
| 27 26 |  | 
| @@ -31,15 +30,13 @@ class LangFactory | |
| 31 30 | 
             
              def get(code)
         | 
| 32 31 | 
             
                return @langs[code] unless @langs[code].nil?
         | 
| 33 32 |  | 
| 34 | 
            -
                 | 
| 35 | 
            -
                 | 
| 36 | 
            -
                 | 
| 37 | 
            -
                 | 
| 33 | 
            +
                Logger.error "LangFactory: Unknown Lang with [code=#{code}]"
         | 
| 34 | 
            +
                Logger.error "             (a) Revise input file code lang"
         | 
| 35 | 
            +
                Logger.error "             (b) Revise asker.ini configuration"
         | 
| 36 | 
            +
                Logger.error "             (c) Revise template files"
         | 
| 38 37 | 
             
                exit 1
         | 
| 39 38 | 
             
              end
         | 
| 40 39 |  | 
| 41 | 
            -
              ##
         | 
| 42 | 
            -
              # Return default Lang object
         | 
| 43 40 | 
             
              def default
         | 
| 44 41 | 
             
                get(@default)
         | 
| 45 42 | 
             
              end
         | 
| @@ -1,9 +1,10 @@ | |
| 1 1 | 
             
            # encoding: utf-8
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 3 | 
            +
            require_relative "../logger"
         | 
| 4 | 
            +
             | 
| 5 5 | 
             
            module TextActions
         | 
| 6 6 | 
             
              ##
         | 
| 7 | 
            +
              # Set of functions used by Lang class
         | 
| 7 8 | 
             
              # Return text indicated by lang code...
         | 
| 8 9 | 
             
              def text_for(option, *input)
         | 
| 9 10 | 
             
                @_text1 = input[0] # FIXME: done to avoid linter complaints.
         | 
| @@ -14,9 +15,10 @@ module TextActions | |
| 14 15 | 
             
                @_text6 = input[5]
         | 
| 15 16 | 
             
                @_text7 = input[6]
         | 
| 16 17 |  | 
| 17 | 
            -
                 | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 18 | 
            +
                if @templates[option].nil?
         | 
| 19 | 
            +
                  Logger.error "TextActions: Unknown template (#{option})"
         | 
| 20 | 
            +
                  exit 1
         | 
| 21 | 
            +
                end
         | 
| 20 22 | 
             
                renderer = ERB.new(@templates[option])
         | 
| 21 23 | 
             
                renderer.result(binding)
         | 
| 22 24 | 
             
              end
         | 
| @@ -55,12 +57,12 @@ module TextActions | |
| 55 57 | 
             
              # @param filter (Boolean) true => apply filter, false => dont filter
         | 
| 56 58 | 
             
              # @return Array
         | 
| 57 59 | 
             
              def text_filter_connectors(input, filter)
         | 
| 58 | 
            -
                input_lines = input.split( | 
| 60 | 
            +
                input_lines = input.split(".")
         | 
| 59 61 | 
             
                output_lines = []
         | 
| 60 62 | 
             
                output_words = []
         | 
| 61 63 | 
             
                input_lines.each_with_index do |line, rowindex|
         | 
| 62 64 | 
             
            	    row = []
         | 
| 63 | 
            -
                  line.split( | 
| 65 | 
            +
                  line.split(" ").each_with_index do |word, colindex|
         | 
| 64 66 | 
             
                    flag = @connectors.include? word.downcase
         | 
| 65 67 |  | 
| 66 68 | 
             
            	      # if <word> is a conector and <pFilter>==true Then Choose this <word>
         | 
| @@ -72,7 +74,7 @@ module TextActions | |
| 72 74 | 
             
                      row << word
         | 
| 73 75 | 
             
                    end
         | 
| 74 76 | 
             
                  end
         | 
| 75 | 
            -
                  row <<  | 
| 77 | 
            +
                  row << "."
         | 
| 76 78 | 
             
                  output_lines << row
         | 
| 77 79 | 
             
                end
         | 
| 78 80 |  | 
| @@ -103,12 +105,12 @@ module TextActions | |
| 103 105 | 
             
                lines = input_struct[:lines]
         | 
| 104 106 | 
             
                indexes = input_indexes.sort
         | 
| 105 107 | 
             
                counter = 1
         | 
| 106 | 
            -
                text =  | 
| 108 | 
            +
                text = ""
         | 
| 107 109 |  | 
| 108 110 | 
             
                lines.each do |line|
         | 
| 109 111 | 
             
                  line.each do |value|
         | 
| 110 112 | 
             
                    if value.class == String
         | 
| 111 | 
            -
                      text += ( | 
| 113 | 
            +
                      text += (" " + value)
         | 
| 112 114 | 
             
                    elsif value == value.to_i
         | 
| 113 115 | 
             
                      # INFO: ruby 2.4 unifies Fixnum and Bignum into Integer
         | 
| 114 116 | 
             
                      #       Avoid using deprecated classes.
         | 
| @@ -117,14 +119,14 @@ module TextActions | |
| 117 119 | 
             
                        counter += 1
         | 
| 118 120 | 
             
                      else
         | 
| 119 121 | 
             
                        word = input_struct[:words][value][:word]
         | 
| 120 | 
            -
                        text += ( | 
| 122 | 
            +
                        text += (" " + word)
         | 
| 121 123 | 
             
                      end
         | 
| 122 124 | 
             
                    end
         | 
| 123 125 | 
             
                  end
         | 
| 124 126 | 
             
                end
         | 
| 125 | 
            -
                text.gsub!( | 
| 126 | 
            -
                text.gsub!( | 
| 127 | 
            -
                text = text[1, text.size] if text[0] ==  | 
| 127 | 
            +
                text.gsub!(" .", ".")
         | 
| 128 | 
            +
                text.gsub!(" ,", ",")
         | 
| 129 | 
            +
                text = text[1, text.size] if text[0] == " "
         | 
| 128 130 | 
             
                text
         | 
| 129 131 | 
             
              end
         | 
| 130 132 |  | 
| @@ -174,20 +176,20 @@ module TextActions | |
| 174 176 | 
             
                end
         | 
| 175 177 | 
             
                return text if text != input
         | 
| 176 178 |  | 
| 177 | 
            -
                text +  | 
| 179 | 
            +
                text + "s"
         | 
| 178 180 | 
             
              end
         | 
| 179 181 |  | 
| 180 182 | 
             
              def hide_text(input_text)
         | 
| 181 183 | 
             
                input = input_text.clone
         | 
| 182 184 | 
             
                if count_words(input) < 2 && input.size < 10
         | 
| 183 | 
            -
                  output =  | 
| 185 | 
            +
                  output = "[*]"
         | 
| 184 186 | 
             
                else
         | 
| 185 | 
            -
                  output =  | 
| 187 | 
            +
                  output = ""
         | 
| 186 188 | 
             
                  input.each_char do |char|
         | 
| 187 189 | 
             
                    if ' !|"@#$%&/()=?¿¡+*(){}[],.-_<>'.include? char
         | 
| 188 190 | 
             
                      output += char
         | 
| 189 191 | 
             
                    else
         | 
| 190 | 
            -
                      output +=  | 
| 192 | 
            +
                      output += "?"
         | 
| 191 193 | 
             
                    end
         | 
| 192 194 | 
             
                  end
         | 
| 193 195 | 
             
                end
         | 
| @@ -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: Unknown 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 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,35 +16,34 @@ 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: Unknown URL type (#{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 = [ | 
| 40 | 
            +
                extens = [".jpg", ".jpeg", ".png"]
         | 
| 41 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 = [ | 
| 46 | 
            +
                extens = [".mp4", ".ogv"]
         | 
| 47 47 | 
             
                extens.each { |ext| return true if filename.downcase.end_with?(ext) }
         | 
| 48 48 | 
             
                false
         | 
| 49 49 | 
             
              end
         | 
| @@ -55,7 +55,7 @@ module EmbeddedFile | |
| 55 55 | 
             
              end
         | 
| 56 56 |  | 
| 57 57 | 
             
              def self.load_audio(value, localdir)
         | 
| 58 | 
            -
                output = { | 
| 58 | 
            +
                output = {text: :error, file: :none, type: :audio}
         | 
| 59 59 |  | 
| 60 60 | 
             
                if is_url? value
         | 
| 61 61 | 
             
                  output[:text] = "<audio src=\"#{value}\" controls></audio>"
         | 
| @@ -66,14 +66,14 @@ module EmbeddedFile | |
| 66 66 |  | 
| 67 67 | 
             
                filepath = File.join(localdir, value)
         | 
| 68 68 | 
             
                unless File.exist?(filepath)
         | 
| 69 | 
            -
                  Logger. | 
| 69 | 
            +
                  Logger.error "EmbebbedFile: Audio file no exists (#{filepath})"
         | 
| 70 70 | 
             
                  exit 1
         | 
| 71 71 | 
             
                end
         | 
| 72 72 | 
             
                output[:text] = '<audio controls><source src="@@PLUGINFILE@@/' + File.basename(filepath) \
         | 
| 73 73 | 
             
                                + '">Your browser does not support the audio tag.</audio>'
         | 
| 74 74 | 
             
                output[:file] = '<file name="' + File.basename(filepath) \
         | 
| 75 75 | 
             
                                + '" path="/" encoding="base64">' \
         | 
| 76 | 
            -
                                + Base64.strict_encode64(File.open(filepath,  | 
| 76 | 
            +
                                + Base64.strict_encode64(File.open(filepath, "rb").read) \
         | 
| 77 77 | 
             
                                + "</file>"
         | 
| 78 78 | 
             
                output[:type] = :audio
         | 
| 79 79 | 
             
                output
         | 
| @@ -91,14 +91,14 @@ module EmbeddedFile | |
| 91 91 |  | 
| 92 92 | 
             
                filepath = File.join(localdir, value)
         | 
| 93 93 | 
             
                unless File.exist?(filepath)
         | 
| 94 | 
            -
                  Logger. | 
| 94 | 
            +
                  Logger.error "EmbeddedFile: Unknown file (#{filepath})"
         | 
| 95 95 | 
             
                  exit 1
         | 
| 96 96 | 
             
                end
         | 
| 97 97 | 
             
                output[:text] = '<img src="@@PLUGINFILE@@/' + File.basename(filepath) \
         | 
| 98 98 | 
             
                                + '" alt="imagen" class="img-responsive atto_image_button_text-bottom">'
         | 
| 99 99 | 
             
                output[:file] = '<file name="' + File.basename(filepath) \
         | 
| 100 100 | 
             
                                + '" path="/" encoding="base64">' \
         | 
| 101 | 
            -
                                + Base64.strict_encode64(File.open(filepath,  | 
| 101 | 
            +
                                + Base64.strict_encode64(File.open(filepath, "rb").read) \
         | 
| 102 102 | 
             
                                + "</file>"
         | 
| 103 103 | 
             
                output[:type] = :image
         | 
| 104 104 | 
             
                output
         | 
| @@ -116,7 +116,7 @@ module EmbeddedFile | |
| 116 116 |  | 
| 117 117 | 
             
                filepath = File.join(localdir, value)
         | 
| 118 118 | 
             
                unless File.exist?(filepath)
         | 
| 119 | 
            -
                  Logger. | 
| 119 | 
            +
                  Logger.error "Unknown file (#{filepath})"
         | 
| 120 120 | 
             
                  exit 1
         | 
| 121 121 | 
             
                end
         | 
| 122 122 | 
             
                output[:text] = '<video controls><source src="@@PLUGINFILE@@/' \
         |