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
    
        data/lib/asker/cli.rb
    CHANGED
    
    | @@ -1,50 +1,70 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 5 | 
            -
            require_relative  | 
| 6 | 
            -
            require_relative  | 
| 3 | 
            +
            require "rainbow"
         | 
| 4 | 
            +
            require "thor"
         | 
| 5 | 
            +
            require_relative "version"
         | 
| 6 | 
            +
            require_relative "../asker"
         | 
| 7 7 |  | 
| 8 8 | 
             
            ##
         | 
| 9 9 | 
             
            # Command Line User Interface
         | 
| 10 10 | 
             
            class CLI < Thor
         | 
| 11 | 
            -
              map [ | 
| 11 | 
            +
              map ["-h", "--help"] => "help"
         | 
| 12 12 |  | 
| 13 | 
            -
              map [ | 
| 14 | 
            -
              desc  | 
| 13 | 
            +
              map ["-v", "--version"] => "version"
         | 
| 14 | 
            +
              desc "version", "Show the program version"
         | 
| 15 15 | 
             
              def version
         | 
| 16 16 | 
             
                puts "#{Asker::NAME} version #{Asker::VERSION}"
         | 
| 17 17 | 
             
                exit 0
         | 
| 18 18 | 
             
              end
         | 
| 19 19 |  | 
| 20 | 
            -
              map [ | 
| 21 | 
            -
              desc  | 
| 20 | 
            +
              map ["--init"] => "init"
         | 
| 21 | 
            +
              desc "init", "Create default INI config file"
         | 
| 22 22 | 
             
              def init
         | 
| 23 23 | 
             
                Asker.init
         | 
| 24 24 | 
             
                exit 0
         | 
| 25 25 | 
             
              end
         | 
| 26 26 |  | 
| 27 | 
            -
              map [ | 
| 28 | 
            -
              desc  | 
| 29 | 
            -
               | 
| 30 | 
            -
             | 
| 31 | 
            -
               | 
| 27 | 
            +
              map ["new", "--new"] => "create_input"
         | 
| 28 | 
            +
              desc "new PATH", "Create Asker sample input file"
         | 
| 29 | 
            +
              long_desc <<-LONGDESC
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              Create Asker sample input file (HAML format).
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              Examples:
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              (1) #{Rainbow("asker new foo/bar.haml").aqua}, Create "foo" dir and "bar.haml" file.
         | 
| 36 | 
            +
              Path to input file can be relative or absolute.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              (2) #{Rainbow("asker new foo").aqua}, Create "foo" dir and sample HAML file.
         | 
| 39 | 
            +
              Path to directory can be relative or absolute.
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              LONGDESC
         | 
| 32 42 | 
             
              def create_input(dirname)
         | 
| 33 43 | 
             
                Asker.create_input(dirname)
         | 
| 34 44 | 
             
                exit 0
         | 
| 35 45 | 
             
              end
         | 
| 36 46 |  | 
| 37 | 
            -
              map [ | 
| 38 | 
            -
              desc  | 
| 47 | 
            +
              map ["--check"] => "check"
         | 
| 48 | 
            +
              desc "check FILEPATH", "Check HAML input file syntax"
         | 
| 49 | 
            +
              long_desc <<-LONGDESC
         | 
| 50 | 
            +
             | 
| 51 | 
            +
              Check HAML input file syntax.
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              Examples:
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              (*) #{Rainbow("asker check foo/bar.haml").aqua}, Check "bar.haml" input file.
         | 
| 56 | 
            +
              Path to input file can be relative or absolute.
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              LONGDESC
         | 
| 39 59 | 
             
              def check(filename)
         | 
| 40 60 | 
             
                # Enable/disable color output
         | 
| 41 | 
            -
                Rainbow.enabled = false if options[ | 
| 61 | 
            +
                Rainbow.enabled = false if options["color"] == false
         | 
| 42 62 | 
             
                # Asker start processing input file
         | 
| 43 63 | 
             
                Asker.check(filename)
         | 
| 44 64 | 
             
              end
         | 
| 45 65 |  | 
| 46 | 
            -
              map [ | 
| 47 | 
            -
              desc  | 
| 66 | 
            +
              map ["f", "-f", "--file"] => "file"
         | 
| 67 | 
            +
              desc "[file] FILEPATH", "Build output files, from HAML/XML input file."
         | 
| 48 68 | 
             
              long_desc <<-LONGDESC
         | 
| 49 69 |  | 
| 50 70 | 
             
              Build questions about contents defined into input file specified.
         | 
| @@ -53,15 +73,14 @@ class CLI < Thor | |
| 53 73 |  | 
| 54 74 | 
             
              Examples:
         | 
| 55 75 |  | 
| 56 | 
            -
              (1) #{Rainbow( | 
| 76 | 
            +
              (1) #{Rainbow("asker foo/bar.haml").aqua}, Build questions from HAML input file.
         | 
| 77 | 
            +
              Path to input file can be relative or absolute.
         | 
| 57 78 |  | 
| 58 | 
            -
              (2) #{Rainbow( | 
| 59 | 
            -
             | 
| 60 | 
            -
              (3) #{Rainbow('asker projects/foo/foo.yaml').aqua}, Build questions from YAML project file.
         | 
| 79 | 
            +
              (2) #{Rainbow("asker foo/bar.xml").aqua}, Build questions from XML input file.
         | 
| 80 | 
            +
              Path to input file can be relative or absolute.
         | 
| 61 81 |  | 
| 62 82 | 
             
              LONGDESC
         | 
| 63 83 | 
             
              def file(filename)
         | 
| 64 | 
            -
                # Asker start processing input file
         | 
| 65 84 | 
             
                Asker.start(filename)
         | 
| 66 85 | 
             
              end
         | 
| 67 86 |  | 
    
        data/lib/asker/data/code.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "../ai/code/code_ai_factory"
         | 
| 4 | 
            +
            require_relative "../logger"
         | 
| 4 5 |  | 
| 5 6 | 
             
            class Code
         | 
| 6 7 | 
             
              attr_reader :dirname, :filename, :type
         | 
| @@ -23,7 +24,7 @@ class Code | |
| 23 24 | 
             
              end
         | 
| 24 25 |  | 
| 25 26 | 
             
              def lines_to_s(lines)
         | 
| 26 | 
            -
                out =  | 
| 27 | 
            +
                out = ""
         | 
| 27 28 | 
             
                lines.each_with_index do |line, index|
         | 
| 28 29 | 
             
                  out << format("%2d| #{line}\n", (index + 1))
         | 
| 29 30 | 
             
                end
         | 
| @@ -36,7 +37,7 @@ class Code | |
| 36 37 | 
             
                return if filepath.nil?
         | 
| 37 38 |  | 
| 38 39 | 
             
                unless File.exist? filepath
         | 
| 39 | 
            -
                   | 
| 40 | 
            +
                  Logger.warn "Code: Unknown file (#{filepath})"
         | 
| 40 41 | 
             
                  return
         | 
| 41 42 | 
             
                end
         | 
| 42 43 | 
             
                content = File.read(filepath)
         | 
| @@ -45,16 +46,16 @@ class Code | |
| 45 46 |  | 
| 46 47 | 
             
              def encode_and_split(text, encoding = :default)
         | 
| 47 48 | 
             
                # Convert text to UTF-8 deleting unknown chars
         | 
| 48 | 
            -
                text ||=  | 
| 49 | 
            -
                flag = [:default,  | 
| 50 | 
            -
                return text.encode( | 
| 49 | 
            +
                text ||= "" # Ensure text is not nil
         | 
| 50 | 
            +
                flag = [:default, "UTF-8"].include? encoding
         | 
| 51 | 
            +
                return text.encode("UTF-8", invalid: :replace).split("\n") if flag
         | 
| 51 52 |  | 
| 52 53 | 
             
                # Convert text from input ENCODING to UTF-8
         | 
| 53 | 
            -
                ec = Encoding::Converter.new(encoding.to_s,  | 
| 54 | 
            +
                ec = Encoding::Converter.new(encoding.to_s, "UTF-8")
         | 
| 54 55 | 
             
                begin
         | 
| 55 56 | 
             
                  text = ec.convert(text)
         | 
| 56 | 
            -
                rescue  | 
| 57 | 
            -
                   | 
| 57 | 
            +
                rescue => e
         | 
| 58 | 
            +
                  Logger.warn "Code: Encoding error (#{e}) with filename (#{@filename})"
         | 
| 58 59 | 
             
                end
         | 
| 59 60 |  | 
| 60 61 | 
             
                text.split("\n")
         | 
    
        data/lib/asker/data/column.rb
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require_relative "../logger"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            # Contain data information for every column
         | 
| 4 6 | 
             
            # Params:
         | 
| 5 7 | 
             
            # * +pRow+ - Parent row for this column
         | 
| @@ -9,28 +11,27 @@ class Column | |
| 9 11 | 
             
              attr_reader :row, :index, :id, :raw, :lang, :type, :simple
         | 
| 10 12 |  | 
| 11 13 | 
             
              ##
         | 
| 12 | 
            -
              # initialize Column
         | 
| 13 14 | 
             
              # @param row (Row)
         | 
| 14 15 | 
             
              # @param index (Integer)
         | 
| 15 16 | 
             
              # @param xml_data (XMLdata)
         | 
| 16 17 | 
             
              def initialize(row, index, xml_data)
         | 
| 17 | 
            -
                @row | 
| 18 | 
            -
                @index | 
| 19 | 
            -
                @id | 
| 20 | 
            -
                @raw | 
| 21 | 
            -
                @lang | 
| 22 | 
            -
                @type | 
| 23 | 
            -
                @simple = { | 
| 18 | 
            +
                @row = row
         | 
| 19 | 
            +
                @index = index
         | 
| 20 | 
            +
                @id = "#{@row.id}.#{@index}"
         | 
| 21 | 
            +
                @raw = ""
         | 
| 22 | 
            +
                @lang = @row.langs[@index]
         | 
| 23 | 
            +
                @type = @row.types[@index]
         | 
| 24 | 
            +
                @simple = {lang: true, type: true}
         | 
| 24 25 | 
             
                read_data_from_xml(xml_data)
         | 
| 25 26 | 
             
              end
         | 
| 26 27 |  | 
| 27 28 | 
             
              def to_html
         | 
| 28 29 | 
             
                case @type
         | 
| 29 | 
            -
                when  | 
| 30 | 
            +
                when "text"
         | 
| 30 31 | 
             
                  return @raw
         | 
| 31 | 
            -
                when  | 
| 32 | 
            -
                  return "<img src=\"#{raw}\" alt | 
| 33 | 
            -
                when  | 
| 32 | 
            +
                when "image_url"
         | 
| 33 | 
            +
                  return "<img src=\"#{raw}\" alt=\"image\">"
         | 
| 34 | 
            +
                when "textfile_path"
         | 
| 34 35 | 
             
                  return "<pre>#{raw}</pre>"
         | 
| 35 36 | 
             
                end
         | 
| 36 37 | 
             
                "ERROR type #{@type}"
         | 
| @@ -39,7 +40,10 @@ class Column | |
| 39 40 | 
             
              private
         | 
| 40 41 |  | 
| 41 42 | 
             
              def read_data_from_xml(xml_data)
         | 
| 42 | 
            -
                 | 
| 43 | 
            +
                if xml_data.elements.count.positive?
         | 
| 44 | 
            +
                  Logger.error "Column: Do not use elements!"
         | 
| 45 | 
            +
                  exit 1
         | 
| 46 | 
            +
                end
         | 
| 43 47 |  | 
| 44 48 | 
             
                @raw = xml_data.text.strip.to_s
         | 
| 45 49 |  | 
| @@ -49,9 +53,9 @@ class Column | |
| 49 53 | 
             
              end
         | 
| 50 54 |  | 
| 51 55 | 
             
              def read_lang_from_xml(xml_data)
         | 
| 52 | 
            -
                return unless xml_data.attributes[ | 
| 56 | 
            +
                return unless xml_data.attributes["lang"]
         | 
| 53 57 |  | 
| 54 | 
            -
                code = xml_data.attributes[ | 
| 58 | 
            +
                code = xml_data.attributes["lang"].strip
         | 
| 55 59 | 
             
                return if code == @lang.code
         | 
| 56 60 |  | 
| 57 61 | 
             
                @lang = LangFactory.instance.get(code)
         | 
| @@ -60,9 +64,9 @@ class Column | |
| 60 64 | 
             
              end
         | 
| 61 65 |  | 
| 62 66 | 
             
              def read_type_from_xml(xml_data)
         | 
| 63 | 
            -
                return unless xml_data.attributes[ | 
| 67 | 
            +
                return unless xml_data.attributes["type"]
         | 
| 64 68 |  | 
| 65 | 
            -
                type = xml_data.attributes[ | 
| 69 | 
            +
                type = xml_data.attributes["type"].strip
         | 
| 66 70 | 
             
                return if type == @type.to_s
         | 
| 67 71 |  | 
| 68 72 | 
             
                @type = type
         | 
    
        data/lib/asker/data/concept.rb
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 | 
            -
            require  | 
| 2 | 
            -
            require 'rexml/document'
         | 
| 1 | 
            +
            require "rexml/document"
         | 
| 3 2 |  | 
| 4 | 
            -
            require_relative  | 
| 5 | 
            -
            require_relative  | 
| 6 | 
            -
            require_relative  | 
| 7 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "../lang/lang_factory"
         | 
| 4 | 
            +
            require_relative "../loader/embedded_file"
         | 
| 5 | 
            +
            require_relative "../logger"
         | 
| 6 | 
            +
            require_relative "table"
         | 
| 7 | 
            +
            require_relative "data_field"
         | 
| 8 8 |  | 
| 9 9 | 
             
            class Concept
         | 
| 10 10 | 
             
              attr_reader :id        # Unique identifer (Integer)
         | 
| 11 | 
            -
              attr_reader :lang      # Lang  | 
| 11 | 
            +
              attr_reader :lang      # Lang object
         | 
| 12 12 | 
             
              attr_reader :context   # Context inherits from map
         | 
| 13 13 | 
             
              attr_reader :names     # Names used to identify or name this concept
         | 
| 14 14 | 
             
              attr_reader :type      # type = text -> Name values are only text
         | 
| @@ -24,24 +24,16 @@ class Concept | |
| 24 24 | 
             
              # @param filename (String)
         | 
| 25 25 | 
             
              # @param lang_code (String)
         | 
| 26 26 | 
             
              # @param context (Array)
         | 
| 27 | 
            -
              def initialize(xml_data, filename,  | 
| 27 | 
            +
              def initialize(xml_data, filename, lang, context)
         | 
| 28 28 | 
             
                @@id += 1
         | 
| 29 29 | 
             
                @id = @@id
         | 
| 30 30 |  | 
| 31 31 | 
             
                @filename = filename
         | 
| 32 32 | 
             
                @process = false
         | 
| 33 | 
            -
                @lang = LangFactory.instance.get(lang_code)
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                 | 
| 36 | 
            -
             | 
| 37 | 
            -
                elsif context.nil?
         | 
| 38 | 
            -
                  @context = []
         | 
| 39 | 
            -
                else
         | 
| 40 | 
            -
                  @context = context.split(',')
         | 
| 41 | 
            -
                  @context.collect!(&:strip)
         | 
| 42 | 
            -
                end
         | 
| 43 | 
            -
                @names = ['concept.' + @id.to_s]
         | 
| 44 | 
            -
                @type  = 'text'
         | 
| 33 | 
            +
                @lang = lang # LangFactory.instance.get(lang_code)
         | 
| 34 | 
            +
                @context = context
         | 
| 35 | 
            +
                @names = ["concept." + @id.to_s]
         | 
| 36 | 
            +
                @type = "text"
         | 
| 45 37 |  | 
| 46 38 | 
             
                @data = {}
         | 
| 47 39 | 
             
                @data[:tags] = []
         | 
| @@ -60,7 +52,7 @@ class Concept | |
| 60 52 | 
             
              end
         | 
| 61 53 |  | 
| 62 54 | 
             
              def text
         | 
| 63 | 
            -
                @data[:texts][0] ||  | 
| 55 | 
            +
                @data[:texts][0] || "..."
         | 
| 64 56 | 
             
              end
         | 
| 65 57 |  | 
| 66 58 | 
             
              def process?
         | 
| @@ -71,16 +63,14 @@ class Concept | |
| 71 63 | 
             
                p = calculate_nearness_to_concept(other)
         | 
| 72 64 | 
             
                return if p.zero?
         | 
| 73 65 |  | 
| 74 | 
            -
                @data[:neighbors] << { | 
| 66 | 
            +
                @data[:neighbors] << {concept: other, value: p}
         | 
| 75 67 | 
             
                # Sort neighbors list
         | 
| 76 68 | 
             
                @data[:neighbors].sort! { |a, b| a[:value] <=> b[:value] }
         | 
| 77 69 | 
             
                @data[:neighbors].reverse!
         | 
| 78 70 | 
             
              end
         | 
| 79 71 |  | 
| 80 72 | 
             
              def calculate_nearness_to_concept(other)
         | 
| 81 | 
            -
                 | 
| 82 | 
            -
                # Application.instance.config['ai']['formula_weights']
         | 
| 83 | 
            -
                weights = a.split(',').map(&:to_f)
         | 
| 73 | 
            +
                weights = ProjectData.instance.get(:weights).split(",").map(&:to_f)
         | 
| 84 74 |  | 
| 85 75 | 
             
                max1 = @context.count
         | 
| 86 76 | 
             
                max2 = @data[:tags].count
         | 
| @@ -105,7 +95,7 @@ class Concept | |
| 105 95 | 
             
                end
         | 
| 106 96 | 
             
                @data[:texts].each do |t|
         | 
| 107 97 | 
             
                  text = t.clone
         | 
| 108 | 
            -
                  text.split( | 
| 98 | 
            +
                  text.split(" ").each do |word|
         | 
| 109 99 | 
             
                    reference_to += 1 unless other.names.index(word.downcase).nil?
         | 
| 110 100 | 
             
                  end
         | 
| 111 101 | 
             
                end
         | 
| @@ -157,26 +147,25 @@ class Concept | |
| 157 147 | 
             
                  when "table"
         | 
| 158 148 | 
             
                    @data[:tables] << Table.new(self, i)
         | 
| 159 149 | 
             
                  else
         | 
| 160 | 
            -
                     | 
| 161 | 
            -
                    puts Rainbow(text).color(:red)
         | 
| 150 | 
            +
                    Logger.warn "Concept #{name} with unkown attribute: #{i.name}"
         | 
| 162 151 | 
             
                  end
         | 
| 163 152 | 
             
                end
         | 
| 164 153 | 
             
              end
         | 
| 165 154 |  | 
| 166 155 | 
             
              def process_names(value)
         | 
| 167 156 | 
             
                @names = []
         | 
| 168 | 
            -
                j = value.text.split( | 
| 157 | 
            +
                j = value.text.split(",")
         | 
| 169 158 | 
             
                j.each { |k| @names << k.strip }
         | 
| 170 | 
            -
                @type = value.attributes[ | 
| 159 | 
            +
                @type = value.attributes["type"].strip if value.attributes["type"]
         | 
| 171 160 | 
             
              end
         | 
| 172 161 |  | 
| 173 162 | 
             
              def process_tags(value)
         | 
| 174 163 | 
             
                if value.text.nil? || value.text.size.zero?
         | 
| 175 | 
            -
                   | 
| 176 | 
            -
                   | 
| 164 | 
            +
                  Logger.warn "Concept: Concept #{name} without <tags>"
         | 
| 165 | 
            +
                  return []
         | 
| 177 166 | 
             
                end
         | 
| 178 167 |  | 
| 179 | 
            -
                @data[:tags] = value.text.split( | 
| 168 | 
            +
                @data[:tags] = value.text.split(",")
         | 
| 180 169 | 
             
                @data[:tags].collect!(&:strip)
         | 
| 181 170 | 
             
              end
         | 
| 182 171 |  | 
| @@ -190,14 +179,12 @@ class Concept | |
| 190 179 | 
             
                  @data[:images] << EmbeddedFile.load(value.text.strip, File.dirname(@filename))
         | 
| 191 180 | 
             
                when nil
         | 
| 192 181 | 
             
                  if value.text.nil?
         | 
| 193 | 
            -
                    warn  | 
| 182 | 
            +
                    Logger.warn "Concept: def/text empty!"
         | 
| 194 183 | 
             
                  else
         | 
| 195 184 | 
             
                    @data[:texts] << value.text.strip
         | 
| 196 185 | 
             
                  end
         | 
| 197 186 | 
             
                else
         | 
| 198 | 
            -
                   | 
| 199 | 
            -
                  puts Rainbow(msg).red.bright
         | 
| 200 | 
            -
                  exit 1
         | 
| 187 | 
            +
                  Logger.warn "Concept: Unknown def/type (#{value.attributes["type"]})"
         | 
| 201 188 | 
             
                end
         | 
| 202 189 | 
             
              end
         | 
| 203 190 | 
             
            end
         | 
| @@ -12,7 +12,7 @@ class DataField | |
| 12 12 | 
             
              # initialize DataField
         | 
| 13 13 | 
             
              def initialize(data, id, type)
         | 
| 14 14 | 
             
                @data = data
         | 
| 15 | 
            -
                @id | 
| 15 | 
            +
                @id = id.to_i # TODO: revise where it comes from? Is it unique value?
         | 
| 16 16 | 
             
                @type = type.to_sym
         | 
| 17 17 | 
             
              end
         | 
| 18 18 |  | 
| @@ -94,7 +94,7 @@ class DataField | |
| 94 94 | 
             
              # rubocop:enable Metrics/MethodLength
         | 
| 95 95 |  | 
| 96 96 | 
             
              def to_screen(text)
         | 
| 97 | 
            -
                return text[0, 7] +  | 
| 97 | 
            +
                return text[0, 7] + "..." + text[-15, 15] if text.size > 25
         | 
| 98 98 |  | 
| 99 99 | 
             
                text
         | 
| 100 100 | 
             
              end
         | 
| @@ -0,0 +1,112 @@ | |
| 1 | 
            +
            require_relative "../logger"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Problem
         | 
| 4 | 
            +
              attr_accessor :lang
         | 
| 5 | 
            +
              attr_accessor :context
         | 
| 6 | 
            +
              attr_accessor :process
         | 
| 7 | 
            +
              attr_accessor :filename
         | 
| 8 | 
            +
              attr_accessor :varnames
         | 
| 9 | 
            +
              attr_accessor :cases
         | 
| 10 | 
            +
              attr_accessor :descs
         | 
| 11 | 
            +
              attr_accessor :asks
         | 
| 12 | 
            +
              attr_accessor :questions
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              @@id = 0
         | 
| 15 | 
            +
              def initialize
         | 
| 16 | 
            +
                @@id += 1
         | 
| 17 | 
            +
                @id = @@id
         | 
| 18 | 
            +
                @lang = nil
         | 
| 19 | 
            +
                @context = nil
         | 
| 20 | 
            +
                @process = false
         | 
| 21 | 
            +
                @filename = "?"
         | 
| 22 | 
            +
                @varnames = []
         | 
| 23 | 
            +
                @cases = []
         | 
| 24 | 
            +
                @descs = []
         | 
| 25 | 
            +
                @asks = []
         | 
| 26 | 
            +
                @questions = []
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def self.from(values)
         | 
| 30 | 
            +
                problem = Problem.new
         | 
| 31 | 
            +
                fields = %i[filename varnames cases descs asks]
         | 
| 32 | 
            +
                fields.each do |fieldname|
         | 
| 33 | 
            +
                  methodname = "#{fieldname}=".to_sym
         | 
| 34 | 
            +
                  problem.send(methodname, values[fieldname])
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
                problem.validate
         | 
| 37 | 
            +
                problem
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              def desc
         | 
| 41 | 
            +
                @descs.first
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def process?
         | 
| 45 | 
            +
                @process
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def name
         | 
| 49 | 
            +
                "problem#{@id}"
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def validate
         | 
| 53 | 
            +
                validate_varnames
         | 
| 54 | 
            +
                validate_cases
         | 
| 55 | 
            +
                validate_asks
         | 
| 56 | 
            +
                validate_descs
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              private
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              def validate_varnames
         | 
| 62 | 
            +
                if !@varnames.size.zero? && @cases.size.zero?
         | 
| 63 | 
            +
                  Logger.warn "Problem: No problem/varnames defined with no problem/case"
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                @varnames.each_with_index do |varname1, index1|
         | 
| 67 | 
            +
                  @varnames.each_with_index do |varname2, index2|
         | 
| 68 | 
            +
                    next if index1 == index2
         | 
| 69 | 
            +
                    if varname1.include? varname2
         | 
| 70 | 
            +
                      Logger.error "Problem: varname(#{varname1}) includes varname(#{varname2}). Change one of them!"
         | 
| 71 | 
            +
                      exit 1
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def validate_cases
         | 
| 78 | 
            +
                @cases.each do |acase|
         | 
| 79 | 
            +
                  if acase.size != @varnames.size
         | 
| 80 | 
            +
                    Logger.error "Problem: problem/cases size not equal to problem/varnames size"
         | 
| 81 | 
            +
                    Logger.error "       : cases size #{acase.size} (#{acase.join(",")})"
         | 
| 82 | 
            +
                    Logger.error "       : varnames size #{@varnames.size} (#{@varnames.join(",")})"
         | 
| 83 | 
            +
                    exit 1
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
              end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
              def validate_asks
         | 
| 89 | 
            +
                if @asks.size.zero?
         | 
| 90 | 
            +
                  Logger.warn "Problem: No problem/ask"
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                @asks.each do |ask|
         | 
| 94 | 
            +
                  Logger.warn "Problem: No problem/ask/text" if ask[:text].nil?
         | 
| 95 | 
            +
                  if ask[:answer].nil? && ask[:steps].size.zero?
         | 
| 96 | 
            +
                    Logger.error "Problem: No problem/ask/answer and no problem/ask/steps"
         | 
| 97 | 
            +
                    exit 1
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
                  if !ask[:answer].nil? && !ask[:steps].size.zero?
         | 
| 100 | 
            +
                    Logger.error "Problem: Choose problem/ask/answer or problem/ask/steps"
         | 
| 101 | 
            +
                    exit 1
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
                  if ask[:steps].size > 0 && ask[:steps].size < 4
         | 
| 104 | 
            +
                    Logger.warn "Problem: problem/ask/steps less than 4"
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              def validate_descs
         | 
| 110 | 
            +
                # require "debug"; binding.break
         | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
            end
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 3 | 
            +
            require "singleton"
         | 
| 4 4 |  | 
| 5 5 | 
             
            class ProjectData
         | 
| 6 6 | 
             
              include Singleton
         | 
| @@ -11,11 +11,11 @@ class ProjectData | |
| 11 11 | 
             
              end
         | 
| 12 12 |  | 
| 13 13 | 
             
              def reset
         | 
| 14 | 
            -
                @default = { | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 14 | 
            +
                @default = {inputbasedir: FileUtils.pwd,
         | 
| 15 | 
            +
                            stages: {d: true, b: true, f: true, i: true, s: true, t: true},
         | 
| 16 | 
            +
                            threshold: 0.5,
         | 
| 17 | 
            +
                            outputdir: "output",
         | 
| 18 | 
            +
                            weights: "1, 1, 1"}
         | 
| 19 19 | 
             
                @param = {}
         | 
| 20 20 | 
             
              end
         | 
| 21 21 |  | 
| @@ -36,17 +36,15 @@ class ProjectData | |
| 36 36 | 
             
              # IMPORTANT: We need at least these values
         | 
| 37 37 | 
             
              # * process_file
         | 
| 38 38 | 
             
              # * inputdirs
         | 
| 39 | 
            -
              # rubocop:disable Metrics/MethodLength
         | 
| 40 | 
            -
              # rubocop:disable Metrics/AbcSize
         | 
| 41 39 | 
             
              def open
         | 
| 42 | 
            -
                ext = File.extname(@param[:process_file]) ||  | 
| 40 | 
            +
                ext = File.extname(@param[:process_file]) || ".haml"
         | 
| 43 41 | 
             
                @param[:projectname] = @param[:projectname] ||
         | 
| 44 | 
            -
             | 
| 42 | 
            +
                  File.basename(@param[:process_file], ext)
         | 
| 45 43 |  | 
| 46 | 
            -
                @param[:logname] = "#{@param[:projectname]} | 
| 44 | 
            +
                @param[:logname] = "#{@param[:projectname]}.log"
         | 
| 47 45 | 
             
                @param[:outputname] = "#{@param[:projectname]}-gift.txt"
         | 
| 48 | 
            -
                @param[:lessonname] = "#{@param[:projectname]} | 
| 49 | 
            -
                @param[:yamlname] = "#{@param[:projectname]}.yaml"
         | 
| 46 | 
            +
                @param[:lessonname] = "#{@param[:projectname]}.txt"
         | 
| 47 | 
            +
                @param[:yamlname] = "#{@param[:projectname]}-questions.yaml"
         | 
| 50 48 | 
             
                @param[:moodlename] = "#{@param[:projectname]}-moodle.xml"
         | 
| 51 49 |  | 
| 52 50 | 
             
                outputdir = get(:outputdir)
         | 
| @@ -58,6 +56,4 @@ class ProjectData | |
| 58 56 |  | 
| 59 57 | 
             
                Dir.mkdir(outputdir) unless Dir.exist?(outputdir)
         | 
| 60 58 | 
             
              end
         | 
| 61 | 
            -
              # rubocop:enable Metrics/MethodLength
         | 
| 62 | 
            -
              # rubocop:enable Metrics/AbcSize
         | 
| 63 59 | 
             
            end
         | 
    
        data/lib/asker/data/row.rb
    CHANGED
    
    | @@ -1,9 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "column"
         | 
| 4 | 
            +
            require_relative "../logger"
         | 
| 4 5 |  | 
| 5 | 
            -
            ##
         | 
| 6 | 
            -
            # Row objects
         | 
| 7 6 | 
             
            class Row
         | 
| 8 7 | 
             
              attr_reader :table, :index, :id
         | 
| 9 8 | 
             
              attr_reader :langs, :types, :raws, :columns
         | 
| @@ -15,14 +14,14 @@ class Row | |
| 15 14 | 
             
              # @param index (Integer)
         | 
| 16 15 | 
             
              # @param xml_data (XML)
         | 
| 17 16 | 
             
              def initialize(table, index, xml_data)
         | 
| 18 | 
            -
                @table | 
| 19 | 
            -
                @index | 
| 20 | 
            -
                @id | 
| 21 | 
            -
                @langs | 
| 22 | 
            -
                @types | 
| 23 | 
            -
                @raws | 
| 17 | 
            +
                @table = table
         | 
| 18 | 
            +
                @index = index
         | 
| 19 | 
            +
                @id = "#{@table.id}.#{@index}"
         | 
| 20 | 
            +
                @langs = @table.langs
         | 
| 21 | 
            +
                @types = @table.types
         | 
| 22 | 
            +
                @raws = []
         | 
| 24 23 | 
             
                @columns = []
         | 
| 25 | 
            -
                @simple | 
| 24 | 
            +
                @simple = {lang: true, type: true}
         | 
| 26 25 | 
             
                read_data_from_xml(xml_data)
         | 
| 27 26 | 
             
              end
         | 
| 28 27 |  | 
| @@ -40,14 +39,17 @@ class Row | |
| 40 39 | 
             
                  build_row_with_n_columns(xml_data)
         | 
| 41 40 | 
             
                end
         | 
| 42 41 |  | 
| 43 | 
            -
                 | 
| 42 | 
            +
                unless @columns.size == @table.fields.size
         | 
| 43 | 
            +
                  Logger.error "Row: columns size != table.fields.size (#{xml_data})"
         | 
| 44 | 
            +
                  exit 1
         | 
| 45 | 
            +
                end
         | 
| 44 46 | 
             
              end
         | 
| 45 47 |  | 
| 46 48 | 
             
              def build_row_with_1_column(xml_data)
         | 
| 47 49 | 
             
                # When row tag only has text, we add this text as one value array
         | 
| 48 50 | 
             
                # This is usefull for tables with only one columns
         | 
| 49 51 | 
             
                @columns = [Column.new(self, @raws.size, xml_data)]
         | 
| 50 | 
            -
                @raws | 
| 52 | 
            +
                @raws = [xml_data.text.strip.to_s]
         | 
| 51 53 |  | 
| 52 54 | 
             
                # read attributes from XML data
         | 
| 53 55 | 
             
                read_lang_from_xml(xml_data)
         | 
| @@ -55,9 +57,9 @@ class Row | |
| 55 57 | 
             
              end
         | 
| 56 58 |  | 
| 57 59 | 
             
              def read_lang_from_xml(xml_data)
         | 
| 58 | 
            -
                return unless xml_data.attributes[ | 
| 60 | 
            +
                return unless xml_data.attributes["lang"]
         | 
| 59 61 |  | 
| 60 | 
            -
                code = xml_data.attributes[ | 
| 62 | 
            +
                code = xml_data.attributes["lang"].strip
         | 
| 61 63 | 
             
                return if code == @langs[0].code
         | 
| 62 64 |  | 
| 63 65 | 
             
                @langs = [LangFactory.instance.get(code)]
         | 
| @@ -66,9 +68,9 @@ class Row | |
| 66 68 | 
             
              end
         | 
| 67 69 |  | 
| 68 70 | 
             
              def read_type_from_xml(xml_data)
         | 
| 69 | 
            -
                return unless xml_data.attributes[ | 
| 71 | 
            +
                return unless xml_data.attributes["type"]
         | 
| 70 72 |  | 
| 71 | 
            -
                type = xml_data.attributes[ | 
| 73 | 
            +
                type = xml_data.attributes["type"].strip
         | 
| 72 74 | 
             
                return if type == @types[0]
         | 
| 73 75 |  | 
| 74 76 | 
             
                @types = [type]
         | 
| @@ -80,11 +82,11 @@ class Row | |
| 80 82 | 
             
              def build_row_with_n_columns(xml_data)
         | 
| 81 83 | 
             
                xml_data.elements.each do |i|
         | 
| 82 84 | 
             
                  case i.name
         | 
| 83 | 
            -
                  when  | 
| 85 | 
            +
                  when "lang"
         | 
| 84 86 | 
             
                    read_langs_from_xml(i)
         | 
| 85 | 
            -
                  when  | 
| 87 | 
            +
                  when "type"
         | 
| 86 88 | 
             
                    read_types_from_xml(i)
         | 
| 87 | 
            -
                  when  | 
| 89 | 
            +
                  when "col"
         | 
| 88 90 | 
             
                    # When row has several columns, we add every value to the array
         | 
| 89 91 | 
             
                    @columns << Column.new(self, @raws.size, i) # Column Objects
         | 
| 90 92 | 
             
                    @raws << i.text.to_s
         | 
| @@ -94,9 +96,9 @@ class Row | |
| 94 96 | 
             
              # rubocop:enable Metrics/MethodLength
         | 
| 95 97 |  | 
| 96 98 | 
             
              def read_langs_from_xml(xml_data)
         | 
| 97 | 
            -
                j = xml_data.text.split( | 
| 99 | 
            +
                j = xml_data.text.split(",")
         | 
| 98 100 | 
             
                codes = @langs.map(&:code)
         | 
| 99 | 
            -
                return if j.join( | 
| 101 | 
            +
                return if j.join(",") == codes.join(",")
         | 
| 100 102 |  | 
| 101 103 | 
             
                @langs = []
         | 
| 102 104 | 
             
                j.each { |k| @langs << LangFactory.instance.get(k.strip.to_s) }
         | 
| @@ -105,8 +107,8 @@ class Row | |
| 105 107 | 
             
              end
         | 
| 106 108 |  | 
| 107 109 | 
             
              def read_types_from_xml(xml_data)
         | 
| 108 | 
            -
                j = xml_data.text.split( | 
| 109 | 
            -
                return if j.join( | 
| 110 | 
            +
                j = xml_data.text.split(",")
         | 
| 111 | 
            +
                return if j.join(",") == @types.join(",")
         | 
| 110 112 |  | 
| 111 113 | 
             
                @types = []
         | 
| 112 114 | 
             
                j.each { |k| @types << k.strip.to_s }
         |