Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/dev_round/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,31 @@ defmodule DevRound.Changeset do
end
end
end

@doc """
Validates that given field contains a valid HTTP or HTTPS URL.

## Examples

iex> changeset = %Ecto.Changeset{changes: %{url: "http://example.com"}}
iex> Changeset.validate_http_url(changeset, :url)
# No error added

iex> changeset = %Ecto.Changeset{changes: %{url: "invalid"}}
iex> Changeset.validate_http_url(changeset, :url)
# Adds error to url field

"""
def validate_http_url(changeset, field) do
validate_change(changeset, field, fn field, url ->
case URI.parse(url) do
%URI{scheme: scheme, host: host}
when scheme in ["http", "https"] and not is_nil(host) and host != "" ->
[]

_ ->
[{field, "Must be a valid HTTP(S) URL."}]
end
end)
end
end
8 changes: 7 additions & 1 deletion lib/dev_round/events.ex
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ defmodule DevRound.Events do
sessions_query = from s in EventSession, order_by: s.begin

event
|> Repo.preload([:langs, :hosts, :last_live_session, sessions: sessions_query])
|> Repo.preload([
:langs,
:hosts,
:last_live_session,
:team_video_conference_rooms,
sessions: sessions_query
])
|> Repo.preload(events_attendees: {attendee_query, [:user, :langs]})
end

Expand Down
14 changes: 13 additions & 1 deletion lib/dev_round/events/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,14 @@ defmodule DevRound.Events.Event do
field :slides_page_number, :integer
field :live, :boolean
field :modified_at, :utc_datetime
field :main_video_conference_room_url, :string

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

has_many :team_video_conference_rooms, DevRound.Events.TeamVideoConferenceRoom,
on_replace: :delete,
on_delete: :delete_all

