<commit_before># frozen_string_literal: true
require 'ossert/presenters/project_v2'

module Ossert
  module Presenters
    class Project
      include Ossert::Presenters::ProjectV2

      attr_reader :project, :decorator

      def initialize(project)
        @project = project
        @reference = Ossert::Classifiers::Growing.current.reference_values_per_grade
        @decorator = Decorator.new(@reference)
      end

      def self.with_presenter(project)
        presenter = new(project)
        presenter.prepare!
        yield(presenter)
        presenter.cleanup_references!
      end

      class Decorator
        def initialize(reference)
          @reference = reference
        end

        # value, Float !
        def with_reference(text, value, metric, type)
          return (text.to_i.positive? ? "+#{text}" : text).to_s if type == :delta
          metric_by_ref = @reference[type][metric.to_s]
          reference = Project::CLASSES.inject('NaN') do |acc, ref|
            metric_by_ref[ref][:range].cover?(value) ? ref : acc
          end

          { text: "#{text}&nbsp;#{Project::KLASS_2_GRADE[reference]}",
            mark: Project::KLASS_2_GRADE[reference].downcase }
        rescue => e
          puts "NO DATA FOR METRIC: '#{metric}'"
          raise e
        end

        TOO_LONG_AGO = 20.years.ago
        METRICS_DECORATIONS = {
          /(percent|divergence)/ => ->(value) { "#{value.ceil}%" },
          /(date|changed)/ => (lambda do |value|
            date = Time.at(value)
            return 'N/A' if date < TOO_LONG_AGO
            date.strftime('%d/%m/%y')
          end),
          /processed_in/ => (lambda do |value|
            return 'N/A' if value >= Ossert::Stats::PER_YEAR_TOO_LONG || value.zero?
            case value
            when 1
              "~#{value.ceil} day"
            when 2..30
              "~#{value.ceil} days"
            when 31..61
              "~#{(value / 31).to_i} month"
            else
              "~#{(value / 31).to_i} months"
            end
          end),
          /period/ => (lambda do |value|
            if (years = value.to_i / 365).positive?
              "#{years}+ years"
            else
              'Less than a year'
            end
          end),
          /count/ => ->(value) { value.to_i },
          /downloads/ => ->(value) { value.ceil.to_s.gsub(/\d(?=(...)+$)/, '\0,') }
        }.freeze

        def value(metric, value)
          value = value.to_f
          METRICS_DECORATIONS.each { |check, decorator| return decorator.call(value) if metric =~ check }
          value.to_i
        end

        def metric(metric, value, type)
          with_reference(value(metric, value), value.to_f, metric, type)
        end

        def quarter_with_diff(project, time, section)
          section_type = "#{section}_quarter".to_sym
          quarter_data = project.send(section).quarters[quarter_start(time)].metrics_to_hash
          diff = quarter_values(project.send(section).quarters[quarter_start(time) - 1.day].metrics_to_hash)

          quarter_data.each_with_object({}) do |(metric, value), result|
            decorated_metric = metric(metric, value, section_type)
            result[Ossert.t(metric)] = <<-TEXT
  #{decorated_metric[:text]} <> #{metric(metric, value.to_i - diff[metric], :delta)}
            TEXT
          end
        end

        def metrics(metrics_data, section_type)
          metrics_data.each_with_object({}) do |(metric, value), result|
            result[metric] = metric(metric, value.to_i, section_type)
          end
        end

        private

        def quarter_start(time)
          Time.at(time).to_date.to_time(:utc).beginning_of_quarter
        end

        def quarter_values(quarter_data)
          quarter_data.each_with_object({}) do |(metric, value), res|
            res[metric] = value.to_i
          end
        end
      end

      def agility_quarter(time)
        decorator.quarter_with_diff(@project, time, :agility)
      end

      def community_quarter(time)
        decorator.quarter_with_diff(@project, time, :community)
      end

      def agility_quarter_values(time)
        decorator.values @project.agility.quarters[time].metrics_to_hash
      end

      def community_quarter_values(time)
        decorator.values @project.community.quarters[time].metrics_to_hash
      end

      def agility_total
        @agility_total ||= decorator.metrics @project.agility.total.metrics_to_hash, :agility_total
      end
<commit_msg>Fix quarter presenters after refactoring<commit_after># frozen_string_literal: true
require 'ossert/presenters/project_v2'

