Skip to content

Commit

Permalink
feat: Add function to merge speakers
Browse files Browse the repository at this point in the history
  • Loading branch information
Betree committed Dec 9, 2020
1 parent 706e4a4 commit b033832
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 3 deletions.
55 changes: 55 additions & 0 deletions apps/cf/lib/speakers/speakers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule CF.Speakers do

alias DB.Repo
alias DB.Schema.Speaker
alias DB.Schema.VideoSpeaker
alias DB.Type.SpeakerPicture

@doc """
Expand Down Expand Up @@ -86,6 +87,60 @@ defmodule CF.Speakers do
end
end

@merged_profile_fields [:full_name, :title, :country, :wikidata_item_id, :picture]

def merge_speakers(speaker_from, speaker_into) do
Ecto.Multi.new()
# Update speaker profile
|> Ecto.Multi.run(:speaker_into, fn _ ->
speaker_into
|> Speaker.changeset(merge_speakers_fields(speaker_from, speaker_into))
|> Repo.update()
end)
# Update VideoSpeakers
|> Ecto.Multi.update_all(:videos_speakers, speaker_videos(speaker_from),
set: [speaker_id: speaker_into.id]
)
# Update statements
|> Ecto.Multi.update_all(:statements, speaker_statements(speaker_from),
set: [speaker_id: speaker_into.id]
)
# Update user profiles
|> Ecto.Multi.update_all(:users, speaker_users(speaker_from),
set: [speaker_id: speaker_into.id]
)
# Mark first speaker as deleted
|> Ecto.Multi.delete(:speaker_from, speaker_from)
|> DB.Repo.transaction()
end

defp merge_speakers_fields(speaker_from, speaker_into) do
speaker_from
|> Map.from_struct()
|> Map.merge(remove_nil_values_from_struct(speaker_into))
|> Map.take([:full_name, :title, :country, :wikidata_item_id, :picture])
|> Enum.into(%{})
end

defp speaker_videos(speaker) do
from(v in VideoSpeaker, where: v.speaker_id == ^speaker.id)
end

defp speaker_statements(speaker) do
from(s in DB.Schema.Statement, where: s.speaker_id == ^speaker.id)
end

defp speaker_users(speaker) do
from(u in DB.Schema.User, where: u.speaker_id == ^speaker.id)
end

defp remove_nil_values_from_struct(struct) do
struct
|> Map.from_struct()
|> Enum.filter(fn {_, v} -> v != nil end)
|> Enum.into(%{})
end

defp picture_filename_from_response(%{"claims" => %{"P18" => images}}) do
case images do
[%{"mainsnak" => %{"datavalue" => %{"value" => filename}}}] ->
Expand Down
2 changes: 1 addition & 1 deletion apps/cf/test/authenticator/authenticator_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ defmodule CF.AuthenticatorTest do

user = insert_user_with_custom_password(password)

check all password <- binary(), max_runs: 3 do
check all(password <- binary(), max_runs: 3) do
assert is_nil(Authenticator.get_user_for_email_or_name_password(user.email, password))
end
end
Expand Down
51 changes: 51 additions & 0 deletions apps/cf/test/speakers/speakers_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,55 @@ defmodule CF.SpeakersTest do
assert DB.Repo.get(DB.Schema.Speaker, speaker.id).slug == @slug
end
end

describe "merge_speakers" do
test "merges profiles and related data" do
speaker1 = insert(:speaker, %{title: "speaker_1"})
speaker2 = insert(:speaker, %{title: nil})
speaker1_statements = insert_list(3, :statement, speaker: speaker1)
speaker2_statements = insert_list(4, :statement, speaker: speaker2)
speaker1_videos = insert_list(3, :video_speaker, speaker: speaker1)
speaker2_videos = insert_list(4, :video_speaker, speaker: speaker2)
speaker1_users = insert_list(3, :user, speaker: speaker1)
speaker2_users = insert_list(4, :user, speaker: speaker2)

{:ok, result} = Speakers.merge_speakers(speaker1, speaker2)

assert result.speaker_from.id === speaker1.id
assert result.speaker_into.id === speaker2.id

# Profiles should be merged
assert result.speaker_into.title == speaker1.title

# Statements should be upddated
assert elem(result.statements, 0) == 3

assert DB.Repo.aggregate(
from(s in DB.Schema.Statement, where: s.speaker_id == ^speaker2.id),
:count,
:id
) == 7

# Videos should be updated
assert elem(result.videos_speakers, 0) == 3

assert DB.Repo.aggregate(
from(s in DB.Schema.VideoSpeaker, where: s.speaker_id == ^speaker2.id),
:count,
:video_id
) == 7

# Users should be upddated
assert elem(result.users, 0) == 3

assert DB.Repo.aggregate(
from(s in DB.Schema.User, where: s.speaker_id == ^speaker2.id),
:count,
:id
) == 7

# First speaker should be deleted
assert DB.Repo.get(DB.Schema.Speaker, speaker1.id) == nil
end
end
end
1 change: 1 addition & 0 deletions apps/cf_jobs/lib/report_manager.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule CF.Jobs.ReportManager do
import Ecto.Query

# TODO Implement this as a behaviour

alias DB.Repo
Expand Down
4 changes: 2 additions & 2 deletions apps/db/test/db_type/video_hash_id_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ defmodule DB.Type.VideoHashIdTest do
end

property "should work with any integer" do
check all id <- id_generator(), do: assert(String.length(VideoHashId.encode(id)) >= 4)
check(all(id <- id_generator(), do: assert(String.length(VideoHashId.encode(id)) >= 4)))
end

defp id_generator do
ExUnitProperties.gen all id <- integer() do
ExUnitProperties.gen all(id <- integer()) do
abs(id)
end
end
Expand Down
8 changes: 8 additions & 0 deletions apps/db/test/support/factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule DB.Factory do
alias DB.Schema.InvitationRequest
alias DB.Schema.UserAction
alias DB.Schema.Video
alias DB.Schema.VideoSpeaker
alias DB.Schema.Comment
alias DB.Schema.Vote
alias DB.Schema.Speaker
Expand Down Expand Up @@ -70,6 +71,13 @@ defmodule DB.Factory do
}
end

def video_speaker_factory do
%VideoSpeaker{
speaker: build(:speaker),
video: build(:video)
}
end

def statement_factory do
%Statement{
text: Faker.Lorem.sentence(6..10),
Expand Down

0 comments on commit b033832

Please sign in to comment.