Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrary station positions (drag+drop and refactor) #93

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
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
5 changes: 3 additions & 2 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ let liveSocket = new LiveSocket("/live", Socket, {
window.Alpine.clone(from, to)
}
}
}
},
hooks: window.customHooks
})

// Connect if there are any LiveViews on the page
Expand All @@ -28,4 +29,4 @@ liveSocket.connect()
// The latency simulator is enabled for the duration of the browser session.
// Call disableLatencySim() to disable:
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket
window.liveSocket = liveSocket
2 changes: 1 addition & 1 deletion lib/lanpartyseating/logic/autoassign_logic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Lanpartyseating.AutoAssignLogic do
{:ok, settings} = SettingsLogic.get_settings()
next_station = rem(las.last_assigned_station, settings.columns * settings.rows) + 1

{:ok, stations} = StationLogic.get_all_stations_sorted_by_number()
{:ok, stations} = StationLogic.get_all_stations()

# Find the first result matching this condition
# The stations collection is split in half and we swap the end with the start so that
Expand Down
21 changes: 4 additions & 17 deletions lib/lanpartyseating/logic/settings_logic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ defmodule Lanpartyseating.SettingsLogic do
end
end

# returns an Ecto.Multi that has to be written
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap this in an @doc """ """ string so it can be rendered out into generated documentation

def save_settings(
rows,
columns,
row_padding,
column_padding,
is_diagonally_mirrored,
horizontal_trailing,
vertical_trailing
) do
Expand All @@ -41,27 +39,16 @@ defmodule Lanpartyseating.SettingsLogic do
last_assigned_station_date: DateTime.truncate(DateTime.utc_now(), :second)
)

case Repo.update(las) do
{:ok, result} -> result
{:error, error} -> error
end

settings =
Ecto.Changeset.change(settings,
rows: rows,
columns: columns,
row_padding: row_padding,
column_padding: column_padding,
is_diagonally_mirrored: is_diagonally_mirrored,
horizontal_trailing: horizontal_trailing,
vertical_trailing: vertical_trailing
)

with {:ok, _updated} <- Repo.update(settings) do
:ok
else
{:error, error} ->
{:error, {:save_settings_failed, error}}
end
Ecto.Multi.new()
|> Ecto.Multi.insert_or_update(:set_last_assigned_station, las)
|> Ecto.Multi.insert_or_update(:insert_settings, settings)
end
end
144 changes: 53 additions & 91 deletions lib/lanpartyseating/logic/station_logic.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule Lanpartyseating.StationLogic do
import Ecto.Query
alias Lanpartyseating.StationLayout
use Timex
alias Ecto.Changeset
alias Lanpartyseating.PubSub, as: PubSub
Expand All @@ -8,19 +9,24 @@ defmodule Lanpartyseating.StationLogic do
alias Lanpartyseating.Station, as: Station
alias Lanpartyseating.TournamentReservation, as: TournamentReservation
alias Lanpartyseating.Repo, as: Repo
alias Lanpartyseating.StationLayout, as: Layout
alias Lanpartyseating.StationStatus, as: Status

def number_stations do
Repo.aggregate(Station, :count)
end

def get_all_stations(now \\ DateTime.utc_now()) do
def get_station_query(now \\ DateTime.utc_now()) do
tournament_buffer = DateTime.add(DateTime.utc_now(), 45, :minute)

