Skip to content

Commit cbafe9c

Browse files
committed
Integrate URLS to Video Conference Rooms
- A new `TeamVideoConferenceRoom` schema for session-specific remote rooms, associated with events. - Updated `DevRound.Events.Event` to store a `main_video_conference_room_url` and manage `team_video_conference_rooms`. - Added URL validation logic to `DevRound.Changeset` ensuring only valid HTTP(S) URLs are used for conference links. - Refactored `DevRound.Hosting` team generation algorithms to accept video conference room URLs and assign them to remote teams, including validation for sufficient availability. - Update the event admin view for video conference URL entry. - Updated templates to display conference room links for users.
1 parent 4f2891e commit cbafe9c

19 files changed

Lines changed: 361 additions & 74 deletions

File tree

lib/dev_round/changeset.ex

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,31 @@ defmodule DevRound.Changeset do
157157
end
158158
end
159159
end
160+
161+
@doc """
162+
Validates that given field contains a valid HTTP or HTTPS URL.
163+
164+
## Examples
165+
166+
iex> changeset = %Ecto.Changeset{changes: %{url: "http://example.com"}}
167+
iex> Changeset.validate_http_url(changeset, :url)
168+
# No error added
169+
170+
iex> changeset = %Ecto.Changeset{changes: %{url: "invalid"}}
171+
iex> Changeset.validate_http_url(changeset, :url)
172+
# Adds error to url field
173+
174+
"""
175+
def validate_http_url(changeset, field) do
176+
validate_change(changeset, field, fn field, url ->
177+
case URI.parse(url) do
178+
%URI{scheme: scheme, host: host}
179+
when scheme in ["http", "https"] and not is_nil(host) and host != "" ->
180+
[]
181+
182+
_ ->
183+
[{field, "Must be a valid HTTP(S) URL."}]
184+
end
185+
end)
186+
end
160187
end

lib/dev_round/events.ex

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,13 @@ defmodule DevRound.Events do
105105
sessions_query = from s in EventSession, order_by: s.begin
106106

107107
event
108-
|> Repo.preload([:langs, :hosts, :last_live_session, sessions: sessions_query])
108+
|> Repo.preload([
109+
:langs,
110+
:hosts,
111+
:last_live_session,
112+
:team_video_conference_rooms,
113+
sessions: sessions_query
114+
])
109115
|> Repo.preload(events_attendees: {attendee_query, [:user, :langs]})
110116
end
111117

lib/dev_round/events/event.ex

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ defmodule DevRound.Events.Event do
3131
field :slides_page_number, :integer
3232
field :live, :boolean
3333
field :modified_at, :utc_datetime
34+
field :main_video_conference_room_url, :string
3435

3536
many_to_many :langs, Lang, join_through: "event_langs", on_replace: :delete
3637

