From 40d69021268ed8162c231e890e26013de2690b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Rodrigues?= Date: Sat, 5 Aug 2023 05:05:50 +0100 Subject: [PATCH] Add proper documentation --- lib/atomic/accounts.ex | 75 +++++++- lib/atomic/accounts/course.ex | 4 + lib/atomic/accounts/user.ex | 3 + lib/atomic/activities.ex | 160 +++++++++++++----- lib/atomic/departments.ex | 11 ++ lib/atomic/organizations.ex | 78 ++++++--- lib/atomic/partnerships.ex | 6 - lib/atomic/partnerships/partner.ex | 5 - lib/atomic/time.ex | 3 + lib/atomic_web/live/activity_live/index.ex | 2 +- lib/atomic_web/live/activity_live/show.ex | 4 +- lib/atomic_web/live/department_live/show.ex | 6 +- lib/atomic_web/live/membership_live/index.ex | 2 +- .../live/partner_live/form_component.ex | 2 +- .../live/user_live/form_component.ex | 2 +- lib/atomic_web/views/helpers.ex | 5 +- 16 files changed, 278 insertions(+), 90 deletions(-) diff --git a/lib/atomic/accounts.ex b/lib/atomic/accounts.ex index 9d5f46563..9f624e2e3 100644 --- a/lib/atomic/accounts.ex +++ b/lib/atomic/accounts.ex @@ -80,11 +80,36 @@ defmodule Atomic.Accounts do |> Repo.insert() end + @doc """ + List all users. + + ## Examples + + iex > list_users() + {:ok, [%User{}]} + + iex > list_users() + {:error, %Ecto.Changeset{}} + + """ def list_users do User |> Repo.all() end + @doc """ + Gets a course by id. + + Raises `Ecto.NoResultsError` if the Course does not exist. + + ## Examples + + iex> get_course(123) + %Course{} + + iex> get_course(456) + ** (Ecto.NoResultsError) + """ def get_course(id) do Repo.get(Course, id) end @@ -417,29 +442,59 @@ defmodule Atomic.Accounts do end end + @doc """ + Updates the user picture. + + ## Examples + + iex> update_user_picture(user, %{profile_picture: ...}) + {:ok, %User{}} + + iex> update_user_picture(user, %{profile_picture: ...}) + {:error, %Ecto.Changeset{}} + + """ def update_user_picture(%User{} = user, attrs \\ %{}) do user |> User.picture_changeset(attrs) |> Repo.update() end + @doc """ + Updates the user. + + ## Examples + + iex> update_user(user, %{field: value}) + {:ok, %User{}} + + iex> update_user(user, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ def update_user(%User{} = user, attrs \\ %{}) do user |> User.changeset(attrs) |> Repo.update() end - def change_user(%User{} = user, attrs \\ %{}) do - user - |> User.changeset(attrs) - end + @doc """ + Gets a list of courses. + + ## Examples + iex> list_courses() + {:ok,[%Course{}]} + + iex> list_courses() + {:error, %Ecto.Changeset{}} + """ def list_courses do Repo.all(Course) end @doc """ - Creates a course + Creates a course. ## Examples @@ -457,7 +512,15 @@ defmodule Atomic.Accounts do end @doc """ - Gets the user's organizations + Gets an user organizations. + + ## Examples + + iex> get_user_organizations(user) + {:ok,[%Organization{}]} + + iex> get_user_organizations(user) + {:error, %Ecto.Changeset{}} """ def get_user_organizations(user) do Repo.all(Ecto.assoc(user, :organizations)) diff --git a/lib/atomic/accounts/course.ex b/lib/atomic/accounts/course.ex index 0ad203b06..d355037fe 100644 --- a/lib/atomic/accounts/course.ex +++ b/lib/atomic/accounts/course.ex @@ -15,6 +15,10 @@ defmodule Atomic.Accounts.Course do timestamps() end + @doc """ + A changeset for a course. + + """ def changeset(course, attrs) do course |> cast(attrs, @required_fields) diff --git a/lib/atomic/accounts/user.ex b/lib/atomic/accounts/user.ex index bce8add2c..270c05804 100644 --- a/lib/atomic/accounts/user.ex +++ b/lib/atomic/accounts/user.ex @@ -63,6 +63,9 @@ defmodule Atomic.Accounts.User do |> cast_attachments(attrs, [:profile_picture]) end + @doc """ + A user changeset for updating the user. + """ def changeset(user, attrs) do user |> cast(attrs, @required_fields ++ @optional_fields) diff --git a/lib/atomic/activities.ex b/lib/atomic/activities.ex index 33020b2b5..23619e52c 100644 --- a/lib/atomic/activities.ex +++ b/lib/atomic/activities.ex @@ -6,6 +6,9 @@ defmodule Atomic.Activities do alias Atomic.Accounts.User alias Atomic.Activities.Activity + alias Atomic.Activities.Enrollment + alias Atomic.Activities.Session + alias Atomic.Activities.Speaker @doc """ Returns the list of activities. @@ -22,6 +25,17 @@ defmodule Atomic.Activities do |> Repo.all() end + @doc """ + Returns the list of activities belonging to an organization. + + ## Examples + + iex> list_activities_by_organization_id(99d7c9e5-4212-4f59-a097-28aaa33c2621, opts) + [%Activity{}, ...] + + iex> list_activities_by_organization_id(99d7c9e5-4212-4f59-a097-28aaa33c2621, opts) + ** (Ecto.NoResultsError) + """ def list_activities_by_organization_id(organization_id, opts) when is_list(opts) do Activity |> apply_filters(opts) @@ -31,8 +45,17 @@ defmodule Atomic.Activities do |> Repo.all() end - alias Atomic.Activities.Session + @doc """ + Returns the list of activity sessions starting and ending between two given dates. + + ## Examples + + iex> list_sessions_from_to(~N[2020-01-01 00:00:00], ~N[2020-01-31 23:59:59], opts) + [%Session{}, ...] + iex> list_sessions_from_to(~N[2024-01-01 00:00:00], ~N[2024-01-31 23:59:59], opts) + ** (Ecto.NoResultsError) + """ def list_sessions_from_to(start, finish, opts) do from(s in Session, join: a in Activity, @@ -63,22 +86,37 @@ defmodule Atomic.Activities do |> Repo.preload(preloads) end - def get_activity_organizations!(activity, _preloads \\ []) do - departments = Map.get(activity, :departments, []) + @doc """ + Returns the list of organizations ids that are associated with an activity. + + ## Examples + + iex> get_activity_organizations!(activity) + [19d7c9e5-4212-4f59-a097-28aaa33c2621, ...] - Enum.map(departments, & &1.organization_id) + iex> get_activity_organizations!(activity) + ** (Ecto.NoResultsError) + """ + def get_activity_organizations!(activity, _preloads \\ []) do + Map.get(activity, :departments, []) + |> Enum.map(& &1.organization_id) end - alias Atomic.Activities.Enrollment + @doc """ + Verifies if an user is enrolled in an activity. + + ## Examples + iex> is_participating?(activity, user) + true + + iex> is_participating?(activity, user) + false + """ def is_participating?(activity_id, user_id) do Enrollment |> where(activity_id: ^activity_id, user_id: ^user_id) - |> Repo.one() - |> case do - nil -> false - _ -> true - end + |> Repo.exists?() end @doc """ @@ -146,8 +184,6 @@ defmodule Atomic.Activities do Activity.changeset(activity, attrs) end - alias Atomic.Activities.Session - @doc """ Returns the list of sessions. @@ -242,8 +278,6 @@ defmodule Atomic.Activities do Session.changeset(session, attrs) end - alias Atomic.Activities.Enrollment - @doc """ Returns the list of enrollments. @@ -279,29 +313,53 @@ defmodule Atomic.Activities do |> Repo.one() end - def get_user_enrolled(user, activity) do - enrollment = - Enrollment - |> where(user_id: ^user.id, activity_id: ^activity.id) - |> Repo.one() + @doc """ + Gets the user enrolled in an given activity. + + ## Examples - case enrollment do + iex> get_user_enrolled(user, activity) + %Enrollment{} + + iex> get_user_enrolled(user, activity) + ** (Ecto.NoResultsError) + """ + def get_user_enrolled(user, activity) do + Enrollment + |> where(user_id: ^user.id, activity_id: ^activity.id) + |> Repo.one() + |> case do nil -> create_enrollment(activity, user) - _ -> enrollment + enrollment -> enrollment end end - def get_user_enrollments(user) do + @doc """ + Gets all user enrollments. + + ## Examples + + iex> get_user_enrollments(user) + [%Enrollment{}, ...] + + iex> get_user_enrollments(user) + ** (Ecto.NoResultsError) + """ + def get_user_enrollments(user_id) do Enrollment - |> where(user_id: ^user.id) + |> where(user_id: ^user_id) |> Repo.all() end - def get_user_activities(user) do - enrollments = get_user_enrollments(user) - activities = for enrollment <- enrollments, do: get_activity!(enrollment.activity_id) + def get_user_activities(user_id) do + activities_ids = + get_user_enrollments(user_id) + |> Enum.map(& &1.activity_id) - activities |> Repo.preload([:enrollments, :activity_sessions, :speakers]) + for activity_id <- activities_ids, + do: + get_activity!(activity_id) + |> Repo.preload([:enrollments, :activity_sessions, :speakers]) end @doc """ @@ -344,13 +402,6 @@ defmodule Atomic.Activities do |> Repo.update() end - def is_user_enrolled?(%Activity{} = activity, %User{} = user) do - Repo.one( - from e in Enrollment, - where: e.user_id == ^user.id and e.activity_id == ^activity.id - ) - end - @doc """ Deletes a enrollment. @@ -371,6 +422,17 @@ defmodule Atomic.Activities do |> broadcast(:deleted_enrollment) end + @doc """ + Returns the total number of enrolled users in an activity. + + ## Examples + + iex> get_total_enrolled(activity) + 10 + + iex> get_total_enrolled(activity) + 0 + """ def get_total_enrolled(%Activity{} = activity) do Enrollment |> where(activity_id: ^activity.id) @@ -390,13 +452,18 @@ defmodule Atomic.Activities do Enrollment.changeset(enrollment, attrs) end - def enrolled?(%Activity{} = activity, %User{} = user) do - Repo.one( - from e in Enrollment, - where: e.user_id == ^user.id and e.activtiy_id == ^activity.id - ) - end + @doc """ + Broadcasts an event to the pubsub. + + ## Examples + + iex> broadcast(:new_enrollment, enrollment) + {:ok, %Enrollment{}} + iex> broadcast(:deleted_enrollment, nil) + {:ok, nil} + + """ def subscribe(topic) when topic in ["new_enrollment", "deleted_enrollment"] do Phoenix.PubSub.subscribe(Atomic.PubSub, topic) end @@ -415,8 +482,6 @@ defmodule Atomic.Activities do {number, nil} end - alias Atomic.Activities.Speaker - @doc """ Returns the list of speakers. @@ -443,6 +508,17 @@ defmodule Atomic.Activities do Repo.all(from s in Speaker, where: s.organization_id == ^id) end + @doc """ + Returns the list of speakers in a list of ids. + + ## Examples + + iex> get_speakers([1, 2, 3]) + [%Speaker{}, ...] + + iex> get_speakers([1, 2, 3]) + [] + """ def get_speakers(nil), do: [] def get_speakers(ids) do diff --git a/lib/atomic/departments.ex b/lib/atomic/departments.ex index 024435cb4..7ce49131f 100644 --- a/lib/atomic/departments.ex +++ b/lib/atomic/departments.ex @@ -34,6 +34,17 @@ defmodule Atomic.Departments do Repo.all(from d in Department, where: d.organization_id == ^id) end + @doc """ + Returns the list of departments in a list of given ids. + + ## Examples + + iex> get_departments([99d7c9e5-4212-4f59-a097-28aaa33c2621, 99d7c9e5-4212-4f59-a097-28aaa33c2621]) + [%Department{}, ...] + + iex> get_departments(nil) + [] + """ def get_departments(nil), do: [] def get_departments(ids) do diff --git a/lib/atomic/organizations.ex b/lib/atomic/organizations.ex index 940f9ae84..b036372a5 100644 --- a/lib/atomic/organizations.ex +++ b/lib/atomic/organizations.ex @@ -47,7 +47,7 @@ defmodule Atomic.Organizations do end @doc """ - Creates a organization. + Creates an organization. ## Examples @@ -65,7 +65,7 @@ defmodule Atomic.Organizations do end @doc """ - Updates a organization. + Updates an organization. ## Examples @@ -82,6 +82,18 @@ defmodule Atomic.Organizations do |> Repo.update() end + @doc """ + Updates an organization card image. + + ## Examples + + iex> update_card_image(organization, %{card_image: new_value}) + {:ok, %Organization{}} + + iex> update_card_image(organization, %{card_image: bad_value}) + {:error, %Ecto.Changeset{}} + + """ def update_card_image(%Organization{} = organization, attrs) do organization |> Organization.card_changeset(attrs) @@ -151,7 +163,7 @@ defmodule Atomic.Organizations do def list_memberships(%{"organization_id" => organization_id}, preloads) do Membership - |> where([a], a.organization_id == ^organization_id) + |> where([a], a.organization_id == ^organization_id and a.role != :follower) |> Repo.all() |> Repo.preload(preloads) end @@ -163,21 +175,52 @@ defmodule Atomic.Organizations do |> Repo.all() end + @doc """ + Verifies if an user is a member of an organization. + + ## Examples + + iex> is_member_of?(user, organization) + true + + iex> is_member_of?(user, organization) + false + + """ def is_member_of?(%User{} = user, %Organization{} = organization) do Membership |> where([m], m.user_id == ^user.id and m.organization_id == ^organization.id) |> Repo.exists?() end - def get_role(user_id, organization_id) do - membership = - Membership - |> where([m], m.user_id == ^user_id and m.organization_id == ^organization_id) - |> Repo.one() + @doc """ + Gets an user role in an organization. - case membership do + ## Examples + + iex> get_role(user_id, organization_id) + :follower + + iex> get_role(user_id, organization_id) + :member + + iex> get_role(user_id, organization_id) + :admin + + iex> get_role(user_id, organization_id) + :owner + + iex> get_role(user_id, organization_id) + nil + + """ + def get_role(user_id, organization_id) do + Membership + |> where([m], m.user_id == ^user_id and m.organization_id == ^organization_id) + |> Repo.one() + |> case do nil -> nil - _ -> membership.role + membership -> membership.role end end @@ -260,8 +303,8 @@ defmodule Atomic.Organizations do """ def roles_less_than_or_equal(role) do - list = [:follower, :member, :admin, :owner] - Enum.drop_while(list, fn elem -> elem != role end) + [:follower, :member, :admin, :owner] + |> Enum.drop_while(fn elem -> elem != role end) end @doc """ @@ -274,8 +317,8 @@ defmodule Atomic.Organizations do """ def roles_bigger_than_or_equal(role) do - list = [:follower, :member, :admin, :owner] - Enum.drop_while(list, fn elem -> elem != role end) + [:follower, :member, :admin, :owner] + |> Enum.drop_while(fn elem -> elem != role end) end @doc """ @@ -316,13 +359,6 @@ defmodule Atomic.Organizations do |> Repo.preload(preloads) end - def get_membership_role!(user_id, organization_id) do - case Repo.get_by(Membership, user_id: user_id, organization_id: organization_id) do - nil -> nil - membership -> membership.role - end - end - @doc """ Creates an user organization. diff --git a/lib/atomic/partnerships.ex b/lib/atomic/partnerships.ex index 0b9521db6..645015ba8 100644 --- a/lib/atomic/partnerships.ex +++ b/lib/atomic/partnerships.ex @@ -114,10 +114,4 @@ defmodule Atomic.Partnerships do def change_partner(%Partner{} = partner, attrs \\ %{}) do Partner.changeset(partner, attrs) end - - def update_image(%Partner{} = partner, attrs) do - partner - |> Partner.image_changeset(attrs) - |> Repo.update() - end end diff --git a/lib/atomic/partnerships/partner.ex b/lib/atomic/partnerships/partner.ex index e907f6858..1fb2f70d4 100644 --- a/lib/atomic/partnerships/partner.ex +++ b/lib/atomic/partnerships/partner.ex @@ -38,9 +38,4 @@ defmodule Atomic.Partnerships.Partner do |> validate_required(@required_fields) |> unique_constraint(:name) end - - def image_changeset(partner, attrs) do - partner - |> cast_attachments(attrs, [:image]) - end end diff --git a/lib/atomic/time.ex b/lib/atomic/time.ex index 5b65c526f..99c1e9940 100644 --- a/lib/atomic/time.ex +++ b/lib/atomic/time.ex @@ -1,5 +1,8 @@ defmodule Atomic.Time do @moduledoc false + @doc """ + Returns the current time in Lisbon. + """ def lisbon_now do Timex.now() |> Timex.Timezone.convert(Timex.Timezone.get("Europe/Lisbon")) end diff --git a/lib/atomic_web/live/activity_live/index.ex b/lib/atomic_web/live/activity_live/index.ex index 0a89f50fc..a7b7c413a 100644 --- a/lib/atomic_web/live/activity_live/index.ex +++ b/lib/atomic_web/live/activity_live/index.ex @@ -62,7 +62,7 @@ defmodule AtomicWeb.ActivityLive.Index do def handle_event("activities-enrolled", _payload, socket) do user = socket.assigns.current_user - activities = Activities.get_user_activities(user) + activities = Activities.get_user_activities(user.id) {:noreply, assign(socket, :activities, activities)} end diff --git a/lib/atomic_web/live/activity_live/show.ex b/lib/atomic_web/live/activity_live/show.ex index b8840e485..7f68391e5 100644 --- a/lib/atomic_web/live/activity_live/show.ex +++ b/lib/atomic_web/live/activity_live/show.ex @@ -35,7 +35,7 @@ defmodule AtomicWeb.ActivityLive.Show do if organization_id in organizations do {:noreply, socket - |> assign(:enrolled?, Activities.is_user_enrolled?(activity, socket.assigns.current_user)) + |> assign(:enrolled?, Activities.is_participating?(activity, socket.assigns.current_user)) |> assign(:page_title, page_title(socket.assigns.live_action)) |> assign(:breadcrumb_entries, entries) |> assign(:current_page, :activities) @@ -143,6 +143,6 @@ defmodule AtomicWeb.ActivityLive.Show do def is_admin?(user, activity) do department = activity.departments |> Enum.at(0) - Organizations.get_membership_role!(user.id, department.organization_id) in [:admin, :owner] + Organizations.get_role(user.id, department.organization_id) in [:admin, :owner] end end diff --git a/lib/atomic_web/live/department_live/show.ex b/lib/atomic_web/live/department_live/show.ex index 6124a4e02..dae8a46d3 100644 --- a/lib/atomic_web/live/department_live/show.ex +++ b/lib/atomic_web/live/department_live/show.ex @@ -28,13 +28,13 @@ defmodule AtomicWeb.DepartmentLive.Show do socket |> assign(:current_page, :departments) |> assign(:breadcrumb_entries, entries) - |> assign(:page_title, page_title(socket.assigns.live_action)) + |> assign(:page_title, page_title(socket.assigns.live_action, department.name)) |> assign(:department, department)} else raise AtomicWeb.MismatchError end end - defp page_title(:show), do: "Show Department" - defp page_title(:edit), do: "Edit Department" + defp page_title(:show, department), do: "Show #{department}" + defp page_title(:edit, department), do: "Edit #{department}" end diff --git a/lib/atomic_web/live/membership_live/index.ex b/lib/atomic_web/live/membership_live/index.ex index dc5aba93e..87e75f0be 100644 --- a/lib/atomic_web/live/membership_live/index.ex +++ b/lib/atomic_web/live/membership_live/index.ex @@ -12,7 +12,7 @@ defmodule AtomicWeb.MembershipLive.Index do def handle_params(%{"organization_id" => id}, _, socket) do memberships = Organizations.list_memberships(%{"organization_id" => id}, [:user, :created_by]) - |> Enum.filter(fn m -> m.role != :follower end) + |> Enum.sort_by(& &1.user.name) organization = Organizations.get_organization!(id) diff --git a/lib/atomic_web/live/partner_live/form_component.ex b/lib/atomic_web/live/partner_live/form_component.ex index 6ee1d0d97..3f73efb52 100644 --- a/lib/atomic_web/live/partner_live/form_component.ex +++ b/lib/atomic_web/live/partner_live/form_component.ex @@ -73,7 +73,7 @@ defmodule AtomicWeb.PartnerLive.FormComponent do defp consume_image_data(socket, partner) do consume_uploaded_entries(socket, :image, fn %{path: path}, entry -> - Partnerships.update_image(partner, %{ + Partnerships.update_partner(partner, %{ "image" => %Plug.Upload{ content_type: entry.client_type, filename: entry.client_name, diff --git a/lib/atomic_web/live/user_live/form_component.ex b/lib/atomic_web/live/user_live/form_component.ex index 975bd1089..07331b157 100644 --- a/lib/atomic_web/live/user_live/form_component.ex +++ b/lib/atomic_web/live/user_live/form_component.ex @@ -5,7 +5,7 @@ defmodule AtomicWeb.UserLive.FormComponent do @impl true def update(%{user: user} = assigns, socket) do - changeset = Accounts.change_user(user) + changeset = Accounts.update_user(user) {:ok, socket diff --git a/lib/atomic_web/views/helpers.ex b/lib/atomic_web/views/helpers.ex index b353b0ac3..55f2f633f 100644 --- a/lib/atomic_web/views/helpers.ex +++ b/lib/atomic_web/views/helpers.ex @@ -10,11 +10,14 @@ defmodule AtomicWeb.ViewUtils do alias Timex.Format.DateTime.Formatters.Relative require Timex.Translator + @doc """ + Returns the frontend url from the config. + """ def frontend_url do Application.fetch_env!(:atomic, AtomicWeb.Endpoint)[:frontend_url] end - @doc ~S""" + @doc """ Returns a relative datetime string for the given datetime. ## Examples