Skip to content

Commit 72c667e

Browse files
committed
feat: Add function to merge speakers
1 parent 029a453 commit 72c667e

File tree

8 files changed

+124
-9
lines changed

8 files changed

+124
-9
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ jobs:
1212
- uses: actions/checkout@v1
1313
- uses: actions/[email protected]
1414
with:
15-
otp-version: 21.x
16-
elixir-version: 1.6.x
15+
otp-version: 22.x
16+
elixir-version: 1.10.1
1717
- name: Check format
1818
run: mix format --check-formatted
1919

@@ -39,8 +39,8 @@ jobs:
3939
${{ runner.os }}-mix-
4040
- uses: actions/[email protected]
4141
with:
42-
otp-version: 21.x
43-
elixir-version: 1.6.x
42+
otp-version: 22.x
43+
elixir-version: 1.10.1
4444
- name: Install dependencies
4545
run: mix deps.get
4646
- name: Prepare DB

.github/workflows/e2e.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ jobs:
3434
key: ${{ runner.os }}-mix-build
3535
- uses: actions/[email protected]
3636
with:
37-
otp-version: 21.x
38-
elixir-version: 1.6.x
37+
otp-version: 22.x
38+
elixir-version: 1.10.1
3939
- name: Install API's dependencies
4040
run: mix deps.get
4141
- name: Compile dependencies

apps/cf/lib/speakers/speakers.ex

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ defmodule CF.Speakers do
88

99
alias DB.Repo
1010
alias DB.Schema.Speaker
11+
alias DB.Schema.VideoSpeaker
1112
alias DB.Type.SpeakerPicture
1213

