Skip to content

Commit 45a413b

Browse files
Merge pull request #93 from epochtalk/polls-update
Polls update
2 parents c7603f5 + eee4208 commit 45a413b

File tree

16 files changed

+1946
-222
lines changed

16 files changed

+1946
-222
lines changed

lib/epochtalk_server/models/poll.ex

Lines changed: 134 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,38 @@ defmodule EpochtalkServer.Models.Poll do
124124
if poll_answers_len > 0, do: poll_cs, else: add_error(poll_cs, :answers, "can't be blank")
125125
end
126126

127+
@doc """
128+
Update changeset for `Poll` model
129+
"""
130+
@spec update_changeset(
131+
poll :: t(),
132+
attrs :: map() | nil
133+
) :: Ecto.Changeset.t()
134+
def update_changeset(poll, attrs \\ %{}) do
135+
poll_answers_len = length(poll.poll_answers || [])
136+
137+
poll
138+
|> cast(attrs, [
139+
:max_answers,
140+
:expiration,
141+
:change_vote,
142+
:display_mode
143+
])
144+
|> validate_required([
145+
:max_answers,
146+
:change_vote,
147+
:display_mode
148+
])
149+
|> validate_naivedatetime(:expiration, after: :utc_now)
150+
|> validate_number(:max_answers, greater_than: 0, less_than_or_equal_to: poll_answers_len)
151+
|> validate_length(:question, min: 1, max: 255)
152+
|> validate_display_mode()
153+
end
154+
127155
@doc """
128156
Creates a new `Poll` in the database
129157
"""
130-
@spec create(post_attrs :: map()) :: {:ok, post :: t()} | {:error, Ecto.Changeset.t()}
158+
@spec create(poll_attrs :: map()) :: {:ok, poll :: t()} | {:error, Ecto.Changeset.t()}
131159
def create(poll_attrs) do
132160
Repo.transaction(fn ->
133161
post_cs = create_changeset(%Poll{}, poll_attrs)
@@ -148,6 +176,19 @@ defmodule EpochtalkServer.Models.Poll do
148176
end)
149177
end
150178

179+
@doc """
180+
Updates an existing `Poll` in the database
181+
"""
182+
@spec update(poll_attrs :: map()) ::
183+
{:ok, poll :: t()} | {:error, Ecto.Changeset.t()}
184+
def update(attrs) do
185+
Poll
186+
|> Repo.get_by(thread_id: attrs["thread_id"])
187+
|> Repo.preload([:poll_answers])
188+
|> update_changeset(attrs)
189+
|> Repo.update()
190+
end
191+
151192
@doc """
152193
Returns boolean indicating if the specified `User` has voted on the specified `Thread`
153194
"""
@@ -167,6 +208,96 @@ defmodule EpochtalkServer.Models.Poll do
167208
Repo.exists?(query)
168209
end
169210

211+
@doc """
212+
Returns boolean indicating if the specified `User` has not voted on the specified `Thread`
213+
"""
214+
@spec has_not_voted(thread_id :: integer, user_id :: integer) :: boolean
215+
def has_not_voted(thread_id, user_id), do: !has_voted(thread_id, user_id)
216+
217+
@doc """
218+
Returns boolean indicating if the specified `Poll` exists given a `Thread` id
219+
"""
220+
@spec exists(thread_id :: integer) :: boolean
221+
def exists(thread_id) do
222+
query =
223+
from p in Poll,
224+
where: p.thread_id == ^thread_id,
225+
select: p.id
226+
227+
Repo.exists?(query)
228+
end
229+
230+
@doc """
231+
Returns integer indicating the max number of answers for the `Poll` given a `Thread` id
232+
"""
233+
@spec max_answers(thread_id :: integer) :: non_neg_integer
234+
def max_answers(thread_id) do
235+
query =
236+
from p in Poll,
237+
where: p.thread_id == ^thread_id,
238+
select: p.max_answers
239+
240+
Repo.one(query)
241+
end
242+
243+
@doc """
244+
Sets boolean indicating if the specified `Poll` is locked given a `Thread` id
245+
"""
246+
@spec set_locked(thread_id :: integer, locked :: boolean) :: {non_neg_integer, nil | [term()]}
247+
def set_locked(thread_id, locked) when is_integer(thread_id) and is_boolean(locked) do
248+
query = from p in Poll, where: p.thread_id == ^thread_id
249+
Repo.update_all(query, set: [locked: locked])
250+
end
251+
252+
@doc """
253+
Returns boolean indicating if the specified `Poll` is locked given a `Thread` id
254+
"""
255+
@spec locked(thread_id :: integer) :: boolean
256+
def locked(thread_id) do
257+
query =
258+
from p in Poll,
259+
where: p.thread_id == ^thread_id,
260+
select: p.locked
261+
262+
Repo.one(query)
263+
end
264+
265+
@doc """
266+
Returns boolean indicating if the specified `Poll` is unlocked given a `Thread` id
267+
"""
268+
@spec unlocked(thread_id :: integer) :: boolean
269+
def unlocked(thread_id), do: !locked(thread_id)
270+
271+
@doc """
272+
Returns boolean indicating if the specified `Poll` allows votes to be changed given a `Thread` id
273+
"""
274+
@spec allow_change_vote(thread_id :: integer) :: boolean
275+
def allow_change_vote(thread_id) do
276+
query =
277+
from p in Poll,
278+
where: p.thread_id == ^thread_id,
279+
select: p.change_vote
280+
281+
Repo.one(query)
282+
end
283+
284+
@doc """
285+
Returns boolean indicating if the specified `Poll` is currently running given a `Thread` id
286+
"""
287+
@spec running(thread_id :: integer) :: boolean
288+
def running(thread_id) do
289+
query =
290+
from p in Poll,
291+
where: p.thread_id == ^thread_id,
292+
select: p.expiration
293+
294+
expiration = Repo.one(query)
295+
296+
if expiration == nil,
297+
do: true,
298+
else: NaiveDateTime.compare(expiration, NaiveDateTime.utc_now()) == :gt
299+
end
300+
170301
@doc """
171302
Queries `Poll` Data by thread
172303
"""
@@ -180,6 +311,8 @@ defmodule EpochtalkServer.Models.Poll do
180311
Repo.one(query)
181312
end
182313