module Ossert
  module Presenters
    class Project
      include Ossert::Presenters::ProjectV2

      attr_reader :project, :decorator

      def initialize(project)
        @project = project
        @reference = Ossert::Classifiers::Growing.current.reference_values_per_grade
        @decorator = Decorator.new(@reference)
      end

      def self.with_presenter(project)
        presenter = new(project)
        presenter.prepare!
        yield(presenter)
        presenter.cleanup_references!
      end

      class Decorator
        def initialize(reference)
          @reference = reference
        end

        # value, Float !
        def with_reference(text, value, metric, type)
          return (text.to_i.positive? ? "+#{text}" : text).to_s if type == :delta
          metric_by_ref = @reference[type][metric.to_s]
          reference = Project::CLASSES.inject('NaN') do |acc, ref|
            metric_by_ref[ref][:range].cover?(value) ? ref : acc
          end

          { text: "#{text}&nbsp;#{Project::KLASS_2_GRADE[reference]}",
            mark: Project::KLASS_2_GRADE[reference].downcase }
        rescue => e
          puts "NO DATA FOR METRIC: '#{metric}'"
          raise e
        end

        TOO_LONG_AGO = 20.years.ago
        METRICS_DECORATIONS = {
          /(percent|divergence)/ => ->(value) { "#{value.ceil}%" },
          /(date|changed)/ => (lambda do |value|
            date = Time.at(value)
            return 'N/A' if date < TOO_LONG_AGO
            date.strftime('%d/%m/%y')
          end),
          /processed_in/ => (lambda do |value|
            return 'N/A' if value >= Ossert::Stats::PER_YEAR_TOO_LONG || value.zero?
            case value
            when 1
              "~#{value.ceil} day"
            when 2..30
              "~#{value.ceil} days"
            when 31..61
              "~#{(value / 31).to_i} month"
            else
              "~#{(value / 31).to_i} months"
            end
          end),
          /period/ => (lambda do |value|
            if (years = value.to_i / 365).positive?
              "#{years}+ years"
            else
              'Less than a year'
            end
          end),
          /count/ => ->(value) { value.to_i },
          /downloads/ => ->(value) { value.ceil.to_s.gsub(/\d(?=(...)+$)/, '\0,') }
        }.freeze

        def value(metric, value)
          value = value.to_f
          METRICS_DECORATIONS.each { |check, decorator| return decorator.call(value) if metric =~ check }
          value.to_i
        end

        def metric(metric, value, type)
          with_reference(value(metric, value), value.to_f, metric, type)
        end

        def quarter_with_diff(project, time, section)
          section_type = "#{section}_quarter".to_sym
          quarter_data = project.send(section).quarters[quarter_start(time)].metrics_to_hash
          diff = quarter_values(project.send(section).quarters[quarter_start(time) - 1.day].metrics_to_hash)

          quarter_data.each_with_object({}) do |(metric, value), result|
            decorated_metric = metric(metric, value, section_type)
            result[Ossert.t(metric)] = <<-TEXT
  #{decorated_metric[:text]} <> #{metric(metric, value.to_i - diff[metric], :delta)}
            TEXT
          end
        end

        def metrics(metrics_data, section_type)
          metrics_data.each_with_object({}) do |(metric, value), result|
            result[metric] = metric(metric, value.to_i, section_type)
          end
        end

        def quarter_values(quarter_data)
          quarter_data.each_with_object({}) do |(metric, value), res|
            res[metric] = value.to_i
          end
        end

        private

        def quarter_start(time)
          Time.at(time).to_date.to_time(:utc).beginning_of_quarter
        end
      end

      def agility_quarter(time)
        decorator.quarter_with_diff(@project, time, :agility)
      end

      def community_quarter(time)
        decorator.quarter_with_diff(@project, time, :community)
      end

      def agility_quarter_values(time)
        decorator.quarter_values @project.agility.quarters[time].metrics_to_hash
      end

      def community_quarter_values(time)
        decorator.quarter_values @project.community.quarters[time].metrics_to_hash
      end

      def agility_total
        @agility_total ||= decorator.metrics @project.agility.total.metrics_to_hash, :agility_total
      end

{'fraction_non_alphanumeric': 0.11793339971235756, 'fraction_numerical': 0.005089058524173028, 'mean_word_length': 2.061293599729089, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6416517', 'n_tokens_mistral': 2818, 'n_tokens_neox': 2701, 'n_words': 544}