1314
@doc """
@@ -86,6 +87,60 @@ defmodule CF.Speakers do
8687
end
8788
end
8889

90+
@merged_profile_fields [:full_name, :title, :country, :wikidata_item_id, :picture]
91+
92+
def merge_speakers(speaker_from, speaker_into) do
93+
Ecto.Multi.new()
94+
# Update speaker profile
95+
|> Ecto.Multi.run(:speaker_into, fn _ ->
96+
speaker_into
97+
|> Speaker.changeset(merge_speakers_fields(speaker_from, speaker_into))
98+
|> Repo.update()
99+
end)
100+
# Update VideoSpeakers
101+
|> Ecto.Multi.update_all(:videos_speakers, speaker_videos(speaker_from),
102+
set: [speaker_id: speaker_into.id]
103+
)
104+
# Update statements
105+
|> Ecto.Multi.update_all(:statements, speaker_statements(speaker_from),
106+
set: [speaker_id: speaker_into.id]
107+
)
108+
# Update user profiles
109+
|> Ecto.Multi.update_all(:users, speaker_users(speaker_from),
110+
set: [speaker_id: speaker_into.id]
111+
)
112+
# Mark first speaker as deleted
113+
|> Ecto.Multi.delete(:speaker_from, speaker_from)
114+
|> DB.Repo.transaction()
115+
end
116+
117+
defp merge_speakers_fields(speaker_from, speaker_into) do
118+
speaker_from
119+
|> Map.from_struct()
120+
|> Map.merge(remove_nil_values_from_struct(speaker_into))
121+
|> Map.take([:full_name, :title, :country, :wikidata_item_id, :picture])
122+
|> Enum.into(%{})
123+
end
124+
125+
defp speaker_videos(speaker) do
126+
from(v in VideoSpeaker, where: v.speaker_id == ^speaker.id)
127+
end
128+
129+
defp speaker_statements(speaker) do
130+
from(s in DB.Schema.Statement, where: s.speaker_id == ^speaker.id)
131+
end
132+
133+
defp speaker_users(speaker) do
134+
from(u in DB.Schema.User, where: u.speaker_id == ^speaker.id)
135+
end
136+
137+
defp remove_nil_values_from_struct(struct) do
138+
struct
139+
|> Map.from_struct()
140+
|> Enum.filter(fn {_, v} -> v != nil end)
141+
|> Enum.into(%{})
142+
end
143+
89144
defp picture_filename_from_response(%{"claims" => %{"P18" => images}}) do
90145
case images do
91146
[%{"mainsnak" => %{"datavalue" => %{"value" => filename}}}] ->

apps/cf/test/authenticator/authenticator_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ defmodule CF.AuthenticatorTest do
2929

3030
user = insert_user_with_custom_password(password)
3131

32-
check all password <- binary(), max_runs: 3 do
32+
check all(password <- binary(), max_runs: 3) do
3333
assert is_nil(Authenticator.get_user_for_email_or_name_password(user.email, password))
3434
end
3535
end

apps/cf/test/speakers/speakers_test.exs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,55 @@ defmodule CF.SpeakersTest do
4646
assert DB.Repo.get(DB.Schema.Speaker, speaker.id).slug == @slug
4747
end
4848
end
49+
50+
describe "merge_speakers" do
51+
test "merges profiles and related data" do
52+
speaker1 = insert(:speaker, %{title: "speaker_1"})
53+
speaker2 = insert(:speaker, %{title: nil})
54+
speaker1_statements = insert_list(3, :statement, speaker: speaker1)
55+
speaker2_statements = insert_list(4, :statement, speaker: speaker2)
56+
speaker1_videos = insert_list(3, :video_speaker, speaker: speaker1)
57+
speaker2_videos = insert_list(4, :video_speaker, speaker: speaker2)
58+
speaker1_users = insert_list(3, :user, speaker: speaker1)
59+
speaker2_users = insert_list(4, :user, speaker: speaker2)
60+
61+
{:ok, result} = Speakers.merge_speakers(speaker1, speaker2)
62+
63+
assert result.speaker_from.id === speaker1.id
64+
assert result.speaker_into.id === speaker2.id
65+
66+
# Profiles should be merged
67+
assert result.speaker_into.title == speaker1.title
68+
69+
# Statements should be upddated
70+
assert elem(result.statements, 0) == 3
71+
72+
assert DB.Repo.aggregate(
73+
from(s in DB.Schema.Statement, where: s.speaker_id == ^speaker2.id),
74+
:count,
75+
:id
76+
) == 7
77+
78+
# Videos should be updated
79+
assert elem(result.videos_speakers, 0) == 3
80+
81+
assert DB.Repo.aggregate(
82+
from(s in DB.Schema.VideoSpeaker, where: s.speaker_id == ^speaker2.id),
83+
:count,
84+
:video_id
85+
) == 7
86+
87+
# Users should be upddated
88+
assert elem(result.users, 0) == 3
89+
90+
assert DB.Repo.aggregate(
91+
from(s in DB.Schema.User, where: s.speaker_id == ^speaker2.id),
92+
:count,
93+
:id
94+
) == 7
95+
96+
# First speaker should be deleted
97+
assert DB.Repo.get(DB.Schema.Speaker, speaker1.id) == nil
98+
end
99+
end
49100
end

apps/cf_jobs/lib/report_manager.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
defmodule CF.Jobs.ReportManager do
22
import Ecto.Query
3+
34
# TODO Implement this as a behaviour
45

56
alias DB.Repo

apps/db/test/db_type/video_hash_id_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ defmodule DB.Type.VideoHashIdTest do
2222
end
2323

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

2828
defp id_generator do
29-
ExUnitProperties.gen all id <- integer() do
29+
ExUnitProperties.gen all(id <- integer()) do
3030
abs(id)
3131
end
3232
end

apps/db/test/support/factory.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ defmodule DB.Factory do
1313
alias DB.Schema.InvitationRequest
1414
alias DB.Schema.UserAction
1515
alias DB.Schema.Video
16+
alias DB.Schema.VideoSpeaker
1617
alias DB.Schema.Comment
1718
alias DB.Schema.Vote
1819
alias DB.Schema.Speaker
@@ -70,6 +71,13 @@ defmodule DB.Factory do
7071
}
7172
end
7273

74+
def video_speaker_factory do
75+
%VideoSpeaker{
76+
speaker: build(:speaker),
77+
video: build(:video)
78+
}
79+
end
80+
7381
def statement_factory do
7482
%Statement{
7583
text: Faker.Lorem.sentence(6..10),

0 commit comments

Comments
 (0)