Files
Claper/lib/claper/polls.ex

321 lines
7.1 KiB
Elixir
Raw Normal View History

defmodule Claper.Polls do
@moduledoc """
The Polls context.
"""
import Ecto.Query, warn: false
alias Claper.Repo
alias Claper.Polls.Poll
alias Claper.Polls.PollOpt
alias Claper.Polls.PollVote
@doc """
Returns the list of polls.
## Examples
iex> list_polls()
[%Poll{}, ...]
"""
def list_polls(presentation_file_id) do
from(p in Poll,
where: p.presentation_file_id == ^presentation_file_id,
order_by: [asc: p.id, asc: p.position]
)
|> Repo.all()
|> Repo.preload([:poll_opts])
end
def list_polls_at_position(presentation_file_id, position) do
from(p in Poll,
where: p.presentation_file_id == ^presentation_file_id and p.position == ^position,
order_by: [asc: p.id]
)
|> Repo.all()
|> Repo.preload([:poll_opts])
end
@doc """
Gets a single poll.
Raises `Ecto.NoResultsError` if the Poll does not exist.
## Examples
iex> get_poll!(123)
%Poll{}
iex> get_poll!(456)
** (Ecto.NoResultsError)
"""
def get_poll!(id),
do:
Repo.get!(Poll, id)
|> Repo.preload(
poll_opts:
from(
o in PollOpt,
order_by: [asc: o.id]
)
)
|> set_percentages()
def get_poll_current_position(presentation_file_id, position) do
from(p in Poll,
where:
p.position == ^position and p.presentation_file_id == ^presentation_file_id and
p.enabled == true
)
|> Repo.one()
|> Repo.preload(
poll_opts:
from(
o in PollOpt,
order_by: [asc: o.id]
)
)
|> set_percentages()
end
def set_percentages(%Poll{poll_opts: poll_opts} = poll) when is_list(poll_opts) do
total = Enum.map(poll.poll_opts, fn e -> e.vote_count end) |> Enum.sum()
%{
poll
| poll_opts:
poll.poll_opts
|> Enum.map(fn o -> %{o | percentage: calculate_percentage(o, total)} end)
}
end
def set_percentages(poll), do: poll
defp calculate_percentage(opt, total) do
if total > 0,
do: Float.round(opt.vote_count / total * 100) |> :erlang.float_to_binary(decimals: 0),
else: 0
end
@doc """
Creates a poll.
## Examples
iex> create_poll(%{field: value})
{:ok, %Poll{}}
iex> create_poll(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_poll(attrs \\ %{}) do
%Poll{}
|> Poll.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a poll.
## Examples
iex> update_poll(poll, %{field: new_value})
{:ok, %Poll{}}
iex> update_poll(poll, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def update_poll(event_uuid, %Poll{} = poll, attrs) do
poll
|> Poll.changeset(attrs)
|> Repo.update()
|> case do
{:ok, poll} ->
broadcast({:ok, poll, event_uuid}, :poll_updated)
{:error, changeset} ->
{:error, %{changeset | action: :update}}
end
end
@doc """
Deletes a poll.
## Examples
iex> delete_poll(poll)
{:ok, %Poll{}}
iex> delete_poll(poll)
{:error, %Ecto.Changeset{}}
"""
def delete_poll(event_uuid, %Poll{} = poll) do
{:ok, poll} = Repo.delete(poll)
broadcast({:ok, poll, event_uuid}, :poll_deleted)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking poll changes.
## Examples
iex> change_poll(poll)
%Ecto.Changeset{data: %Poll{}}
"""
def change_poll(%Poll{} = poll, attrs \\ %{}) do
Poll.changeset(poll, attrs)
end
@doc """
Add an empty poll opt to a poll changeset.
"""
def add_poll_opt(changeset) do
changeset
|> Ecto.Changeset.put_assoc(
:poll_opts,
Ecto.Changeset.get_field(changeset, :poll_opts) ++ [%PollOpt{}]
)
end
@doc """
Remove a poll opt from a poll changeset.
"""
def remove_poll_opt(changeset, poll_opt) do
changeset
|> Ecto.Changeset.put_assoc(
:poll_opts,
Ecto.Changeset.get_field(changeset, :poll_opts) -- [poll_opt]
)
end
def vote(user_id, event_uuid, %PollOpt{} = poll_opt, poll_id) when is_number(user_id) do
case Ecto.Multi.new()
|> Ecto.Multi.update(
:poll_opt,
PollOpt.changeset(poll_opt, %{"vote_count" => poll_opt.vote_count + 1})
)
|> Ecto.Multi.insert(:poll_vote, %PollVote{
user_id: user_id,
poll_opt_id: poll_opt.id,
poll_id: poll_id
})
|> Repo.transaction() do
{:ok, %{poll_opt: opt}} ->
opt =
Repo.preload(opt,
poll: [
poll_opts:
from(
o in PollOpt,
order_by: [asc: o.id]
)
]
)
broadcast({:ok, opt.poll |> set_percentages, event_uuid}, :poll_updated)
end
end
def vote(attendee_identifier, event_uuid, %PollOpt{} = poll_opt, poll_id) do
case Ecto.Multi.new()
|> Ecto.Multi.update(
:poll_opt,
PollOpt.changeset(poll_opt, %{"vote_count" => poll_opt.vote_count + 1})
)
|> Ecto.Multi.insert(:poll_vote, %PollVote{
attendee_identifier: attendee_identifier,
poll_opt_id: poll_opt.id,
poll_id: poll_id
})
|> Repo.transaction() do
{:ok, %{poll_opt: opt}} ->
opt =
Repo.preload(opt,
poll: [
poll_opts:
from(
o in PollOpt,
order_by: [asc: o.id]
)
]
)
broadcast({:ok, opt.poll |> set_percentages, event_uuid}, :poll_updated)
end
end
def set_default(id, presentation_file_id, position) do
from(p in Poll,
where:
p.presentation_file_id == ^presentation_file_id and p.position == ^position and
p.id != ^id
)
|> Repo.update_all(set: [enabled: false])
from(p in Poll,
where:
p.presentation_file_id == ^presentation_file_id and p.position == ^position and
p.id == ^id
)
|> Repo.update_all(set: [enabled: true])
end
defp broadcast({:error, _reason} = error, _poll), do: error
defp broadcast({:ok, poll, event_uuid}, event) do
Phoenix.PubSub.broadcast(
Claper.PubSub,
"event:#{event_uuid}",
{event, poll}
)
{:ok, poll}
end
@doc """
Gets a single poll_vote.
Raises `Ecto.NoResultsError` if the Poll vote does not exist.
## Examples
iex> get_poll_vote!(123)
%PollVote{}
iex> get_poll_vote!(456)
** (Ecto.NoResultsError)
"""
def get_poll_vote(user_id, poll_id) when is_number(user_id),
do: Repo.get_by(PollVote, poll_id: poll_id, user_id: user_id)
def get_poll_vote(attendee_identifier, poll_id),
do: Repo.get_by(PollVote, poll_id: poll_id, attendee_identifier: attendee_identifier)
@doc """
Creates a poll_vote.
## Examples
iex> create_poll_vote(%{field: value})
{:ok, %PollVote{}}
iex> create_poll_vote(%{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
def create_poll_vote(attrs \\ %{}) do
%PollVote{}
|> PollVote.changeset(attrs)
|> Repo.insert()
end
end