38+
has_many :team_video_conference_rooms, DevRound.Events.TeamVideoConferenceRoom,
39+
on_replace: :delete,
40+
on_delete: :delete_all
41+
3742
has_many :event_hosts, EventHost,
3843
preload_order: [asc: :position],
3944
on_replace: :delete,
@@ -60,7 +65,8 @@ defmodule DevRound.Events.Event do
6065
:location,
6166
:published,
6267
:registration_deadline_local,
63-
:slides_filename
68+
:slides_filename,
69+
:main_video_conference_room_url
6470
])
6571
|> cast_assoc(:event_hosts,
6672
with: &EventHost.changeset/3,
@@ -76,6 +82,11 @@ defmodule DevRound.Events.Event do
7682
sort_param: :sessions_order,
7783
drop_param: :sessions_delete
7884
)
85+
|> cast_assoc(:team_video_conference_rooms,
86+
with: &DevRound.Events.TeamVideoConferenceRoom.changeset/2,
87+
sort_param: :team_video_conference_rooms_order,
88+
drop_param: :team_video_conference_rooms_delete
89+
)
7990
|> put_langs_assoc(Keyword.get(opts, :put_langs))
8091
|> validate_required(
8192
[
@@ -107,6 +118,7 @@ defmodule DevRound.Events.Event do
107118
_ -> []
108119
end
109120
end)
121+
|> validate_http_url(:main_video_conference_room_url)
110122
|> unique_constraint(:slug)
111123
|> change(modified_at: DateTime.utc_now(:second))
112124
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
defmodule DevRound.Events.TeamVideoConferenceRoom do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
import DevRound.Changeset
5+
alias DevRound.Events.Event
6+
7+
schema "team_video_conference_rooms" do
8+
field :url, :string
9+
belongs_to :event, Event
10+
end
11+
12+
def changeset(room, attrs) do
13+
room
14+
|> cast(attrs, [:url])
15+
|> validate_required([:url])
16+
|> validate_http_url(:url)
17+
end
18+
end

lib/dev_round/hosting.ex

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@ defmodule DevRound.Hosting do
124124
EventAttendee.check_changeset(attendee, %{checked: checked})
125125
end
126126

127-
def validate_team_generation_constraints(attendees, team_names) do
127+
def validate_team_generation_constraints(attendees, team_names, team_rooms) do
128128
attendees = filter_checked(attendees)
129129

130130
if Enum.count(attendees) >= 2 do
131-
messages = build_validation_messages(attendees, team_names)
131+
messages = build_validation_messages(attendees, team_names, team_rooms)
132132

133133
case messages do
134134
[] -> {:ok, []}
@@ -139,7 +139,7 @@ defmodule DevRound.Hosting do
139139
end
140140
end
141141

142-
defp build_validation_messages(attendees, team_names) do
142+
defp build_validation_messages(attendees, team_names, team_rooms) do
143143
attendee_messages =
144144
for attendee <- attendees do
145145
potential_team_mates =
@@ -162,7 +162,18 @@ defmodule DevRound.Hosting do
162162
[]
163163
end
164164

165-
attendee_messages ++ team_names_message
165+
remote_attendees = Enum.filter(attendees, & &1.is_remote)
166+
167+
video_conference_rooms_message =
168+
if Integer.floor_div(length(remote_attendees), 2) > length(team_rooms) do
169+
[
170+
"Not enough session video conference room URLs to build teams for checked remote participants."
171+
]
172+
else
173+
[]
174+
end
175+
176+
attendee_messages ++ team_names_message ++ video_conference_rooms_message
166177
end
167178

168179
defp filter_checked(attendees) do
@@ -197,29 +208,32 @@ defmodule DevRound.Hosting do
197208
|> Enum.into(%{}, fn team -> {team.session_id, team} end)
198209
end
199210

200-
def build_teams_for_session(%EventSession{} = session, attendees, team_names) do
211+
def build_teams_for_session(%EventSession{} = session, attendees, team_names, team_rooms) do
201212
attendees = filter_checked(attendees)
202-
{:ok, []} = validate_team_generation_constraints(attendees, team_names)
213+
{:ok, []} = validate_team_generation_constraints(attendees, team_names, team_rooms)
203214

204215
Multi.new()
205216
|> Multi.delete_all(:teams, Ecto.assoc(session, :teams))
206-
|> insert_teams(session, attendees, team_names)
217+
|> insert_teams(session, attendees, team_names, team_rooms)
207218
|> Repo.transaction()
208219
end
209220

210-
defp insert_teams(multi, session, attendees, team_names) do
211-
generate_team_changesets(session, attendees, team_names)
221+
defp insert_teams(multi, session, attendees, team_names, team_rooms) do
222+
generate_team_changesets(session, attendees, team_names, team_rooms)
212223
|> Enum.reduce(multi, &Multi.insert(&2, Changeset.get_change(&1, :slug), &1))
213224
end
214225

215-
defp generate_team_changesets(session, attendees, team_names) do
226+
defp generate_team_changesets(session, attendees, team_names, team_rooms) do
216227
{local_teams, local_langs} = generate_teams_langs(attendees, false)
217228
{remote_teams, remote_langs} = generate_teams_langs(attendees, true)
218229

230+
room_urls = team_rooms |> Enum.map(& &1.url)
231+
219232
create_team_changesets(
220233
{local_teams ++ remote_teams, local_langs ++ remote_langs},
221234
session,
222-
team_names
235+
team_names,
236+
room_urls
223237
)
224238
end
225239

@@ -293,34 +307,48 @@ defmodule DevRound.Hosting do
293307
MapSet.new()
294308
end
295309

296-
defp create_team_changesets({teams_attendees, teams_langs}, session, team_names) do
310+
defp create_team_changesets({teams_attendees, teams_langs}, session, team_names, room_urls) do
297311
names = team_names |> Enum.shuffle() |> Enum.take(length(teams_attendees))
298312

299-
Enum.zip([teams_attendees, teams_langs, names])
300-
|> Enum.map(fn {team_attendees, team_langs, name} ->
301-
lang = Enum.random(team_langs)
302-
is_remote = hd(team_attendees).is_remote
313+
{changesets, _remaining_urls} =
314+
Enum.zip([teams_attendees, teams_langs, names])
315+
|> Enum.map_reduce(room_urls, fn {team_attendees, team_langs, name}, available_urls ->
316+
lang = Enum.random(team_langs)
317+
is_remote = hd(team_attendees).is_remote
318+
319+
{room_url, next_urls} =
320+
if is_remote && available_urls != [] do
321+
{hd(available_urls), tl(available_urls)}
322+
else
323+
{nil, available_urls}
324+
end
325+
326+
members =
327+
Enum.map(team_attendees, fn attendee ->
328+
%TeamMember{}
329+
|> Changeset.change(
330+
is_remote: attendee.is_remote,
331+
experience_level: attendee.experience_level,
332+
user: attendee.user
333+
)
334+
|> Changeset.put_assoc(:langs, attendee.langs)
335+
end)
303336

304-
members =
305-
Enum.map(team_attendees, fn attendee ->
306-
%TeamMember{}
337+
changeset =
338+
%Team{}
307339
|> Changeset.change(
308-
is_remote: attendee.is_remote,
309-
experience_level: attendee.experience_level,
310-
user: attendee.user
340+
name: name.name,
341+
slug: name.slug,
342+
is_remote: is_remote,
343+
session: session,
344+
lang: lang,
345+
video_conference_room_url: room_url
311346
)
312-
|> Changeset.put_assoc(:langs, attendee.langs)
313-
end)
314-
315-
%Team{}
316-
|> Changeset.change(
317-
name: name.name,
318-
slug: name.slug,
319-
is_remote: is_remote,
320-
session: session,
321-
lang: lang
322-
)
323-
|> Changeset.put_assoc(:members, members)
324-
end)
347+
|> Changeset.put_assoc(:members, members)
348+
349+
{changeset, next_urls}
350+
end)
351+
352+
changesets
325353
end
326354
end

lib/dev_round/hosting/team.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ defmodule DevRound.Hosting.Team do
1717
field :name, :string
1818
field :slug, :string
1919
field :is_remote, :boolean, default: false
20+
field :video_conference_room_url, :string
2021

2122
belongs_to :session, EventSession
2223
belongs_to :lang, Lang
@@ -28,7 +29,7 @@ defmodule DevRound.Hosting.Team do
2829
@doc false
2930
def changeset(team, attrs) do
3031
team
31-
|> cast(attrs, [:name, :slug, :is_remote])
32+
|> cast(attrs, [:name, :slug, :is_remote, :video_conference_room_url])
3233
|> validate_required([:name, :slug, :is_remote])
3334
end
3435
end

0 commit comments

Comments
 (0)