rename ammo groups to packs

This commit is contained in:
shibao 2023-03-29 22:54:55 -04:00
parent 0cae7c2940
commit 6d26103784
68 changed files with 1829 additions and 1852 deletions

View File

@ -17,8 +17,8 @@ If you're multilingual, this project can use your translations! Visit
functions as short as possible while keeping variable names descriptive! For
instance, use inline `do:` blocks for short functions and make your aliases as
short as possible without introducing ambiguity.
- I.e. since there's only one `AmmoGroup` in the app, please alias
`AmmoGroup.t()` instead of using `Cannery.Ammo.AmmoGroup.t()`
- I.e. since there's only one `Pack` in the app, please alias
`Pack.t()` instead of using `Cannery.Ammo.Pack.t()`
- Use pipelines when possible. If only calling a single method, a pipeline isn't
strictly necessary but still encouraged for future modification.
- Please add typespecs to your functions! Even your private functions may be

View File

@ -4,7 +4,7 @@ defmodule Cannery.ActivityLog do
"""
import Ecto.Query, warn: false
alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Cannery.Ammo.{Pack, AmmoType}
alias Cannery.{Accounts.User, ActivityLog.ShotGroup, Repo}
alias Ecto.{Multi, Queryable}
@ -29,9 +29,9 @@ defmodule Cannery.ActivityLog do
def list_shot_groups(search \\ nil, type, %{id: user_id}) do
from(sg in ShotGroup,
as: :sg,
left_join: ag in AmmoGroup,
left_join: ag in Pack,
as: :ag,
on: sg.ammo_group_id == ag.id,
on: sg.pack_id == ag.id,
left_join: at in AmmoType,
as: :at,
on: ag.ammo_type_id == at.id,
@ -92,14 +92,14 @@ defmodule Cannery.ActivityLog do
defp list_shot_groups_filter_type(query, _all), do: query
@spec list_shot_groups_for_ammo_group(AmmoGroup.t(), User.t()) :: [ShotGroup.t()]
def list_shot_groups_for_ammo_group(
%AmmoGroup{id: ammo_group_id, user_id: user_id},
@spec list_shot_groups_for_pack(Pack.t(), User.t()) :: [ShotGroup.t()]
def list_shot_groups_for_pack(
%Pack{id: pack_id, user_id: user_id},
%User{id: user_id}
) do
Repo.all(
from sg in ShotGroup,
where: sg.ammo_group_id == ^ammo_group_id,
where: sg.pack_id == ^pack_id,
where: sg.user_id == ^user_id
)
end
@ -140,31 +140,31 @@ defmodule Cannery.ActivityLog do
{:error, %Ecto.Changeset{}}
"""
@spec create_shot_group(attrs :: map(), User.t(), AmmoGroup.t()) ::
@spec create_shot_group(attrs :: map(), User.t(), Pack.t()) ::
{:ok, ShotGroup.t()} | {:error, ShotGroup.changeset() | nil}
def create_shot_group(attrs, user, ammo_group) do
def create_shot_group(attrs, user, pack) do
Multi.new()
|> Multi.insert(
:create_shot_group,
%ShotGroup{} |> ShotGroup.create_changeset(user, ammo_group, attrs)
%ShotGroup{} |> ShotGroup.create_changeset(user, pack, attrs)
)
|> Multi.run(
:ammo_group,
fn _repo, %{create_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
ammo_group =
:pack,
fn _repo, %{create_shot_group: %{pack_id: pack_id, user_id: user_id}} ->
pack =
Repo.one(
from ag in AmmoGroup,
where: ag.id == ^ammo_group_id,
from ag in Pack,
where: ag.id == ^pack_id,
where: ag.user_id == ^user_id
)
{:ok, ammo_group}
{:ok, pack}
end
)
|> Multi.update(
:update_ammo_group,
fn %{create_shot_group: %{count: shot_group_count}, ammo_group: %{count: ammo_group_count}} ->
ammo_group |> AmmoGroup.range_changeset(%{"count" => ammo_group_count - shot_group_count})
:update_pack,
fn %{create_shot_group: %{count: shot_group_count}, pack: %{count: pack_count}} ->
pack |> Pack.range_changeset(%{"count" => pack_count - shot_group_count})
end
)
|> Repo.transaction()
@ -200,21 +200,20 @@ defmodule Cannery.ActivityLog do
shot_group |> ShotGroup.update_changeset(user, attrs)
)
|> Multi.run(
:ammo_group,
fn repo, %{update_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
{:ok,
repo.one(from ag in AmmoGroup, where: ag.id == ^ammo_group_id and ag.user_id == ^user_id)}
:pack,
fn repo, %{update_shot_group: %{pack_id: pack_id, user_id: user_id}} ->
{:ok, repo.one(from ag in Pack, where: ag.id == ^pack_id and ag.user_id == ^user_id)}
end
)
|> Multi.update(
:update_ammo_group,
:update_pack,
fn %{
update_shot_group: %{count: new_count},
ammo_group: %{count: ammo_group_count} = ammo_group
pack: %{count: pack_count} = pack
} ->
shot_diff_to_add = new_count - count
new_ammo_group_count = ammo_group_count - shot_diff_to_add
ammo_group |> AmmoGroup.range_changeset(%{"count" => new_ammo_group_count})
new_pack_count = pack_count - shot_diff_to_add
pack |> Pack.range_changeset(%{"count" => new_pack_count})
end
)
|> Repo.transaction()
@ -246,20 +245,19 @@ defmodule Cannery.ActivityLog do
Multi.new()
|> Multi.delete(:delete_shot_group, shot_group)
|> Multi.run(
:ammo_group,
fn repo, %{delete_shot_group: %{ammo_group_id: ammo_group_id, user_id: user_id}} ->
{:ok,
repo.one(from ag in AmmoGroup, where: ag.id == ^ammo_group_id and ag.user_id == ^user_id)}
:pack,
fn repo, %{delete_shot_group: %{pack_id: pack_id, user_id: user_id}} ->
{:ok, repo.one(from ag in Pack, where: ag.id == ^pack_id and ag.user_id == ^user_id)}
end
)
|> Multi.update(
:update_ammo_group,
:update_pack,
fn %{
delete_shot_group: %{count: count},
ammo_group: %{count: ammo_group_count} = ammo_group
pack: %{count: pack_count} = pack
} ->
new_ammo_group_count = ammo_group_count + count
ammo_group |> AmmoGroup.range_changeset(%{"count" => new_ammo_group_count})
new_pack_count = pack_count + count
pack |> Pack.range_changeset(%{"count" => new_pack_count})
end
)
|> Repo.transaction()
@ -273,29 +271,29 @@ defmodule Cannery.ActivityLog do
@doc """
Returns the number of shot rounds for an ammo group
"""
@spec get_used_count(AmmoGroup.t(), User.t()) :: non_neg_integer()
def get_used_count(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
[ammo_group]
@spec get_used_count(Pack.t(), User.t()) :: non_neg_integer()
def get_used_count(%Pack{id: pack_id} = pack, user) do
[pack]
|> get_used_counts(user)
|> Map.get(ammo_group_id, 0)
|> Map.get(pack_id, 0)
end
@doc """
Returns the number of shot rounds for multiple ammo groups
"""
@spec get_used_counts([AmmoGroup.t()], User.t()) ::
%{optional(AmmoGroup.id()) => non_neg_integer()}
def get_used_counts(ammo_groups, %User{id: user_id}) do
ammo_group_ids =
ammo_groups
|> Enum.map(fn %{id: ammo_group_id} -> ammo_group_id end)
@spec get_used_counts([Pack.t()], User.t()) ::
%{optional(Pack.id()) => non_neg_integer()}
def get_used_counts(packs, %User{id: user_id}) do
pack_ids =
packs
|> Enum.map(fn %{id: pack_id} -> pack_id end)
Repo.all(
from sg in ShotGroup,
where: sg.ammo_group_id in ^ammo_group_ids,
where: sg.pack_id in ^pack_ids,
where: sg.user_id == ^user_id,
group_by: sg.ammo_group_id,
select: {sg.ammo_group_id, sum(sg.count)}
group_by: sg.pack_id,
select: {sg.pack_id, sum(sg.count)}
)
|> Map.new()
end
@ -303,28 +301,28 @@ defmodule Cannery.ActivityLog do
@doc """
Returns the last entered shot group date for an ammo group
"""
@spec get_last_used_date(AmmoGroup.t(), User.t()) :: Date.t() | nil
def get_last_used_date(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
[ammo_group]
@spec get_last_used_date(Pack.t(), User.t()) :: Date.t() | nil
def get_last_used_date(%Pack{id: pack_id} = pack, user) do
[pack]
|> get_last_used_dates(user)
|> Map.get(ammo_group_id)
|> Map.get(pack_id)
end
@doc """
Returns the last entered shot group date for an ammo group
"""
@spec get_last_used_dates([AmmoGroup.t()], User.t()) :: %{optional(AmmoGroup.id()) => Date.t()}
def get_last_used_dates(ammo_groups, %User{id: user_id}) do
ammo_group_ids =
ammo_groups
|> Enum.map(fn %AmmoGroup{id: ammo_group_id, user_id: ^user_id} -> ammo_group_id end)
@spec get_last_used_dates([Pack.t()], User.t()) :: %{optional(Pack.id()) => Date.t()}
def get_last_used_dates(packs, %User{id: user_id}) do
pack_ids =
packs
|> Enum.map(fn %Pack{id: pack_id, user_id: ^user_id} -> pack_id end)
Repo.all(
from sg in ShotGroup,
where: sg.ammo_group_id in ^ammo_group_ids,
where: sg.pack_id in ^pack_ids,
where: sg.user_id == ^user_id,
group_by: sg.ammo_group_id,
select: {sg.ammo_group_id, max(sg.date)}
group_by: sg.pack_id,
select: {sg.pack_id, max(sg.date)}
)
|> Map.new()
end
@ -367,9 +365,9 @@ defmodule Cannery.ActivityLog do
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
left_join: sg in ShotGroup,
on: ag.id == sg.ammo_group_id,
on: ag.id == sg.pack_id,
where: ag.ammo_type_id in ^ammo_type_ids,
where: not (sg.count |> is_nil()),
group_by: ag.ammo_type_id,

View File

@ -6,7 +6,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
use Ecto.Schema
import CanneryWeb.Gettext
import Ecto.Changeset
alias Cannery.{Accounts.User, Ammo, Ammo.AmmoGroup}
alias Cannery.{Accounts.User, Ammo, Ammo.Pack}
alias Ecto.{Changeset, UUID}
@derive {Jason.Encoder,
@ -15,7 +15,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
:count,
:date,
:notes,
:ammo_group_id
:pack_id
]}
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@ -25,7 +25,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
field :notes, :string
field :user_id, :binary_id
field :ammo_group_id, :binary_id
field :pack_id, :binary_id
timestamps()
end
@ -35,7 +35,7 @@ defmodule Cannery.ActivityLog.ShotGroup do
count: integer,
notes: String.t() | nil,
date: Date.t() | nil,
ammo_group_id: AmmoGroup.id(),
pack_id: Pack.id(),
user_id: User.id(),
inserted_at: NaiveDateTime.t(),
updated_at: NaiveDateTime.t()
@ -48,46 +48,44 @@ defmodule Cannery.ActivityLog.ShotGroup do
@spec create_changeset(
new_shot_group(),
User.t() | any(),
AmmoGroup.t() | any(),
Pack.t() | any(),
attrs :: map()
) :: changeset()
def create_changeset(
shot_group,
%User{id: user_id},
%AmmoGroup{id: ammo_group_id, user_id: user_id} = ammo_group,
%Pack{id: pack_id, user_id: user_id} = pack,
attrs
) do
shot_group
|> change(user_id: user_id)
|> change(ammo_group_id: ammo_group_id)
|> change(pack_id: pack_id)
|> cast(attrs, [:count, :notes, :date])
|> validate_length(:notes, max: 255)
|> validate_create_shot_group_count(ammo_group)
|> validate_required([:date, :ammo_group_id, :user_id])
|> validate_create_shot_group_count(pack)
|> validate_required([:date, :pack_id, :user_id])
end
def create_changeset(shot_group, _invalid_user, _invalid_ammo_group, attrs) do
def create_changeset(shot_group, _invalid_user, _invalid_pack, attrs) do
shot_group
|> cast(attrs, [:count, :notes, :date])
|> validate_length(:notes, max: 255)
|> validate_required([:ammo_group_id, :user_id])
|> validate_required([:pack_id, :user_id])
|> add_error(:invalid, dgettext("errors", "Please select a valid user and ammo pack"))
end
defp validate_create_shot_group_count(changeset, %AmmoGroup{count: ammo_group_count}) do
defp validate_create_shot_group_count(changeset, %Pack{count: pack_count}) do
case changeset |> Changeset.get_field(:count) do
nil ->
changeset |> Changeset.add_error(:ammo_left, dgettext("errors", "can't be blank"))
count when count > ammo_group_count ->
count when count > pack_count ->
changeset
|> Changeset.add_error(:ammo_left, dgettext("errors", "Ammo left must be at least 0"))
count when count <= 0 ->
error =
dgettext("errors", "Ammo left can be at most %{count} rounds",
count: ammo_group_count - 1
)
dgettext("errors", "Ammo left can be at most %{count} rounds", count: pack_count - 1)
changeset |> Changeset.add_error(:ammo_left, error)
@ -109,17 +107,16 @@ defmodule Cannery.ActivityLog.ShotGroup do
defp validate_update_shot_group_count(
changeset,
%__MODULE__{ammo_group_id: ammo_group_id, count: count},
%__MODULE__{pack_id: pack_id, count: count},
user
) do
%{count: ammo_group_count} = Ammo.get_ammo_group!(ammo_group_id, user)
%{count: pack_count} = Ammo.get_pack!(pack_id, user)
new_shot_group_count = changeset |> Changeset.get_field(:count)
shot_diff_to_add = new_shot_group_count - count
if shot_diff_to_add > ammo_group_count do
error =
dgettext("errors", "Count can be at most %{count} shots", count: ammo_group_count + count)
if shot_diff_to_add > pack_count do
error = dgettext("errors", "Count can be at most %{count} shots", count: pack_count + count)
changeset |> Changeset.add_error(:count, error)
else

View File

@ -8,12 +8,12 @@ defmodule Cannery.Ammo do
alias Cannery.{Accounts.User, Containers, Repo}
alias Cannery.Containers.{Container, ContainerTag, Tag}
alias Cannery.{ActivityLog, ActivityLog.ShotGroup}
alias Cannery.Ammo.{AmmoGroup, AmmoType}
alias Cannery.Ammo.{Pack, AmmoType}
alias Ecto.{Changeset, Queryable}
@ammo_group_create_limit 10_000
@ammo_group_preloads [:ammo_type]
@ammo_type_preloads [:ammo_groups]
@pack_create_limit 10_000
@pack_preloads [:ammo_type]
@ammo_type_preloads [:packs]
@doc """
Returns the list of ammo_types.
@ -169,14 +169,14 @@ defmodule Cannery.Ammo do
sg_total_query =
from sg in ShotGroup,
where: not (sg.count |> is_nil()),
group_by: sg.ammo_group_id,
select: %{ammo_group_id: sg.ammo_group_id, total: sum(sg.count)}
group_by: sg.pack_id,
select: %{pack_id: sg.pack_id, total: sum(sg.count)}
Repo.all(
from ag in AmmoGroup,
as: :ammo_group,
from ag in Pack,
as: :pack,
left_join: sg_query in subquery(sg_total_query),
on: ag.id == sg_query.ammo_group_id,
on: ag.id == sg_query.pack_id,
where: ag.ammo_type_id in ^ammo_type_ids,
group_by: ag.ammo_type_id,
where: not (ag.price_paid |> is_nil()),
@ -225,7 +225,7 @@ defmodule Cannery.Ammo do
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
where: ag.ammo_type_id in ^ammo_type_ids,
where: ag.user_id == ^user_id,
group_by: ag.ammo_type_id,
@ -376,150 +376,150 @@ defmodule Cannery.Ammo do
end
@doc """
Returns the list of ammo_groups for a user and type.
Returns the list of packs for a user and type.
## Examples
iex> list_ammo_groups_for_type(
iex> list_packs_for_type(
...> %AmmoType{id: 123, user_id: 456},
...> %User{id: 456}
...> )
[%AmmoGroup{}, ...]
[%Pack{}, ...]
iex> list_ammo_groups_for_type(
iex> list_packs_for_type(
...> %AmmoType{id: 123, user_id: 456},
...> %User{id: 456},
...> true
...> )
[%AmmoGroup{}, %AmmoGroup{}, ...]
[%Pack{}, %Pack{}, ...]
"""
@spec list_ammo_groups_for_type(AmmoType.t(), User.t()) :: [AmmoGroup.t()]
@spec list_ammo_groups_for_type(AmmoType.t(), User.t(), show_used :: boolean()) ::
[AmmoGroup.t()]
def list_ammo_groups_for_type(ammo_type, user, show_used \\ false)
@spec list_packs_for_type(AmmoType.t(), User.t()) :: [Pack.t()]
@spec list_packs_for_type(AmmoType.t(), User.t(), show_used :: boolean()) ::
[Pack.t()]
def list_packs_for_type(ammo_type, user, show_used \\ false)
def list_ammo_groups_for_type(
def list_packs_for_type(
%AmmoType{id: ammo_type_id, user_id: user_id},
%User{id: user_id},
show_used
) do
from(ag in AmmoGroup,
from(ag in Pack,
as: :ag,
where: ag.ammo_type_id == ^ammo_type_id,
where: ag.user_id == ^user_id,
preload: ^@ammo_group_preloads
preload: ^@pack_preloads
)
|> list_ammo_groups_for_type_show_used(show_used)
|> list_packs_for_type_show_used(show_used)
|> Repo.all()
end
@spec list_ammo_groups_for_type_show_used(Queryable.t(), show_used :: boolean()) ::
@spec list_packs_for_type_show_used(Queryable.t(), show_used :: boolean()) ::
Queryable.t()
def list_ammo_groups_for_type_show_used(query, false),
def list_packs_for_type_show_used(query, false),
do: query |> where([ag: ag], ag.count > 0)
def list_ammo_groups_for_type_show_used(query, _true), do: query
def list_packs_for_type_show_used(query, _true), do: query
@doc """
Returns the list of ammo_groups for a user and container.
Returns the list of packs for a user and container.
## Examples
iex> list_ammo_groups_for_container(
iex> list_packs_for_container(
...> %Container{id: 123, user_id: 456},
...> :rifle,
...> %User{id: 456}
...> )
[%AmmoGroup{}, ...]
[%Pack{}, ...]
iex> list_ammo_groups_for_container(
iex> list_packs_for_container(
...> %Container{id: 123, user_id: 456},
...> :all,
...> %User{id: 456}
...> )
[%AmmoGroup{}, %AmmoGroup{}, ...]
[%Pack{}, %Pack{}, ...]
"""
@spec list_ammo_groups_for_container(
@spec list_packs_for_container(
Container.t(),
AmmoType.t() | :all,
User.t()
) :: [AmmoGroup.t()]
def list_ammo_groups_for_container(
) :: [Pack.t()]
def list_packs_for_container(
%Container{id: container_id, user_id: user_id},
type,
%User{id: user_id}
) do
from(ag in AmmoGroup,
from(ag in Pack,
as: :ag,
join: at in assoc(ag, :ammo_type),
as: :at,
where: ag.container_id == ^container_id,
where: ag.user_id == ^user_id,
where: ag.count > 0,
preload: ^@ammo_group_preloads
preload: ^@pack_preloads
)
|> list_ammo_groups_for_container_filter_type(type)
|> list_packs_for_container_filter_type(type)
|> Repo.all()
end
@spec list_ammo_groups_for_container_filter_type(Queryable.t(), AmmoType.class() | :all) ::
@spec list_packs_for_container_filter_type(Queryable.t(), AmmoType.class() | :all) ::
Queryable.t()
defp list_ammo_groups_for_container_filter_type(query, :rifle),
defp list_packs_for_container_filter_type(query, :rifle),
do: query |> where([at: at], at.class == :rifle)
defp list_ammo_groups_for_container_filter_type(query, :pistol),
defp list_packs_for_container_filter_type(query, :pistol),
do: query |> where([at: at], at.class == :pistol)
defp list_ammo_groups_for_container_filter_type(query, :shotgun),
defp list_packs_for_container_filter_type(query, :shotgun),
do: query |> where([at: at], at.class == :shotgun)
defp list_ammo_groups_for_container_filter_type(query, _all), do: query
defp list_packs_for_container_filter_type(query, _all), do: query
@doc """
Returns a count of ammo_groups.
Returns a count of packs.
## Examples
iex> get_ammo_groups_count!(%User{id: 123})
iex> get_packs_count!(%User{id: 123})
3
iex> get_ammo_groups_count!(%User{id: 123}, true)
iex> get_packs_count!(%User{id: 123}, true)
4
"""
@spec get_ammo_groups_count!(User.t()) :: integer()
@spec get_ammo_groups_count!(User.t(), show_used :: boolean()) :: integer()
def get_ammo_groups_count!(%User{id: user_id}, show_used \\ false) do
from(ag in AmmoGroup,
@spec get_packs_count!(User.t()) :: integer()
@spec get_packs_count!(User.t(), show_used :: boolean()) :: integer()
def get_packs_count!(%User{id: user_id}, show_used \\ false) do
from(ag in Pack,
as: :ag,
where: ag.user_id == ^user_id,
select: count(ag.id),
distinct: true
)
|> get_ammo_groups_count_show_used(show_used)
|> get_packs_count_show_used(show_used)
|> Repo.one() || 0
end
@spec get_ammo_groups_count_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t()
defp get_ammo_groups_count_show_used(query, false),
@spec get_packs_count_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t()
defp get_packs_count_show_used(query, false),
do: query |> where([ag: ag], ag.count > 0)
defp get_ammo_groups_count_show_used(query, _true), do: query
defp get_packs_count_show_used(query, _true), do: query
@doc """
Returns the count of ammo_groups for an ammo type.
Returns the count of packs for an ammo type.
## Examples
iex> get_ammo_groups_count_for_type(
iex> get_packs_count_for_type(
...> %AmmoType{id: 123, user_id: 456},
...> %User{id: 456}
...> )
3
iex> get_ammo_groups_count_for_type(
iex> get_packs_count_for_type(
...> %AmmoType{id: 123, user_id: 456},
...> %User{id: 456},
...> true
@ -527,31 +527,31 @@ defmodule Cannery.Ammo do
5
"""
@spec get_ammo_groups_count_for_type(AmmoType.t(), User.t()) :: non_neg_integer()
@spec get_ammo_groups_count_for_type(AmmoType.t(), User.t(), show_used :: boolean()) ::
@spec get_packs_count_for_type(AmmoType.t(), User.t()) :: non_neg_integer()
@spec get_packs_count_for_type(AmmoType.t(), User.t(), show_used :: boolean()) ::
non_neg_integer()
def get_ammo_groups_count_for_type(
def get_packs_count_for_type(
%AmmoType{id: ammo_type_id} = ammo_type,
user,
show_used \\ false
) do
[ammo_type]
|> get_ammo_groups_count_for_types(user, show_used)
|> get_packs_count_for_types(user, show_used)
|> Map.get(ammo_type_id, 0)
end
@doc """
Returns the count of ammo_groups for multiple ammo types.
Returns the count of packs for multiple ammo types.
## Examples
iex> get_ammo_groups_count_for_types(
iex> get_packs_count_for_types(
...> [%AmmoType{id: 123, user_id: 456}],
...> %User{id: 456}
...> )
3
iex> get_ammo_groups_count_for_types(
iex> get_packs_count_for_types(
...> [%AmmoType{id: 123, user_id: 456}],
...> %User{id: 456},
...> true
@ -559,75 +559,75 @@ defmodule Cannery.Ammo do
5
"""
@spec get_ammo_groups_count_for_types([AmmoType.t()], User.t()) ::
@spec get_packs_count_for_types([AmmoType.t()], User.t()) ::
%{optional(AmmoType.id()) => non_neg_integer()}
@spec get_ammo_groups_count_for_types([AmmoType.t()], User.t(), show_used :: boolean()) ::
@spec get_packs_count_for_types([AmmoType.t()], User.t(), show_used :: boolean()) ::
%{optional(AmmoType.id()) => non_neg_integer()}
def get_ammo_groups_count_for_types(ammo_types, %User{id: user_id}, show_used \\ false) do
def get_packs_count_for_types(ammo_types, %User{id: user_id}, show_used \\ false) do
ammo_type_ids =
ammo_types
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
from(ag in AmmoGroup,
from(ag in Pack,
as: :ag,
where: ag.user_id == ^user_id,
where: ag.ammo_type_id in ^ammo_type_ids,
group_by: ag.ammo_type_id,
select: {ag.ammo_type_id, count(ag.id)}
)
|> get_ammo_groups_count_for_types_maybe_show_used(show_used)
|> get_packs_count_for_types_maybe_show_used(show_used)
|> Repo.all()
|> Map.new()
end
@spec get_ammo_groups_count_for_types_maybe_show_used(Queryable.t(), show_used :: boolean()) ::
@spec get_packs_count_for_types_maybe_show_used(Queryable.t(), show_used :: boolean()) ::
Queryable.t()
defp get_ammo_groups_count_for_types_maybe_show_used(query, true), do: query
defp get_packs_count_for_types_maybe_show_used(query, true), do: query
defp get_ammo_groups_count_for_types_maybe_show_used(query, _false) do
defp get_packs_count_for_types_maybe_show_used(query, _false) do
query |> where([ag: ag], not (ag.count == 0))
end
@doc """
Returns the count of used ammo_groups for an ammo type.
Returns the count of used packs for an ammo type.
## Examples
iex> get_used_ammo_groups_count_for_type(
iex> get_used_packs_count_for_type(
...> %AmmoType{id: 123, user_id: 456},
...> %User{id: 456}
...> )
3
"""
@spec get_used_ammo_groups_count_for_type(AmmoType.t(), User.t()) :: non_neg_integer()
def get_used_ammo_groups_count_for_type(%AmmoType{id: ammo_type_id} = ammo_type, user) do
@spec get_used_packs_count_for_type(AmmoType.t(), User.t()) :: non_neg_integer()
def get_used_packs_count_for_type(%AmmoType{id: ammo_type_id} = ammo_type, user) do
[ammo_type]
|> get_used_ammo_groups_count_for_types(user)
|> get_used_packs_count_for_types(user)
|> Map.get(ammo_type_id, 0)
end
@doc """
Returns the count of used ammo_groups for multiple ammo types.
Returns the count of used packs for multiple ammo types.
## Examples
iex> get_used_ammo_groups_count_for_types(
iex> get_used_packs_count_for_types(
...> [%AmmoType{id: 123, user_id: 456}],
...> %User{id: 456}
...> )
%{123 => 3}
"""
@spec get_used_ammo_groups_count_for_types([AmmoType.t()], User.t()) ::
@spec get_used_packs_count_for_types([AmmoType.t()], User.t()) ::
%{optional(AmmoType.id()) => non_neg_integer()}
def get_used_ammo_groups_count_for_types(ammo_types, %User{id: user_id}) do
def get_used_packs_count_for_types(ammo_types, %User{id: user_id}) do
ammo_type_ids =
ammo_types
|> Enum.map(fn %AmmoType{id: ammo_type_id, user_id: ^user_id} -> ammo_type_id end)
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
where: ag.user_id == ^user_id,
where: ag.ammo_type_id in ^ammo_type_ids,
where: ag.count == 0,
@ -642,20 +642,20 @@ defmodule Cannery.Ammo do
## Examples
iex> get_ammo_groups_count_for_container(
iex> get_packs_count_for_container(
...> %Container{id: 123, user_id: 456},
...> %User{id: 456}
...> )
3
"""
@spec get_ammo_groups_count_for_container!(Container.t(), User.t()) :: non_neg_integer()
def get_ammo_groups_count_for_container!(
@spec get_packs_count_for_container!(Container.t(), User.t()) :: non_neg_integer()
def get_packs_count_for_container!(
%Container{id: container_id} = container,
%User{} = user
) do
[container]
|> get_ammo_groups_count_for_containers(user)
|> get_packs_count_for_containers(user)
|> Map.get(container_id, 0)
end
@ -664,23 +664,23 @@ defmodule Cannery.Ammo do
## Examples
iex> get_ammo_groups_count_for_containers(
iex> get_packs_count_for_containers(
...> [%Container{id: 123, user_id: 456}],
...> %User{id: 456}
...> )
%{123 => 3}
"""
@spec get_ammo_groups_count_for_containers([Container.t()], User.t()) :: %{
@spec get_packs_count_for_containers([Container.t()], User.t()) :: %{
Container.id() => non_neg_integer()
}
def get_ammo_groups_count_for_containers(containers, %User{id: user_id}) do
def get_packs_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 ag in AmmoGroup,
from ag in Pack,
where: ag.container_id in ^container_ids,
where: ag.count > 0,
group_by: ag.container_id,
@ -728,7 +728,7 @@ defmodule Cannery.Ammo do
|> Enum.map(fn %Container{id: container_id, user_id: ^user_id} -> container_id end)
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
where: ag.container_id in ^container_ids,
group_by: ag.container_id,
select: {ag.container_id, sum(ag.count)}
@ -737,27 +737,27 @@ defmodule Cannery.Ammo do
end
@doc """
Returns the list of ammo_groups.
Returns the list of packs.
## Examples
iex> list_ammo_groups(%User{id: 123})
[%AmmoGroup{}, ...]
iex> list_packs(%User{id: 123})
[%Pack{}, ...]
iex> list_ammo_groups("cool", %User{id: 123}, true)
[%AmmoGroup{notes: "My cool ammo group"}, ...]
iex> list_packs("cool", %User{id: 123}, true)
[%Pack{notes: "My cool ammo group"}, ...]
"""
@spec list_ammo_groups(search :: String.t() | nil, AmmoType.class() | :all, User.t()) ::
[AmmoGroup.t()]
@spec list_ammo_groups(
@spec list_packs(search :: String.t() | nil, AmmoType.class() | :all, User.t()) ::
[Pack.t()]
@spec list_packs(
search :: nil | String.t(),
AmmoType.class() | :all,
User.t(),
show_used :: boolean()
) :: [AmmoGroup.t()]
def list_ammo_groups(search, type, %{id: user_id}, show_used \\ false) do
from(ag in AmmoGroup,
) :: [Pack.t()]
def list_packs(search, type, %{id: user_id}, show_used \\ false) do
from(ag in Pack,
as: :ag,
join: at in assoc(ag, :ammo_type),
as: :at,
@ -773,38 +773,38 @@ defmodule Cannery.Ammo do
as: :t,
where: ag.user_id == ^user_id,
distinct: ag.id,
preload: ^@ammo_group_preloads
preload: ^@pack_preloads
)
|> list_ammo_groups_filter_on_type(type)
|> list_ammo_groups_show_used(show_used)
|> list_ammo_groups_search(search)
|> list_packs_filter_on_type(type)
|> list_packs_show_used(show_used)
|> list_packs_search(search)
|> Repo.all()
end
@spec list_ammo_groups_filter_on_type(Queryable.t(), AmmoType.class() | :all) :: Queryable.t()
defp list_ammo_groups_filter_on_type(query, :rifle),
@spec list_packs_filter_on_type(Queryable.t(), AmmoType.class() | :all) :: Queryable.t()
defp list_packs_filter_on_type(query, :rifle),
do: query |> where([at: at], at.class == :rifle)
defp list_ammo_groups_filter_on_type(query, :pistol),
defp list_packs_filter_on_type(query, :pistol),
do: query |> where([at: at], at.class == :pistol)
defp list_ammo_groups_filter_on_type(query, :shotgun),
defp list_packs_filter_on_type(query, :shotgun),
do: query |> where([at: at], at.class == :shotgun)
defp list_ammo_groups_filter_on_type(query, _all), do: query
defp list_packs_filter_on_type(query, _all), do: query
@spec list_ammo_groups_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t()
defp list_ammo_groups_show_used(query, true), do: query
@spec list_packs_show_used(Queryable.t(), show_used :: boolean()) :: Queryable.t()
defp list_packs_show_used(query, true), do: query
defp list_ammo_groups_show_used(query, _false) do
defp list_packs_show_used(query, _false) do
query |> where([ag: ag], not (ag.count == 0))
end
@spec list_ammo_groups_show_used(Queryable.t(), search :: String.t() | nil) :: Queryable.t()
defp list_ammo_groups_search(query, nil), do: query
defp list_ammo_groups_search(query, ""), do: query
@spec list_packs_show_used(Queryable.t(), search :: String.t() | nil) :: Queryable.t()
defp list_packs_search(query, nil), do: query
defp list_packs_search(query, ""), do: query
defp list_ammo_groups_search(query, search) do
defp list_packs_search(query, search) do
trimmed_search = String.trim(search)
query
@ -843,60 +843,60 @@ defmodule Cannery.Ammo do
end
@doc """
Returns the list of staged ammo_groups for a user.
Returns the list of staged packs for a user.
## Examples
iex> list_staged_ammo_groups(%User{id: 123})
[%AmmoGroup{}, ...]
iex> list_staged_packs(%User{id: 123})
[%Pack{}, ...]
"""
@spec list_staged_ammo_groups(User.t()) :: [AmmoGroup.t()]
def list_staged_ammo_groups(%User{id: user_id}) do
@spec list_staged_packs(User.t()) :: [Pack.t()]
def list_staged_packs(%User{id: user_id}) do
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
where: ag.user_id == ^user_id,
where: ag.staged == true,
preload: ^@ammo_group_preloads
preload: ^@pack_preloads
)
end
@doc """
Gets a single ammo_group.
Gets a single pack.
Raises `KeyError` if the Ammo group does not exist.
## Examples
iex> get_ammo_group!(123, %User{id: 123})
%AmmoGroup{}
iex> get_pack!(123, %User{id: 123})
%Pack{}
iex> get_ammo_group!(456, %User{id: 123})
iex> get_pack!(456, %User{id: 123})
** (KeyError)
"""
@spec get_ammo_group!(AmmoGroup.id(), User.t()) :: AmmoGroup.t()
def get_ammo_group!(id, user) do
[id] |> get_ammo_groups(user) |> Map.fetch!(id)
@spec get_pack!(Pack.id(), User.t()) :: Pack.t()
def get_pack!(id, user) do
[id] |> get_packs(user) |> Map.fetch!(id)
end
@doc """
Gets a group of ammo_groups by their ID.
Gets a group of packs by their ID.
## Examples
iex> get_ammo_groups([123, 456], %User{id: 123})
%{123 => %AmmoGroup{}, 456 => %AmmoGroup{}}
iex> get_packs([123, 456], %User{id: 123})
%{123 => %Pack{}, 456 => %Pack{}}
"""
@spec get_ammo_groups([AmmoGroup.id()], User.t()) ::
%{optional(AmmoGroup.id()) => AmmoGroup.t()}
def get_ammo_groups(ids, %User{id: user_id}) do
@spec get_packs([Pack.id()], User.t()) ::
%{optional(Pack.id()) => Pack.t()}
def get_packs(ids, %User{id: user_id}) do
Repo.all(
from ag in AmmoGroup,
from ag in Pack,
where: ag.id in ^ids,
where: ag.user_id == ^user_id,
preload: ^@ammo_group_preloads,
preload: ^@pack_preloads,
select: {ag.id, ag}
)
|> Map.new()
@ -908,17 +908,17 @@ defmodule Cannery.Ammo do
## Examples
iex> get_percentage_remaining(
...> %AmmoGroup{id: 123, count: 5, user_id: 456},
...> %Pack{id: 123, count: 5, user_id: 456},
...> %User{id: 456}
...> )
100
"""
@spec get_percentage_remaining(AmmoGroup.t(), User.t()) :: non_neg_integer()
def get_percentage_remaining(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
[ammo_group]
@spec get_percentage_remaining(Pack.t(), User.t()) :: non_neg_integer()
def get_percentage_remaining(%Pack{id: pack_id} = pack, user) do
[pack]
|> get_percentages_remaining(user)
|> Map.fetch!(ammo_group_id)
|> Map.fetch!(pack_id)
end
@doc """
@ -927,26 +927,26 @@ defmodule Cannery.Ammo do
## Examples
iex> get_percentages_remaining(
...> [%AmmoGroup{id: 123, count: 5, user_id: 456}],
...> [%Pack{id: 123, count: 5, user_id: 456}],
...> %User{id: 456}
...> )
%{123 => 100}
"""
@spec get_percentages_remaining([AmmoGroup.t()], User.t()) ::
%{optional(AmmoGroup.id()) => non_neg_integer()}
def get_percentages_remaining(ammo_groups, %User{id: user_id} = user) do
original_counts = get_original_counts(ammo_groups, user)
@spec get_percentages_remaining([Pack.t()], User.t()) ::
%{optional(Pack.id()) => non_neg_integer()}
def get_percentages_remaining(packs, %User{id: user_id} = user) do
original_counts = get_original_counts(packs, user)
ammo_groups
|> Map.new(fn %AmmoGroup{id: ammo_group_id, count: count, user_id: ^user_id} ->
packs
|> Map.new(fn %Pack{id: pack_id, count: count, user_id: ^user_id} ->
percentage =
case count do
0 -> 0
count -> round(count / Map.fetch!(original_counts, ammo_group_id) * 100)
count -> round(count / Map.fetch!(original_counts, pack_id) * 100)
end
{ammo_group_id, percentage}
{pack_id, percentage}
end)
end
@ -956,17 +956,17 @@ defmodule Cannery.Ammo do
## Examples
iex> get_original_count(
...> %AmmoGroup{id: 123, count: 5, user_id: 456},
...> %Pack{id: 123, count: 5, user_id: 456},
...> %User{id: 456}
...> )
5
"""
@spec get_original_count(AmmoGroup.t(), User.t()) :: non_neg_integer()
def get_original_count(%AmmoGroup{id: ammo_group_id} = ammo_group, current_user) do
[ammo_group]
@spec get_original_count(Pack.t(), User.t()) :: non_neg_integer()
def get_original_count(%Pack{id: pack_id} = pack, current_user) do
[pack]
|> get_original_counts(current_user)
|> Map.fetch!(ammo_group_id)
|> Map.fetch!(pack_id)
end
@doc """
@ -975,20 +975,20 @@ defmodule Cannery.Ammo do
## Examples
iex> get_original_counts(
...> [%AmmoGroup{id: 123, count: 5, user_id: 456}],
...> [%Pack{id: 123, count: 5, user_id: 456}],
...> %User{id: 456}
...> )
%{123 => 5}
"""
@spec get_original_counts([AmmoGroup.t()], User.t()) ::
%{optional(AmmoGroup.id()) => non_neg_integer()}
def get_original_counts(ammo_groups, %User{id: user_id} = current_user) do
used_counts = ActivityLog.get_used_counts(ammo_groups, current_user)
@spec get_original_counts([Pack.t()], User.t()) ::
%{optional(Pack.id()) => non_neg_integer()}
def get_original_counts(packs, %User{id: user_id} = current_user) do
used_counts = ActivityLog.get_used_counts(packs, current_user)
ammo_groups
|> Map.new(fn %AmmoGroup{id: ammo_group_id, count: count, user_id: ^user_id} ->
{ammo_group_id, count + Map.get(used_counts, ammo_group_id, 0)}
packs
|> Map.new(fn %Pack{id: pack_id, count: count, user_id: ^user_id} ->
{pack_id, count + Map.get(used_counts, pack_id, 0)}
end)
end
@ -998,17 +998,17 @@ defmodule Cannery.Ammo do
## Examples
iex> get_cpr(
...> %AmmoGroup{id: 123, price_paid: 5, count: 5, user_id: 456},
...> %Pack{id: 123, price_paid: 5, count: 5, user_id: 456},
...> %User{id: 456}
...> )
1
"""
@spec get_cpr(AmmoGroup.t(), User.t()) :: float() | nil
def get_cpr(%AmmoGroup{id: ammo_group_id} = ammo_group, user) do
[ammo_group]
@spec get_cpr(Pack.t(), User.t()) :: float() | nil
def get_cpr(%Pack{id: pack_id} = pack, user) do
[pack]
|> get_cprs(user)
|> Map.get(ammo_group_id)
|> Map.get(pack_id)
end
@doc """
@ -1017,22 +1017,22 @@ defmodule Cannery.Ammo do
## Examples
iex> get_cprs(
...> [%AmmoGroup{id: 123, price_paid: 5, count: 5, user_id: 456}],
...> [%Pack{id: 123, price_paid: 5, count: 5, user_id: 456}],
...> %User{id: 456}
...> )
%{123 => 1}
"""
@spec get_cprs([AmmoGroup.t()], User.t()) :: %{optional(AmmoGroup.id()) => float()}
def get_cprs(ammo_groups, %User{id: user_id} = current_user) do
original_counts = get_original_counts(ammo_groups, current_user)
@spec get_cprs([Pack.t()], User.t()) :: %{optional(Pack.id()) => float()}
def get_cprs(packs, %User{id: user_id} = current_user) do
original_counts = get_original_counts(packs, current_user)
ammo_groups
|> Enum.reject(fn %AmmoGroup{price_paid: price_paid, user_id: ^user_id} ->
packs
|> Enum.reject(fn %Pack{price_paid: price_paid, user_id: ^user_id} ->
price_paid |> is_nil()
end)
|> Map.new(fn %{id: ammo_group_id, price_paid: price_paid} ->
{ammo_group_id, calculate_cpr(price_paid, Map.fetch!(original_counts, ammo_group_id))}
|> Map.new(fn %{id: pack_id, price_paid: price_paid} ->
{pack_id, calculate_cpr(price_paid, Map.fetch!(original_counts, pack_id))}
end)
end
@ -1042,41 +1042,41 @@ defmodule Cannery.Ammo do
defp calculate_cpr(price_paid, total_count), do: price_paid / total_count
@doc """
Creates multiple ammo_groups at once.
Creates multiple packs at once.
## Examples
iex> create_ammo_groups(%{field: value}, 3, %User{id: 123})
{:ok, {3, [%AmmoGroup{}]}}
iex> create_packs(%{field: value}, 3, %User{id: 123})
{:ok, {3, [%Pack{}]}}
iex> create_ammo_groups(%{field: bad_value}, 3, %User{id: 123})
iex> create_packs(%{field: bad_value}, 3, %User{id: 123})
{:error, %Changeset{}}
"""
@spec create_ammo_groups(attrs :: map(), multiplier :: non_neg_integer(), User.t()) ::
{:ok, {count :: non_neg_integer(), [AmmoGroup.t()] | nil}}
| {:error, AmmoGroup.changeset()}
def create_ammo_groups(attrs, multiplier, %User{} = user) do
@spec create_packs(attrs :: map(), multiplier :: non_neg_integer(), User.t()) ::
{:ok, {count :: non_neg_integer(), [Pack.t()] | nil}}
| {:error, Pack.changeset()}
def create_packs(attrs, multiplier, %User{} = user) do
attrs
|> Map.new(fn {k, v} -> {to_string(k), v} end)
|> do_create_ammo_groups(multiplier, user)
|> do_create_packs(multiplier, user)
end
defp do_create_ammo_groups(
defp do_create_packs(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
multiplier,
user
)
when multiplier >= 1 and
multiplier <= @ammo_group_create_limit and
multiplier <= @pack_create_limit and
ammo_type_id |> is_binary() and
container_id |> is_binary() do
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
changesets =
Enum.map(1..multiplier, fn _count ->
%AmmoGroup{}
|> AmmoGroup.create_changeset(
%Pack{}
|> Pack.create_changeset(
get_ammo_type!(ammo_type_id, user),
Containers.get_container!(container_id, user),
user,
@ -1085,9 +1085,9 @@ defmodule Cannery.Ammo do
end)
if changesets |> Enum.all?(fn %{valid?: valid} -> valid end) do
{count, inserted_ammo_groups} =
{count, inserted_packs} =
Repo.insert_all(
AmmoGroup,
Pack,
changesets
|> Enum.map(fn changeset ->
changeset
@ -1097,7 +1097,7 @@ defmodule Cannery.Ammo do
returning: true
)
{:ok, {count, inserted_ammo_groups |> preload_ammo_group()}}
{:ok, {count, inserted_packs |> preload_pack()}}
else
changesets
|> Enum.reject(fn %{valid?: valid} -> valid end)
@ -1106,15 +1106,15 @@ defmodule Cannery.Ammo do
end
end
defp do_create_ammo_groups(
defp do_create_packs(
%{"ammo_type_id" => ammo_type_id, "container_id" => container_id} = attrs,
_multiplier,
user
)
when is_binary(ammo_type_id) and is_binary(container_id) do
changeset =
%AmmoGroup{}
|> AmmoGroup.create_changeset(
%Pack{}
|> Pack.create_changeset(
get_ammo_type!(ammo_type_id, user),
Containers.get_container!(container_id, user),
user,
@ -1125,79 +1125,79 @@ defmodule Cannery.Ammo do
{:error, changeset}
end
defp do_create_ammo_groups(invalid_attrs, _multiplier, user) do
{:error, %AmmoGroup{} |> AmmoGroup.create_changeset(nil, nil, user, invalid_attrs)}