2025-02-21 11:57:59 -05:00
|
|
|
class Series
|
2025-05-25 20:40:18 -04:00
|
|
|
# Behave like an Array whose elements are the `Value` structs stored in `values`
|
|
|
|
include Enumerable
|
|
|
|
|
|
|
|
# Forward any undefined method calls (e.g. `first`, `[]`, `map`) to `values`
|
|
|
|
delegate_missing_to :values
|
|
|
|
|
|
|
|
# Enumerable needs `#each`
|
|
|
|
def each(&block)
|
|
|
|
values.each(&block)
|
|
|
|
end
|
|
|
|
|
|
|
|
attr_reader :start_date, :end_date, :interval, :trend, :values, :favorable_direction
|
2025-02-21 11:57:59 -05:00
|
|
|
|
|
|
|
Value = Struct.new(
|
|
|
|
:date,
|
|
|
|
:date_formatted,
|
2025-05-25 20:40:18 -04:00
|
|
|
:value,
|
2025-02-21 11:57:59 -05:00
|
|
|
:trend,
|
|
|
|
keyword_init: true
|
|
|
|
)
|
|
|
|
|
|
|
|
class << self
|
|
|
|
def from_raw_values(values, interval: "1 day")
|
|
|
|
raise ArgumentError, "Must be an array of at least 2 values" unless values.size >= 2
|
|
|
|
raise ArgumentError, "Must have date and value properties" unless values.all? { |value| value.has_key?(:date) && value.has_key?(:value) }
|
|
|
|
|
|
|
|
ordered = values.sort_by { |value| value[:date] }
|
|
|
|
start_date = ordered.first[:date]
|
|
|
|
end_date = ordered.last[:date]
|
|
|
|
|
|
|
|
new(
|
|
|
|
start_date: start_date,
|
|
|
|
end_date: end_date,
|
|
|
|
interval: interval,
|
|
|
|
values: [ nil, *ordered ].each_cons(2).map do |prev_value, curr_value|
|
|
|
|
Value.new(
|
|
|
|
date: curr_value[:date],
|
|
|
|
date_formatted: I18n.l(curr_value[:date], format: :long),
|
2025-05-25 20:40:18 -04:00
|
|
|
value: curr_value[:value],
|
2025-02-21 11:57:59 -05:00
|
|
|
trend: Trend.new(
|
|
|
|
current: curr_value[:value],
|
|
|
|
previous: prev_value&.[](:value)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2025-05-25 20:40:18 -04:00
|
|
|
def initialize(start_date:, end_date:, interval:, values:, favorable_direction: "up")
|
2025-02-21 11:57:59 -05:00
|
|
|
@start_date = start_date
|
|
|
|
@end_date = end_date
|
|
|
|
@interval = interval
|
|
|
|
@values = values
|
2025-05-25 20:40:18 -04:00
|
|
|
@favorable_direction = favorable_direction
|
2025-02-21 11:57:59 -05:00
|
|
|
end
|
|
|
|
|
2025-05-25 20:40:18 -04:00
|
|
|
def trend
|
|
|
|
@trend ||= Trend.new(
|
|
|
|
current: values.last&.value,
|
|
|
|
previous: values.first&.value,
|
|
|
|
favorable_direction: favorable_direction
|
|
|
|
)
|
2025-02-21 11:57:59 -05:00
|
|
|
end
|
|
|
|
|
2025-05-25 20:40:18 -04:00
|
|
|
def as_json
|
|
|
|
{
|
|
|
|
start_date: start_date,
|
|
|
|
end_date: end_date,
|
|
|
|
interval: interval,
|
|
|
|
trend: trend,
|
|
|
|
values: values.map { |v| { date: v.date, date_formatted: v.date_formatted, value: v.value, trend: v.trend } }
|
|
|
|
}
|
2025-02-21 11:57:59 -05:00
|
|
|
end
|
|
|
|
end
|