improve Ammo.get_grouped_round_count

This commit is contained in:
shibao 2023-06-05 22:08:42 -04:00
parent 7e55446b3e
commit 9edeb1e803
11 changed files with 76 additions and 63 deletions

View File

@ -228,35 +228,62 @@ defmodule Cannery.Ammo do
defp get_round_count_container_id(query, _nil), do: query 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 """ @doc """
Gets the total number of rounds for multiple types Gets the total number of rounds for multiple types
## Examples ## Examples
iex> get_round_count_for_types( iex> get_grouped_round_count(
...> [%Type{id: 123, user_id: 456}], ...> %User{id: 456},
...> %User{id: 456} ...> group_by: :type_id,
...> types: [%Type{id: 123, user_id: 456}]
...> ) ...> )
%{123 => 35} %{123 => 35}
""" """
@spec get_round_count_for_types([Type.t()], User.t()) :: @spec get_grouped_round_count(User.t(), get_grouped_round_count_options()) ::
%{optional(Type.id()) => non_neg_integer()} %{optional(Type.id() | Container.id()) => non_neg_integer()}
def get_round_count_for_types(types, %User{id: user_id}) do def get_grouped_round_count(%User{id: user_id}, opts) do
type_ids = from(p in Pack,
types as: :p,
|> Enum.map(fn %Type{id: type_id, user_id: ^user_id} -> type_id end) where: p.user_id == ^user_id
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)}
) )
|> 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() |> Map.new()
end 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 """ @doc """
Gets the total number of ammo ever bought for a type 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()} %{optional(Type.id()) => non_neg_integer()}
def get_historical_count_for_types(types, %User{id: user_id} = user) do 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) 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 types
|> Enum.filter(fn %Type{id: type_id, user_id: ^user_id} -> |> 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)) query |> where([p: p], not (p.count == 0))
end 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 """ @doc """
Gets a single pack. Gets a single pack.

View File

@ -73,7 +73,8 @@ defmodule CanneryWeb.Components.ContainerTableComponent do
actions: actions, actions: actions,
pack_count: pack_count:
Ammo.get_grouped_packs_count(current_user, containers: containers, group_by: :container_id), 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 = rows =

View File

@ -151,7 +151,7 @@ defmodule CanneryWeb.Components.TypeTableComponent do
) )
|> TableComponent.maybe_compose_columns(%{label: gettext("Name"), key: :name, type: :name}) |> 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) 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) average_costs = types |> Ammo.get_average_cost_for_types(current_user)

View File

@ -8,7 +8,7 @@ defmodule CanneryWeb.ExportController do
used_counts = used_counts =
ActivityLog.get_grouped_used_counts(current_user, types: types, group_by: :type_id) 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) pack_counts = Ammo.get_grouped_packs_count(current_user, types: types, group_by: :type_id)
total_pack_counts = total_pack_counts =

View File

@ -170,7 +170,7 @@ msgstr ""
"Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War " "Ungültige Nummer an Kopien. Muss zwischen 1 and %{max} liegen. War "
"%{multiplier}" "%{multiplier}"
#: lib/cannery/ammo.ex:985 #: lib/cannery/ammo.ex:984
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""

View File

@ -153,7 +153,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:985 #: lib/cannery/ammo.ex:984
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""

View File

@ -152,7 +152,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:985 #: lib/cannery/ammo.ex:984
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""

View File

@ -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}" 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" 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 #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "Multiplicador inválido" msgstr "Multiplicador inválido"

View File

@ -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}" 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}" 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 #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "Multiplicateur invalide" msgstr "Multiplicateur invalide"

View File

@ -168,7 +168,7 @@ msgstr ""
msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}" msgid "Invalid number of copies, must be between 1 and %{max}. Was %{multiplier}"
msgstr "" msgstr ""
#: lib/cannery/ammo.ex:985 #: lib/cannery/ammo.ex:984
#, elixir-autogen, elixir-format #, elixir-autogen, elixir-format
msgid "Invalid multiplier" msgid "Invalid multiplier"
msgstr "" msgstr ""

View File

@ -371,34 +371,38 @@ defmodule Cannery.AmmoTest do
{1, [first_pack]} = pack_fixture(%{count: 1}, type, container, current_user) {1, [first_pack]} = pack_fixture(%{count: 1}, type, container, current_user)
assert %{type_id => 1} == 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) %{id: another_type_id} = another_type = type_fixture(current_user)
{1, [_another_pack]} = pack_fixture(%{count: 1}, another_type, container, 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 %{^type_id => 1} = round_counts
assert %{^another_type_id => 1} = round_counts assert %{^another_type_id => 1} = round_counts
{1, [pack]} = pack_fixture(%{count: 50}, type, container, current_user) {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 %{^type_id => 51} = round_counts
assert %{^another_type_id => 1} = round_counts assert %{^another_type_id => 1} = round_counts
shot_record_fixture(%{count: 26}, current_user, pack) 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 %{^type_id => 25} = round_counts
assert %{^another_type_id => 1} = round_counts assert %{^another_type_id => 1} = round_counts
shot_record_fixture(%{count: 1}, current_user, first_pack) 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 %{^type_id => 24} = round_counts
assert %{^another_type_id => 1} = 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) assert 125 = Ammo.get_round_count(current_user, container_id: container.id)
end 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, type: type,
current_user: current_user, current_user: current_user,
@ -662,7 +666,10 @@ defmodule Cannery.AmmoTest do
{1, [_first_pack]} = pack_fixture(%{count: 5}, type, another_container, current_user) {1, [_first_pack]} = pack_fixture(%{count: 5}, type, another_container, current_user)
round_counts = 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 %{^container_id => 5} = round_counts
assert %{^another_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) {25, _packs} = pack_fixture(%{count: 5}, 25, type, container, current_user)
round_counts = 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 %{^container_id => 130} = round_counts
assert %{^another_container_id => 5} = 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) shot_record_fixture(%{count: 5}, current_user, first_pack)
round_counts = 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 %{^container_id => 125} = round_counts
assert %{^another_container_id => 5} = round_counts assert %{^another_container_id => 5} = round_counts