diff options
Diffstat (limited to 'lib/gitlab/analytics/date_filler.rb')
-rw-r--r-- | lib/gitlab/analytics/date_filler.rb | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/lib/gitlab/analytics/date_filler.rb b/lib/gitlab/analytics/date_filler.rb new file mode 100644 index 00000000000..aa3db9f3635 --- /dev/null +++ b/lib/gitlab/analytics/date_filler.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + # This class generates a date => value hash without gaps in the data points. + # + # Simple usage: + # + # > # We have the following data for the last 5 day: + # > input = { 3.days.ago.to_date => 10, Date.today => 5 } + # + # > # Format this data, so we can chart the complete date range: + # > Gitlab::Analytics::DateFiller.new(input, from: 4.days.ago, to: Date.today, default_value: 0).fill + # > { + # > Sun, 28 Aug 2022=>0, + # > Mon, 29 Aug 2022=>10, + # > Tue, 30 Aug 2022=>0, + # > Wed, 31 Aug 2022=>0, + # > Thu, 01 Sep 2022=>5 + # > } + # + # Parameters: + # + # **input** + # A Hash containing data for the series or the chart. The key is a Date object + # or an object which can be converted to Date. + # + # **from** + # Start date of the range + # + # **to** + # End date of the range + # + # **period** + # Specifies the period in wich the dates should be generated. Options: + # + # - :day, generate date-value pair for each day in the given period + # - :week, generate date-value pair for each week (beginning of the week date) + # - :month, generate date-value pair for each week (beginning of the month date) + # + # Note: the Date objects in the `input` should follow the same pattern (beginning of ...) + # + # **default_value** + # + # Which value use when the `input` Hash does not contain data for the given day. + # + # **date_formatter** + # + # How to format the dates in the resulting hash. + class DateFiller + DEFAULT_DATE_FORMATTER = -> (date) { date } + PERIOD_STEPS = { + day: 1.day, + week: 1.week, + month: 1.month + }.freeze + + def initialize( + input, + from:, + to:, + period: :day, + default_value: nil, + date_formatter: DEFAULT_DATE_FORMATTER) + @input = input.transform_keys(&:to_date) + @from = from.to_date + @to = to.to_date + @period = period + @default_value = default_value + @date_formatter = date_formatter + end + + def fill + data = {} + + current_date = from + loop do + transformed_date = transform_date(current_date) + break if transformed_date > to + + formatted_date = date_formatter.call(transformed_date) + + value = input.delete(transformed_date) + data[formatted_date] = value.nil? ? default_value : value + + current_date = (current_date + PERIOD_STEPS.fetch(period)).to_date + end + + raise "Input contains values which doesn't fall under the given period!" if input.any? + + data + end + + private + + attr_reader :input, :from, :to, :period, :default_value, :date_formatter + + def transform_date(date) + case period + when :day + date.beginning_of_day.to_date + when :week + date.beginning_of_week.to_date + when :month + date.beginning_of_month.to_date + else + raise "Unknown period given: #{period}" + end + end + end + end +end |