has_many :event_hosts, EventHost,
preload_order: [asc: :position],
on_replace: :delete,
Expand All @@ -60,7 +65,8 @@ defmodule DevRound.Events.Event do
:location,
:published,
:registration_deadline_local,
:slides_filename
:slides_filename,
:main_video_conference_room_url
])
|> cast_assoc(:event_hosts,
with: &EventHost.changeset/3,
Expand All @@ -76,6 +82,11 @@ defmodule DevRound.Events.Event do
sort_param: :sessions_order,
drop_param: :sessions_delete
)
|> cast_assoc(:team_video_conference_rooms,
with: &DevRound.Events.TeamVideoConferenceRoom.changeset/2,
sort_param: :team_video_conference_rooms_order,
drop_param: :team_video_conference_rooms_delete
)
|> put_langs_assoc(Keyword.get(opts, :put_langs))
|> validate_required(
[
Expand Down Expand Up @@ -107,6 +118,7 @@ defmodule DevRound.Events.Event do
_ -> []
end
end)
|> validate_http_url(:main_video_conference_room_url)
|> unique_constraint(:slug)
|> change(modified_at: DateTime.utc_now(:second))
end
Expand Down
18 changes: 18 additions & 0 deletions lib/dev_round/events/team_video_conference_room.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule DevRound.Events.TeamVideoConferenceRoom do
use Ecto.Schema
import Ecto.Changeset
import DevRound.Changeset
alias DevRound.Events.Event

schema "team_video_conference_rooms" do
field :url, :string
belongs_to :event, Event
end

def changeset(room, attrs) do
room
|> cast(attrs, [:url])
|> validate_required([:url])
|> validate_http_url(:url)
end
end
47 changes: 32 additions & 15 deletions lib/dev_round/hosting.ex
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ defmodule DevRound.Hosting do
EventAttendee.check_changeset(attendee, %{checked: checked})
end

def validate_team_generation_constraints(attendees, team_names) do
def validate_team_generation_constraints(attendees, team_names, team_rooms) do
attendees = filter_checked(attendees)

if Enum.count(attendees) >= 2 do
messages = build_validation_messages(attendees, team_names)
messages = build_validation_messages(attendees, team_names, team_rooms)

case messages do
[] -> {:ok, []}
Expand All @@ -139,7 +139,7 @@ defmodule DevRound.Hosting do
end
end

defp build_validation_messages(attendees, team_names) do
defp build_validation_messages(attendees, team_names, team_rooms) do
attendee_messages =
for attendee <- attendees do
potential_team_mates =
Expand All @@ -162,7 +162,18 @@ defmodule DevRound.Hosting do
[]
end

attendee_messages ++ team_names_message
remote_attendees = Enum.filter(attendees, & &1.is_remote)

video_conference_rooms_message =
if Integer.floor_div(length(remote_attendees), 2) > length(team_rooms) do
[
"Not enough session video conference room URLs to build teams for checked remote participants."
]
else
[]
end

attendee_messages ++ team_names_message ++ video_conference_rooms_message
end

defp filter_checked(attendees) do
Expand Down Expand Up @@ -197,29 +208,34 @@ defmodule DevRound.Hosting do
|> Enum.into(%{}, fn team -> {team.session_id, team} end)
end

def build_teams_for_session(%EventSession{} = session, attendees, team_names) do
def build_teams_for_session(%EventSession{} = session, attendees, team_names, team_rooms) do
attendees = filter_checked(attendees)
{:ok, []} = validate_team_generation_constraints(attendees, team_names)
{:ok, []} = validate_team_generation_constraints(attendees, team_names, team_rooms)

Multi.new()
|> Multi.delete_all(:teams, Ecto.assoc(session, :teams))
|> insert_teams(session, attendees, team_names)
|> insert_teams(session, attendees, team_names, team_rooms)
|> Repo.transaction()
end

defp insert_teams(multi, session, attendees, team_names) do
generate_team_changesets(session, attendees, team_names)
defp insert_teams(multi, session, attendees, team_names, team_rooms) do
generate_team_changesets(session, attendees, team_names, team_rooms)
|> Enum.reduce(multi, &Multi.insert(&2, Changeset.get_change(&1, :slug), &1))
end

defp generate_team_changesets(session, attendees, team_names) do
defp generate_team_changesets(session, attendees, team_names, team_rooms) do
{local_teams, local_langs} = generate_teams_langs(attendees, false)
{remote_teams, remote_langs} = generate_teams_langs(attendees, true)

room_urls = team_rooms |> Enum.map(& &1.url)
local_room_urls = List.duplicate(nil, length(local_teams))
remote_room_urls = Enum.take(room_urls, length(remote_teams))

create_team_changesets(
{local_teams ++ remote_teams, local_langs ++ remote_langs},
session,
team_names
team_names,
local_room_urls ++ remote_room_urls
)
end

Expand Down Expand Up @@ -293,11 +309,11 @@ defmodule DevRound.Hosting do
MapSet.new()
end

defp create_team_changesets({teams_attendees, teams_langs}, session, team_names) do
defp create_team_changesets({teams_attendees, teams_langs}, session, team_names, room_urls) do
names = team_names |> Enum.shuffle() |> Enum.take(length(teams_attendees))

Enum.zip([teams_attendees, teams_langs, names])
|> Enum.map(fn {team_attendees, team_langs, name} ->
Enum.zip([teams_attendees, teams_langs, names, room_urls])
|> Enum.map(fn {team_attendees, team_langs, name, room_url} ->
lang = Enum.random(team_langs)
is_remote = hd(team_attendees).is_remote

Expand All @@ -318,7 +334,8 @@ defmodule DevRound.Hosting do
slug: name.slug,
is_remote: is_remote,
session: session,
lang: lang
lang: lang,
video_conference_room_url: room_url
)
|> Changeset.put_assoc(:members, members)
end)
Expand Down
3 changes: 2 additions & 1 deletion lib/dev_round/hosting/team.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule DevRound.Hosting.Team do
field :name, :string
field :slug, :string
field :is_remote, :boolean, default: false
field :video_conference_room_url, :string

belongs_to :session, EventSession
belongs_to :lang, Lang
Expand All @@ -28,7 +29,7 @@ defmodule DevRound.Hosting.Team do
@doc false
def changeset(team, attrs) do
team
|> cast(attrs, [:name, :slug, :is_remote])
|> cast(attrs, [:name, :slug, :is_remote, :video_conference_room_url])
|> validate_required([:name, :slug, :is_remote])
end
end
71 changes: 53 additions & 18 deletions lib/dev_round_web/admin/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,38 @@ defmodule DevRoundWeb.Admin.Event do
]
end

