Skip to content

Commit ed15b04

Browse files
feat: auto approve swap request based on shift capacity (#41)
Co-authored-by: Nuno Miguel <nmpf.2005@gmail.com>
1 parent 772265c commit ed15b04

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

lib/atlas/exchange.ex

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,23 @@ defmodule Atlas.Exchange do
4646
|> Repo.all()
4747
end
4848

49+
@doc """
50+
Returns the list of pending shift exchange requests.
51+
52+
## Examples
53+
54+
iex> list_pending_shift_exchange_requests()
55+
[%ShiftExchangeRequest{}, ...]
56+
57+
"""
58+
def list_pending_shift_exchange_requests(opts \\ []) do
59+
ShiftExchangeRequest
60+
|> apply_filters(opts)
61+
|> where([r], r.status == :pending)
62+
|> order_by([r], asc: r.inserted_at)
63+
|> Repo.all()
64+
end
65+
4966
@doc """
5067
Gets a single shift_exchange_request.
5168
@@ -239,6 +256,32 @@ defmodule Atlas.Exchange do
239256
end)
240257
end
241258

259+
def maybe_auto_approve_pending_requests(opts \\ []) do
260+
pending_requests = list_pending_shift_exchange_requests(opts)
261+
262+
Enum.each(pending_requests, fn req ->
263+
case Repo.transaction(maybe_auto_approve_request(req)) do
264+
{:ok, _changes} ->
265+
# Reload student and shift for notification email
266+
user = University.get_student!(req.student_id, preloads: [:user]).user
267+
shift_to = Shifts.get_shift!(req.shift_to, preloads: [:course])
268+
269+
UserNotifier.deliver_shift_exchange_request_fulfilled(
270+
user,
271+
shift_to.course.name,
272+
Shifts.Shift.short_name(shift_to)
273+
)
274+
275+
{:error, :shift_has_space, :no_space, _} ->
276+
# Couldn't auto approve → do nothing
277+
:ok
278+
279+
{:error, _step, _reason, _changes} ->
280+
:ok
281+
end
282+
end)
283+
end
284+
242285
## Graph-related utility functions
243286

244287
defp build_graph(requests) do
@@ -378,4 +421,50 @@ defmodule Atlas.Exchange do
378421
{:error, :transaction_failed}
379422
end
380423
end
424+
425+
defp maybe_auto_approve_request(%ShiftExchangeRequest{} = req) do
426+
Multi.new()
427+
|> Multi.run(:shift_has_space, fn _repo, _changes ->
428+
enrolled_count =
429+
Repo.one(
430+
from(se in ShiftEnrollment,
431+
where: se.shift_id == ^req.shift_to and se.status in [:active, :inactive],
432+
select: count(se.student_id, :distinct)
433+
)
434+
)
435+
436+
shift = Shifts.get_shift!(req.shift_to)
437+
from_shift = Shifts.get_shift!(req.shift_from)
438+
from_shift_occupation = University.get_shift_enrollment_count(req.shift_from)
439+
440+
cond do
441+
from_shift_occupation - 1 <= round(from_shift.capacity * 0.8) ->
442+
{:error, :shift_from_underoccupied}
443+
444+
enrolled_count < shift.capacity ->
445+
{:ok, :has_space}
446+
447+
true ->
448+
{:error, :no_space}
449+
end
450+
end)
451+
|> Multi.update(
452+
:approve_request,
453+
Ecto.Changeset.change(req, status: :approved)
454+
)
455+
|> Multi.delete_all(
456+
:delete_from_enrollment,
457+
from(se in ShiftEnrollment,
458+
where: se.student_id == ^req.student_id and se.shift_id == ^req.shift_from
459+
)
460+
)
461+
|> Multi.insert(
462+
:insert_to_enrollment,
463+
ShiftEnrollment.changeset(%ShiftEnrollment{}, %{
464+
student_id: req.student_id,
465+
shift_id: req.shift_to,
466+
status: :active
467+
})
468+
)
469+
end
381470
end

lib/atlas/university.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,22 @@ defmodule Atlas.University do
397397
ShiftEnrollment.changeset(shift_enrollment, attrs)
398398
end
399399

400+
@doc """
401+
Gets the count of active enrollments for a specific shift.
402+
403+
## Examples
404+
405+
iex> get_shift_enrollment_count(123)
406+
25
407+
408+
"""
409+
def get_shift_enrollment_count(shift_id) do
410+
ShiftEnrollment
411+
|> where([se], se.shift_id == ^shift_id and se.status in [:active, :inactive])
412+
|> select([se], count(se.id))
413+
|> Repo.one()
414+
end
415+
400416
@doc """
401417
Lists the schedule for a student.
402418

lib/atlas/workers/shift_exchange.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule Atlas.Workers.ShiftExchange do
99
@impl Oban.Worker
1010
def perform(_job) do
1111
Exchange.solve_exchanges()
12+
Exchange.maybe_auto_approve_pending_requests()
1213

1314
:ok
1415
end

0 commit comments

Comments
 (0)