2024-03-19 09:10:40 -04:00
|
|
|
class TimeSeries::Trend
|
2024-04-18 20:03:46 -06:00
|
|
|
include ActiveModel::Validations
|
2024-03-19 09:10:40 -04:00
|
|
|
|
2024-04-18 20:03:46 -06:00
|
|
|
attr_reader :current, :previous
|
2024-03-19 09:10:40 -04:00
|
|
|
|
2024-04-18 20:03:46 -06:00
|
|
|
delegate :favorable_direction, to: :series
|
|
|
|
|
|
|
|
validate :values_must_be_of_same_type, :values_must_be_of_known_type
|
|
|
|
|
|
|
|
def initialize(current:, previous:, series: nil)
|
|
|
|
@current = current || 0
|
2024-03-19 09:10:40 -04:00
|
|
|
@previous = previous
|
2024-04-18 20:03:46 -06:00
|
|
|
@series = series
|
|
|
|
|
|
|
|
validate!
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def direction
|
2024-04-18 20:03:46 -06:00
|
|
|
if previous.nil? || current == previous
|
|
|
|
"flat"
|
|
|
|
elsif current && current > previous
|
|
|
|
"up"
|
|
|
|
else
|
|
|
|
"down"
|
|
|
|
end.inquiry
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def value
|
2024-04-18 20:03:46 -06:00
|
|
|
if previous.nil? && current.is_a?(Money)
|
|
|
|
Money.new 0
|
|
|
|
elsif previous.nil?
|
|
|
|
0
|
|
|
|
else
|
|
|
|
current - previous
|
|
|
|
end
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def percent
|
2024-04-18 20:03:46 -06:00
|
|
|
if previous.nil?
|
|
|
|
0.0
|
|
|
|
elsif previous == 0
|
|
|
|
Float::INFINITY
|
|
|
|
else
|
|
|
|
change = (current_amount - previous_amount).abs
|
|
|
|
base = previous_amount.abs.to_f
|
|
|
|
|
|
|
|
(change / base * 100).round(1).to_f
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def as_json
|
|
|
|
{
|
|
|
|
favorable_direction: favorable_direction,
|
|
|
|
direction: direction,
|
|
|
|
value: value,
|
|
|
|
percent: percent
|
|
|
|
}.as_json
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2024-04-18 20:03:46 -06:00
|
|
|
attr_reader :series
|
|
|
|
|
|
|
|
def values_must_be_of_same_type
|
|
|
|
unless current.class == previous.class || previous.nil?
|
|
|
|
errors.add :current, "must be of the same type as previous"
|
|
|
|
errors.add :previous, "must be of the same type as current"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def values_must_be_of_known_type
|
|
|
|
unless current.is_a?(Money) || current.is_a?(Numeric)
|
|
|
|
errors.add :current, "must be of type Money or Numeric"
|
|
|
|
end
|
|
|
|
|
|
|
|
unless previous.is_a?(Money) || previous.is_a?(Numeric) || previous.nil?
|
|
|
|
errors.add :previous, "must be of type Money, Numeric or nil"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def current_amount
|
|
|
|
extract_numeric current
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
2024-04-18 20:03:46 -06:00
|
|
|
def previous_amount
|
|
|
|
extract_numeric previous
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def extract_numeric(obj)
|
2024-04-18 20:03:46 -06:00
|
|
|
if obj.is_a? Money
|
|
|
|
obj.amount
|
|
|
|
else
|
|
|
|
obj
|
|
|
|
end
|
2024-03-19 09:10:40 -04:00
|
|
|
end
|
|
|
|
end
|