Skip to content

Commit

Permalink
Add sorting for grading (#1107)
Browse files Browse the repository at this point in the history
* handled backend sorting

* fixed sorting by inserted_at

* removed unused code

* changed inner_join to left_join

* added new tests

* fixed formatting

* reformatted code

* added more test

* added more tests

* updated grading sorter for progressStatus

* fixed test

* fixed sorting by group bug and made sorting work regardless of upper lower case

* fixed formatting

* fixed bug where xp was not sorted properly

* fixed sorting by grading bug

* fixed test

* Prefer piping

* Reduce nesting

* Revert "Prefer piping"

This reverts commit 415cdc0.
  • Loading branch information
thortol authored Aug 19, 2024
1 parent 76cb9f7 commit ddd4378
Show file tree
Hide file tree
Showing 2 changed files with 312 additions and 3 deletions.
133 changes: 130 additions & 3 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1906,7 +1906,9 @@ defmodule Cadet.Assessments do
"group" => "false",
"isFullyGraded" => "false",
"pageSize" => "10",
"offset" => "0"
"offset" => "0",
"sortBy" => "",
"sortDirection" => ""
}
) do
submission_answers_query =
Expand All @@ -1927,7 +1929,9 @@ defmodule Cadet.Assessments do
on: q.assessment_id == a.id,
select: %{
assessment_id: q.assessment_id,
question_count: count(q.id)
question_count: count(q.id),
title: max(a.title),
config_id: max(a.config_id)
}
)

Expand All @@ -1939,12 +1943,23 @@ defmodule Cadet.Assessments do
left_join: asst in subquery(question_answers_query),
on: asst.assessment_id == s.assessment_id,
as: :asst,
left_join: user in User,
on: user.id == s.student_id,
as: :user,
left_join: cr in CourseRegistration,
on: user.id == cr.user_id,
as: :cr,
left_join: group in Group,
on: cr.group_id == group.id,
as: :group,
inner_join: config in AssessmentConfig,
on: asst.config_id == config.id,
as: :config,
where: ^build_user_filter(params),
where: s.assessment_id in subquery(build_assessment_filter(params, course_id)),
where: s.assessment_id in subquery(build_assessment_config_filter(params)),
where: ^build_submission_filter(params),
where: ^build_course_registration_filter(params, grader),
order_by: [desc: s.inserted_at],
limit: ^elem(Integer.parse(Map.get(params, "pageSize", "10")), 0),
offset: ^elem(Integer.parse(Map.get(params, "offset", "0")), 0),
select: %{
Expand All @@ -1964,6 +1979,10 @@ defmodule Cadet.Assessments do
}
)

query =
sort_submission(query, Map.get(params, "sortBy", ""), Map.get(params, "sortDirection", ""))

query = from([s, ans, asst, user, cr, group] in query, order_by: [desc: s.inserted_at])
submissions = Repo.all(query)

count_query =
Expand All @@ -1987,6 +2006,114 @@ defmodule Cadet.Assessments do
{:ok, %{count: count, data: generate_grading_summary_view_model(submissions, course_id)}}
end

# Given a query from submissions_by_grader_for_index,
# sorts it by the relevant field and direction
# sort_by is a string of either "", "assessmentName", "assessmentType", "studentName",
# "studentUsername", "groupName", "progressStatus", "xp"
# sort_direction is a string of either "", "sort-asc", "sort-desc"
defp sort_submission(query, sort_by, sort_direction) do
cond do
sort_direction == "sort-asc" ->
sort_submission_asc(query, sort_by)

sort_direction == "sort-desc" ->
sort_submission_desc(query, sort_by)

true ->
query
end
end

defp sort_submission_asc(query, sort_by) do
cond do
sort_by == "assessmentName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: fragment("upper(?)", asst.title)
)

sort_by == "assessmentType" ->
from([s, ans, asst, user, cr, group, config] in query, order_by: asst.config_id)

sort_by == "studentName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: fragment("upper(?)", user.name)
)

sort_by == "studentUsername" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: fragment("upper(?)", user.username)
)

sort_by == "groupName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: fragment("upper(?)", group.name)
)

sort_by == "progressStatus" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [
asc: config.is_manually_graded,
asc: s.status,
asc: ans.graded_count - asst.question_count,
asc: s.is_grading_published
]
)

