<commit_before># frozen_string_literal: true

module GobiertoPlans
  class RowNodeDecorator < BaseDecorator
    STATUS_TRANSLATIONS = {
      %w(finalitzats terminada completado) => 100.0,
      %w(actius en\ ejecución) => 50.0,
      %w(empezada iniciado) => 10.0,
      %w(en\ cartera pendiente\ de\ iniciar planificada aplazada) => 0.0
    }.freeze

    def initialize(object, options = {})
      if object.is_a? CSV::Row
        @object = object
        @plan = options[:plan]
      elsif object.is_a? Node
        @node = object
        @plan = @node.categories.first&.plan
        @category = node_category
        @object = CSV::Row.new(plan_csv_columns, node_csv_values)
      elsif object.is_a? Category
        @category = object
        @plan = @category.plan
        @node = Node.new
        @object = CSV::Row.new(plan_csv_columns, node_csv_values)
      end
    end

    def categories
      @categories ||= begin
                        level_names = object.headers.select { |h| /Level \d+/.match(h) }.sort_by { |h| h[/\d+/].to_i }
                        current_level = @plan
                        categories = []
                        level_names.each_with_index do |name, index|
                          current_level =
                            current_level.categories.with_name_translation(object[name], locale).where(level: index).first ||
                            current_level.categories.new(
                              "name_#{ locale }": object[name],
                              level: index,
                              plan: @plan
                            )
                          categories << current_level
                        end
                        categories
                      end
    end

    def node
      @node ||= begin
                  return nil if node_data.compact.blank?
                  category = categories.last
                  (category.nodes.with_name_translation(node_data["Title"], locale).first || category.nodes.new).tap do |node|
                    node.assign_attributes node_attributes
                    node.progress = progress_from_status(node.status) unless has_progress_column?
                    node.categories << category unless node.categories.include?(category)
                  end
                end
    end

    protected

    def node_data
      @node_data ||= prefixed_row_data(/\ANode\./)
    end

    def prefixed_row_data(prefix)
      object.headers.select { |h| prefix.match(h) }.map do |h|
        [h.sub(prefix, ""), object[h]]
      end.to_h
    end

    def has_progress_column?
      node_data.has_key? "Progress"
    end

    def locale
      @locale ||= @plan.site.configuration.default_locale
    end

    def node_attributes
      attributes = node_mandatory_columns.invert.transform_values { |column| object[column] }
      attributes.merge(options: node_data.except("Title", "Status", "Start", "End", "Progress"))
    end

    def progress_from_status(status)
      return if status.blank?
      status = status.strip.downcase
      key = STATUS_TRANSLATIONS.keys.find { |k| k.include?(status) }
      STATUS_TRANSLATIONS[key]
    end

    def node_mandatory_columns
      @node_mandatory_columns ||= { "Node.Title" => :"name_#{ locale }",
                                    "Node.Status" => :"status_#{ locale }",
                                    "Node.Progress" => :progress,
                                    "Node.Start" => :starts_at,
                                    "Node.End" => :ends_at }
    end

    def plan_options_keys
      @plan_options_keys ||= @plan.nodes.map { |node| node.options.keys }.uniq.flatten.uniq
    end

    def plan_nodes_extra_columns
      plan_options_keys.map do |key|
        "Node.#{ key }"
      end
    end

    def plan_csv_columns
      plan_categories_range.map { |num| "Level #{ num }" } +
        node_mandatory_columns.keys +
        plan_nodes_extra_columns
    end

    def plan_categories_range
      @plan.categories.minimum(:level)..@plan.categories.maximum(:level)
    end

    def categories_hierarchy(category)
      categories = []
      while category.present?
        categories.unshift(category)
        category = category.parent_category
      end
      categories
    end

    def node_category
      @node.categories.where(level: @plan.categories.maximum(:level)).first
    end

    def node_mandatory_values
      node_mandatory_columns.map do |_, method|
        @node.send(method)
      end
    end<commit_msg>Use a CategoryTermDecorator for plan categories in RowNodeDecorator<commit_after># frozen_string_literal: true

