<commit_before>class CountPerDateFactory
  # Group occurrences per hour and return an array of CountPerDate
  # @param [Array<Occurrence>] occurrences
  # @return [Array<CountPerDate>]
  def self.per_hour(occurrences)
    occurrences.sort! { |a, b| Time.zone.parse(a.date) <=> Time.zone.parse(b.date) }
    first_occurrence = occurrences.first
    last_occurrence = occurrences.last
    counts_per_date = generate_array_per_hours(Time.zone.parse(first_occurrence.date), Time.zone.parse(last_occurrence.date))
    occurrences.each do |occurrence|
      index = compute_hours_between(Time.zone.parse(first_occurrence.date), Time.zone.parse(occurrence.date))
      counts_per_date[index].count += 1
    end
    counts_per_date
  end

  # Generate and return an array of CountPerDate containing one instance per hour between first and last date
  def self.generate_array_per_hours(first, last)
    number_of_hours = compute_hours_between(first, last) + 1
    generate_array_for_unit(first, number_of_hours, :hours)
  end

  # Generate and return an array of CountPerDate containing number_of_elements elements, separated by 1 unit
  # @param [ActiveSupport::TimeWithZone] first starting date
  # @param [integer] number_of_elements to create in the array
  # @param [symbol] unit either [:hours, :days, :months, :years]
  def self.generate_array_for_unit(first, number_of_elements, unit)
    counts_per_date = Array.new(number_of_elements) { CountPerDate.new }
    counts_per_date.each_with_index do |count_per_date, index|
      count_per_date.date = first + index.send(unit)
    end
    counts_per_date
  end

  # Compute the number of hours between the first and the last DateTime
  # The returned value is always positive
  def self.compute_hours_between(first, last)
    seconds_in_an_hour = 3600<commit_msg>ref #26<commit_after>class CountPerDateFactory
  # Group occurrences per hour and return an array of CountPerDate
  # @param [Array<Occurrence>] occurrences
  # @return [Array<CountPerDate>]
  def self.per_hour(occurrences)
    occurrences.sort! { |a, b| Time.zone.parse(a.date) <=> Time.zone.parse(b.date) }
    first_occurrence = occurrences.first
    last_occurrence = occurrences.last
    counts_per_date = generate_array_per_hours(Time.zone.parse(first_occurrence.date), Time.zone.parse(last_occurrence.date))
    occurrences.each do |occurrence|
      index = compute_hours_between(Time.zone.parse(first_occurrence.date), Time.zone.parse(occurrence.date))
      counts_per_date.fetch(index).count += 1
    end
    counts_per_date
  end

  # Generate and return an array of CountPerDate containing one instance per hour between first and last date
  def self.generate_array_per_hours(first, last)
    number_of_hours = compute_hours_between(first, last) + 1
    generate_array_for_unit(first, number_of_hours, :hours)
  end

  # Generate and return an array of CountPerDate containing number_of_elements elements, separated by 1 unit
  # @param [ActiveSupport::TimeWithZone] first starting date
  # @param [integer] number_of_elements to create in the array
  # @param [symbol] unit either [:hours, :days, :months, :years]
  def self.generate_array_for_unit(first, number_of_elements, unit)
    counts_per_date = Array.new(number_of_elements) { CountPerDate.new }
    counts_per_date.each_with_index do |count_per_date, index|
      count_per_date.date = first + index.send(unit)
    end
    counts_per_date
  end

  # Compute the number of hours between the first and the last DateTime
  # The returned value is always positive
  def self.compute_hours_between(first, last)
    seconds_in_an_hour = 3600
{'fraction_non_alphanumeric': 0.08236594803758983, 'fraction_numerical': 0.004422332780541736, 'mean_word_length': 4.753577106518283, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, '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': '4883545', 'n_tokens_mistral': 1192, 'n_tokens_neox': 1118, 'n_words': 290}