sort_by == "xp" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: ans.xp + ans.xp_adjustment
)

true ->
query
end
end

defp sort_submission_desc(query, sort_by) do
cond do
sort_by == "assessmentName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [desc: fragment("upper(?)", asst.title)]
)

sort_by == "assessmentType" ->
from([s, ans, asst, user, cr, group, config] in query, order_by: [desc: asst.config_id])

sort_by == "studentName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [desc: fragment("upper(?)", user.name)]
)

sort_by == "studentUsername" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [desc: fragment("upper(?)", user.username)]
)

sort_by == "groupName" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [desc: fragment("upper(?)", group.name)]
)

sort_by == "progressStatus" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [
desc: config.is_manually_graded,
desc: s.status,
desc: ans.graded_count - asst.question_count,
desc: s.is_grading_published
]
)

sort_by == "xp" ->
from([s, ans, asst, user, cr, group, config] in query,
order_by: [desc: ans.xp + ans.xp_adjustment]
)

true ->
query
end
end

defp build_assessment_filter(params, course_id) do
assessments_filters =
Enum.reduce(params, dynamic(true), fn
Expand Down
182 changes: 182 additions & 0 deletions test/cadet/assessments/assessments_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2815,6 +2815,188 @@ defmodule Cadet.AssessmentsTest do
assert Enum.find(assessments_from_res, fn a -> a.id == s.assessment_id end) != nil
end)
end

test "sorting by assessment title ascending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "assessmentName",
"sortDirection" => "sort-asc"
})

submissions_from_res = res[:data][:submissions]
assessments_from_res = res[:data][:assessments]

submissions_by_title =
Enum.map(
submissions_from_res,
fn s ->
Enum.find(assessments_from_res, fn a ->
s.assessment_id == a.id
end)
end
)

Enum.reduce(
submissions_by_title,
fn x, y ->
assert x.title >= y.title
y
end
)
end

test "sorting by assessment title descending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "assessmentName",
"sortDirection" => "sort-desc"
})

submissions_from_res = res[:data][:submissions]
assessments_from_res = res[:data][:assessments]

submissions_by_title =
Enum.map(
submissions_from_res,
fn s ->
Enum.find(assessments_from_res, fn a ->
s.assessment_id == a.id
end)
end
)

Enum.reduce(
submissions_by_title,
fn x, y ->
assert x.title <= y.title
y
end
)
end

test "sorting by assessment type ascending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "assessmentType",
"sortDirection" => "sort-asc"
})

submissions_from_res = res[:data][:submissions]
assessments_from_res = res[:data][:assessments]

submissions_by_assessments_type =
Enum.map(
submissions_from_res,
fn s ->
Enum.find(assessments_from_res, fn a ->
s.assessment_id == a.id
end)
end
)

Enum.reduce(
submissions_by_assessments_type,
fn x, y ->
assert x.config_id >= y.config_id
y
end
)
end

test "sorting by assessment type descending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "assessmentType",
"sortDirection" => "sort-desc"
})

submissions_from_res = res[:data][:submissions]
assessments_from_res = res[:data][:assessments]

submissions_by_assessments_type =
Enum.map(
submissions_from_res,
fn s ->
Enum.find(assessments_from_res, fn a ->
s.assessment_id == a.id
end)
end
)

Enum.reduce(
submissions_by_assessments_type,
fn x, y ->
assert x.config_id <= y.config_id
y
end
)
end

test "sorting by xp ascending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "xp",
"sortDirection" => "sort-asc"
})

submissions_from_res = res[:data][:submissions]

Enum.reduce(
submissions_from_res,
fn x, y ->
assert x.xp >= y.xp
y
end
)
end

test "sorting by xp descending", %{
course_regs: %{avenger1_cr: avenger},
assessments: _assessments,
total_submissions: total_submissions
} do
{_, res} =
Assessments.submissions_by_grader_for_index(avenger, %{
"pageSize" => total_submissions,
"sortBy" => "xp",
"sortDirection" => "sort-desc"
})

submissions_from_res = res[:data][:submissions]

Enum.reduce(
submissions_from_res,
fn x, y ->
assert x.xp <= y.xp
y
end
)
end
end

describe "is_fully_autograded? function" do
Expand Down

0 comments on commit ddd4378

Please sign in to comment.