Add the ability to "rollup" values in a time series #554

Merged
zachgoll merged 16 commits from account-rollups into main 2024-03-19 21:10:40 +08:00
zachgoll commented 2024-03-19 05:48:28 +08:00 (Migrated from github.com)

Another prerequisite PR for #543 that aims to make it much easier to work with time series and aggregated data.

Overview

This PR introduces two important domain concepts:

  • TimeSeries + TimeSeries::Value
  • ValueGroup

Time Series Data

Maybe will be presenting user data in a time series format often. The TimeSeries model accepts an array of TimeSeries::Value objects, which have a date and value. This is a generic domain concept that works with numeric and money values:

money_series = TimeSeries.new([ { date: 1.day.ago.to_date, value: Money.new(100) }, { date: Date.current, value: Money.new(200) } ])

regular_numeric_series = TimeSeries.new([{ date: 1.day.ago.to_date, value: 100 }])

It also works with collections via the from_collection class method:

balance_series = TimeSeries.from_collection @account.balances, :balance_money

Value Groups

The purpose of value groups is to recursively "roll up" both individual values and time series arrays based on an arbitrary hierarchy. For example, we can create a hierarchy of assets:

  • assets
    • depositories
      • checking account
      • savings account
    • other assets
      • collectable account
# See value_group_test.rb
setup do
    checking = accounts(:checking)
    savings = accounts(:savings_with_valuation_overrides)
    collectable = accounts(:collectable)

    # Level 1
    @assets = ValueGroup.new("Assets")

    # Level 2
    @depositories = @assets.add_child_node("Depositories")
    @other_assets = @assets.add_child_node("Other Assets")

    # Level 3 (leaf/value nodes)
    @checking_node = @depositories.add_value_node(checking)
    @savings_node = @depositories.add_value_node(savings)
    @collectable_node = @other_assets.add_value_node(collectable)

    # Can attach time series to one or more nodes (which will also be "rolled up")
    @checking_node.attach_series(TimeSeries.from_collection(@checking.series, :balance_money))
end
Another prerequisite PR for #543 that aims to make it much easier to work with time series and aggregated data. ## Overview This PR introduces two important domain concepts: - `TimeSeries` + `TimeSeries::Value` - `ValueGroup` ### Time Series Data Maybe will be presenting user data in a time series format often. The `TimeSeries` model accepts an array of `TimeSeries::Value` objects, which have a `date` and `value`. This is a generic domain concept that works with numeric and money values: ```rb money_series = TimeSeries.new([ { date: 1.day.ago.to_date, value: Money.new(100) }, { date: Date.current, value: Money.new(200) } ]) regular_numeric_series = TimeSeries.new([{ date: 1.day.ago.to_date, value: 100 }]) ``` It also works with collections via the `from_collection` class method: ```rb balance_series = TimeSeries.from_collection @account.balances, :balance_money ``` ### Value Groups The purpose of value groups is to recursively "roll up" both _individual values_ and _time series arrays_ based on an arbitrary hierarchy. For example, we can create a hierarchy of assets: - assets - depositories - checking account - savings account - other assets - collectable account ```rb # See value_group_test.rb setup do checking = accounts(:checking) savings = accounts(:savings_with_valuation_overrides) collectable = accounts(:collectable) # Level 1 @assets = ValueGroup.new("Assets") # Level 2 @depositories = @assets.add_child_node("Depositories") @other_assets = @assets.add_child_node("Other Assets") # Level 3 (leaf/value nodes) @checking_node = @depositories.add_value_node(checking) @savings_node = @depositories.add_value_node(savings) @collectable_node = @other_assets.add_value_node(collectable) # Can attach time series to one or more nodes (which will also be "rolled up") @checking_node.attach_series(TimeSeries.from_collection(@checking.series, :balance_money)) end ```
Sign in to join this conversation.