module GobiertoPlans
  class RowNodeDecorator < BaseDecorator
    STATUS_TRANSLATIONS = {
      %w(finalitzats terminada completado) => 100.0,
      %w(actius en\ ejecución) => 50.0,
      %w(empezada iniciado) => 10.0,
      %w(en\ cartera pendiente\ de\ iniciar planificada aplazada) => 0.0
    }.freeze

    def initialize(object, options = {})
      if object.is_a? CSV::Row
        @object = object
        @plan = options[:plan]
      elsif object.is_a? Node
        @node = object
        @plan = CategoryTermDecorator.new(@node.categories.first).plan
        @category = node_category
        @object = CSV::Row.new(plan_csv_columns, node_csv_values)
      elsif object.is_a? GobiertoCommon::Term
        @category = CategoryTermDecorator.new(object)
        @plan = @category.plan
        @node = Node.new
        @object = CSV::Row.new(plan_csv_columns, node_csv_values)
      end
    end

    def categories
      @categories ||= begin
                        level_names = object.headers.select { |h| /Level \d+/.match(h) }.sort_by { |h| h[/\d+/].to_i }
                        current_level = @plan
                        categories = []
                        level_names.each_with_index do |name, index|
                          current_level =
                            current_level.categories.with_name_translation(object[name], locale).where(level: index).first ||
                            current_level.categories.new(
                              "name_#{ locale }": object[name],
                              level: index,
                              vocabulary_id: @plan.categories_vocabulary.id
                            )
                          current_level = CategoryTermDecorator.new(current_level)
                          categories << current_level
                        end
                        categories
                      end
    end

    def node
      @node ||= begin
                  return nil if node_data.compact.blank?
                  category = CategoryTermDecorator.new(categories.last)
                  (category.nodes.with_name_translation(node_data["Title"], locale).first || category.nodes.new).tap do |node|
                    node.assign_attributes node_attributes
                    node.progress = progress_from_status(node.status) unless has_progress_column?
                    node.categories << category unless node.categories.include?(category)
                  end
                end
    end

    protected

    def node_data
      @node_data ||= prefixed_row_data(/\ANode\./)
    end

    def prefixed_row_data(prefix)
      object.headers.select { |h| prefix.match(h) }.map do |h|
        [h.sub(prefix, ""), object[h]]
      end.to_h
    end

    def has_progress_column?
      node_data.has_key? "Progress"
    end

    def locale
      @locale ||= @plan.site.configuration.default_locale
    end

    def node_attributes
      attributes = node_mandatory_columns.invert.transform_values { |column| object[column] }
      attributes.merge(options: node_data.except("Title", "Status", "Start", "End", "Progress"))
    end

    def progress_from_status(status)
      return if status.blank?
      status = status.strip.downcase
      key = STATUS_TRANSLATIONS.keys.find { |k| k.include?(status) }
      STATUS_TRANSLATIONS[key]
    end

    def node_mandatory_columns
      @node_mandatory_columns ||= { "Node.Title" => :"name_#{ locale }",
                                    "Node.Status" => :"status_#{ locale }",
                                    "Node.Progress" => :progress,
                                    "Node.Start" => :starts_at,
                                    "Node.End" => :ends_at }
    end

    def plan_options_keys
      @plan_options_keys ||= @plan.nodes.map { |node| node.options.keys }.uniq.flatten.uniq
    end

    def plan_nodes_extra_columns
      plan_options_keys.map do |key|
        "Node.#{ key }"
      end
    end

    def plan_csv_columns
      plan_categories_range.map { |num| "Level #{ num }" } +
        node_mandatory_columns.keys +
        plan_nodes_extra_columns
    end

    def plan_categories_range
      @plan.categories.minimum(:level)..@plan.categories.maximum(:level)
    end

    def categories_hierarchy(category)
      categories = []
      while category.present?
        categories.unshift(CategoryTermDecorator.new(category))
        category = category.parent_category
      end
      categories
    end

    def node_category
      CategoryTermDecorator.new(@node.categories.where(level: @plan.categories.maximum(:level)).first)
    end

    def node_mandatory_values
      node_mandatory_columns.map do |_, method|
        @node.send(method)
      end
    end
{'fraction_non_alphanumeric': 0.09842730287792875, 'fraction_numerical': 0.002567668770728576, 'mean_word_length': 1.8353048225659692, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4933936', 'n_tokens_mistral': 2729, 'n_tokens_neox': 2619, 'n_words': 516}