philomena/lib/philomena/interactions.ex

148 lines
3.7 KiB
Elixir

defmodule Philomena.Interactions do
import Ecto.Query
alias Philomena.ImageHides.ImageHide
alias Philomena.ImageFaves.ImageFave
alias Philomena.ImageVotes.ImageVote
alias Philomena.Images.Image
alias Philomena.Repo
alias Ecto.Multi
def user_interactions(_images, nil),
do: []
def user_interactions(images, user) do
ids =
images
|> flatten_images()
|> Enum.uniq()
hide_interactions =
ImageHide
|> select([h], %{
image_id: h.image_id,
user_id: h.user_id,
interaction_type: ^"hidden",
value: ^""
})
|> where([h], h.image_id in ^ids)
|> where(user_id: ^user.id)
fave_interactions =
ImageFave
|> select([f], %{
image_id: f.image_id,
user_id: f.user_id,
interaction_type: ^"faved",
value: ^""
})
|> where([f], f.image_id in ^ids)
|> where(user_id: ^user.id)
upvote_interactions =
ImageVote
|> select([v], %{
image_id: v.image_id,
user_id: v.user_id,
interaction_type: ^"voted",
value: ^"up"
})
|> where([v], v.image_id in ^ids)
|> where(user_id: ^user.id, up: true)
downvote_interactions =
ImageVote
|> select([v], %{
image_id: v.image_id,
user_id: v.user_id,
interaction_type: ^"voted",
value: ^"down"
})
|> where([v], v.image_id in ^ids)
|> where(user_id: ^user.id, up: false)
[
hide_interactions,
fave_interactions,
upvote_interactions,
downvote_interactions
]
|> union_all_queries()
|> Repo.all()
end
def migrate_interactions(source, target) do
now = DateTime.utc_now() |> DateTime.truncate(:second)
source = Repo.preload(source, [:hiders, :favers, :upvoters, :downvoters])
new_hides = Enum.map(source.hiders, &%{image_id: target.id, user_id: &1.id, created_at: now})
new_faves = Enum.map(source.favers, &%{image_id: target.id, user_id: &1.id, created_at: now})
new_upvotes =
Enum.map(
source.upvoters,
&%{image_id: target.id, user_id: &1.id, created_at: now, up: true}
)
new_downvotes =
Enum.map(
source.downvoters,
&%{image_id: target.id, user_id: &1.id, created_at: now, up: false}
)
Multi.new()
|> Multi.run(:hides, fn repo, %{} ->
{count, nil} = repo.insert_all(ImageHide, new_hides, on_conflict: :nothing)
{:ok, count}
end)
|> Multi.run(:faves, fn repo, %{} ->
{count, nil} = repo.insert_all(ImageFave, new_faves, on_conflict: :nothing)
{:ok, count}
end)
|> Multi.run(:upvotes, fn repo, %{} ->
{count, nil} = repo.insert_all(ImageVote, new_upvotes, on_conflict: :nothing)
{:ok, count}
end)
|> Multi.run(:downvotes, fn repo, %{} ->
{count, nil} = repo.insert_all(ImageVote, new_downvotes, on_conflict: :nothing)
{:ok, count}
end)
|> Multi.run(:image, fn repo,
%{hides: hides, faves: faves, upvotes: upvotes, downvotes: downvotes} ->
image_query = where(Image, id: ^target.id)
repo.update_all(
image_query,
inc: [
hides_count: hides,
faves_count: faves,
upvotes_count: upvotes,
downvotes_count: downvotes,
score: upvotes - downvotes
]
)
{:ok, nil}
end)
|> Repo.transaction()
end
defp union_all_queries([query]),
do: query
defp union_all_queries([query | rest]),
do: query |> union_all(^union_all_queries(rest))
defp flatten_images(images) do
Enum.flat_map(images, fn
nil -> []
%{id: id} -> [id]
{%{id: id}, _hit} -> [id]
enum -> flatten_images(enum)
end)
end
end