Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
Squash accounts balance history table
Browse files Browse the repository at this point in the history
  • Loading branch information
Damir Gaynetdinov authored and gaynetdinov committed Feb 15, 2018
1 parent 2f83aa2 commit 657aec8
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 62 deletions.
27 changes: 27 additions & 0 deletions lib/ex_money/accounts/accounts.ex
@@ -0,0 +1,27 @@
defmodule ExMoney.Accounts do
@moduledoc """
The Accounts context.
"""

import Ecto.Query, warn: false
alias ExMoney.Repo

alias ExMoney.Accounts.BalanceHistory

def get_account_history_balance(from, to) do
{:ok, from} = Date.from_iso8601(from)
erl_from = Date.to_erl(from)
{:ok, naive_from} = NaiveDateTime.from_erl({erl_from, {0, 0, 0}})

{:ok, to} = Date.from_iso8601(to)
erl_to = Date.to_erl(to)
{:ok, naive_to} = NaiveDateTime.from_erl({erl_to, {0, 0, 0}})

query =
from h in BalanceHistory,
where: h.inserted_at >= ^naive_from,
where: h.inserted_at <= ^naive_to

Repo.all(query)
end
end
16 changes: 16 additions & 0 deletions lib/ex_money/accounts/balance_history.ex
@@ -0,0 +1,16 @@
defmodule ExMoney.Accounts.BalanceHistory do
use Ecto.Schema
import Ecto.Changeset

schema "accounts_balance_history" do
field :state, :map

timestamps()
end

def changeset(model, params \\ %{}) do
model
|> cast(params, ~w(state)a)
|> validate_required(~w(state)a)
end
end
20 changes: 12 additions & 8 deletions lib/ex_money/accounts_balance_history_worker.ex
@@ -1,20 +1,24 @@
defmodule ExMoney.AccountsBalanceHistoryWorker do
use GenServer

alias ExMoney.{Account, Repo, AccountsBalanceHistory}
alias ExMoney.{Repo, Account}
alias ExMoney.Accounts.BalanceHistory

def start_link(_opts \\ []) do
GenServer.start_link(__MODULE__, :ok, name: :accounts_balance_history_worker)
end

def handle_call(:store_current_balance, _from, state) do
Account
|> Repo.all
|> Enum.each(fn(account) ->
%AccountsBalanceHistory{}
|> AccountsBalanceHistory.changeset(%{account_id: account.id, balance: account.balance})
|> Repo.insert!
end)
accounts_state =
Account
|> Repo.all
|> Enum.reduce(%{}, fn(account, acc) ->
Map.put(acc, account.id, account.balance)
end)

%BalanceHistory{}
|> BalanceHistory.changeset(%{state: accounts_state})
|> Repo.insert!

{:reply, :stored, state}
end
Expand Down
11 changes: 5 additions & 6 deletions lib/ex_money_web/controllers/mobile/account_controller.ex
Expand Up @@ -2,7 +2,7 @@ defmodule ExMoney.Web.Mobile.AccountController do
use ExMoney.Web, :controller

alias ExMoney.DateHelper
alias ExMoney.{Repo, Transaction, Account, AccountsBalanceHistory}
alias ExMoney.{Repo, Transaction, Account}

plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Mobile.Unauthenticated
plug :put_layout, "mobile.html"
Expand Down Expand Up @@ -99,15 +99,14 @@ defmodule ExMoney.Web.Mobile.AccountController do
end

