mirror of
https://github.com/ClaperCo/Claper.git
synced 2025-12-16 11:57:58 +01:00
352 lines
7.9 KiB
Elixir
352 lines
7.9 KiB
Elixir
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 for a given presentation file.
|
|
|
|
## Examples
|
|
|
|
iex> list_polls(123)
|
|
[%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
|
|
|
|
@doc """
|
|
Returns the list of polls for a given presentation file and a given position.
|
|
|
|
## Examples
|
|
|
|
iex> list_polls_at_position(123, 0)
|
|
[%Poll{}, ...]
|
|
|
|
"""
|
|
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 and set percentages for each poll options.
|
|
|
|
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()
|
|
|
|
@doc """
|
|
Gets a single poll for a given position.
|
|
|
|
## Examples
|
|
|
|
iex> get_poll!(123, 0)
|
|
%Poll{}
|
|
|
|
"""
|
|
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
|
|
|
|
@doc """
|
|
Calculate percentage of all poll options for a given poll.
|
|
|
|
## Examples
|
|
|
|
iex> set_percentages(poll)
|
|
%Poll{}
|
|
|
|
"""
|
|
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()
|
|
|> case do
|
|
{:ok, poll} ->
|
|
poll = Repo.preload(poll, presentation_file: :event)
|
|
broadcast({:ok, poll, poll.presentation_file.event.uuid}, :poll_created)
|
|
|
|
{:error, changeset} ->
|
|
{:error, %{changeset | action: :insert}}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Updates a poll.
|
|
|
|
## Examples
|
|
|
|
iex> update_poll("123e4567-e89b-12d3-a456-426614174000", poll, %{field: new_value})
|
|
{:ok, %Poll{}}
|
|
|
|
iex> update_poll("123e4567-e89b-12d3-a456-426614174000", 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("123e4567-e89b-12d3-a456-426614174000", poll)
|
|
{:ok, %Poll{}}
|
|
|
|
iex> delete_poll("123e4567-e89b-12d3-a456-426614174000", 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, poll_opts, poll_id)
|
|
when is_number(user_id) and is_list(poll_opts) do
|
|
case Enum.reduce(poll_opts, Ecto.Multi.new(), fn opt, multi ->
|
|
Ecto.Multi.update(
|
|
multi,
|
|
{:update_poll_opt, opt.id},
|
|
PollOpt.changeset(opt, %{"vote_count" => opt.vote_count + 1})
|
|
)
|
|
|> Ecto.Multi.insert(
|
|
{:insert_poll_vote, opt.id},
|
|
PollVote.changeset(%PollVote{}, %{
|
|
user_id: user_id,
|
|
poll_opt_id: opt.id,
|
|
poll_id: poll_id
|
|
})
|
|
)
|
|
end)
|
|
|> Repo.transaction() do
|
|
{:ok, _} ->
|
|
poll = get_poll!(poll_id)
|
|
broadcast({:ok, poll, event_uuid}, :poll_updated)
|
|
end
|
|
end
|
|
|
|
def vote(attendee_identifier, event_uuid, poll_opts, poll_id) when is_list(poll_opts) do
|
|
case Enum.reduce(poll_opts, Ecto.Multi.new(), fn opt, multi ->
|
|
Ecto.Multi.update(
|
|
multi,
|
|
{:update_poll_opt, opt.id},
|
|
PollOpt.changeset(opt, %{"vote_count" => opt.vote_count + 1})
|
|
)
|
|
|> Ecto.Multi.insert(
|
|
{:insert_poll_vote, opt.id},
|
|
PollVote.changeset(%PollVote{}, %{
|
|
attendee_identifier: attendee_identifier,
|
|
poll_opt_id: opt.id,
|
|
poll_id: poll_id
|
|
})
|
|
)
|
|
end)
|
|
|> Repo.transaction() do
|
|
{:ok, _} ->
|
|
poll = get_poll!(poll_id)
|
|
broadcast({:ok, poll, event_uuid}, :poll_updated)
|
|
end
|
|
end
|
|
|
|
def disable_all(presentation_file_id, position) do
|
|
from(p in Poll,
|
|
where: p.presentation_file_id == ^presentation_file_id and p.position == ^position
|
|
)
|
|
|> Repo.update_all(set: [enabled: false])
|
|
end
|
|
|
|
def set_enabled(id) do
|
|
get_poll!(id)
|
|
|> Ecto.Changeset.change(enabled: true)
|
|
|> Repo.update()
|
|
end
|
|
|
|
def set_disabled(id) do
|
|
get_poll!(id)
|
|
|> Ecto.Changeset.change(enabled: false)
|
|
|> Repo.update()
|
|
end
|
|
|
|
defp broadcast({:ok, poll, event_uuid}, event) do
|
|
Phoenix.PubSub.broadcast(
|
|
Claper.PubSub,
|
|
"event:#{event_uuid}",
|
|
{event, poll}
|
|
)
|
|
|
|
{:ok, poll}
|
|
end
|
|
|
|
@doc """
|
|
Gets a all poll_vote.
|
|
|
|
|
|
## Examples
|
|
|
|
iex> get_poll_vote!(321, 123)
|
|
[%PollVote{}]
|
|
|
|
"""
|
|
def get_poll_vote(user_id, poll_id) when is_number(user_id) do
|
|
from(p in PollVote,
|
|
where: p.poll_id == ^poll_id and p.user_id == ^user_id,
|
|
order_by: [asc: p.id]
|
|
)
|
|
|> Repo.all()
|
|
end
|
|
|
|
def get_poll_vote(attendee_identifier, poll_id) do
|
|
from(p in PollVote,
|
|
where: p.poll_id == ^poll_id and p.attendee_identifier == ^attendee_identifier,
|
|
order_by: [asc: p.id]
|
|
)
|
|
|> Repo.all()
|
|
end
|
|
|
|
@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
|