Skip to content

Commit

Permalink
Merge pull request #39 from epochtalk/thread-create
Browse files Browse the repository at this point in the history
Thread create
  • Loading branch information
unenglishable authored Apr 28, 2023
2 parents f3bc9bc + 5819c9e commit 979c09d
Show file tree
Hide file tree
Showing 18 changed files with 912 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .iex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ alias EpochtalkServer.Models.{
Notification,
Permission,
Post,
Poll,
PollAnswer,
PollResponse,
Preference,
Profile,
Role,
Expand Down
9 changes: 9 additions & 0 deletions lib/epochtalk_server/models/banned_address.ex
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ defmodule EpochtalkServer.Models.BannedAddress do
|> calculate_score_decay
end

defp calculate_ip32_score({ip1, ip2, ip3, ip4, _, _, _, _}),
do: calculate_ip32_score({ip1, ip2, ip3, ip4})

defp calculate_ip32_score({ip1, ip2, ip3, ip4}) do
from(ba in BannedAddress,
where: ba.ip1 == ^ip1 and ba.ip2 == ^ip2 and ba.ip3 == ^ip3 and ba.ip4 == ^ip4,
Expand All @@ -267,6 +270,9 @@ defmodule EpochtalkServer.Models.BannedAddress do
|> calculate_score_decay
end

defp calculate_ip24_score({ip1, ip2, ip3, ip4, _, _, _, _}),
do: calculate_ip24_score({ip1, ip2, ip3, ip4})

defp calculate_ip24_score({ip1, ip2, ip3, _}) do
from(ba in BannedAddress,
where: ba.ip1 == ^ip1 and ba.ip2 == ^ip2 and ba.ip3 == ^ip3,
Expand All @@ -281,6 +287,9 @@ defmodule EpochtalkServer.Models.BannedAddress do
|> calculate_score_decay
end

defp calculate_ip16_score({ip1, ip2, ip3, ip4, _, _, _, _}),
do: calculate_ip16_score({ip1, ip2, ip3, ip4})

defp calculate_ip16_score({ip1, ip2, _, _}) do
from(ba in BannedAddress,
where: ba.ip1 == ^ip1 and ba.ip2 == ^ip2,
Expand Down
10 changes: 9 additions & 1 deletion lib/epochtalk_server/models/board_mapping.ex
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ defmodule EpochtalkServer.Models.BoardMapping do
on: bm.board_id == mb.board_id,
left_join: t in Thread,
on: mb.last_thread_id == t.id,
select_merge: %{stats: mb, thread: t},
select_merge: %{
stats: mb,
thread: %{
last_thread_slug: t.slug,
last_thread_post_count: t.post_count,
last_thread_created_at: t.created_at,
last_thread_updated_at: t.updated_at
}
},
preload: [:board]
end

Expand Down
143 changes: 143 additions & 0 deletions lib/epochtalk_server/models/poll.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
defmodule EpochtalkServer.Models.Poll do
use Ecto.Schema
import Ecto.Changeset
import EpochtalkServer.Validators.NaiveDateTime
# import Ecto.Query
alias EpochtalkServer.Repo
alias EpochtalkServer.Models.Poll
alias EpochtalkServer.Models.PollAnswer
alias EpochtalkServer.Models.Thread

@moduledoc """
`Poll` model, for performing actions relating to `Thread` polls
"""
@type t :: %__MODULE__{
id: non_neg_integer | nil,
thread_id: non_neg_integer | nil,
question: String.t() | nil,
locked: boolean | nil,
max_answers: non_neg_integer | nil,
expiration: NaiveDateTime.t() | nil,
change_vote: boolean | nil,
display_mode: String.t() | nil
}
@derive {Jason.Encoder,
only: [
:thread_id,
:question,
:locked,
:max_answers,
:expiration,
:change_vote,
:display_mode
]}
schema "polls" do
belongs_to :thread, Thread
field :question, :string
field :locked, :boolean
field :max_answers, :integer
field :expiration, :naive_datetime
field :change_vote, :boolean
field :display_mode, Ecto.Enum, values: [:always, :voted, :expired]
has_many :poll_answers, PollAnswer
end

## === Changesets Functions ===

@doc """
Generic changeset for `Poll` model
"""
@spec changeset(
poll :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def changeset(poll, attrs \\ %{}) do
poll
|> cast(attrs, [
:id,
:thread_id,
:question,
:locked,
:max_answers,
:expiration,
:change_vote,
:display_mode
])
|> validate_required([
:thread_id,
:question,
:locked,
:max_answers,
:change_vote,
:display_mode
])
end

@doc """
Create changeset for `Poll` model
"""
@spec create_changeset(
poll :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def create_changeset(poll, attrs \\ %{}) do
poll =
poll
|> Map.put(:max_answers, 1)
|> Map.put(:change_vote, false)

poll_answers = attrs["answers"]
poll_answers_len = length(poll_answers || [])

poll_cs =
poll
|> cast(attrs, [
:thread_id,
:question,
:max_answers,
:expiration,
:change_vote,
:display_mode
])
|> validate_required([
:thread_id,
:question,
:max_answers,
:change_vote,
:display_mode
])
|> validate_naivedatetime(:expiration, after: :utc_now)
|> validate_number(:max_answers, greater_than: 0, less_than_or_equal_to: poll_answers_len)
|> validate_length(:question, min: 1, max: 255)
|> unique_constraint(:id, name: :polls_pkey)
|> unique_constraint(:thread_id, name: :polls_thread_id_index)
|> foreign_key_constraint(:thread_id, name: :polls_thread_id_fkey)

# validate answers
if poll_answers_len > 0, do: poll_cs, else: add_error(poll_cs, :answers, "can't be blank")
end

@doc """
Creates a new `Poll` in the database
"""
@spec create(post_attrs :: map()) :: {:ok, post :: t()} | {:error, Ecto.Changeset.t()}
def create(poll_attrs) do
Repo.transaction(fn ->
post_cs = create_changeset(%Poll{}, poll_attrs)

case Repo.insert(post_cs) do
{:ok, db_poll} ->
# iterate over each answer, create answer in db
Enum.each(poll_attrs["answers"], fn answer ->
poll_answer_attrs = %{"poll_id" => db_poll.id, "answer" => answer}
PollAnswer.create(poll_answer_attrs)
end)

db_poll

{:error, cs} ->
Repo.rollback(cs)
end
end)
end
end
64 changes: 64 additions & 0 deletions lib/epochtalk_server/models/poll_answer.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
defmodule EpochtalkServer.Models.PollAnswer do
use Ecto.Schema
import Ecto.Changeset
# import Ecto.Query
alias EpochtalkServer.Repo
alias EpochtalkServer.Models.PollAnswer
alias EpochtalkServer.Models.Poll
alias EpochtalkServer.Models.PollResponse

@moduledoc """
`PollAnswer` model, for performing actions relating to `Poll` answers
"""
@type t :: %__MODULE__{
id: non_neg_integer | nil,
poll_id: non_neg_integer | nil,
answer: String.t() | nil
}
@derive {Jason.Encoder, only: [:poll_id, :answer]}
schema "poll_answers" do
belongs_to :poll, Poll
field :answer, :string
has_many :poll_responses, PollResponse
end

## === Changesets Functions ===

@doc """
Generic changeset for `PollAnswer` model
"""
@spec changeset(
poll_answer :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def changeset(poll_answer, attrs \\ %{}) do
poll_answer
|> cast(attrs, [:id, :poll_id, :answer])
|> validate_required([:poll_id, :answer])
end

@doc """
Create changeset for `PollAnswer` model
"""
@spec create_changeset(
poll_answer :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def create_changeset(poll_answer, attrs \\ %{}) do
poll_answer
|> cast(attrs, [:poll_id, :answer])
|> validate_required([:poll_id, :answer])
|> validate_length(:answer, min: 1, max: 255)
|> unique_constraint(:id, name: :poll_answers_pkey)
|> foreign_key_constraint(:poll_id, name: :poll_answers_poll_id_fkey)
end

@doc """
Creates a new `PollAnswer` in the database
"""
@spec create(post_attrs :: map()) :: {:ok, post :: t()} | {:error, Ecto.Changeset.t()}
def create(poll_answer_attrs) do
post_cs = create_changeset(%PollAnswer{}, poll_answer_attrs)
Repo.insert(post_cs)
end
end
53 changes: 53 additions & 0 deletions lib/epochtalk_server/models/poll_response.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule EpochtalkServer.Models.PollResponse do
use Ecto.Schema
import Ecto.Changeset
# import Ecto.Query
# alias EpochtalkServer.Repo
# alias EpochtalkServer.Models.PollResponse
alias EpochtalkServer.Models.PollAnswer
alias EpochtalkServer.Models.User

@moduledoc """
`PollResponse` model, for performing actions relating to `Poll` answers
"""
@type t :: %__MODULE__{
poll_answer_id: non_neg_integer | nil,
user_id: non_neg_integer | nil
}
@derive {Jason.Encoder, only: [:poll_answer_id, :user_id]}
@primary_key false
schema "poll_response" do
belongs_to :poll_answer, PollAnswer
belongs_to :user, User
end

## === Changesets Functions ===

@doc """
Generic changeset for `PollResponse` model
"""
@spec changeset(
poll_response :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def changeset(poll_response, attrs \\ %{}) do
poll_response
|> cast(attrs, [:poll_answer_id, :user_id])
|> validate_required([:poll_answer_id, :user_id])
end

@doc """
Create changeset for `PollResponse` model
"""
@spec create_changeset(
poll_response :: t(),
attrs :: map() | nil
) :: Ecto.Changeset.t()
def create_changeset(poll_response, attrs \\ %{}) do
poll_response
|> cast(attrs, [:poll_answer_id, :user_id])
|> validate_required([:poll_answer_id, :user_id])
|> foreign_key_constraint(:poll_answer_id, name: :poll_responses_answer_id_fkey)
|> foreign_key_constraint(:user_id, name: :poll_responses_user_id_fkey)
end
end
Loading

0 comments on commit 979c09d

Please sign in to comment.