stations =
from(s in Station,
order_by: [asc: s.id],
from(s in Station,
order_by: [asc: s.station_number], # only needed by autoassign
where: is_nil(s.deleted_at),
preload: [
stations_status:
^from(Status), # may return nil
station_layout:
^from(Layout),
reservations:
^from(
r in Reservation,
Expand All @@ -39,7 +45,10 @@ defmodule Lanpartyseating.StationLogic do
)
]
)
|> Repo.all()
end

def get_all_stations(now \\ DateTime.utc_now()) do
stations = get_station_query(now) |> Repo.all()

case stations do
[] ->
Expand All @@ -53,18 +62,32 @@ defmodule Lanpartyseating.StationLogic do
end
end

def get_station_layout() do
rows = Repo.all(from(Layout))
Enum.map(rows, fn r -> {{r.x, r.y}, r.station_number} end)
|> Enum.into(%{})
end

def stations_by_xy(stations) do
by_pos = stations
|> Enum.map(fn s -> {{s.station.station_layout.x, s.station.station_layout.y}, s} end)
|> Enum.into(%{})

{max_x, max_y} = Map.keys(by_pos)
|> Enum.reduce({0, 0}, fn {acc_x, acc_y}, {x, y} -> {max(x, acc_x), max(y, acc_y)} end)

# {columns, rows}
{by_pos, {max_x + 1, max_y + 1}}
end

def set_station_broken(station_number, is_broken) do
station =
from(s in Station,
where: s.station_number == ^station_number
) |> Repo.one()

station =
Ecto.Changeset.change(station,
is_closed: is_broken
)
result = Repo.insert(
%Lanpartyseating.StationStatus{station_id: station_number, is_broken: is_broken},
on_conflict: [set: [is_broken: is_broken]],
conflict_target: :station_id
)

with {:ok, update} <- Repo.update(station),
with {:ok, update} <- result,
{:ok, stations} <- StationLogic.get_all_stations()
do
Phoenix.PubSub.broadcast(
Expand All @@ -79,98 +102,37 @@ defmodule Lanpartyseating.StationLogic do
end
end

def get_all_stations_sorted_by_number(now \\ DateTime.utc_now()) do
tournament_buffer = DateTime.add(DateTime.utc_now(), 45, :minute)

stations =
from(s in Station,
order_by: [asc: s.station_number],
where: is_nil(s.deleted_at),
preload: [
reservations:
^from(
r in Reservation,
where: r.start_date <= ^now,
where: r.end_date > ^now,
where: is_nil(r.deleted_at),
order_by: [desc: r.inserted_at]
),
tournament_reservations:
^from(tr in TournamentReservation,
join: t in assoc(tr, :tournament),
where: t.start_date < ^tournament_buffer,
where: t.end_date > ^now,
where: is_nil(t.deleted_at),
preload: [tournament: t]
)
]
)
|> Repo.all()

stations_map =
Enum.map(stations, fn station ->
Map.merge(%{station: station}, get_station_status(station))
end)

{:ok, stations_map}
end

def get_station(station_number, now \\ DateTime.utc_now()) do
tournament_buffer = DateTime.add(DateTime.utc_now(), 45, :minute)

station = from(s in Station,
order_by: [asc: s.id],
where: is_nil(s.deleted_at),
where: s.station_number == ^station_number,
preload: [
reservations:
^from(
r in Reservation,
where: r.start_date <= ^now,
where: r.end_date > ^now,
where: is_nil(r.deleted_at),
order_by: [desc: r.inserted_at]
),
tournament_reservations:
^from(tr in TournamentReservation,
join: t in assoc(tr, :tournament),
where: t.start_date < ^tournament_buffer,
where: t.end_date > ^now,
where: is_nil(t.deleted_at),
preload: [tournament: t]
)
]
)
|> Repo.one()
station = get_station_query(now) |> Repo.one()

case station do
nil -> {:error, :station_not_found}
_ -> {:ok, station}
end
end

def save_station_positions(table) do
Repo.delete_all(Station)

def save_stations(grid) do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename this function to something more clear because it does not actually save the stations as it is written now.

now_naive =
NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)

positions =
table
|> Enum.flat_map(fn row ->
row
|> Enum.map(fn station_number ->
%{station_number: station_number, display_order: station_number, inserted_at: now_naive, updated_at: now_naive}
end)
stations = grid
|> Enum.map(fn {_xy, station_number} ->
%{station_number: station_number, inserted_at: now_naive, updated_at: now_naive}
end)

Repo.insert_all(Station, positions)
:ok
layout = grid
|> Enum.map(fn {{x, y}, num} -> %{station_number: num, x: x, y: y} end)

Ecto.Multi.new()
# because of the foreign key these need to be deleted and inserted specifically in this order
|> Ecto.Multi.delete_all(:delete_stations, from(Lanpartyseating.Station))
|> Ecto.Multi.delete_all(:delete_layout, from(Lanpartyseating.StationLayout))
|> Ecto.Multi.insert_all(:insert_layout, Lanpartyseating.StationLayout, layout)
|> Ecto.Multi.insert_all(:insert_stations, Station, stations)
end

def get_station_status(station) do
case station do
%Station{is_closed: true} ->
%Station{stations_status: %{is_broken: true}} ->
%{status: :broken, reservation: nil}

%Station{tournament_reservations: [res | _]}
Expand Down
2 changes: 1 addition & 1 deletion lib/lanpartyseating/repositories/reservation_repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Lanpartyseating.Reservation do
field :badge, :string
field :incident, :string
field :deleted_at, :utc_datetime
belongs_to :station, Lanpartyseating.Station
belongs_to :station, Lanpartyseating.Station, foreign_key: :station_id, references: :station_number
field :start_date, :utc_datetime
field :end_date, :utc_datetime
timestamps()
Expand Down
7 changes: 1 addition & 6 deletions lib/lanpartyseating/repositories/setting_repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ defmodule Lanpartyseating.Setting do
@foreign_key_type :id

schema "settings" do
field :rows, :integer, default: 10
field :columns, :integer, default: 4
field :row_padding, :integer, default: 1
field :column_padding, :integer, default: 1
field :is_diagonally_mirrored, :integer, default: 0
field :horizontal_trailing, :integer
field :vertical_trailing, :integer
field :deleted_at, :utc_datetime
Expand All @@ -20,9 +17,7 @@ defmodule Lanpartyseating.Setting do
@doc false
def changeset(reservation, attrs) do
reservation
|> cast(attrs, [:rows, :columns, :row_padding, :column_padding, :horizontal_trailing, :vertical_trailing, :deleted_at])
|> validate_number(:rows, greater_than: 0)
|> validate_number(:columns, greater_than: 0)
|> cast(attrs, [:row_padding, :column_padding, :horizontal_trailing, :vertical_trailing, :deleted_at])
|> validate_number(:row_padding, greater_than: -1)
|> validate_number(:column_padding, greater_than: -1)
end
Expand Down
24 changes: 24 additions & 0 deletions lib/lanpartyseating/repositories/station_layout_repo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
defmodule Lanpartyseating.StationLayout do
alias Lanpartyseating.Station
use Ecto.Schema
import Ecto.Changeset

@primary_key {:station_number, :integer, autogenerate: false}
@foreign_key_type :integer

schema "station_layout" do
has_one :stations, Station, foreign_key: :station_number
field :x, :integer
field :y, :integer
end

@doc false
def changeset(station, attrs) do
station
|> cast(attrs, [:station_number, :x, :y])
|> validate_required([:station_number, :x, :y])
|> validate_number(:x, greater_than: -1)
|> validate_number(:y, greater_than: -1)
|> unique_constraint(:x, :y)
end
end
17 changes: 8 additions & 9 deletions lib/lanpartyseating/repositories/station_repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ defmodule Lanpartyseating.Station do
alias Lanpartyseating.Reservation, as: Reservation
alias Lanpartyseating.TournamentReservation, as: TournamentReservation

@primary_key {:id, :id, autogenerate: true}
@foreign_key_type :id
@primary_key {:station_number, :integer, autogenerate: false}
@foreign_key_type :integer

schema "stations" do
field :station_number, :integer
field :display_order, :integer
field :is_closed, :boolean, default: false
field :deleted_at, :utc_datetime
has_many :reservations, Reservation
has_many :tournament_reservations, TournamentReservation
belongs_to :station_layout, StationLayout, foreign_key: :station_number, references: :station_number, define_field: false
has_one :stations_status, StationStatus, foreign_key: :station_id, references: :station_number
has_many :reservations, Reservation, foreign_key: :station_id
has_many :tournament_reservations, TournamentReservation, foreign_key: :station_id
timestamps()
end

@doc false
def changeset(reservation, attrs) do
reservation
|> cast(attrs, [:station_number, :display_order, :is_displayed, :is_closed, :deleted_at])
|> validate_required([:station_number, :display_order])
|> cast(attrs, [:station_number, :is_displayed, :is_closed, :deleted_at])
|> validate_required([:station_number])
|> validate_number(:station_number, greater_than: 0)
end
end
11 changes: 5 additions & 6 deletions lib/lanpartyseating/repositories/station_status_repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ defmodule Lanpartyseating.StationStatus do
use Ecto.Schema
import Ecto.Changeset

@primary_key {:id, :id, autogenerate: true}
@foreign_key_type :id
@primary_key {:station_id, :integer, autogenerate: false}
@foreign_key_type :integer

schema "stations_status" do
field :station_id, :integer
field :is_assigned, :boolean, default: false
field :is_out_of_order, :boolean, default: false
field :is_broken, :boolean, default: false
timestamps()
end

@doc false
def changeset(reservation, attrs) do
reservation
|> cast(attrs, [:station_id, :tournament_id])
|> validate_required([:station_id, :tournament_id])
|> cast(attrs, [:station_id, :is_assigned, :is_broken])
|> validate_required([:station_id])
end
end
Loading