diff --git a/lib/cannery/ammo.ex b/lib/cannery/ammo.ex index 300feb8..bafde6c 100644 --- a/lib/cannery/ammo.ex +++ b/lib/cannery/ammo.ex @@ -228,35 +228,62 @@ defmodule Cannery.Ammo do defp get_round_count_container_id(query, _nil), do: query + @type get_grouped_round_count_option :: + {:types, [Type.t()] | nil} | {:containers, [Container.t()] | nil} | {:group_by, atom()} + @type get_grouped_round_count_options :: [get_grouped_round_count_option()] + @doc """ Gets the total number of rounds for multiple types ## Examples - iex> get_round_count_for_types( - ...> [%Type{id: 123, user_id: 456}], - ...> %User{id: 456} + iex> get_grouped_round_count( + ...> %User{id: 456}, + ...> group_by: :type_id, + ...> types: [%Type{id: 123, user_id: 456}] ...> ) %{123 => 35} """ - @spec get_round_count_for_types([Type.t()], User.t()) :: - %{optional(Type.id()) => non_neg_integer()} - def get_round_count_for_types(types, %User{id: user_id}) do - type_ids = - types - |> Enum.map(fn %Type{id: type_id, user_id: ^user_id} -> type_id end) - - Repo.all( - from p in Pack, - where: p.type_id in ^type_ids, - where: p.user_id == ^user_id, - group_by: p.type_id, - select: {p.type_id, sum(p.count)} + @spec get_grouped_round_count(User.t(), get_grouped_round_count_options()) :: + %{optional(Type.id() | Container.id()) => non_neg_integer()} + def get_grouped_round_count(%User{id: user_id}, opts) do + from(p in Pack, + as: :p, + where: p.user_id == ^user_id ) + |> get_grouped_round_count_filter_ids( + Keyword.fetch!(opts, :group_by), + Keyword.get(opts, :types) + ) + |> get_grouped_round_count_filter_ids( + Keyword.fetch!(opts, :group_by), + Keyword.get(opts, :containers) + ) + |> get_grouped_round_count_group_by(Keyword.fetch!(opts, :group_by)) + |> Repo.all() |> Map.new() end + @spec get_grouped_round_count_group_by(Queryable.t(), atom()) :: Queryable.t() + defp get_grouped_round_count_group_by(query, group_key) when group_key |> is_atom() do + query + |> group_by([p: p], field(p, ^group_key)) + |> select([p: p], {field(p, ^group_key), sum(p.count)}) + end + + @spec get_grouped_round_count_filter_ids( + Queryable.t(), + atom(), + [Type.t()] | [Container.t()] | nil + ) :: Queryable.t() + defp get_grouped_round_count_filter_ids(query, group_key, items) when items |> is_list() do + item_ids = items |> Enum.map(fn %{id: id} -> id end) + query |> where([p: p], field(p, ^group_key) in ^item_ids) + end + + defp get_grouped_round_count_filter_ids(query, _group_key, _nil), do: query + @doc """ Gets the total number of ammo ever bought for a type @@ -292,7 +319,7 @@ defmodule Cannery.Ammo do %{optional(Type.id()) => non_neg_integer()} def get_historical_count_for_types(types, %User{id: user_id} = user) do used_counts = ActivityLog.get_grouped_used_counts(user, types: types, group_by: :type_id) - round_counts = types |> get_round_count_for_types(user) + round_counts = get_grouped_round_count(user, types: types, group_by: :type_id) types |> Enum.filter(fn %Type{id: type_id, user_id: ^user_id} -> @@ -694,34 +721,6 @@ defmodule Cannery.Ammo do query |> where([p: p], not (p.count == 0)) end - @doc """ - Returns number of ammo packs in multiple containers. - - ## Examples - - iex> get_round_count_for_containers( - ...> [%Container{id: 123, user_id: 456}], - ...> %User{id: 456} - ...> ) - %{123 => 5} - - """ - @spec get_round_count_for_containers([Container.t()], User.t()) :: - %{Container.id() => non_neg_integer()} - def get_round_count_for_containers(containers, %User{id: user_id}) do - container_ids = - containers - |> Enum.map(fn %Container{id: container_id, user_id: ^user_id} -> container_id end) - - Repo.all( - from p in Pack, - where: p.container_id in ^container_ids, - group_by: p.container_id, - select: {p.container_id, sum(p.count)} - ) - |> Map.new() - end - @doc """ Gets a single pack. diff --git a/lib/cannery_web/components/container_table_component.ex b/lib/cannery_web/components/container_table_component.ex index da8cfda..9d67027 100644 --- a/lib/cannery_web/components/container_table_component.ex +++ b/lib/cannery_web/components/container_table_component.ex @@ -73,7 +73,8 @@ defmodule CanneryWeb.Components.ContainerTableComponent do actions: actions, pack_count: Ammo.get_grouped_packs_count(current_user, containers: containers, group_by: :container_id), - round_count: Ammo.get_round_count_for_containers(containers, current_user) + round_count: + Ammo.get_grouped_round_count(current_user, containers: containers, group_by: :container_id) } rows = diff --git a/lib/cannery_web/components/type_table_component.ex b/lib/cannery_web/components/type_table_component.ex index bfde917..0f786ff 100644 --- a/lib/cannery_web/components/type_table_component.ex +++ b/lib/cannery_web/components/type_table_component.ex @@ -151,7 +151,7 @@ defmodule CanneryWeb.Components.TypeTableComponent do ) |> TableComponent.maybe_compose_columns(%{label: gettext("Name"), key: :name, type: :name}) - round_counts = types |> Ammo.get_round_count_for_types(current_user) + round_counts = Ammo.get_grouped_round_count(current_user, types: types, group_by: :type_id) packs_count = Ammo.get_grouped_packs_count(current_user, types: types, group_by: :type_id) average_costs = types |> Ammo.get_average_cost_for_types(current_user) diff --git a/lib/cannery_web/controllers/export_controller.ex b/lib/cannery_web/controllers/export_controller.ex index 7ec9d4a..3740744 100644 --- a/lib/cannery_web/controllers/export_controller.ex +++ b/lib/cannery_web/controllers/export_controller.ex @@ -8,7 +8,7 @@ defmodule CanneryWeb.ExportController do used_counts = ActivityLog.get_grouped_used_counts(current_user, types: types, group_by: :type_id) - round_counts = types |> Ammo.get_round_count_for_types(current_user) + round_counts = Ammo.get_grouped_round_count(current_user, types: types, group_by: :type_id) pack_counts = Ammo.get_grouped_packs_count(current_user, types: types, group_by: :type_id) total_pack_counts = diff --git a/priv/gettext/de/LC_MESSAGES/errors.po b/priv/gettext/de/LC_MESSAGES/errors.po index 474336e..bc7ef84 100644 --- a/priv/gettext/de/LC_MESSAGES/errors.po +++ b/priv/gettext/de/LC_MESSAGES/errors.po @@ -170,7 +170,7 @@ msgstr "" "Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War " "%{multiplier}" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po index 0b613cf..5d4b9bf 100644 --- a/priv/gettext/en/LC_MESSAGES/errors.po +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -153,7 +153,7 @@ msgstr "" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgstr "" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "" diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index c115f5c..8154b42 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -152,7 +152,7 @@ msgstr "" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgstr "" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "" diff --git a/priv/gettext/es/LC_MESSAGES/errors.po b/priv/gettext/es/LC_MESSAGES/errors.po index a8d564f..522ddc0 100644 --- a/priv/gettext/es/LC_MESSAGES/errors.po +++ b/priv/gettext/es/LC_MESSAGES/errors.po @@ -168,7 +168,7 @@ msgstr "No se ha podido procesar el número de copias" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgstr "Número inválido de copias, debe ser entre 1 y %{max}. Fue %{multiplier" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "Multiplicador inválido" diff --git a/priv/gettext/fr/LC_MESSAGES/errors.po b/priv/gettext/fr/LC_MESSAGES/errors.po index a419ed5..8fe50eb 100644 --- a/priv/gettext/fr/LC_MESSAGES/errors.po +++ b/priv/gettext/fr/LC_MESSAGES/errors.po @@ -169,7 +169,7 @@ msgstr "Impossible d'analyser le nombre de copies" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgstr "Nombre de copies invalide, doit être 1 et %{max}. Été %{multiplier}" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "Multiplicateur invalide" diff --git a/priv/gettext/ga/LC_MESSAGES/errors.po b/priv/gettext/ga/LC_MESSAGES/errors.po index 946299a..7959163 100644 --- a/priv/gettext/ga/LC_MESSAGES/errors.po +++ b/priv/gettext/ga/LC_MESSAGES/errors.po @@ -168,7 +168,7 @@ msgstr "" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgstr "" -#: lib/cannery/ammo.ex:985 +#: lib/cannery/ammo.ex:984 #, elixir-autogen, elixir-format msgid "Invalid multiplier" msgstr "" diff --git a/test/cannery/ammo_test.exs b/test/cannery/ammo_test.exs index 5d63aba..90ddb84 100644 --- a/test/cannery/ammo_test.exs +++ b/test/cannery/ammo_test.exs @@ -371,34 +371,38 @@ defmodule Cannery.AmmoTest do {1, [first_pack]} = pack_fixture(%{count: 1}, type, container, current_user) assert %{type_id => 1} == - [type] |> Ammo.get_round_count_for_types(current_user) + Ammo.get_grouped_round_count(current_user, types: [type], group_by: :type_id) %{id: another_type_id} = another_type = type_fixture(current_user) {1, [_another_pack]} = pack_fixture(%{count: 1}, another_type, container, current_user) - round_counts = [type, another_type] |> Ammo.get_round_count_for_types(current_user) + round_counts = + Ammo.get_grouped_round_count(current_user, types: [type, another_type], group_by: :type_id) assert %{^type_id => 1} = round_counts assert %{^another_type_id => 1} = round_counts {1, [pack]} = pack_fixture(%{count: 50}, type, container, current_user) - round_counts = [type, another_type] |> Ammo.get_round_count_for_types(current_user) + round_counts = + Ammo.get_grouped_round_count(current_user, types: [type, another_type], group_by: :type_id) assert %{^type_id => 51} = round_counts assert %{^another_type_id => 1} = round_counts shot_record_fixture(%{count: 26}, current_user, pack) - round_counts = [type, another_type] |> Ammo.get_round_count_for_types(current_user) + round_counts = + Ammo.get_grouped_round_count(current_user, types: [type, another_type], group_by: :type_id) assert %{^type_id => 25} = round_counts assert %{^another_type_id => 1} = round_counts shot_record_fixture(%{count: 1}, current_user, first_pack) - round_counts = [type, another_type] |> Ammo.get_round_count_for_types(current_user) + round_counts = + Ammo.get_grouped_round_count(current_user, types: [type, another_type], group_by: :type_id) assert %{^type_id => 24} = round_counts assert %{^another_type_id => 1} = round_counts @@ -649,7 +653,7 @@ defmodule Cannery.AmmoTest do assert 125 = Ammo.get_round_count(current_user, container_id: container.id) end - test "get_round_count_for_containers/2 gets accurate total round count for containers", + test "get_grouped_round_count/2 gets accurate total round count for containers", %{ type: type, current_user: current_user, @@ -662,7 +666,10 @@ defmodule Cannery.AmmoTest do {1, [_first_pack]} = pack_fixture(%{count: 5}, type, another_container, current_user) round_counts = - [container, another_container] |> Ammo.get_round_count_for_containers(current_user) + Ammo.get_grouped_round_count(current_user, + containers: [container, another_container], + group_by: :container_id + ) assert %{^container_id => 5} = round_counts assert %{^another_container_id => 5} = round_counts @@ -670,7 +677,10 @@ defmodule Cannery.AmmoTest do {25, _packs} = pack_fixture(%{count: 5}, 25, type, container, current_user) round_counts = - [container, another_container] |> Ammo.get_round_count_for_containers(current_user) + Ammo.get_grouped_round_count(current_user, + containers: [container, another_container], + group_by: :container_id + ) assert %{^container_id => 130} = round_counts assert %{^another_container_id => 5} = round_counts @@ -678,7 +688,10 @@ defmodule Cannery.AmmoTest do shot_record_fixture(%{count: 5}, current_user, first_pack) round_counts = - [container, another_container] |> Ammo.get_round_count_for_containers(current_user) + Ammo.get_grouped_round_count(current_user, + containers: [container, another_container], + group_by: :container_id + ) assert %{^container_id => 125} = round_counts assert %{^another_container_id => 5} = round_counts