314+
# var q = 'UPDATE polls SET (max_answers, change_vote, expiration, display_mode) = ($1, $2, $3, $4) WHERE id = $5';
315+
183316
# === Private Helper Functions ===
184317

185318
defp validate_display_mode(changeset) do

lib/epochtalk_server/models/poll_response.ex

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
defmodule EpochtalkServer.Models.PollResponse do
22
use Ecto.Schema
33
import Ecto.Changeset
4-
# import Ecto.Query
4+
import Ecto.Query
55
alias EpochtalkServer.Repo
66
alias EpochtalkServer.Models.PollResponse
77
alias EpochtalkServer.Models.PollAnswer
8+
alias EpochtalkServer.Models.Poll
89
alias EpochtalkServer.Models.User
910

1011
@moduledoc """
@@ -54,17 +55,43 @@ defmodule EpochtalkServer.Models.PollResponse do
5455
## === Database Functions ===
5556

5657
@doc """
57-
Create on `PollResponse` for specific poll. Used to vote for a `Poll`
58+
Create on `PollResponse` for specific `Poll`. Used to vote for a `Poll`
5859
"""
59-
@spec create(attrs :: map, user_id :: integer) :: :ok
60+
@spec create(attrs :: map, user_id :: integer) :: {:ok, any()} | {:error, any()}
6061
def create(%{"answer_ids" => answer_ids} = attrs, user_id)
6162
when is_map(attrs) and is_integer(user_id),
62-
do: Enum.each(answer_ids, &PollResponse.create(&1, user_id))
63+
do:
64+
Repo.transaction(fn ->
65+
Enum.map(answer_ids, &PollResponse.create(&1, user_id))
66+
end)
6367

6468
def create(answer_id, user_id) do
6569
poll_response_cs =
6670
create_changeset(%PollResponse{}, %{answer_id: answer_id, user_id: user_id})
6771

68-
Repo.insert(poll_response_cs)
72+
case Repo.insert(poll_response_cs) do
73+
{:ok, poll_response} -> poll_response
74+
{:error, cs} -> Repo.rollback(cs)
75+
end
76+
end
77+
78+
@doc """
79+
Delete `PollResponse` for specific `Poll` and `User`. Used to remove vote from a `Poll`
80+
"""
81+
@spec delete(thread_id :: non_neg_integer, user_id :: non_neg_integer) ::
82+
{non_neg_integer(), nil | [t()]}
83+
def delete(thread_id, user_id) do
84+
poll_answer_ids =
85+
from pa in PollAnswer,
86+
join: p in Poll,
87+
on: p.thread_id == ^thread_id and pa.poll_id == p.id,
88+
select: pa.id
89+
90+
poll_responses_by_user =
91+
from pr in PollResponse,
92+
where: pr.answer_id in subquery(poll_answer_ids) and pr.user_id == ^user_id,
93+
select: pr
94+
95+
Repo.delete_all(poll_responses_by_user)
6996
end
7097
end

lib/epochtalk_server/models/thread.ex

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,13 @@ defmodule EpochtalkServer.Models.Thread do
419419
body: p.content["body"],
420420
thread_id: p.thread_id,
421421
thread_slug: t.slug,
422-
username: u.username
422+
username: u.username,
423+
user_id: u.id,
424+
user: u
423425
}
424426

425-
Repo.one(query_first_thread_post_data)
427+
first_post = Repo.one(query_first_thread_post_data)
428+
Map.put(first_post, :user, Repo.preload(first_post.user, :roles))
426429
end
427430

428431
@doc """
@@ -446,10 +449,13 @@ defmodule EpochtalkServer.Models.Thread do
446449
body: p.content["body"],
447450
thread_id: p.thread_id,
448451
thread_slug: t.slug,
449-
username: u.username
452+
username: u.username,
453+
user_id: u.id,
454+
user: u
450455
}
451456

452-
Repo.one(query_first_thread_post_data)
457+
first_post = Repo.one(query_first_thread_post_data)
458+
Map.put(first_post, :user, Repo.preload(first_post.user, :roles))
453459
end
454460

455461
## === Private Helper Functions ===

0 commit comments

Comments
 (0)