@impl Backpex.LiveResource
def panels do
[
content: "Content",
video_conference_rooms: "Video Conference Rooms",
settings: "Settings"
]
end

@impl Backpex.LiveResource
def fields do
[
title: %{
module: Backpex.Fields.Text,
label: "Title"
},
event_hosts: %{
module: DevRoundWeb.Admin.Fields.InlineCRUD,
label: "Hosts",
type: :assoc,
child_fields: [
user: %{
module: DevRoundWeb.Admin.Fields.BelongsTo,
label: "User",
display_field: :full_name,
live_resource: DevRoundWeb.Admin.User,
prompt: "Select user",
options_query: fn query, _field -> query |> order_by(asc: :full_name) end
}
],
except: [:index]
},
begin_local: %{
module: Backpex.Fields.DateTime,
label: "Begin"
Expand Down Expand Up @@ -86,36 +111,23 @@ defmodule DevRoundWeb.Admin.Event do
live_resource: DevRoundWeb.Admin.Lang,
prompt: "Select",
not_found_text: "No languages found",
except: [:index]
},
event_hosts: %{
module: DevRoundWeb.Admin.Fields.InlineCRUD,
label: "Hosts",
type: :assoc,
child_fields: [
user: %{
module: DevRoundWeb.Admin.Fields.BelongsTo,
label: "User",
display_field: :full_name,
live_resource: DevRoundWeb.Admin.User,
prompt: "Select user",
options_query: fn query, _field -> query |> order_by(asc: :full_name) end
}
],
panel: :content,
except: [:index]
},
teaser: %{
module: Backpex.Fields.Textarea,
label: "Teaser",
help_text: "Shown on event listing page.",
rows: 5,
panel: :content,
except: [:index]
},
body: %{
module: Backpex.Fields.Textarea,
label: "Body",
help_text: "Markdown is supported.",
rows: 15,
panel: :content,
except: [:index]
},
slides_filename: %{
Expand All @@ -141,6 +153,7 @@ defmodule DevRoundWeb.Admin.Event do
</p>
"""
end,
panel: :content,
except: [:index]
},
sessions: %{
Expand All @@ -161,11 +174,33 @@ defmodule DevRoundWeb.Admin.Event do
module: Backpex.Fields.DateTime,
label: "End"
}
]
],
panel: :content
},
main_video_conference_room_url: %{
module: Backpex.Fields.URL,
label: "Main Video Conference Room URL",
panel: :video_conference_rooms,
except: [:index]
},
team_video_conference_rooms: %{
module: Backpex.Fields.InlineCRUD,
type: :assoc,
label: "Team Video Conference Rooms",
child_fields: [
url: %{
module: Backpex.Fields.URL,
label: "URL"
}
],
help_text: "Used for teams with remote attendees.",
panel: :video_conference_rooms,
except: [:index]
},
published: %{
module: Backpex.Fields.Boolean,
label: "Published"
label: "Published",
panel: :settings
}
]
end
Expand Down
12 changes: 11 additions & 1 deletion lib/dev_round_web/admin/item_actions/duplicate_event_action.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,21 @@ defmodule DevRoundWeb.Admin.ItemActions.DuplicateEventAction do
|> Map.merge(data)
|> shift_event_dates(date_diff)
|> Map.put(:sessions, Enum.map(item.sessions, &process_session(&1, date_diff)))
|> Map.put(
:event_hosts,
Enum.map(item.event_hosts, &Map.take(&1, [:event_id, :user_id, :position]))
)
|> Map.put(
:team_video_conference_rooms,
Enum.map(item.team_video_conference_rooms, &Map.take(&1, [:url]))
)
|> Map.put(:published, false)

opts = [
put_langs: item.langs,
put_hosts: item.hosts
put_hosts: item.hosts,
put_team_video_conference_rooms:
Enum.map(item.team_video_conference_rooms, &Map.take(&1, [:url]))
]

case Events.create_event(attrs, opts) do
Expand Down
Loading
Loading