From 5c91a8863d1bbcb97b7829eae392f48fe6d3b574 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Mon, 29 Sep 2025 01:03:47 +0100 Subject: [PATCH 01/10] feat: events --- lib/atlas/events.ex | 200 ++++++++++++++++++ lib/atlas/events/event.ex | 25 +++ lib/atlas/events/event_category.ex | 20 ++ .../controllers/event_category_controller.ex | 43 ++++ .../controllers/event_category_json.ex | 25 +++ lib/atlas_web/controllers/event_controller.ex | 43 ++++ lib/atlas_web/controllers/event_json.ex | 28 +++ lib/atlas_web/router.ex | 16 ++ ...20250928185617_create_event_categories.exs | 13 ++ .../20250928185624_create_events.exs | 21 ++ test/atlas/events_test.exs | 123 +++++++++++ .../event_category_controller_test.exs | 88 ++++++++ .../controllers/event_controller_test.exs | 100 +++++++++ test/support/fixtures/events_fixtures.ex | 39 ++++ 14 files changed, 784 insertions(+) create mode 100644 lib/atlas/events.ex create mode 100644 lib/atlas/events/event.ex create mode 100644 lib/atlas/events/event_category.ex create mode 100644 lib/atlas_web/controllers/event_category_controller.ex create mode 100644 lib/atlas_web/controllers/event_category_json.ex create mode 100644 lib/atlas_web/controllers/event_controller.ex create mode 100644 lib/atlas_web/controllers/event_json.ex create mode 100644 priv/repo/migrations/20250928185617_create_event_categories.exs create mode 100644 priv/repo/migrations/20250928185624_create_events.exs create mode 100644 test/atlas/events_test.exs create mode 100644 test/atlas_web/controllers/event_category_controller_test.exs create mode 100644 test/atlas_web/controllers/event_controller_test.exs create mode 100644 test/support/fixtures/events_fixtures.ex diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex new file mode 100644 index 0000000..b4f0856 --- /dev/null +++ b/lib/atlas/events.ex @@ -0,0 +1,200 @@ +defmodule Atlas.Events do + @moduledoc """ + The Events context. + """ + + import Ecto.Query, warn: false + alias Atlas.Repo + + alias Atlas.Events.EventCategory + + @doc """ + Returns the list of event_categories. + + ## Examples + + iex> list_event_categories() + [%EventCategory{}, ...] + + """ + def list_event_categories do + Repo.all(EventCategory) + end + + @doc """ + Gets a single event_category. + + Raises `Ecto.NoResultsError` if the Event category does not exist. + + ## Examples + + iex> get_event_category!(123) + %EventCategory{} + + iex> get_event_category!(456) + ** (Ecto.NoResultsError) + + """ + def get_event_category!(id), do: Repo.get!(EventCategory, id) + + @doc """ + Creates a event_category. + + ## Examples + + iex> create_event_category(%{field: value}) + {:ok, %EventCategory{}} + + iex> create_event_category(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_event_category(attrs \\ %{}) do + %EventCategory{} + |> EventCategory.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a event_category. + + ## Examples + + iex> update_event_category(event_category, %{field: new_value}) + {:ok, %EventCategory{}} + + iex> update_event_category(event_category, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_event_category(%EventCategory{} = event_category, attrs) do + event_category + |> EventCategory.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a event_category. + + ## Examples + + iex> delete_event_category(event_category) + {:ok, %EventCategory{}} + + iex> delete_event_category(event_category) + {:error, %Ecto.Changeset{}} + + """ + def delete_event_category(%EventCategory{} = event_category) do + Repo.delete(event_category) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking event_category changes. + + ## Examples + + iex> change_event_category(event_category) + %Ecto.Changeset{data: %EventCategory{}} + + """ + def change_event_category(%EventCategory{} = event_category, attrs \\ %{}) do + EventCategory.changeset(event_category, attrs) + end + + alias Atlas.Events.Event + + @doc """ + Returns the list of events. + + ## Examples + + iex> list_events() + [%Event{}, ...] + + """ + def list_events do + Repo.all(Event) + end + + @doc """ + Gets a single event. + + Raises `Ecto.NoResultsError` if the Event does not exist. + + ## Examples + + iex> get_event!(123) + %Event{} + + iex> get_event!(456) + ** (Ecto.NoResultsError) + + """ + def get_event!(id), do: Repo.get!(Event, id) + + @doc """ + Creates a event. + + ## Examples + + iex> create_event(%{field: value}) + {:ok, %Event{}} + + iex> create_event(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_event(attrs \\ %{}) do + %Event{} + |> Event.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a event. + + ## Examples + + iex> update_event(event, %{field: new_value}) + {:ok, %Event{}} + + iex> update_event(event, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_event(%Event{} = event, attrs) do + event + |> Event.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a event. + + ## Examples + + iex> delete_event(event) + {:ok, %Event{}} + + iex> delete_event(event) + {:error, %Ecto.Changeset{}} + + """ + def delete_event(%Event{} = event) do + Repo.delete(event) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking event changes. + + ## Examples + + iex> change_event(event) + %Ecto.Changeset{data: %Event{}} + + """ + def change_event(%Event{} = event, attrs \\ %{}) do + Event.changeset(event, attrs) + end +end diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex new file mode 100644 index 0000000..cee0f46 --- /dev/null +++ b/lib/atlas/events/event.ex @@ -0,0 +1,25 @@ +defmodule Atlas.Events.Event do + use Ecto.Schema + import Ecto.Changeset + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "events" do + field :start, :time + field :link, :string + field :title, :string + field :end, :time + field :place, :string + field :category_id, :binary_id + field :course, :binary_id + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(event, attrs) do + event + |> cast(attrs, [:title, :start, :end, :place, :link]) + |> validate_required([:title, :start, :end, :place, :link]) + end +end diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex new file mode 100644 index 0000000..2672f35 --- /dev/null +++ b/lib/atlas/events/event_category.ex @@ -0,0 +1,20 @@ +defmodule Atlas.Events.EventCategory do + use Ecto.Schema + import Ecto.Changeset + + @primary_key {:id, :binary_id, autogenerate: true} + @foreign_key_type :binary_id + schema "event_categories" do + field :name, :string + field :color, :string + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(event_category, attrs) do + event_category + |> cast(attrs, [:name, :color]) + |> validate_required([:name, :color]) + end +end diff --git a/lib/atlas_web/controllers/event_category_controller.ex b/lib/atlas_web/controllers/event_category_controller.ex new file mode 100644 index 0000000..bd1eff0 --- /dev/null +++ b/lib/atlas_web/controllers/event_category_controller.ex @@ -0,0 +1,43 @@ +defmodule AtlasWeb.EventCategoryController do + use AtlasWeb, :controller + + alias Atlas.Events + alias Atlas.Events.EventCategory + + action_fallback AtlasWeb.FallbackController + + def index(conn, _params) do + event_categories = Events.list_event_categories() + render(conn, :index, event_categories: event_categories) + end + + def create(conn, %{"event_category" => event_category_params}) do + with {:ok, %EventCategory{} = event_category} <- Events.create_event_category(event_category_params) do + conn + |> put_status(:created) + |> put_resp_header("location", ~p"/v1/event_categories/#{event_category}") + |> render(:show, event_category: event_category) + end + end + + def show(conn, %{"id" => id}) do + event_category = Events.get_event_category!(id) + render(conn, :show, event_category: event_category) + end + + def update(conn, %{"id" => id, "event_category" => event_category_params}) do + event_category = Events.get_event_category!(id) + + with {:ok, %EventCategory{} = event_category} <- Events.update_event_category(event_category, event_category_params) do + render(conn, :show, event_category: event_category) + end + end + + def delete(conn, %{"id" => id}) do + event_category = Events.get_event_category!(id) + + with {:ok, %EventCategory{}} <- Events.delete_event_category(event_category) do + send_resp(conn, :no_content, "") + end + end +end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex new file mode 100644 index 0000000..c651562 --- /dev/null +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -0,0 +1,25 @@ +defmodule AtlasWeb.EventCategoryJSON do + alias Atlas.Events.EventCategory + + @doc """ + Renders a list of event_categories. + """ + def index(%{event_categories: event_categories}) do + %{data: for(event_category <- event_categories, do: data(event_category))} + end + + @doc """ + Renders a single event_category. + """ + def show(%{event_category: event_category}) do + %{data: data(event_category)} + end + + defp data(%EventCategory{} = event_category) do + %{ + id: event_category.id, + name: event_category.name, + color: event_category.color + } + end +end diff --git a/lib/atlas_web/controllers/event_controller.ex b/lib/atlas_web/controllers/event_controller.ex new file mode 100644 index 0000000..8a36254 --- /dev/null +++ b/lib/atlas_web/controllers/event_controller.ex @@ -0,0 +1,43 @@ +defmodule AtlasWeb.EventController do + use AtlasWeb, :controller + + alias Atlas.Events + alias Atlas.Events.Event + + action_fallback AtlasWeb.FallbackController + + def index(conn, _params) do + events = Events.list_events() + render(conn, :index, events: events) + end + + def create(conn, %{"event" => event_params}) do + with {:ok, %Event{} = event} <- Events.create_event(event_params) do + conn + |> put_status(:created) + |> put_resp_header("location", ~p"/v1/events/#{event}") + |> render(:show, event: event) + end + end + + def show(conn, %{"id" => id}) do + event = Events.get_event!(id) + render(conn, :show, event: event) + end + + def update(conn, %{"id" => id, "event" => event_params}) do + event = Events.get_event!(id) + + with {:ok, %Event{} = event} <- Events.update_event(event, event_params) do + render(conn, :show, event: event) + end + end + + def delete(conn, %{"id" => id}) do + event = Events.get_event!(id) + + with {:ok, %Event{}} <- Events.delete_event(event) do + send_resp(conn, :no_content, "") + end + end +end diff --git a/lib/atlas_web/controllers/event_json.ex b/lib/atlas_web/controllers/event_json.ex new file mode 100644 index 0000000..7f0b473 --- /dev/null +++ b/lib/atlas_web/controllers/event_json.ex @@ -0,0 +1,28 @@ +defmodule AtlasWeb.EventJSON do + alias Atlas.Events.Event + + @doc """ + Renders a list of events. + """ + def index(%{events: events}) do + %{data: for(event <- events, do: data(event))} + end + + @doc """ + Renders a single event. + """ + def show(%{event: event}) do + %{data: data(event)} + end + + defp data(%Event{} = event) do + %{ + id: event.id, + title: event.title, + start: event.start, + end: event.end, + place: event.place, + link: event.link + } + end +end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index a5309ef..3d8b9d6 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -89,6 +89,22 @@ defmodule AtlasWeb.Router do resources "/", ShiftExchangeRequestController, only: [:index, :create, :show, :delete] end + scope "/events" do + resources "/", EventController, only: [:index, :show] + + pipe_through :is_at_least_professor + + resources "/", EventController, only: [:create, :update, :delete] + end + + scope "/event_categories" do + resources "/", EventCategoryController, only: [:index, :show] + + pipe_through :is_at_least_professor + + resources "/", EventCategoryController, only: [:create, :update, :delete] + end + pipe_through :is_at_least_professor get "/students", University.StudentsController, :index diff --git a/priv/repo/migrations/20250928185617_create_event_categories.exs b/priv/repo/migrations/20250928185617_create_event_categories.exs new file mode 100644 index 0000000..b12abbd --- /dev/null +++ b/priv/repo/migrations/20250928185617_create_event_categories.exs @@ -0,0 +1,13 @@ +defmodule Atlas.Repo.Migrations.CreateEventCategories do + use Ecto.Migration + + def change do + create table(:event_categories, primary_key: false) do + add :id, :binary_id, primary_key: true + add :name, :string + add :color, :string + + timestamps(type: :utc_datetime) + end + end +end diff --git a/priv/repo/migrations/20250928185624_create_events.exs b/priv/repo/migrations/20250928185624_create_events.exs new file mode 100644 index 0000000..c1fafd5 --- /dev/null +++ b/priv/repo/migrations/20250928185624_create_events.exs @@ -0,0 +1,21 @@ +defmodule Atlas.Repo.Migrations.CreateEvents do + use Ecto.Migration + + def change do + create table(:events, primary_key: false) do + add :id, :binary_id, primary_key: true + add :title, :string + add :start, :time + add :end, :time + add :place, :string + add :link, :string + add :category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) + add :course, references(:courses, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:events, [:category_id]) + create index(:events, [:course]) + end +end diff --git a/test/atlas/events_test.exs b/test/atlas/events_test.exs new file mode 100644 index 0000000..b091a3f --- /dev/null +++ b/test/atlas/events_test.exs @@ -0,0 +1,123 @@ +defmodule Atlas.EventsTest do + use Atlas.DataCase + + alias Atlas.Events + + describe "event_categories" do + alias Atlas.Events.EventCategory + + import Atlas.EventsFixtures + + @invalid_attrs %{name: nil, color: nil} + + test "list_event_categories/0 returns all event_categories" do + event_category = event_category_fixture() + assert Events.list_event_categories() == [event_category] + end + + test "get_event_category!/1 returns the event_category with given id" do + event_category = event_category_fixture() + assert Events.get_event_category!(event_category.id) == event_category + end + + test "create_event_category/1 with valid data creates a event_category" do + valid_attrs = %{name: "some name", color: "some color"} + + assert {:ok, %EventCategory{} = event_category} = Events.create_event_category(valid_attrs) + assert event_category.name == "some name" + assert event_category.color == "some color" + end + + test "create_event_category/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Events.create_event_category(@invalid_attrs) + end + + test "update_event_category/2 with valid data updates the event_category" do + event_category = event_category_fixture() + update_attrs = %{name: "some updated name", color: "some updated color"} + + assert {:ok, %EventCategory{} = event_category} = Events.update_event_category(event_category, update_attrs) + assert event_category.name == "some updated name" + assert event_category.color == "some updated color" + end + + test "update_event_category/2 with invalid data returns error changeset" do + event_category = event_category_fixture() + assert {:error, %Ecto.Changeset{}} = Events.update_event_category(event_category, @invalid_attrs) + assert event_category == Events.get_event_category!(event_category.id) + end + + test "delete_event_category/1 deletes the event_category" do + event_category = event_category_fixture() + assert {:ok, %EventCategory{}} = Events.delete_event_category(event_category) + assert_raise Ecto.NoResultsError, fn -> Events.get_event_category!(event_category.id) end + end + + test "change_event_category/1 returns a event_category changeset" do + event_category = event_category_fixture() + assert %Ecto.Changeset{} = Events.change_event_category(event_category) + end + end + + describe "events" do + alias Atlas.Events.Event + + import Atlas.EventsFixtures + + @invalid_attrs %{start: nil, link: nil, title: nil, end: nil, place: nil} + + test "list_events/0 returns all events" do + event = event_fixture() + assert Events.list_events() == [event] + end + + test "get_event!/1 returns the event with given id" do + event = event_fixture() + assert Events.get_event!(event.id) == event + end + + test "create_event/1 with valid data creates a event" do + valid_attrs = %{start: ~T[14:00:00], link: "some link", title: "some title", end: ~T[14:00:00], place: "some place"} + + assert {:ok, %Event{} = event} = Events.create_event(valid_attrs) + assert event.start == ~T[14:00:00] + assert event.link == "some link" + assert event.title == "some title" + assert event.end == ~T[14:00:00] + assert event.place == "some place" + end + + test "create_event/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Events.create_event(@invalid_attrs) + end + + test "update_event/2 with valid data updates the event" do + event = event_fixture() + update_attrs = %{start: ~T[15:01:01], link: "some updated link", title: "some updated title", end: ~T[15:01:01], place: "some updated place"} + + assert {:ok, %Event{} = event} = Events.update_event(event, update_attrs) + assert event.start == ~T[15:01:01] + assert event.link == "some updated link" + assert event.title == "some updated title" + assert event.end == ~T[15:01:01] + assert event.place == "some updated place" + end + + test "update_event/2 with invalid data returns error changeset" do + event = event_fixture() + assert {:error, %Ecto.Changeset{}} = Events.update_event(event, @invalid_attrs) + assert event == Events.get_event!(event.id) + end + + test "delete_event/1 deletes the event" do + event = event_fixture() + assert {:ok, %Event{}} = Events.delete_event(event) + assert_raise Ecto.NoResultsError, fn -> Events.get_event!(event.id) end + end + + test "change_event/1 returns a event changeset" do + event = event_fixture() + assert %Ecto.Changeset{} = Events.change_event(event) + end + end +end diff --git a/test/atlas_web/controllers/event_category_controller_test.exs b/test/atlas_web/controllers/event_category_controller_test.exs new file mode 100644 index 0000000..2fd6a11 --- /dev/null +++ b/test/atlas_web/controllers/event_category_controller_test.exs @@ -0,0 +1,88 @@ +defmodule AtlasWeb.EventCategoryControllerTest do + use AtlasWeb.ConnCase + + import Atlas.EventsFixtures + + alias Atlas.Events.EventCategory + + @create_attrs %{ + name: "some name", + color: "some color" + } + @update_attrs %{ + name: "some updated name", + color: "some updated color" + } + @invalid_attrs %{name: nil, color: nil} + + setup %{conn: conn} do + {:ok, conn: put_req_header(conn, "accept", "application/json")} + end + + describe "index" do + test "lists all event_categories", %{conn: conn} do + conn = get(conn, ~p"/api/event_categories") + assert json_response(conn, 200)["data"] == [] + end + end + + describe "create event_category" do + test "renders event_category when data is valid", %{conn: conn} do + conn = post(conn, ~p"/api/event_categories", event_category: @create_attrs) + assert %{"id" => id} = json_response(conn, 201)["data"] + + conn = get(conn, ~p"/api/event_categories/#{id}") + + assert %{ + "id" => ^id, + "color" => "some color", + "name" => "some name" + } = json_response(conn, 200)["data"] + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, ~p"/api/event_categories", event_category: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "update event_category" do + setup [:create_event_category] + + test "renders event_category when data is valid", %{conn: conn, event_category: %EventCategory{id: id} = event_category} do + conn = put(conn, ~p"/api/event_categories/#{event_category}", event_category: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["data"] + + conn = get(conn, ~p"/api/event_categories/#{id}") + + assert %{ + "id" => ^id, + "color" => "some updated color", + "name" => "some updated name" + } = json_response(conn, 200)["data"] + end + + test "renders errors when data is invalid", %{conn: conn, event_category: event_category} do + conn = put(conn, ~p"/api/event_categories/#{event_category}", event_category: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "delete event_category" do + setup [:create_event_category] + + test "deletes chosen event_category", %{conn: conn, event_category: event_category} do + conn = delete(conn, ~p"/api/event_categories/#{event_category}") + assert response(conn, 204) + + assert_error_sent 404, fn -> + get(conn, ~p"/api/event_categories/#{event_category}") + end + end + end + + defp create_event_category(_) do + event_category = event_category_fixture() + %{event_category: event_category} + end +end diff --git a/test/atlas_web/controllers/event_controller_test.exs b/test/atlas_web/controllers/event_controller_test.exs new file mode 100644 index 0000000..662a05b --- /dev/null +++ b/test/atlas_web/controllers/event_controller_test.exs @@ -0,0 +1,100 @@ +defmodule AtlasWeb.EventControllerTest do + use AtlasWeb.ConnCase + + import Atlas.EventsFixtures + + alias Atlas.Events.Event + + @create_attrs %{ + start: ~T[14:00:00], + link: "some link", + title: "some title", + end: ~T[14:00:00], + place: "some place" + } + @update_attrs %{ + start: ~T[15:01:01], + link: "some updated link", + title: "some updated title", + end: ~T[15:01:01], + place: "some updated place" + } + @invalid_attrs %{start: nil, link: nil, title: nil, end: nil, place: nil} + + setup %{conn: conn} do + {:ok, conn: put_req_header(conn, "accept", "application/json")} + end + + describe "index" do + test "lists all events", %{conn: conn} do + conn = get(conn, ~p"/api/events") + assert json_response(conn, 200)["data"] == [] + end + end + + describe "create event" do + test "renders event when data is valid", %{conn: conn} do + conn = post(conn, ~p"/api/events", event: @create_attrs) + assert %{"id" => id} = json_response(conn, 201)["data"] + + conn = get(conn, ~p"/api/events/#{id}") + + assert %{ + "id" => ^id, + "end" => "14:00:00", + "link" => "some link", + "place" => "some place", + "start" => "14:00:00", + "title" => "some title" + } = json_response(conn, 200)["data"] + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post(conn, ~p"/api/events", event: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "update event" do + setup [:create_event] + + test "renders event when data is valid", %{conn: conn, event: %Event{id: id} = event} do + conn = put(conn, ~p"/api/events/#{event}", event: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["data"] + + conn = get(conn, ~p"/api/events/#{id}") + + assert %{ + "id" => ^id, + "end" => "15:01:01", + "link" => "some updated link", + "place" => "some updated place", + "start" => "15:01:01", + "title" => "some updated title" + } = json_response(conn, 200)["data"] + end + + test "renders errors when data is invalid", %{conn: conn, event: event} do + conn = put(conn, ~p"/api/events/#{event}", event: @invalid_attrs) + assert json_response(conn, 422)["errors"] != %{} + end + end + + describe "delete event" do + setup [:create_event] + + test "deletes chosen event", %{conn: conn, event: event} do + conn = delete(conn, ~p"/api/events/#{event}") + assert response(conn, 204) + + assert_error_sent 404, fn -> + get(conn, ~p"/api/events/#{event}") + end + end + end + + defp create_event(_) do + event = event_fixture() + %{event: event} + end +end diff --git a/test/support/fixtures/events_fixtures.ex b/test/support/fixtures/events_fixtures.ex new file mode 100644 index 0000000..6868d2e --- /dev/null +++ b/test/support/fixtures/events_fixtures.ex @@ -0,0 +1,39 @@ +defmodule Atlas.EventsFixtures do + @moduledoc """ + This module defines test helpers for creating + entities via the `Atlas.Events` context. + """ + + @doc """ + Generate a event_category. + """ + def event_category_fixture(attrs \\ %{}) do + {:ok, event_category} = + attrs + |> Enum.into(%{ + color: "some color", + name: "some name" + }) + |> Atlas.Events.create_event_category() + + event_category + end + + @doc """ + Generate a event. + """ + def event_fixture(attrs \\ %{}) do + {:ok, event} = + attrs + |> Enum.into(%{ + end: ~T[14:00:00], + link: "some link", + place: "some place", + start: ~T[14:00:00], + title: "some title" + }) + |> Atlas.Events.create_event() + + event + end +end From 9171688f50186c6b16a25fe7328f0ac16e18d7c1 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Fri, 3 Oct 2025 23:42:36 +0100 Subject: [PATCH 02/10] fix: change time to datetime in events --- lib/atlas/events/event.ex | 4 ++-- priv/repo/migrations/20250928185624_create_events.exs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index cee0f46..17c9b61 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -5,10 +5,10 @@ defmodule Atlas.Events.Event do @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "events" do - field :start, :time + field :start, :utc_datetime field :link, :string field :title, :string - field :end, :time + field :end, :utc_datetime field :place, :string field :category_id, :binary_id field :course, :binary_id diff --git a/priv/repo/migrations/20250928185624_create_events.exs b/priv/repo/migrations/20250928185624_create_events.exs index c1fafd5..129d458 100644 --- a/priv/repo/migrations/20250928185624_create_events.exs +++ b/priv/repo/migrations/20250928185624_create_events.exs @@ -5,8 +5,8 @@ defmodule Atlas.Repo.Migrations.CreateEvents do create table(:events, primary_key: false) do add :id, :binary_id, primary_key: true add :title, :string - add :start, :time - add :end, :time + add :start, :utc_datetime + add :end, :utc_datetime add :place, :string add :link, :string add :category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) From 0fe7cd3b222b1d3d34348d515d1f4dbf34dd6c1e Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Sat, 4 Oct 2025 00:45:29 +0100 Subject: [PATCH 03/10] feat: add course and category to event json --- lib/atlas/events.ex | 9 ++++++--- lib/atlas/events/event.ex | 5 +++-- .../controllers/event_category_controller.ex | 6 ++++-- .../controllers/event_category_json.ex | 2 +- lib/atlas_web/controllers/event_json.ex | 18 +++++++++++++++--- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index b4f0856..c850ef1 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -7,6 +7,7 @@ defmodule Atlas.Events do alias Atlas.Repo alias Atlas.Events.EventCategory + alias Atlas.Events.Event @doc """ Returns the list of event_categories. @@ -102,8 +103,6 @@ defmodule Atlas.Events do EventCategory.changeset(event_category, attrs) end - alias Atlas.Events.Event - @doc """ Returns the list of events. @@ -114,7 +113,9 @@ defmodule Atlas.Events do """ def list_events do - Repo.all(Event) + Event + |> preload([:category, :course]) + |> Repo.all() end @doc """ @@ -148,6 +149,7 @@ defmodule Atlas.Events do def create_event(attrs \\ %{}) do %Event{} |> Event.changeset(attrs) + |> preload([:category, :course]) |> Repo.insert() end @@ -166,6 +168,7 @@ defmodule Atlas.Events do def update_event(%Event{} = event, attrs) do event |> Event.changeset(attrs) + |> preload([:category, :course]) |> Repo.update() end diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index 17c9b61..0b34a2f 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -10,8 +10,9 @@ defmodule Atlas.Events.Event do field :title, :string field :end, :utc_datetime field :place, :string - field :category_id, :binary_id - field :course, :binary_id + + belongs_to :category, Atlas.Events.EventCategory + belongs_to :course, Atlas.University.Degrees.Courses.Course timestamps(type: :utc_datetime) end diff --git a/lib/atlas_web/controllers/event_category_controller.ex b/lib/atlas_web/controllers/event_category_controller.ex index bd1eff0..bfac5dc 100644 --- a/lib/atlas_web/controllers/event_category_controller.ex +++ b/lib/atlas_web/controllers/event_category_controller.ex @@ -12,7 +12,8 @@ defmodule AtlasWeb.EventCategoryController do end def create(conn, %{"event_category" => event_category_params}) do - with {:ok, %EventCategory{} = event_category} <- Events.create_event_category(event_category_params) do + with {:ok, %EventCategory{} = event_category} <- + Events.create_event_category(event_category_params) do conn |> put_status(:created) |> put_resp_header("location", ~p"/v1/event_categories/#{event_category}") @@ -28,7 +29,8 @@ defmodule AtlasWeb.EventCategoryController do def update(conn, %{"id" => id, "event_category" => event_category_params}) do event_category = Events.get_event_category!(id) - with {:ok, %EventCategory{} = event_category} <- Events.update_event_category(event_category, event_category_params) do + with {:ok, %EventCategory{} = event_category} <- + Events.update_event_category(event_category, event_category_params) do render(conn, :show, event_category: event_category) end end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex index c651562..4e5a99c 100644 --- a/lib/atlas_web/controllers/event_category_json.ex +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -15,7 +15,7 @@ defmodule AtlasWeb.EventCategoryJSON do %{data: data(event_category)} end - defp data(%EventCategory{} = event_category) do + def data(%EventCategory{} = event_category) do %{ id: event_category.id, name: event_category.name, diff --git a/lib/atlas_web/controllers/event_json.ex b/lib/atlas_web/controllers/event_json.ex index 7f0b473..17b6efb 100644 --- a/lib/atlas_web/controllers/event_json.ex +++ b/lib/atlas_web/controllers/event_json.ex @@ -5,14 +5,14 @@ defmodule AtlasWeb.EventJSON do Renders a list of events. """ def index(%{events: events}) do - %{data: for(event <- events, do: data(event))} + %{events: for(event <- events, do: data(event))} end @doc """ Renders a single event. """ def show(%{event: event}) do - %{data: data(event)} + %{event: data(event)} end defp data(%Event{} = event) do @@ -22,7 +22,19 @@ defmodule AtlasWeb.EventJSON do start: event.start, end: event.end, place: event.place, - link: event.link + link: event.link, + category: + if Ecto.assoc_loaded?(event.category) do + AtlasWeb.EventCategoryJSON.data(event.category) + else + nil + end, + course: + if Ecto.assoc_loaded?(event.course) do + AtlasWeb.University.CourseJSON.data(event.course) + else + nil + end } end end From c5b419c2a64525100fa3632c929c943a390cc488 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Sat, 4 Oct 2025 01:23:28 +0100 Subject: [PATCH 04/10] fix: incorrect preload --- lib/atlas/events.ex | 10 ++++++++-- lib/atlas/events/event.ex | 12 ++++++------ lib/atlas/events/event_category.ex | 11 +++++------ lib/atlas_web/controllers/event_category_json.ex | 4 ++-- lib/atlas_web/controllers/event_json.ex | 4 ++-- .../repo/migrations/20250928185624_create_events.exs | 4 ++-- 6 files changed, 25 insertions(+), 20 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index c850ef1..3fde91f 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -149,8 +149,11 @@ defmodule Atlas.Events do def create_event(attrs \\ %{}) do %Event{} |> Event.changeset(attrs) - |> preload([:category, :course]) |> Repo.insert() + |> case do + {:ok, event} -> {:ok, Repo.preload(event, [:category, :course])} + {:error, changeset} -> {:error, changeset} + end end @doc """ @@ -168,8 +171,11 @@ defmodule Atlas.Events do def update_event(%Event{} = event, attrs) do event |> Event.changeset(attrs) - |> preload([:category, :course]) |> Repo.update() + |> case do + {:ok, event} -> {:ok, Repo.preload(event, [:category, :course])} + {:error, changeset} -> {:error, changeset} + end end @doc """ diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index 0b34a2f..e6e5b44 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -1,9 +1,9 @@ defmodule Atlas.Events.Event do - use Ecto.Schema - import Ecto.Changeset + use Atlas.Schema + + @required_fields ~w(title start end place link category_id)a + @optional_fields ~w(course_id)a - @primary_key {:id, :binary_id, autogenerate: true} - @foreign_key_type :binary_id schema "events" do field :start, :utc_datetime field :link, :string @@ -20,7 +20,7 @@ defmodule Atlas.Events.Event do @doc false def changeset(event, attrs) do event - |> cast(attrs, [:title, :start, :end, :place, :link]) - |> validate_required([:title, :start, :end, :place, :link]) + |> cast(attrs, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) end end diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex index 2672f35..35f52c8 100644 --- a/lib/atlas/events/event_category.ex +++ b/lib/atlas/events/event_category.ex @@ -1,9 +1,8 @@ defmodule Atlas.Events.EventCategory do - use Ecto.Schema - import Ecto.Changeset + use Atlas.Schema + + @required_fields ~w(name color)a - @primary_key {:id, :binary_id, autogenerate: true} - @foreign_key_type :binary_id schema "event_categories" do field :name, :string field :color, :string @@ -14,7 +13,7 @@ defmodule Atlas.Events.EventCategory do @doc false def changeset(event_category, attrs) do event_category - |> cast(attrs, [:name, :color]) - |> validate_required([:name, :color]) + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) end end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex index 4e5a99c..8148bd4 100644 --- a/lib/atlas_web/controllers/event_category_json.ex +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -5,14 +5,14 @@ defmodule AtlasWeb.EventCategoryJSON do Renders a list of event_categories. """ def index(%{event_categories: event_categories}) do - %{data: for(event_category <- event_categories, do: data(event_category))} + %{event_categories: for(event_category <- event_categories, do: data(event_category))} end @doc """ Renders a single event_category. """ def show(%{event_category: event_category}) do - %{data: data(event_category)} + %{event_category: data(event_category)} end def data(%EventCategory{} = event_category) do diff --git a/lib/atlas_web/controllers/event_json.ex b/lib/atlas_web/controllers/event_json.ex index 17b6efb..f562189 100644 --- a/lib/atlas_web/controllers/event_json.ex +++ b/lib/atlas_web/controllers/event_json.ex @@ -24,13 +24,13 @@ defmodule AtlasWeb.EventJSON do place: event.place, link: event.link, category: - if Ecto.assoc_loaded?(event.category) do + if Ecto.assoc_loaded?(event.category) and event.category do AtlasWeb.EventCategoryJSON.data(event.category) else nil end, course: - if Ecto.assoc_loaded?(event.course) do + if Ecto.assoc_loaded?(event.course) and event.course do AtlasWeb.University.CourseJSON.data(event.course) else nil diff --git a/priv/repo/migrations/20250928185624_create_events.exs b/priv/repo/migrations/20250928185624_create_events.exs index 129d458..d11fb18 100644 --- a/priv/repo/migrations/20250928185624_create_events.exs +++ b/priv/repo/migrations/20250928185624_create_events.exs @@ -10,12 +10,12 @@ defmodule Atlas.Repo.Migrations.CreateEvents do add :place, :string add :link, :string add :category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) - add :course, references(:courses, on_delete: :nothing, type: :binary_id) + add :course_id, references(:courses, on_delete: :nothing, type: :binary_id) timestamps(type: :utc_datetime) end create index(:events, [:category_id]) - create index(:events, [:course]) + create index(:events, [:course_id]) end end From b95317240af55406b3525f9347bf8d62a7c78069 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Mon, 6 Oct 2025 00:51:35 +0100 Subject: [PATCH 05/10] feat: move course association to event_category --- lib/atlas/events.ex | 38 ++++++++++++++++--- lib/atlas/events/event.ex | 4 +- lib/atlas/events/event_category.ex | 6 ++- .../controllers/event_category_json.ex | 8 +++- lib/atlas_web/controllers/event_json.ex | 6 --- ...20250928185617_create_event_categories.exs | 3 ++ .../20250928185624_create_events.exs | 2 - 7 files changed, 48 insertions(+), 19 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index 3fde91f..03070bc 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -19,7 +19,9 @@ defmodule Atlas.Events do """ def list_event_categories do - Repo.all(EventCategory) + EventCategory + |> preload(:course) + |> Repo.all() end @doc """ @@ -36,7 +38,11 @@ defmodule Atlas.Events do ** (Ecto.NoResultsError) """ - def get_event_category!(id), do: Repo.get!(EventCategory, id) + def get_event_category!(id) do + EventCategory + |> preload(:course) + |> Repo.get!(id) + end @doc """ Creates a event_category. @@ -54,6 +60,10 @@ defmodule Atlas.Events do %EventCategory{} |> EventCategory.changeset(attrs) |> Repo.insert() + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end end @doc """ @@ -72,6 +82,10 @@ defmodule Atlas.Events do event_category |> EventCategory.changeset(attrs) |> Repo.update() + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end end @doc """ @@ -88,6 +102,10 @@ defmodule Atlas.Events do """ def delete_event_category(%EventCategory{} = event_category) do Repo.delete(event_category) + |> case do + {:ok, event_category} -> {:ok, Repo.preload(event_category, :course)} + {:error, changeset} -> {:error, changeset} + end end @doc """ @@ -114,7 +132,7 @@ defmodule Atlas.Events do """ def list_events do Event - |> preload([:category, :course]) + |> preload(category: :course) |> Repo.all() end @@ -132,7 +150,11 @@ defmodule Atlas.Events do ** (Ecto.NoResultsError) """ - def get_event!(id), do: Repo.get!(Event, id) + def get_event!(id) do + Event + |> preload(category: :course) + |> Repo.get!(id) + end @doc """ Creates a event. @@ -151,7 +173,7 @@ defmodule Atlas.Events do |> Event.changeset(attrs) |> Repo.insert() |> case do - {:ok, event} -> {:ok, Repo.preload(event, [:category, :course])} + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} {:error, changeset} -> {:error, changeset} end end @@ -173,7 +195,7 @@ defmodule Atlas.Events do |> Event.changeset(attrs) |> Repo.update() |> case do - {:ok, event} -> {:ok, Repo.preload(event, [:category, :course])} + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} {:error, changeset} -> {:error, changeset} end end @@ -192,6 +214,10 @@ defmodule Atlas.Events do """ def delete_event(%Event{} = event) do Repo.delete(event) + |> case do + {:ok, event} -> {:ok, Repo.preload(event, category: :course)} + {:error, changeset} -> {:error, changeset} + end end @doc """ diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index e6e5b44..91ab01c 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -2,7 +2,6 @@ defmodule Atlas.Events.Event do use Atlas.Schema @required_fields ~w(title start end place link category_id)a - @optional_fields ~w(course_id)a schema "events" do field :start, :utc_datetime @@ -12,7 +11,6 @@ defmodule Atlas.Events.Event do field :place, :string belongs_to :category, Atlas.Events.EventCategory - belongs_to :course, Atlas.University.Degrees.Courses.Course timestamps(type: :utc_datetime) end @@ -20,7 +18,7 @@ defmodule Atlas.Events.Event do @doc false def changeset(event, attrs) do event - |> cast(attrs, @required_fields ++ @optional_fields) + |> cast(attrs, @required_fields) |> validate_required(@required_fields) end end diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex index 35f52c8..5ec5bd7 100644 --- a/lib/atlas/events/event_category.ex +++ b/lib/atlas/events/event_category.ex @@ -3,17 +3,21 @@ defmodule Atlas.Events.EventCategory do @required_fields ~w(name color)a + @optional_fields ~w(course_id)a + schema "event_categories" do field :name, :string field :color, :string + belongs_to :course, Atlas.University.Degrees.Courses.Course + timestamps(type: :utc_datetime) end @doc false def changeset(event_category, attrs) do event_category - |> cast(attrs, @required_fields) + |> cast(attrs, @required_fields ++ @optional_fields) |> validate_required(@required_fields) end end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex index 8148bd4..67ac4cb 100644 --- a/lib/atlas_web/controllers/event_category_json.ex +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -19,7 +19,13 @@ defmodule AtlasWeb.EventCategoryJSON do %{ id: event_category.id, name: event_category.name, - color: event_category.color + color: event_category.color, + course: + if Ecto.assoc_loaded?(event_category.course) and event_category.course do + AtlasWeb.University.CourseJSON.data(event_category.course) + else + nil + end } end end diff --git a/lib/atlas_web/controllers/event_json.ex b/lib/atlas_web/controllers/event_json.ex index f562189..189802f 100644 --- a/lib/atlas_web/controllers/event_json.ex +++ b/lib/atlas_web/controllers/event_json.ex @@ -28,12 +28,6 @@ defmodule AtlasWeb.EventJSON do AtlasWeb.EventCategoryJSON.data(event.category) else nil - end, - course: - if Ecto.assoc_loaded?(event.course) and event.course do - AtlasWeb.University.CourseJSON.data(event.course) - else - nil end } end diff --git a/priv/repo/migrations/20250928185617_create_event_categories.exs b/priv/repo/migrations/20250928185617_create_event_categories.exs index b12abbd..a31bedb 100644 --- a/priv/repo/migrations/20250928185617_create_event_categories.exs +++ b/priv/repo/migrations/20250928185617_create_event_categories.exs @@ -6,8 +6,11 @@ defmodule Atlas.Repo.Migrations.CreateEventCategories do add :id, :binary_id, primary_key: true add :name, :string add :color, :string + add :course_id, references(:courses, on_delete: :nothing, type: :binary_id) timestamps(type: :utc_datetime) end + + create index(:event_categories, [:course_id]) end end diff --git a/priv/repo/migrations/20250928185624_create_events.exs b/priv/repo/migrations/20250928185624_create_events.exs index d11fb18..22ca97f 100644 --- a/priv/repo/migrations/20250928185624_create_events.exs +++ b/priv/repo/migrations/20250928185624_create_events.exs @@ -10,12 +10,10 @@ defmodule Atlas.Repo.Migrations.CreateEvents do add :place, :string add :link, :string add :category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) - add :course_id, references(:courses, on_delete: :nothing, type: :binary_id) timestamps(type: :utc_datetime) end create index(:events, [:category_id]) - create index(:events, [:course_id]) end end From 47ab1a3843a741c4c6adb4f72d1f6050f703cbc4 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Wed, 8 Oct 2025 00:31:15 +0100 Subject: [PATCH 06/10] feat: user event categories association --- lib/atlas/events.ex | 56 ++++++++++++++++++- lib/atlas/events/event_category.ex | 2 + lib/atlas/events/user_event_category.ex | 19 +++++++ .../controllers/event_category_controller.ex | 27 +++++++++ .../controllers/event_category_json.ex | 3 +- lib/atlas_web/router.ex | 3 + ...06110636_create_users_event_categories.exs | 16 ++++++ 7 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 lib/atlas/events/user_event_category.ex create mode 100644 priv/repo/migrations/20251006110636_create_users_event_categories.exs diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index 03070bc..c15d8c6 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -6,8 +6,7 @@ defmodule Atlas.Events do import Ecto.Query, warn: false alias Atlas.Repo - alias Atlas.Events.EventCategory - alias Atlas.Events.Event + alias Atlas.Events.{Event, EventCategory, UserEventCategory} @doc """ Returns the list of event_categories. @@ -24,6 +23,59 @@ defmodule Atlas.Events do |> Repo.all() end + @doc """ + Returns the list of event categories selected by a user. + + ## Examples + + iex> list_event_categories_by_user(user_id) + [%EventCategory{}, ...] + + """ + def list_event_categories_by_user(user_id) do + EventCategory + |> join(:inner, [ec], uec in assoc(ec, :users_event_categories)) + |> where([ec, uec], uec.user_id == ^user_id) + |> preload(:course) + |> Repo.all() + end + + @doc """ + Updates the event categories selected by a user. + + ## Examples + iex> update_event_categories_for_user(user_id, category_ids) + {:ok, [%UserEventCategory{}, ...]} + + iex> update_event_categories_for_user(user_id, bad_category_ids) + {:error, %Ecto.Changeset{}} + """ + def update_event_categories_for_user(user_id, category_ids) do + Ecto.Multi.new() + |> Ecto.Multi.delete_all( + :delete_all, + UserEventCategory + |> where([uec], uec.user_id == ^user_id) + ) + |> Ecto.Multi.run(:insert_all, fn repo, _changes -> + insert_user_event_categories(repo, user_id, category_ids) + end) + |> Repo.transact() + end + + defp insert_user_event_categories(repo, user_id, category_ids) do + category_ids + |> Enum.reduce_while({:ok, []}, fn category_id, {:ok, acc} -> + %UserEventCategory{} + |> UserEventCategory.changeset(%{user_id: user_id, event_category_id: category_id}) + |> repo.insert() + |> case do + {:ok, user_event_category} -> {:cont, {:ok, [user_event_category | acc]}} + {:error, changeset} -> {:halt, {:error, changeset}} + end + end) + end + @doc """ Gets a single event_category. diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex index 5ec5bd7..348fdd8 100644 --- a/lib/atlas/events/event_category.ex +++ b/lib/atlas/events/event_category.ex @@ -11,6 +11,8 @@ defmodule Atlas.Events.EventCategory do belongs_to :course, Atlas.University.Degrees.Courses.Course + has_many :users_event_categories, Atlas.Events.UserEventCategory + timestamps(type: :utc_datetime) end diff --git a/lib/atlas/events/user_event_category.ex b/lib/atlas/events/user_event_category.ex new file mode 100644 index 0000000..e5748b5 --- /dev/null +++ b/lib/atlas/events/user_event_category.ex @@ -0,0 +1,19 @@ +defmodule Atlas.Events.UserEventCategory do + use Atlas.Schema + + @required_fields ~w(user_id event_category_id)a + + schema "users_event_categories" do + belongs_to :user, Atlas.Accounts.User + belongs_to :event_category, Atlas.Events.EventCategory + + timestamps(type: :utc_datetime) + end + + @doc false + def changeset(user_event_category, attrs) do + user_event_category + |> cast(attrs, @required_fields) + |> validate_required(@required_fields) + end +end diff --git a/lib/atlas_web/controllers/event_category_controller.ex b/lib/atlas_web/controllers/event_category_controller.ex index bfac5dc..94333d5 100644 --- a/lib/atlas_web/controllers/event_category_controller.ex +++ b/lib/atlas_web/controllers/event_category_controller.ex @@ -42,4 +42,31 @@ defmodule AtlasWeb.EventCategoryController do send_resp(conn, :no_content, "") end end + + def selected_index(conn, _params) do + {user, _session} = Guardian.Plug.current_resource(conn) + + user_event_categories = Events.list_event_categories_by_user(user.id) + render(conn, :index, event_categories: user_event_categories) + end + + def selected_update(conn, %{"event_categories" => event_categories}) do + {user, _session} = Guardian.Plug.current_resource(conn) + + case Events.update_event_categories_for_user(user.id, event_categories) do + {:ok, _} -> + event_categories = Events.list_event_categories_by_user(user.id) + render(conn, :index, event_categories: event_categories) + + {:error, _reason} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "Could not update selected event categories."}) + + {:error, _, _, _} -> + conn + |> put_status(:internal_server_error) + |> json(%{error: "Could not update selected event categories."}) + end + end end diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex index 67ac4cb..d3683f6 100644 --- a/lib/atlas_web/controllers/event_category_json.ex +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -1,5 +1,6 @@ defmodule AtlasWeb.EventCategoryJSON do alias Atlas.Events.EventCategory + alias AtlasWeb.University.CourseJSON @doc """ Renders a list of event_categories. @@ -22,7 +23,7 @@ defmodule AtlasWeb.EventCategoryJSON do color: event_category.color, course: if Ecto.assoc_loaded?(event_category.course) and event_category.course do - AtlasWeb.University.CourseJSON.data(event_category.course) + CourseJSON.data(event_category.course) else nil end diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index 3d8b9d6..f080e93 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -98,6 +98,9 @@ defmodule AtlasWeb.Router do end scope "/event_categories" do + get "/selected", EventCategoryController, :selected_index + post "/selected", EventCategoryController, :selected_update + resources "/", EventCategoryController, only: [:index, :show] pipe_through :is_at_least_professor diff --git a/priv/repo/migrations/20251006110636_create_users_event_categories.exs b/priv/repo/migrations/20251006110636_create_users_event_categories.exs new file mode 100644 index 0000000..e53f1f5 --- /dev/null +++ b/priv/repo/migrations/20251006110636_create_users_event_categories.exs @@ -0,0 +1,16 @@ +defmodule Atlas.Repo.Migrations.CreateUsersEventCategories do + use Ecto.Migration + + def change do + create table(:users_event_categories, primary_key: false) do + add :id, :binary_id, primary_key: true + add :user_id, references(:users, on_delete: :nothing, type: :binary_id) + add :event_category_id, references(:event_categories, on_delete: :nothing, type: :binary_id) + + timestamps(type: :utc_datetime) + end + + create index(:users_event_categories, [:user_id]) + create index(:users_event_categories, [:event_category_id]) + end +end From 929afbbd3a5719af330f5b3527e27666c89e22fe Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Thu, 16 Oct 2025 12:15:06 +0100 Subject: [PATCH 07/10] feat: mandatory event categories --- lib/atlas/events.ex | 13 +++++++++++-- lib/atlas/events/event.ex | 1 + lib/atlas/events/event_category.ex | 4 +++- lib/atlas_web/controllers/event_category_json.ex | 3 ++- lib/atlas_web/controllers/event_controller.ex | 7 +++++++ lib/atlas_web/router.ex | 1 + .../20250928185617_create_event_categories.exs | 1 + 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index c15d8c6..2809e9f 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -34,8 +34,8 @@ defmodule Atlas.Events do """ def list_event_categories_by_user(user_id) do EventCategory - |> join(:inner, [ec], uec in assoc(ec, :users_event_categories)) - |> where([ec, uec], uec.user_id == ^user_id) + |> join(:left, [ec], uec in assoc(ec, :users_event_categories)) + |> where([ec, uec], uec.user_id == ^user_id or ec.type == :mandatory) |> preload(:course) |> Repo.all() end @@ -188,6 +188,15 @@ defmodule Atlas.Events do |> Repo.all() end + def list_events_by_user(user_id) do + Event + |> join(:inner, [e], ec in assoc(e, :category)) + |> join(:left, [e, ec], uec in assoc(ec, :users_event_categories)) + |> where([e, ec, uec], uec.user_id == ^user_id or ec.type == :mandatory) + |> preload(category: :course) + |> Repo.all() + end + @doc """ Gets a single event. diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index 91ab01c..ed10c19 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -20,5 +20,6 @@ defmodule Atlas.Events.Event do event |> cast(attrs, @required_fields) |> validate_required(@required_fields) + |> foreign_key_constraint(:category_id) end end diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex index 348fdd8..cd19d43 100644 --- a/lib/atlas/events/event_category.ex +++ b/lib/atlas/events/event_category.ex @@ -1,13 +1,15 @@ defmodule Atlas.Events.EventCategory do use Atlas.Schema - @required_fields ~w(name color)a + @types ~w(optional mandatory)a + @required_fields ~w(name color type)a @optional_fields ~w(course_id)a schema "event_categories" do field :name, :string field :color, :string + field :type, Ecto.Enum, values: @types belongs_to :course, Atlas.University.Degrees.Courses.Course diff --git a/lib/atlas_web/controllers/event_category_json.ex b/lib/atlas_web/controllers/event_category_json.ex index d3683f6..0e7afd2 100644 --- a/lib/atlas_web/controllers/event_category_json.ex +++ b/lib/atlas_web/controllers/event_category_json.ex @@ -26,7 +26,8 @@ defmodule AtlasWeb.EventCategoryJSON do CourseJSON.data(event_category.course) else nil - end + end, + type: event_category.type } end end diff --git a/lib/atlas_web/controllers/event_controller.ex b/lib/atlas_web/controllers/event_controller.ex index 8a36254..b10eb50 100644 --- a/lib/atlas_web/controllers/event_controller.ex +++ b/lib/atlas_web/controllers/event_controller.ex @@ -11,6 +11,13 @@ defmodule AtlasWeb.EventController do render(conn, :index, events: events) end + def selected_index(conn, _params) do + {user, _session} = Guardian.Plug.current_resource(conn) + + events = Events.list_events_by_user(user.id) + render(conn, :index, events: events) + end + def create(conn, %{"event" => event_params}) do with {:ok, %Event{} = event} <- Events.create_event(event_params) do conn diff --git a/lib/atlas_web/router.ex b/lib/atlas_web/router.ex index f080e93..4e294ce 100644 --- a/lib/atlas_web/router.ex +++ b/lib/atlas_web/router.ex @@ -90,6 +90,7 @@ defmodule AtlasWeb.Router do end scope "/events" do + get "/selected", EventController, :selected_index resources "/", EventController, only: [:index, :show] pipe_through :is_at_least_professor diff --git a/priv/repo/migrations/20250928185617_create_event_categories.exs b/priv/repo/migrations/20250928185617_create_event_categories.exs index a31bedb..b201e3b 100644 --- a/priv/repo/migrations/20250928185617_create_event_categories.exs +++ b/priv/repo/migrations/20250928185617_create_event_categories.exs @@ -6,6 +6,7 @@ defmodule Atlas.Repo.Migrations.CreateEventCategories do add :id, :binary_id, primary_key: true add :name, :string add :color, :string + add :type, :string add :course_id, references(:courses, on_delete: :nothing, type: :binary_id) timestamps(type: :utc_datetime) From 2dfdff6d38765d96a39a34ac28c8336d88429e64 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Tue, 21 Oct 2025 11:38:48 +0100 Subject: [PATCH 08/10] fix: tests and extract function --- lib/atlas/events.ex | 25 ++++++-- lib/atlas/events/event.ex | 3 + lib/atlas/events/event_category.ex | 3 + lib/atlas/events/user_event_category.ex | 3 + test/atlas/events_test.exs | 41 +++++++++---- .../event_category_controller_test.exs | 40 +++++++------ .../controllers/event_controller_test.exs | 59 +++++++++++-------- test/support/fixtures/events_fixtures.ex | 22 ++++--- 8 files changed, 129 insertions(+), 67 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index 2809e9f..27a9380 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -66,16 +66,29 @@ defmodule Atlas.Events do defp insert_user_event_categories(repo, user_id, category_ids) do category_ids |> Enum.reduce_while({:ok, []}, fn category_id, {:ok, acc} -> - %UserEventCategory{} - |> UserEventCategory.changeset(%{user_id: user_id, event_category_id: category_id}) - |> repo.insert() - |> case do - {:ok, user_event_category} -> {:cont, {:ok, [user_event_category | acc]}} - {:error, changeset} -> {:halt, {:error, changeset}} + case repo.get(EventCategory, category_id) do + nil -> + {:halt, {:error, "EventCategory not found: #{category_id}"}} + + %EventCategory{type: :mandatory} -> + {:cont, {:ok, acc}} + + _category -> + insert_user_event_category(repo, user_id, category_id, acc) end end) end + defp insert_user_event_category(repo, user_id, category_id, acc) do + %UserEventCategory{} + |> UserEventCategory.changeset(%{user_id: user_id, event_category_id: category_id}) + |> repo.insert() + |> case do + {:ok, user_event_category} -> {:cont, {:ok, [user_event_category | acc]}} + {:error, changeset} -> {:halt, {:error, changeset}} + end + end + @doc """ Gets a single event_category. diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index ed10c19..c4921a9 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -1,4 +1,7 @@ defmodule Atlas.Events.Event do + @moduledoc """ + Event schema. + """ use Atlas.Schema @required_fields ~w(title start end place link category_id)a diff --git a/lib/atlas/events/event_category.ex b/lib/atlas/events/event_category.ex index cd19d43..4bf3ea9 100644 --- a/lib/atlas/events/event_category.ex +++ b/lib/atlas/events/event_category.ex @@ -1,4 +1,7 @@ defmodule Atlas.Events.EventCategory do + @moduledoc """ + Event category schema. + """ use Atlas.Schema @types ~w(optional mandatory)a diff --git a/lib/atlas/events/user_event_category.ex b/lib/atlas/events/user_event_category.ex index e5748b5..754a8e2 100644 --- a/lib/atlas/events/user_event_category.ex +++ b/lib/atlas/events/user_event_category.ex @@ -1,4 +1,7 @@ defmodule Atlas.Events.UserEventCategory do + @moduledoc """ + User event category schema. + """ use Atlas.Schema @required_fields ~w(user_id event_category_id)a diff --git a/test/atlas/events_test.exs b/test/atlas/events_test.exs index b091a3f..3afd347 100644 --- a/test/atlas/events_test.exs +++ b/test/atlas/events_test.exs @@ -8,7 +8,7 @@ defmodule Atlas.EventsTest do import Atlas.EventsFixtures - @invalid_attrs %{name: nil, color: nil} + @invalid_attrs %{name: nil, color: nil, type: nil} test "list_event_categories/0 returns all event_categories" do event_category = event_category_fixture() @@ -21,7 +21,7 @@ defmodule Atlas.EventsTest do end test "create_event_category/1 with valid data creates a event_category" do - valid_attrs = %{name: "some name", color: "some color"} + valid_attrs = %{name: "some name", color: "some color", type: "optional"} assert {:ok, %EventCategory{} = event_category} = Events.create_event_category(valid_attrs) assert event_category.name == "some name" @@ -36,14 +36,19 @@ defmodule Atlas.EventsTest do event_category = event_category_fixture() update_attrs = %{name: "some updated name", color: "some updated color"} - assert {:ok, %EventCategory{} = event_category} = Events.update_event_category(event_category, update_attrs) + assert {:ok, %EventCategory{} = event_category} = + Events.update_event_category(event_category, update_attrs) + assert event_category.name == "some updated name" assert event_category.color == "some updated color" end test "update_event_category/2 with invalid data returns error changeset" do event_category = event_category_fixture() - assert {:error, %Ecto.Changeset{}} = Events.update_event_category(event_category, @invalid_attrs) + + assert {:error, %Ecto.Changeset{}} = + Events.update_event_category(event_category, @invalid_attrs) + assert event_category == Events.get_event_category!(event_category.id) end @@ -77,13 +82,22 @@ defmodule Atlas.EventsTest do end test "create_event/1 with valid data creates a event" do - valid_attrs = %{start: ~T[14:00:00], link: "some link", title: "some title", end: ~T[14:00:00], place: "some place"} + category = event_category_fixture() + + valid_attrs = %{ + start: ~U[2025-01-01 14:00:00Z], + link: "some link", + title: "some title", + end: ~U[2025-01-01 14:00:00Z], + place: "some place", + category_id: category.id + } assert {:ok, %Event{} = event} = Events.create_event(valid_attrs) - assert event.start == ~T[14:00:00] + assert event.start == ~U[2025-01-01 14:00:00Z] assert event.link == "some link" assert event.title == "some title" - assert event.end == ~T[14:00:00] + assert event.end == ~U[2025-01-01 14:00:00Z] assert event.place == "some place" end @@ -93,13 +107,20 @@ defmodule Atlas.EventsTest do test "update_event/2 with valid data updates the event" do event = event_fixture() - update_attrs = %{start: ~T[15:01:01], link: "some updated link", title: "some updated title", end: ~T[15:01:01], place: "some updated place"} + + update_attrs = %{ + start: ~U[2025-01-01 15:01:01Z], + link: "some updated link", + title: "some updated title", + end: ~U[2025-01-01 15:01:01Z], + place: "some updated place" + } assert {:ok, %Event{} = event} = Events.update_event(event, update_attrs) - assert event.start == ~T[15:01:01] + assert event.start == ~U[2025-01-01 15:01:01Z] assert event.link == "some updated link" assert event.title == "some updated title" - assert event.end == ~T[15:01:01] + assert event.end == ~U[2025-01-01 15:01:01Z] assert event.place == "some updated place" end diff --git a/test/atlas_web/controllers/event_category_controller_test.exs b/test/atlas_web/controllers/event_category_controller_test.exs index 2fd6a11..6a9edcf 100644 --- a/test/atlas_web/controllers/event_category_controller_test.exs +++ b/test/atlas_web/controllers/event_category_controller_test.exs @@ -13,35 +13,38 @@ defmodule AtlasWeb.EventCategoryControllerTest do name: "some updated name", color: "some updated color" } - @invalid_attrs %{name: nil, color: nil} + @create_attrs Map.put(@create_attrs, :type, "optional") + @update_attrs Map.put(@update_attrs, :type, "optional") + @invalid_attrs %{name: nil, color: nil, type: nil} - setup %{conn: conn} do + setup %{conn: _conn} do + conn = AtlasWeb.ConnCase.authenticated_conn(%{type: :professor}) {:ok, conn: put_req_header(conn, "accept", "application/json")} end describe "index" do test "lists all event_categories", %{conn: conn} do - conn = get(conn, ~p"/api/event_categories") - assert json_response(conn, 200)["data"] == [] + conn = get(conn, ~p"/v1/event_categories") + assert json_response(conn, 200)["event_categories"] == [] end end describe "create event_category" do test "renders event_category when data is valid", %{conn: conn} do - conn = post(conn, ~p"/api/event_categories", event_category: @create_attrs) - assert %{"id" => id} = json_response(conn, 201)["data"] + conn = post(conn, ~p"/v1/event_categories", event_category: @create_attrs) + assert %{"id" => id} = json_response(conn, 201)["event_category"] - conn = get(conn, ~p"/api/event_categories/#{id}") + conn = get(conn, ~p"/v1/event_categories/#{id}") assert %{ "id" => ^id, "color" => "some color", "name" => "some name" - } = json_response(conn, 200)["data"] + } = json_response(conn, 200)["event_category"] end test "renders errors when data is invalid", %{conn: conn} do - conn = post(conn, ~p"/api/event_categories", event_category: @invalid_attrs) + conn = post(conn, ~p"/v1/event_categories", event_category: @invalid_attrs) assert json_response(conn, 422)["errors"] != %{} end end @@ -49,21 +52,24 @@ defmodule AtlasWeb.EventCategoryControllerTest do describe "update event_category" do setup [:create_event_category] - test "renders event_category when data is valid", %{conn: conn, event_category: %EventCategory{id: id} = event_category} do - conn = put(conn, ~p"/api/event_categories/#{event_category}", event_category: @update_attrs) - assert %{"id" => ^id} = json_response(conn, 200)["data"] + test "renders event_category when data is valid", %{ + conn: conn, + event_category: %EventCategory{id: id} = event_category + } do + conn = put(conn, ~p"/v1/event_categories/#{event_category}", event_category: @update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["event_category"] - conn = get(conn, ~p"/api/event_categories/#{id}") + conn = get(conn, ~p"/v1/event_categories/#{id}") assert %{ "id" => ^id, "color" => "some updated color", "name" => "some updated name" - } = json_response(conn, 200)["data"] + } = json_response(conn, 200)["event_category"] end test "renders errors when data is invalid", %{conn: conn, event_category: event_category} do - conn = put(conn, ~p"/api/event_categories/#{event_category}", event_category: @invalid_attrs) + conn = put(conn, ~p"/v1/event_categories/#{event_category}", event_category: @invalid_attrs) assert json_response(conn, 422)["errors"] != %{} end end @@ -72,11 +78,11 @@ defmodule AtlasWeb.EventCategoryControllerTest do setup [:create_event_category] test "deletes chosen event_category", %{conn: conn, event_category: event_category} do - conn = delete(conn, ~p"/api/event_categories/#{event_category}") + conn = delete(conn, ~p"/v1/event_categories/#{event_category}") assert response(conn, 204) assert_error_sent 404, fn -> - get(conn, ~p"/api/event_categories/#{event_category}") + get(conn, ~p"/v1/event_categories/#{event_category}") end end end diff --git a/test/atlas_web/controllers/event_controller_test.exs b/test/atlas_web/controllers/event_controller_test.exs index 662a05b..86987e5 100644 --- a/test/atlas_web/controllers/event_controller_test.exs +++ b/test/atlas_web/controllers/event_controller_test.exs @@ -6,51 +6,58 @@ defmodule AtlasWeb.EventControllerTest do alias Atlas.Events.Event @create_attrs %{ - start: ~T[14:00:00], + start: "2025-01-01T14:00:00Z", link: "some link", title: "some title", - end: ~T[14:00:00], + end: "2025-01-01T14:00:00Z", place: "some place" } @update_attrs %{ - start: ~T[15:01:01], + start: "2025-01-01T15:01:01Z", link: "some updated link", title: "some updated title", - end: ~T[15:01:01], + end: "2025-01-01T15:01:01Z", place: "some updated place" } @invalid_attrs %{start: nil, link: nil, title: nil, end: nil, place: nil} - setup %{conn: conn} do - {:ok, conn: put_req_header(conn, "accept", "application/json")} + setup %{conn: _conn} do + conn = AtlasWeb.ConnCase.authenticated_conn(%{type: :professor}) + category = event_category_fixture() + + create_attrs = Map.put(@create_attrs, :category_id, category.id) + update_attrs = Map.put(@update_attrs, :category_id, category.id) + + {:ok, conn: put_req_header(conn, "accept", "application/json"), + create_attrs: create_attrs, update_attrs: update_attrs} end describe "index" do test "lists all events", %{conn: conn} do - conn = get(conn, ~p"/api/events") - assert json_response(conn, 200)["data"] == [] + conn = get(conn, ~p"/v1/events") + assert json_response(conn, 200)["events"] == [] end end describe "create event" do - test "renders event when data is valid", %{conn: conn} do - conn = post(conn, ~p"/api/events", event: @create_attrs) - assert %{"id" => id} = json_response(conn, 201)["data"] + test "renders event when data is valid", %{conn: conn, create_attrs: create_attrs} do + conn = post(conn, ~p"/v1/events", event: create_attrs) + assert %{"id" => id} = json_response(conn, 201)["event"] - conn = get(conn, ~p"/api/events/#{id}") + conn = get(conn, ~p"/v1/events/#{id}") assert %{ "id" => ^id, - "end" => "14:00:00", + "end" => "2025-01-01T14:00:00Z", "link" => "some link", "place" => "some place", - "start" => "14:00:00", + "start" => "2025-01-01T14:00:00Z", "title" => "some title" - } = json_response(conn, 200)["data"] + } = json_response(conn, 200)["event"] end test "renders errors when data is invalid", %{conn: conn} do - conn = post(conn, ~p"/api/events", event: @invalid_attrs) + conn = post(conn, ~p"/v1/events", event: @invalid_attrs) assert json_response(conn, 422)["errors"] != %{} end end @@ -58,24 +65,24 @@ defmodule AtlasWeb.EventControllerTest do describe "update event" do setup [:create_event] - test "renders event when data is valid", %{conn: conn, event: %Event{id: id} = event} do - conn = put(conn, ~p"/api/events/#{event}", event: @update_attrs) - assert %{"id" => ^id} = json_response(conn, 200)["data"] + test "renders event when data is valid", %{conn: conn, event: %Event{id: id} = event, update_attrs: update_attrs} do + conn = put(conn, ~p"/v1/events/#{event}", event: update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["event"] - conn = get(conn, ~p"/api/events/#{id}") + conn = get(conn, ~p"/v1/events/#{id}") assert %{ "id" => ^id, - "end" => "15:01:01", + "end" => "2025-01-01T15:01:01Z", "link" => "some updated link", "place" => "some updated place", - "start" => "15:01:01", + "start" => "2025-01-01T15:01:01Z", "title" => "some updated title" - } = json_response(conn, 200)["data"] + } = json_response(conn, 200)["event"] end test "renders errors when data is invalid", %{conn: conn, event: event} do - conn = put(conn, ~p"/api/events/#{event}", event: @invalid_attrs) + conn = put(conn, ~p"/v1/events/#{event}", event: @invalid_attrs) assert json_response(conn, 422)["errors"] != %{} end end @@ -84,11 +91,11 @@ defmodule AtlasWeb.EventControllerTest do setup [:create_event] test "deletes chosen event", %{conn: conn, event: event} do - conn = delete(conn, ~p"/api/events/#{event}") + conn = delete(conn, ~p"/v1/events/#{event}") assert response(conn, 204) assert_error_sent 404, fn -> - get(conn, ~p"/api/events/#{event}") + get(conn, ~p"/v1/events/#{event}") end end end diff --git a/test/support/fixtures/events_fixtures.ex b/test/support/fixtures/events_fixtures.ex index 6868d2e..ff734dc 100644 --- a/test/support/fixtures/events_fixtures.ex +++ b/test/support/fixtures/events_fixtures.ex @@ -12,7 +12,8 @@ defmodule Atlas.EventsFixtures do attrs |> Enum.into(%{ color: "some color", - name: "some name" + name: "some name", + type: "optional" }) |> Atlas.Events.create_event_category() @@ -23,15 +24,20 @@ defmodule Atlas.EventsFixtures do Generate a event. """ def event_fixture(attrs \\ %{}) do + category = event_category_fixture() + + defaults = %{ + end: ~N[2025-01-01 14:00:00], + link: "some link", + place: "some place", + start: ~N[2025-01-01 14:00:00], + title: "some title", + category_id: category.id + } + {:ok, event} = attrs - |> Enum.into(%{ - end: ~T[14:00:00], - link: "some link", - place: "some place", - start: ~T[14:00:00], - title: "some title" - }) + |> Enum.into(defaults) |> Atlas.Events.create_event() event From c4aebf827d3843de680f027406d471f4cccc43f6 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Tue, 21 Oct 2025 11:42:57 +0100 Subject: [PATCH 09/10] docs: add missing docs to function --- lib/atlas/events.ex | 8 ++++++++ test/atlas/events_test.exs | 16 ++++++++-------- .../controllers/event_controller_test.exs | 18 ++++++++++++------ 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/atlas/events.ex b/lib/atlas/events.ex index 27a9380..09aa94b 100644 --- a/lib/atlas/events.ex +++ b/lib/atlas/events.ex @@ -201,6 +201,14 @@ defmodule Atlas.Events do |> Repo.all() end + @doc """ + Returns the list of events for a specific user. + + ## Examples + + iex> list_events_by_user(user_id) + [%Event{}, ...] + """ def list_events_by_user(user_id) do Event |> join(:inner, [e], ec in assoc(e, :category)) diff --git a/test/atlas/events_test.exs b/test/atlas/events_test.exs index 3afd347..7551f9f 100644 --- a/test/atlas/events_test.exs +++ b/test/atlas/events_test.exs @@ -8,7 +8,7 @@ defmodule Atlas.EventsTest do import Atlas.EventsFixtures - @invalid_attrs %{name: nil, color: nil, type: nil} + @invalid_attrs %{name: nil, color: nil, type: nil} test "list_event_categories/0 returns all event_categories" do event_category = event_category_fixture() @@ -21,7 +21,7 @@ defmodule Atlas.EventsTest do end test "create_event_category/1 with valid data creates a event_category" do - valid_attrs = %{name: "some name", color: "some color", type: "optional"} + valid_attrs = %{name: "some name", color: "some color", type: "optional"} assert {:ok, %EventCategory{} = event_category} = Events.create_event_category(valid_attrs) assert event_category.name == "some name" @@ -94,10 +94,10 @@ defmodule Atlas.EventsTest do } assert {:ok, %Event{} = event} = Events.create_event(valid_attrs) - assert event.start == ~U[2025-01-01 14:00:00Z] + assert event.start == ~U[2025-01-01 14:00:00Z] assert event.link == "some link" assert event.title == "some title" - assert event.end == ~U[2025-01-01 14:00:00Z] + assert event.end == ~U[2025-01-01 14:00:00Z] assert event.place == "some place" end @@ -109,18 +109,18 @@ defmodule Atlas.EventsTest do event = event_fixture() update_attrs = %{ - start: ~U[2025-01-01 15:01:01Z], + start: ~U[2025-01-01 15:01:01Z], link: "some updated link", title: "some updated title", - end: ~U[2025-01-01 15:01:01Z], + end: ~U[2025-01-01 15:01:01Z], place: "some updated place" } assert {:ok, %Event{} = event} = Events.update_event(event, update_attrs) - assert event.start == ~U[2025-01-01 15:01:01Z] + assert event.start == ~U[2025-01-01 15:01:01Z] assert event.link == "some updated link" assert event.title == "some updated title" - assert event.end == ~U[2025-01-01 15:01:01Z] + assert event.end == ~U[2025-01-01 15:01:01Z] assert event.place == "some updated place" end diff --git a/test/atlas_web/controllers/event_controller_test.exs b/test/atlas_web/controllers/event_controller_test.exs index 86987e5..e36a6bd 100644 --- a/test/atlas_web/controllers/event_controller_test.exs +++ b/test/atlas_web/controllers/event_controller_test.exs @@ -28,8 +28,10 @@ defmodule AtlasWeb.EventControllerTest do create_attrs = Map.put(@create_attrs, :category_id, category.id) update_attrs = Map.put(@update_attrs, :category_id, category.id) - {:ok, conn: put_req_header(conn, "accept", "application/json"), - create_attrs: create_attrs, update_attrs: update_attrs} + {:ok, + conn: put_req_header(conn, "accept", "application/json"), + create_attrs: create_attrs, + update_attrs: update_attrs} end describe "index" do @@ -57,7 +59,7 @@ defmodule AtlasWeb.EventControllerTest do end test "renders errors when data is invalid", %{conn: conn} do - conn = post(conn, ~p"/v1/events", event: @invalid_attrs) + conn = post(conn, ~p"/v1/events", event: @invalid_attrs) assert json_response(conn, 422)["errors"] != %{} end end @@ -65,9 +67,13 @@ defmodule AtlasWeb.EventControllerTest do describe "update event" do setup [:create_event] - test "renders event when data is valid", %{conn: conn, event: %Event{id: id} = event, update_attrs: update_attrs} do - conn = put(conn, ~p"/v1/events/#{event}", event: update_attrs) - assert %{"id" => ^id} = json_response(conn, 200)["event"] + test "renders event when data is valid", %{ + conn: conn, + event: %Event{id: id} = event, + update_attrs: update_attrs + } do + conn = put(conn, ~p"/v1/events/#{event}", event: update_attrs) + assert %{"id" => ^id} = json_response(conn, 200)["event"] conn = get(conn, ~p"/v1/events/#{id}") From 490d4137c40b3ff6e9047dc624e0db906fcbfa62 Mon Sep 17 00:00:00 2001 From: Nuno Miguel Date: Tue, 21 Oct 2025 13:12:19 +0100 Subject: [PATCH 10/10] refactor: change attribute order --- lib/atlas/events/event.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/atlas/events/event.ex b/lib/atlas/events/event.ex index c4921a9..32dec95 100644 --- a/lib/atlas/events/event.ex +++ b/lib/atlas/events/event.ex @@ -8,9 +8,9 @@ defmodule Atlas.Events.Event do schema "events" do field :start, :utc_datetime + field :end, :utc_datetime field :link, :string field :title, :string - field :end, :utc_datetime field :place, :string belongs_to :category, Atlas.Events.EventCategory