Skip to content
This repository has been archived by the owner on Aug 28, 2019. It is now read-only.

Commit

Permalink
Merge pull request #6 from geshuming/add-notifications-unsubmit
Browse files Browse the repository at this point in the history
Add unsubmit feature and tests
  • Loading branch information
geshuming authored Jun 15, 2019
2 parents 1826dbe + d86ab7c commit 254ca84
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 23 deletions.
113 changes: 105 additions & 8 deletions lib/cadet/accounts/notification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule Cadet.Accounts.Notification do

schema "notifications" do
field(:type, NotificationType)
field(:read, :boolean)
field(:read, :boolean, default: false)
field(:role, Role, virtual: true)

belongs_to(:user, User)
Expand Down Expand Up @@ -66,17 +66,90 @@ defmodule Cadet.Accounts.Notification do
end

@doc """
Writes a new notification into the database
Writes a new notification into the database, or updates an existing one
"""
@spec write(:any) :: Ecto.Changeset.t()
def write(params) do
%Notification{}
|> changeset(params)
|> Repo.insert!()
@spec write(map()) :: {:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
def write(params = %{role: role}) do
case role do
:student -> write_student(params)
:staff -> write_avenger(params)
_ -> {:error, changeset(%Notification{}, params)}
end
end

def write(params), do: {:error, changeset(%Notification{}, params)}

defp write_student(
params = %{
user_id: user_id,
assessment_id: assessment_id,
type: type
}
) do
question_id = Map.get(params, :question_id)

Notification
|> where(user_id: ^user_id)
|> where(assessment_id: ^assessment_id)
|> where(type: ^type)
|> query_question_id(question_id)
|> Repo.one()
|> case do
nil ->
changeset(%Notification{}, params)

notification ->
notification
|> changeset(%{
read: false,
role: :student
})
end
|> Repo.insert_or_update()
end

defp write_student(params), do: {:error, changeset(%Notification{}, params)}

defp write_avenger(
params = %{
user_id: user_id,
submission_id: submission_id,
type: type
}
) do
question_id = Map.get(params, :question_id)

Notification
|> where(user_id: ^user_id)
|> where(submission_id: ^submission_id)
|> query_question_id(question_id)
|> where(type: ^type)
|> Repo.one()
|> case do
nil ->
changeset(%Notification{}, params)

notification ->
notification
|> changeset(%{
read: false,
role: :staff
})
end
|> Repo.insert_or_update()
end

defp write_avenger(params), do: {:error, changeset(%Notification{}, params)}

defp query_question_id(query, question_id) do
case question_id do
nil -> query
question_id -> where(query, question_id: ^question_id)
end
end

@doc """
Changes a notification's read status from false to true
Changes a notification's read status from false to true.
"""
@spec acknowledge(:integer, %User{}) :: {:ok, Ecto.Schema.t()} | {:error, :any}
def acknowledge(notification_id, user = %User{}) do
Expand All @@ -92,4 +165,28 @@ defmodule Cadet.Accounts.Notification do
|> Repo.update()
end
end

@doc """
Function that handles notifications when a submission is unsubmitted.
"""
@spec handle_unsubmit_notifications(:integer, %User{}) ::
{:ok, Ecto.Schema.t()} | {:error, Ecto.Changeset.t()}
def handle_unsubmit_notifications(assessment_id, student = %User{})
when is_ecto_id(assessment_id) do
# Fetch and delete all notifications of :autograded and :graded
# Add new notification :unsubmitted

Notification
|> where(user_id: ^student.id)
|> where(assessment_id: ^assessment_id)
|> where([n], n.type in ^[:autograded, :graded])
|> Repo.delete_all()

write(%{
type: :unsubmitted,
role: student.role,
user_id: student.id,
assessment_id: assessment_id
})
end
end
2 changes: 2 additions & 0 deletions lib/cadet/accounts/notification_type.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ defenum(Cadet.Accounts.NotificationType, :notification_type, [
:autograded,
# Notifications for manually graded assessments
:graded,
# Notifications for unsubmitted submissions
:unsubmitted,

# Notifications for Submitted assessments
:submitted
Expand Down
5 changes: 5 additions & 0 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,11 @@ defmodule Cadet.Assessments do
end)
|> Repo.transaction()

Cadet.Accounts.Notification.handle_unsubmit_notifications(
submission.assessment.id,
Cadet.Accounts.get_user(submission.student_id)
)

{:ok, nil}
else
{:submission_found?, false} ->
Expand Down
6 changes: 4 additions & 2 deletions lib/cadet_web/controllers/notification_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ defmodule CadetWeb.NotificationController do

swagger_path :acknowledge do
post("/notification/{notificationId}/acknowledge")
summary("Finalise submission for an assessment")
summary("Acknowledge a notification")
security([%{JWT: []}])

parameters do
Expand All @@ -64,7 +64,9 @@ defmodule CadetWeb.NotificationController do

response(200, "OK")
response(400, "Invalid parameters")
response(404, "Notification not found")
response(401, "Unauthorised")
response(404, "Notification does not exist or does not belong to
user")
end

def swagger_definitions do
Expand Down
127 changes: 114 additions & 13 deletions test/cadet/accounts/notification_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Cadet.Accounts.NotificationTest do

use Cadet.ChangesetCase, entity: Notification

@required_fields ~w(type read user_id)a
@required_fields ~w(type role user_id)a

setup do
assessment = insert(:assessment, %{is_published: true})
Expand All @@ -30,6 +30,7 @@ defmodule Cadet.Accounts.NotificationTest do
{:ok,
%{
assessment: assessment,
avenger: avenger,
student: student,
submission: submission,
valid_params_for_student: valid_params_for_student,
Expand Down Expand Up @@ -80,28 +81,128 @@ defmodule Cadet.Accounts.NotificationTest do
end

describe "repo" do
test "fetch notifications when there are none" do
# TODO
test "fetch notifications when there are none", %{avenger: avenger, student: student} do
{:ok, notifications_avenger} = Notification.fetch(avenger)
{:ok, notifications_student} = Notification.fetch(student)

assert notifications_avenger == []
assert notifications_student == []
end

test "fetch notifications when there are some" do
# TODO
test "fetch notifications when all unread", %{assessment: assessment, student: student} do
notifications =
insert_list(3, :notification, %{
read: false,
assessment_id: assessment.id,
user_id: student.id
})

expected = Enum.sort(notifications, &(&1.id < &2.id))

{:ok, notifications_db} = Notification.fetch(student)

results = Enum.sort(notifications_db, &(&1.id < &2.id))

assert results == expected
end

test "fetch notifications when all read" do
# TODO
test "fetch notifications when all read", %{assessment: assessment, student: student} do
insert_list(3, :notification, %{
read: true,
assessment_id: assessment.id,
user_id: student.id
})

{:ok, notifications_db} = Notification.fetch(student)

assert notifications_db == []
end

test "create notification" do
# TODO
test "write notification valid params", %{
assessment: assessment,
avenger: avenger,
student: student,
submission: submission
} do
params_student = %{
type: :new,
read: false,
role: student.role,
user_id: student.id,
assessment_id: assessment.id
}

params_avenger = %{
type: :submitted,
read: false,
role: avenger.role,
user_id: avenger.id,
submission_id: submission.id
}

assert {:ok, _} = Notification.write(params_student)
assert {:ok, _} = Notification.write(params_avenger)
end

test "acknowledge notification when not read" do
# TODO
test "write notification missing params", %{
assessment: assessment,
student: student
} do
params_student = %{
type: :new,
read: false,
role: student.role,
user_id: student.id,
assessment_id: assessment.id
}

for field <- @required_fields do
params = Map.delete(params_student, field)

{:error, changeset} = Notification.write(params)

assert changeset.valid? == false
end
end

test "acknowledge notification valid user", %{
assessment: assessment,
student: student
} do
notification =
insert(:notification, %{
read: false,
assessment_id: assessment.id,
user_id: student.id
})

{:ok, notification_db} = Notification.acknowledge(notification.id, student)

assert %{read: true} = notification_db
end

test "acknowledge notification when read" do
# TODO
test "acknowledge notification invalid user", %{
assessment: assessment,
avenger: avenger,
student: student
} do
notification =
insert(:notification, %{
read: false,
assessment_id: assessment.id,
user_id: student.id
})

assert {:error, _} = Notification.acknowledge(notification.id, avenger)
end

test "handle unsubmit works properly", %{
assessment: assessment,
student: student
} do
{:ok, notification_db} = Notification.handle_unsubmit_notifications(assessment.id, student)

assert %{type: :unsubmitted} = notification_db
end
end
end
Loading

0 comments on commit 254ca84

Please sign in to comment.