diff --git a/lib/atlas/university/degrees/courses/shifts.ex b/lib/atlas/university/degrees/courses/shifts.ex index c7e2205..e1e2bd0 100644 --- a/lib/atlas/university/degrees/courses/shifts.ex +++ b/lib/atlas/university/degrees/courses/shifts.ex @@ -5,6 +5,7 @@ defmodule Atlas.University.Degrees.Courses.Shifts do use Atlas.Context alias Atlas.University.Degrees.Courses.Shifts.Shift + alias Ecto.Multi @doc """ Returns the list of shifts. @@ -21,6 +22,13 @@ defmodule Atlas.University.Degrees.Courses.Shifts do |> Repo.all() end + def list_shifts_with_timeslots(opts \\ []) do + Shift + |> apply_filters(opts) + |> Repo.all() + |> Repo.preload([:timeslots]) + end + @doc """ Gets a single shift. @@ -41,6 +49,13 @@ defmodule Atlas.University.Degrees.Courses.Shifts do |> Repo.get!(id) end + def get_shift_with_timeslots(id, opts \\ []) do + Shift + |> apply_filters(opts) + |> Repo.get(id) + |> Repo.preload([:timeslots]) + end + @doc """ Creates a shift. @@ -208,4 +223,50 @@ defmodule Atlas.University.Degrees.Courses.Shifts do def change_timeslot(%Timeslot{} = timeslot, attrs \\ %{}) do Timeslot.changeset(timeslot, attrs) end + + @doc """ + Updates a shift that contains + """ + + def update_shift_with_timeslots(shift, shift_attrs, timeslot_attrs) do + Multi.new() + |> Multi.update(:shift, change_shift(shift, shift_attrs)) + |> process_timeslots(shift, timeslot_attrs) + |> Repo.transact() + end + + defp process_timeslots(multi, shift, timeslot_attrs) do + multi + |> then(fn multi -> + timeslot_attrs + |> Enum.with_index() + |> Enum.reduce(multi, fn {timeslot_attr, index}, acc_multi -> + process_timeslot(acc_multi, timeslot_attr, shift.id, index) + end) + end) + end + + defp process_timeslot(multi, timeslot_attr, shift_id, index) do + if timeslot_attr["id"] && timeslot_exists?(timeslot_attr["id"]) do + timeslot = get_timeslot!(timeslot_attr["id"]) + changeset = change_timeslot(timeslot, Map.delete(timeslot_attr, "id")) + Multi.update(multi, {:timeslot, index}, changeset) + else + clean_attrs = + timeslot_attr + |> Map.delete("id") + |> Map.put("shift_id", shift_id) + |> Map.replace("weekday", timeslot_attr["weekday"] |> String.to_atom()) + + changeset = change_timeslot(%Timeslot{}, clean_attrs) + Multi.insert(multi, {:timeslot, index}, changeset) + end + end + + defp timeslot_exists?(id) do + case Repo.get(Timeslot, id) do + nil -> false + _ -> true + end + end end diff --git a/lib/atlas/university/degrees/courses/timeslots.ex b/lib/atlas/university/degrees/courses/timeslots.ex new file mode 100644 index 0000000..44b53b7 --- /dev/null +++ b/lib/atlas/university/degrees/courses/timeslots.ex @@ -0,0 +1,38 @@ +defmodule Atlas.University.Degrees.Courses.Timeslots do + @moduledoc """ + The Timeslots context. + """ + use Atlas.Context + alias Atlas.University.Degrees.Courses.Shifts.Timeslot + + @doc """ + Gets a single timeslot. + + ## Examples + + iex> get_timeslot!(123) + %Timeslot{} + + iex> get_timeslot!(456) + ** (Ecto.NoResultsError) + """ + def get_timeslot!(id, opts \\ []) do + Timeslot + |> apply_filters(opts) + |> Repo.get!(id) + end + + @doc """ + Deletes a timeslot. + ## Examples + + iex> delete_timeslot(timeslot) + {:ok, %Timeslot{}} + + iex> delete_timeslot(timeslot) + {:error, %Ecto.Changeset{}} + """ + def delete_timeslot(%Timeslot{} = timeslot) do + Repo.delete(timeslot) + end +end diff --git a/lib/atlas_web/controllers/exchanges/shift_exchange_request_json.ex b/lib/atlas_web/controllers/exchanges/shift_exchange_request_json.ex index ef0c8f4..7acdf4a 100644 --- a/lib/atlas_web/controllers/exchanges/shift_exchange_request_json.ex +++ b/lib/atlas_web/controllers/exchanges/shift_exchange_request_json.ex @@ -2,7 +2,8 @@ defmodule AtlasWeb.ShiftExchangeRequestJSON do @moduledoc """ A module for rendering shift exchange request data in JSON format. """ - alias AtlasWeb.University.{CourseJSON, ShiftJSON} + alias AtlasWeb.ShiftsJSON + alias AtlasWeb.University.CourseJSON @doc """ Renders a list of shift exchange requests as JSON. @@ -28,8 +29,8 @@ defmodule AtlasWeb.ShiftExchangeRequestJSON do %{ id: shift_exchange_request.id, status: shift_exchange_request.status, - from: ShiftJSON.data(shift_exchange_request.from), - to: ShiftJSON.data(shift_exchange_request.to), + from: ShiftsJSON.data(shift_exchange_request.from), + to: ShiftsJSON.data(shift_exchange_request.to), course: CourseJSON.data(shift_exchange_request.from.course), inserted_at: shift_exchange_request.inserted_at } diff --git a/lib/atlas_web/controllers/shifts/shifts_controller.ex b/lib/atlas_web/controllers/shifts/shifts_controller.ex new file mode 100644 index 0000000..249e5fd --- /dev/null +++ b/lib/atlas_web/controllers/shifts/shifts_controller.ex @@ -0,0 +1,53 @@ +defmodule AtlasWeb.ShiftsController do + use AtlasWeb, :controller + + alias Atlas.Repo + alias Atlas.University.Degrees.Courses.Shifts + + action_fallback AtlasWeb.FallbackController + + def index(conn, attrs) do + {user, _} = Guardian.Plug.current_resource(conn) + + if user_has_elevated_privileges?(user) do + shifts = Shifts.list_shifts_with_timeslots(attrs) + + conn + |> render(:index, shifts: shifts) + else + conn + |> put_status(:forbidden) + |> json(%{error: "Unauthorized"}) + end + end + + def update(conn, %{"id" => id} = attrs) do + {user, _} = Guardian.Plug.current_resource(conn) + + if user_has_elevated_privileges?(user) do + shift = Shifts.get_shift_with_timeslots(id) + timeslot_attrs = Map.get(attrs, "timeslots", []) + shift_attrs = Map.delete(attrs, "timeslots") + + case Shifts.update_shift_with_timeslots(shift, shift_attrs, timeslot_attrs) do + {:ok, %{shift: updated_shift} = _results} -> + conn + |> render(:show, shift: updated_shift |> Repo.preload([:timeslots])) + + {:error, _operation, changeset, _changeset} -> + conn + |> put_status(:unprocessable_entity) + |> put_view(json: AtlasWeb.ChangesetJSON) + |> render(:error, changeset: changeset) + end + else + conn + |> put_status(:forbidden) + |> json(%{error: "Unauthorized"}) + end + end + + defp user_has_elevated_privileges?(user) do + (user && user.type == :admin) || user.type == :professor + end +end diff --git a/lib/atlas_web/controllers/timeslots_controller.ex b/lib/atlas_web/controllers/timeslots_controller.ex new file mode 100644 index 0000000..be018c3 --- /dev/null +++ b/lib/atlas_web/controllers/timeslots_controller.ex @@ -0,0 +1,14 @@ +defmodule AtlasWeb.TimeslotsController do + use AtlasWeb, :controller + + alias Atlas.University.Degrees.Courses.Shifts.Timeslot + alias Atlas.University.Degrees.Courses.Timeslots + + def delete(conn, %{"id" => id}) do + timeslot = Timeslots.get_timeslot!(id) + + with {:ok, %Timeslot{}} <- Timeslots.delete_timeslot(timeslot) do + send_resp(conn, :no_content, "") + end + end +end diff --git a/lib/atlas_web/controllers/university/course_json.ex b/lib/atlas_web/controllers/university/course_json.ex index fa7d66e..44db3d5 100644 --- a/lib/atlas_web/controllers/university/course_json.ex +++ b/lib/atlas_web/controllers/university/course_json.ex @@ -1,6 +1,7 @@ defmodule AtlasWeb.University.CourseJSON do alias Atlas.University.Degrees.Courses.Course - alias AtlasWeb.University.{CourseJSON, ShiftJSON} + alias AtlasWeb.ShiftsJSON + alias AtlasWeb.University.CourseJSON def index(%{courses: courses}) do %{courses: for(course <- courses, do: data(course))} @@ -22,7 +23,7 @@ defmodule AtlasWeb.University.CourseJSON do end, shifts: if Ecto.assoc_loaded?(course.shifts) do - for(shift <- course.shifts, do: ShiftJSON.data(shift)) + for(shift <- course.shifts, do: ShiftsJSON.data(shift)) else [] end diff --git a/lib/atlas_web/controllers/university/shift_json.ex b/lib/atlas_web/controllers/university/shifts_json.ex similarity index 50% rename from lib/atlas_web/controllers/university/shift_json.ex rename to lib/atlas_web/controllers/university/shifts_json.ex index 082e404..829b11e 100644 --- a/lib/atlas_web/controllers/university/shift_json.ex +++ b/lib/atlas_web/controllers/university/shifts_json.ex @@ -1,17 +1,32 @@ -defmodule AtlasWeb.University.ShiftJSON do +defmodule AtlasWeb.ShiftsJSON do alias Atlas.University.Degrees.Courses.Shifts.Shift alias AtlasWeb.University.TimeslotJSON + def index(%{shifts: shifts}) do + %{data: for(shift <- shifts, do: data(shift))} + end + + def show(%{shift: shift}) do + %{data: data(shift)} + end + def data(%Shift{} = shift) do %{ id: shift.id, number: shift.number, type: shift.type, professor: shift.professor, - timeslots: for(timeslot <- shift.timeslots, do: TimeslotJSON.data(timeslot)), + timeslots: + if Ecto.assoc_loaded?(shift.timeslots) do + for timeslot <- shift.timeslots, do: TimeslotJSON.data(timeslot) + else + [] + end, enrollment_status: if Ecto.assoc_loaded?(shift.enrollments) && shift.enrollments != [] do hd(shift.enrollments).status + else + nil end } end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index a5309ef..35fc66c 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -93,6 +93,15 @@ defmodule AtlasWeb.Router do get "/students", University.StudentsController, :index + scope "/shifts" do + get "/", ShiftsController, :index + put "/:id", ShiftsController, :update + end + + scope "/timeslots" do + delete "/:id", TimeslotsController, :delete + end + scope "/jobs" do get "/", JobController, :index get "/:id", JobController, :show