defp account_balance(from, to, account_id) do
AccountsBalanceHistory.history(from, to, account_id)
|> Repo.all
|> Enum.reduce(%{}, fn(h, acc) ->
ExMoney.Accounts.get_account_history_balance(from, to)
|> Enum.reduce(%{}, fn(history, acc) ->
inserted_at =
h.inserted_at
history.inserted_at
|> NaiveDateTime.to_date()
|> Date.to_string()

Map.put(acc, inserted_at, h.balance)
Map.put(acc, inserted_at, history.state[to_string(account_id)])
end)
end

Expand Down
3 changes: 0 additions & 3 deletions lib/ex_money_web/models/account.ex
Expand Up @@ -22,9 +22,6 @@ defmodule ExMoney.Account do
foreign_key: :saltedge_account_id,
references: :saltedge_account_id

has_many :balance_history, ExMoney.AccountsBalanceHistory,
on_delete: :delete_all

timestamps()
end

Expand Down
33 changes: 0 additions & 33 deletions lib/ex_money_web/models/accounts_balance_history.ex

This file was deleted.

10 changes: 6 additions & 4 deletions lib/ex_money_web/views/shared_view.ex
Expand Up @@ -21,7 +21,7 @@ defmodule ExMoney.Web.SharedView do
end

def description(transaction) do
String.replace(transaction.extra["payee"], ~r/\A[A-Z]{2}\w{15,32}/, "")
String.replace(transaction.extra["payee"] || "", ~r/\A[A-Z]{2}\w{15,32}/, "")
end

def category_name(nil), do: ""
Expand All @@ -33,9 +33,11 @@ defmodule ExMoney.Web.SharedView do
def account_balance(_date, _, nil), do: ""
def account_balance(date, account_balance, account) do
date = to_string(date)
case account_balance[date] do
nil -> ""
balance -> "#{balance} #{account.currency_label}"
balance = account_balance[date]
if balance do
"#{balance} #{account.currency_label}"
else
""
end
end
end
43 changes: 43 additions & 0 deletions lib/mix/tasks/account_history_balance_state.ex
@@ -0,0 +1,43 @@
defmodule Mix.Tasks.ExMoney.AccountsHistoryBalanceState do
use Mix.Task
import Mix.Ecto
import Ecto.Query, warn: false

alias ExMoney.{Repo, Accounts.BalanceHistory}

@shortdoc "Move balance history entries into `state` map field to reduce number of rows"

def run(_args) do
ensure_repo(ExMoney.Repo, [])
ensure_started(ExMoney.Repo, [])

query = from h in BalanceHistory,
group_by: fragment("inserted_at::timestamp::date"),
select: %{
min_id: min(h.id),
grouped_json: fragment("json_agg(json_build_object('id', id, 'account_id', account_id, 'balance', balance))")
}

h = Repo.all(query)

Enum.each h, fn(%{min_id: id, grouped_json: grouped_json}) ->
accounts_state = Enum.reduce grouped_json, %{}, fn(row, acc) ->
Map.put(acc, row["account_id"], row["balance"])
end

ids_to_remove = Enum.reduce grouped_json, [], fn(row, acc) ->
if row["id"] != id do
[row["id"] | acc]
else
acc
end
end

Repo.get(BalanceHistory, id)
|> BalanceHistory.changeset(%{state: accounts_state})
|> Repo.update!

(from h in BalanceHistory, where: h.id in ^ids_to_remove) |> Repo.delete_all
end
end
end
@@ -0,0 +1,9 @@
defmodule ExMoney.Repo.Migrations.AddStateToBalanceHistory do
use Ecto.Migration

def change do
alter table(:accounts_balance_history) do
add :state, :map
end
end
end
8 changes: 3 additions & 5 deletions test/lib/accounts_balance_history_worker_test.exs
@@ -1,7 +1,7 @@
defmodule ExMoney.AccountsBalanceHistoryWorkerTest do
use ExUnit.Case

alias ExMoney.{AccountsBalanceHistory, Repo}
alias ExMoney.{Accounts.BalanceHistory, Repo}

import ExMoney.Factory

Expand All @@ -18,10 +18,8 @@ defmodule ExMoney.AccountsBalanceHistoryWorkerTest do
test "store current balance", %{account_1: account_1, account_2: account_2} do
assert :stored == GenServer.call(:accounts_balance_history_worker, :store_current_balance)

history_1 = Repo.get_by(AccountsBalanceHistory, account_id: account_1.id)
history_2 = Repo.get_by(AccountsBalanceHistory, account_id: account_2.id)
h = Repo.all(BalanceHistory) |> List.first

assert Decimal.new(10) == history_1.balance
assert Decimal.new(20) == history_2.balance
assert h.state == %{to_string(account_1.id) => "10", to_string(account_2.id) => "20"}
end
end
5 changes: 2 additions & 3 deletions test/support/factory.ex
Expand Up @@ -31,9 +31,8 @@ defmodule ExMoney.Factory do
end

def accounts_balance_history do
%ExMoney.AccountsBalanceHistory {
account: build(:account),
balance: Decimal.new(10)
%ExMoney.Accounts.BalanceHistory {
state: %{"1" => Decimal.new(10)}
}
end

Expand Down

0 comments on commit 657aec8

Please sign in to comment.