<commit_before>require "simple_metric/version"
require "ostruct"
require 'spliner'

module SimpleMetric
  module Rails
    class Engine < ::Rails::Engine
    end
  end

  module Helpers

    #   simple_metric_graph([[:metric_key, :title], [:metric_key_2, :title_2]])
    # OR
    #   simple_metric_graph([:metric_key, :metric_key_2])
    def simple_metric_graph(*metrics)
      if metrics.blank?
        ""
      else
        titles = {}

        metrics = metrics.map do |m|
          metric, title = m

          metric = metric.is_a?(Metric) ? metric : Metric.find_by(key: metric)

          next if metric.blank?

          titles[metric.id] = title || metric.key.mb_chars.titleize.to_s

          metric
        end.compact

        dates = metrics.map(&:data_set_object).map(&:data_points).flatten.map(&:x).uniq.sort.map { |x| Time.at(x) }


        content_tag :div do
          concat content_tag(:div, nil, :id => "metric_graph_#{metrics.map(&:id).join("_")}", :style => "width: 100%", :class => "dygraph_container")

          concat javascript_tag <<-JS
          var container_id = "metric_graph_#{metrics.map(&:id).join("_")}";
          g = new Dygraph(

            // containing div
            document.getElementById(container_id),

            // CSV or path to a CSV file.
            "Date,#{metrics.map{ |m| titles[m.id] }.join(',')}\\n" +
            "#{dates.map { |date| [date.strftime("%Y-%m-%d %H:%M"), metrics.map { |m| m.get_value(date) }].flatten.join(", ") + "\\n" }.join("")}"

          );

          $("#" + container_id).data("dygraph", g);
          JS
        end
      end
    end
  end

  class DataSet
    attr_reader :data_points

    def initialize(data_points)
      @data_points = data_points.sort_by { |point| point.x }
      @spliner = Spliner::Spliner.new(@data_points.map(&:x), @data_points.map(&:y))
    end

    def get_value(x)
      @spliner[x.to_f]
    end
  end

  class DataPoint
    attr_reader :x, :y

    def initialize(x, y)
      @x, @y = x.to_f, y.to_f
    end
  end

  class Metric < ActiveRecord::Base
    serialize :data_set
    validates :key, :presence => true, :uniqueness => true

    def self.add_data_point(key, date, value)
      find_or_create_by(:key => key).add_data_point(date, value)
    end

    def add_data_point(date, value)
      self.data_set ||= []

      raise "value '#{value}' is not a number" unless value.is_a? Numeric

      data_set << [date.to_time, value]

      save!
    end

    def get_value(date)
      @data_set_object ||= data_set_object

      @data_set_object.get_value(date.to_f)
    end

    def data_set_object
      if data_set.present?
        DataSet.new(data_points)
      end
    end

    def data_points
      if data_set.present?
        data_set.map { |row| DataPoint.new(row[0], row[1]) }
      end
    end
  end
end

ActionView::Base.send :include, SimpleMetric::Helpers<commit_msg>speedup, MAX 1000 items<commit_after>require "simple_metric/version"
require "ostruct"
require 'spliner'

module SimpleMetric
  module Rails
    class Engine < ::Rails::Engine
    end
  end

  module Helpers

    #   simple_metric_graph([[:metric_key, :title], [:metric_key_2, :title_2]])
    # OR
    #   simple_metric_graph([:metric_key, :metric_key_2])
    def simple_metric_graph(*metrics)
      if metrics.blank?
        ""
      else
        titles = {}

        metrics = metrics.map do |m|
          metric, title = m

          metric = metric.is_a?(Metric) ? metric : Metric.find_by(key: metric)

          next if metric.blank?

          titles[metric.id] = title || metric.key.mb_chars.titleize.to_s

          metric
        end.compact

        dates = metrics.map(&:data_set_object).map(&:data_points).flatten.map(&:x).uniq.sort.map { |x| Time.at(x) }


        content_tag :div do
          concat content_tag(:div, nil, :id => "metric_graph_#{metrics.map(&:id).join("_")}", :style => "width: 100%", :class => "dygraph_container")

          concat javascript_tag <<-JS
          var container_id = "metric_graph_#{metrics.map(&:id).join("_")}";
          g = new Dygraph(

            // containing div
            document.getElementById(container_id),

            // CSV or path to a CSV file.
            "Date,#{metrics.map{ |m| titles[m.id] }.join(',')}\\n" +
            "#{dates.map { |date| [date.strftime("%Y-%m-%d %H:%M"), metrics.map { |m| m.get_value(date) }].flatten.join(", ") + "\\n" }.join("")}"

          );

          $("#" + container_id).data("dygraph", g);
          JS
        end
      end
    end
  end

  class DataSet
    attr_reader :data_points

    def initialize(data_points)
      @data_points = data_points.sort_by { |point| point.x }
    end

    def get_value(x)
      x = x.to_f
      value = @data_points.find { |point| point.x == x }.try(:y)

      if value.blank?
        points = [points_before(x, 2), points_after(x, 2)].flatten

        spliner = Spliner::Spliner.new(points.map(&:x), points.map(&:y))
        value = spliner[x]
      end

      value
    end

    private

    def points_before(x, count)
      points = []

      @data_points.reverse.each do |point|
        break if points.size >= count

        points << point if point.x <= x
      end

      points.sort_by{ |p| p.x }
    end

    def points_after(x, count)
      points = []

      @data_points.each do |point|
        break if points.size >= count

        points << point if point.x >= x
      end

      points.sort_by{ |p| p.x }
    end
  end

  class DataPoint
    attr_reader :x, :y

    def initialize(x, y)
      @x, @y = x.to_f, y.to_f
    end
  end

  class Metric < ActiveRecord::Base
    MAX_SIZE = 1000

    serialize :data_set
    validates :key, :presence => true, :uniqueness => true

    def self.add_data_point(key, date, value)
      find_or_create_by(:key => key).add_data_point(date, value)
    end

    def add_data_point(date, value)
      self.data_set ||= []

      raise "value '#{value}' is not a number" unless value.is_a? Numeric

      data_set << [date.to_time, value]

      data_set.shift(data_set.size - MAX_SIZE) if data_set.size > MAX_SIZE

      save!
    end

    def get_value(date)
      @data_set_object ||= data_set_object

      @data_set_object.get_value(date.to_f)
    end

    def data_set_object
      if data_set.present?
        DataSet.new(data_points)
      end
    end

    def data_points
      if data_set.present?
        data_set.map { |row| DataPoint.new(row[0], row[1]) }
      end
    end
  end
end

ActionView::Base.send :include, SimpleMetric::Helpers
{'fraction_non_alphanumeric': 0.13680831549984715, 'fraction_numerical': 0.003974319779883827, 'mean_word_length': 2.3230066023362115, 'pattern_counts': {'":': 0, '<': 20, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '727682', 'n_tokens_mistral': 2316, 'n_tokens_neox': 2218, 'n_words': 482}