From ea4335143c3b813372bcadaab21f61558511106f Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 03:12:54 +0800 Subject: [PATCH 1/9] Create index on assessment ID in submissions table Improves read performance. --- ...1105164101_create_submissions_assessment_index.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 priv/repo/migrations/20231105164101_create_submissions_assessment_index.exs diff --git a/priv/repo/migrations/20231105164101_create_submissions_assessment_index.exs b/priv/repo/migrations/20231105164101_create_submissions_assessment_index.exs new file mode 100644 index 000000000..04016dcb1 --- /dev/null +++ b/priv/repo/migrations/20231105164101_create_submissions_assessment_index.exs @@ -0,0 +1,11 @@ +defmodule Cadet.Repo.Migrations.CreateSubmissionsAssessmentIndex do + use Ecto.Migration + + def up do + create(index(:submissions, [:assessment_id])) + end + + def down do + drop(index(:submissions, [:assessment_id])) + end +end From a4aed5a12b3c62443de6e8047e0ce50559876589 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 03:17:03 +0800 Subject: [PATCH 2/9] Create separate view layer for grading summary --- lib/cadet_web/views/assessments_view.ex | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/lib/cadet_web/views/assessments_view.ex b/lib/cadet_web/views/assessments_view.ex index 20aad951f..fcefd7a0c 100644 --- a/lib/cadet_web/views/assessments_view.ex +++ b/lib/cadet_web/views/assessments_view.ex @@ -8,6 +8,94 @@ defmodule CadetWeb.AssessmentsView do render_many(assessments, CadetWeb.AssessmentsView, "overview.json", as: :assessment) end + def render("gradingsummaries.json", %{ + users: users, + assessments: assessments, + submissions: submissions + }) do + for submission <- submissions do + user = users |> Enum.find(&(&1.id == submission.student_id)) + assessment = assessments |> Enum.find(&(&1.id == submission.assessment_id)) + + render( + CadetWeb.AssessmentsView, + "gradingsummary.json", + %{ + user: user, + assessment: assessment, + submission: submission, + unsubmitter: + case submission.unsubmitted_by_id do + nil -> nil + _ -> users |> Enum.find(&(&1.id == submission.unsubmitted_by_id)) + end + } + ) + end + end + + def render("gradingsummary.json", %{ + user: user, + assessment: a, + submission: s, + unsubmitter: unsubmitter + }) do + s + |> transform_map_for_view(%{ + id: :id, + status: :status, + unsubmittedAt: :unsubmitted_at, + xp: :xp, + xpAdjustment: :xp_adjustment, + xpBonus: :xp_bonus, + gradedCount: + &case &1.graded_count do + nil -> 0 + x -> x + end + }) + |> Map.merge(%{ + assessment: + render_one(a, CadetWeb.AssessmentsView, "gradingsummaryassessment.json", as: :assessment), + student: render_one(user, CadetWeb.AssessmentsView, "gradingsummaryuser.json", as: :cr), + unsubmittedBy: + case unsubmitter do + nil -> nil + cr -> transform_map_for_view(cr, %{id: :id, name: & &1.user.name}) + end + }) + end + + def render("gradingsummaryassessment.json", %{assessment: a}) do + %{ + id: a.id, + title: a.title, + assessmentNumber: a.number, + isManuallyGraded: a.config.is_manually_graded, + type: a.config.type, + maxXp: a.questions |> Enum.map(& &1.max_xp) |> Enum.sum(), + questionCount: a.questions |> Enum.count() + } + end + + def render("gradingsummaryuser.json", %{cr: cr}) do + %{ + id: cr.id, + name: cr.user.name, + username: cr.user.username, + groupName: + case cr.group do + nil -> nil + _ -> cr.group.name + end, + groupLeaderId: + case cr.group do + nil -> nil + _ -> cr.group.leader_id + end + } + end + def render("overview.json", %{assessment: assessment}) do transform_map_for_view(assessment, %{ id: :id, From ab9ac011b1f7eae4fae5ca53fe68ddc9a859138b Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 03:47:05 +0800 Subject: [PATCH 3/9] Move views under AdminGradingView --- .../admin_views/admin_grading_view.ex | 88 +++++++++++++++++++ lib/cadet_web/views/assessments_view.ex | 88 ------------------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/lib/cadet_web/admin_views/admin_grading_view.ex b/lib/cadet_web/admin_views/admin_grading_view.ex index 363d63ccc..128bfb59d 100644 --- a/lib/cadet_web/admin_views/admin_grading_view.ex +++ b/lib/cadet_web/admin_views/admin_grading_view.ex @@ -7,6 +7,94 @@ defmodule CadetWeb.AdminGradingView do render_many(answers, CadetWeb.AdminGradingView, "grading_info.json", as: :answer) end + def render("gradingsummaries.json", %{ + users: users, + assessments: assessments, + submissions: submissions + }) do + for submission <- submissions do + user = users |> Enum.find(&(&1.id == submission.student_id)) + assessment = assessments |> Enum.find(&(&1.id == submission.assessment_id)) + + render( + CadetWeb.AdminGradingView, + "gradingsummary.json", + %{ + user: user, + assessment: assessment, + submission: submission, + unsubmitter: + case submission.unsubmitted_by_id do + nil -> nil + _ -> users |> Enum.find(&(&1.id == submission.unsubmitted_by_id)) + end + } + ) + end + end + + def render("gradingsummary.json", %{ + user: user, + assessment: a, + submission: s, + unsubmitter: unsubmitter + }) do + s + |> transform_map_for_view(%{ + id: :id, + status: :status, + unsubmittedAt: :unsubmitted_at, + xp: :xp, + xpAdjustment: :xp_adjustment, + xpBonus: :xp_bonus, + gradedCount: + &case &1.graded_count do + nil -> 0 + x -> x + end + }) + |> Map.merge(%{ + assessment: + render_one(a, CadetWeb.AdminGradingView, "gradingsummaryassessment.json", as: :assessment), + student: render_one(user, CadetWeb.AdminGradingView, "gradingsummaryuser.json", as: :cr), + unsubmittedBy: + case unsubmitter do + nil -> nil + cr -> transform_map_for_view(cr, %{id: :id, name: & &1.user.name}) + end + }) + end + + def render("gradingsummaryassessment.json", %{assessment: a}) do + %{ + id: a.id, + title: a.title, + assessmentNumber: a.number, + isManuallyGraded: a.config.is_manually_graded, + type: a.config.type, + maxXp: a.questions |> Enum.map(& &1.max_xp) |> Enum.sum(), + questionCount: a.questions |> Enum.count() + } + end + + def render("gradingsummaryuser.json", %{cr: cr}) do + %{ + id: cr.id, + name: cr.user.name, + username: cr.user.username, + groupName: + case cr.group do + nil -> nil + _ -> cr.group.name + end, + groupLeaderId: + case cr.group do + nil -> nil + _ -> cr.group.leader_id + end + } + end + def render("grading_info.json", %{answer: answer}) do transform_map_for_view(answer, %{ student: diff --git a/lib/cadet_web/views/assessments_view.ex b/lib/cadet_web/views/assessments_view.ex index fcefd7a0c..20aad951f 100644 --- a/lib/cadet_web/views/assessments_view.ex +++ b/lib/cadet_web/views/assessments_view.ex @@ -8,94 +8,6 @@ defmodule CadetWeb.AssessmentsView do render_many(assessments, CadetWeb.AssessmentsView, "overview.json", as: :assessment) end - def render("gradingsummaries.json", %{ - users: users, - assessments: assessments, - submissions: submissions - }) do - for submission <- submissions do - user = users |> Enum.find(&(&1.id == submission.student_id)) - assessment = assessments |> Enum.find(&(&1.id == submission.assessment_id)) - - render( - CadetWeb.AssessmentsView, - "gradingsummary.json", - %{ - user: user, - assessment: assessment, - submission: submission, - unsubmitter: - case submission.unsubmitted_by_id do - nil -> nil - _ -> users |> Enum.find(&(&1.id == submission.unsubmitted_by_id)) - end - } - ) - end - end - - def render("gradingsummary.json", %{ - user: user, - assessment: a, - submission: s, - unsubmitter: unsubmitter - }) do - s - |> transform_map_for_view(%{ - id: :id, - status: :status, - unsubmittedAt: :unsubmitted_at, - xp: :xp, - xpAdjustment: :xp_adjustment, - xpBonus: :xp_bonus, - gradedCount: - &case &1.graded_count do - nil -> 0 - x -> x - end - }) - |> Map.merge(%{ - assessment: - render_one(a, CadetWeb.AssessmentsView, "gradingsummaryassessment.json", as: :assessment), - student: render_one(user, CadetWeb.AssessmentsView, "gradingsummaryuser.json", as: :cr), - unsubmittedBy: - case unsubmitter do - nil -> nil - cr -> transform_map_for_view(cr, %{id: :id, name: & &1.user.name}) - end - }) - end - - def render("gradingsummaryassessment.json", %{assessment: a}) do - %{ - id: a.id, - title: a.title, - assessmentNumber: a.number, - isManuallyGraded: a.config.is_manually_graded, - type: a.config.type, - maxXp: a.questions |> Enum.map(& &1.max_xp) |> Enum.sum(), - questionCount: a.questions |> Enum.count() - } - end - - def render("gradingsummaryuser.json", %{cr: cr}) do - %{ - id: cr.id, - name: cr.user.name, - username: cr.user.username, - groupName: - case cr.group do - nil -> nil - _ -> cr.group.name - end, - groupLeaderId: - case cr.group do - nil -> nil - _ -> cr.group.leader_id - end - } - end - def render("overview.json", %{assessment: assessment}) do transform_map_for_view(assessment, %{ id: :id, From 97890a6c1bd2ab0caf130fed77e9a9c1826628d1 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 03:54:18 +0800 Subject: [PATCH 4/9] Migrate to more optimised query * Use subquery instead of join for answers * Split main model query and view model generation * Split queries into 3 * Use ORM where possible --- lib/cadet/assessments/assessments.ex | 79 +++++++++++++++++++ .../admin_grading_controller.ex | 4 +- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index fc8db2902..b07b6a9e4 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1262,6 +1262,7 @@ defmodule Cadet.Assessments do # We bypass Ecto here and use a raw query to generate JSON directly from # PostgreSQL, because doing it in Elixir/Erlang is too inefficient. + # TODO: Remove old query case Repo.query( """ select json_agg(q)::TEXT from @@ -1346,6 +1347,84 @@ defmodule Cadet.Assessments do {:ok, %{rows: [[nil]]}} -> {:ok, "[]"} {:ok, %{rows: [[json]]}} -> {:ok, json} end + + # TODO: Implement filtering + submissions = + case Repo.query(""" + SELECT + s.id, + s.status, + s.unsubmitted_at, + s.unsubmitted_by_id, + s_ans.xp, + s_ans.xp_adjustment, + s.xp_bonus, + s_ans.graded_count, + s.student_id, + s.assessment_id + FROM + submissions AS s + LEFT JOIN ( + SELECT + ans.submission_id, + SUM(ans.xp) AS xp, + SUM(ans.xp_adjustment) AS xp_adjustment, + COUNT(ans.id) FILTER ( + WHERE + ans.grader_id IS NOT NULL + ) AS graded_count + FROM + answers AS ans + GROUP BY + ans.submission_id + ) AS s_ans ON s_ans.submission_id = s.id + WHERE + s.assessment_id IN ( + SELECT + id + FROM + assessments + WHERE + assessments.course_id = #{course_id} + ); + """) do + {:ok, %{columns: columns, rows: result}} -> + result + |> Enum.map( + &(columns + |> Enum.map(fn c -> String.to_atom(c) end) + |> Enum.zip(&1) + |> Enum.into(%{})) + ) + end + + {:ok, generate_grading_summary_view_model(submissions, course_id)} + end + + defp generate_grading_summary_view_model(submissions, course_id) do + users = + CourseRegistration + |> where([cr], cr.course_id == ^course_id) + |> join(:inner, [cr], u in assoc(cr, :user)) + |> join(:left, [cr, u], g in assoc(cr, :group)) + |> preload([cr, u, g], user: u, group: g) + |> Repo.all() + + assessment_ids = submissions |> Enum.map(& &1.assessment_id) |> Enum.uniq() + + assessments = + Assessment + |> where([a], a.id in ^assessment_ids) + |> join(:left, [a], q in assoc(a, :questions)) + |> join(:inner, [a], ac in assoc(a, :config)) + |> preload([a, q, ac], questions: q, config: ac) + |> Repo.all() + + %{ + users: users, + assessments: assessments, + submissions: submissions + } end @spec get_answers_in_submission(integer() | String.t()) :: diff --git a/lib/cadet_web/admin_controllers/admin_grading_controller.ex b/lib/cadet_web/admin_controllers/admin_grading_controller.ex index 9658b531a..caa9771a1 100644 --- a/lib/cadet_web/admin_controllers/admin_grading_controller.ex +++ b/lib/cadet_web/admin_controllers/admin_grading_controller.ex @@ -10,11 +10,11 @@ defmodule CadetWeb.AdminGradingController do group = String.to_atom(group) case Assessments.all_submissions_by_grader_for_index(course_reg, group) do - {:ok, submissions} -> + {:ok, view_model} -> conn |> put_status(:ok) |> put_resp_content_type("application/json") - |> text(submissions) + |> render("gradingsummaries.json", view_model) end end From 26c74edc46af5ec99215165dc484443292e70435 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 03:59:05 +0800 Subject: [PATCH 5/9] Fix typing --- lib/cadet/assessments/assessments.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index b07b6a9e4..5fbbd44a9 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1238,7 +1238,7 @@ defmodule Cadet.Assessments do {:forbidden, "Forbidden."}} """ @spec all_submissions_by_grader_for_index(CourseRegistration.t()) :: - {:ok, String.t()} + {:ok, %{:assessments => [any()], :submissions => [any()], :users => [any()]}} def all_submissions_by_grader_for_index( grader = %CourseRegistration{course_id: course_id}, group_only \\ false, From 69040aca6cd2e911e6f92033df4d0ade67000ed9 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 04:11:08 +0800 Subject: [PATCH 6/9] Restore group filtering functionality --- lib/cadet/assessments/assessments.ex | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 5fbbd44a9..9f736c9dd 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1250,14 +1250,20 @@ defmodule Cadet.Assessments do if show_all, do: "", else: - "where s.student_id in (select cr.id from course_registrations cr inner join groups g on cr.group_id = g.id where g.leader_id = $2) or s.student_id = $2" + "where s.student_id in (select cr.id from course_registrations cr inner join groups g on cr.group_id = g.id where g.leader_id = $1) or s.student_id = $1" + + group_filter = + if show_all, + do: "", + else: + "AND s.student_id IN (SELECT cr.id FROM course_registrations AS cr INNER JOIN groups AS g ON cr.group_id = g.id WHERE g.leader_id = #{grader.id}) OR s.student_id = #{grader.id}" ungraded_where = if ungraded_only, do: "where s.\"gradedCount\" < assts.\"questionCount\"", else: "" - params = if show_all, do: [course_id], else: [course_id, grader.id] + params = if show_all, do: [], else: [grader.id] # We bypass Ecto here and use a raw query to generate JSON directly from # PostgreSQL, because doing it in Elixir/Erlang is too inefficient. @@ -1312,7 +1318,7 @@ defmodule Cadet.Assessments do questions q on a.id = q.assessment_id inner join assessment_configs ac on ac.id = a.config_id - where a.course_id = $1 + where a.course_id = #{course_id} group by a.id) a) assts on assts.id = s.assessment_id inner join (select @@ -1386,7 +1392,7 @@ defmodule Cadet.Assessments do assessments WHERE assessments.course_id = #{course_id} - ); + ) #{group_filter}; """) do {:ok, %{columns: columns, rows: result}} -> result From af5c9e17e1d9ea7e46196dba103484a67cda3926 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 04:21:03 +0800 Subject: [PATCH 7/9] Update Avenger backlog notification workers Fixes compatibilty with new function signature. --- lib/cadet/workers/NotificationWorker.ex | 11 +++-------- test/cadet/email_test.exs | 6 ++---- .../notification_worker/notification_worker_test.exs | 6 ++---- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/cadet/workers/NotificationWorker.ex b/lib/cadet/workers/NotificationWorker.ex index d96a3df22..609aab13c 100644 --- a/lib/cadet/workers/NotificationWorker.ex +++ b/lib/cadet/workers/NotificationWorker.ex @@ -73,15 +73,10 @@ defmodule Cadet.Workers.NotificationWorker do for avenger_cr <- avengers_crs do avenger = Cadet.Accounts.get_user(avenger_cr.user_id) - ungraded_submissions = - Jason.decode!( - elem( - Cadet.Assessments.all_submissions_by_grader_for_index(avenger_cr, true, true), - 1 - ) - ) + {:ok, %{submissions: ungraded_submissions}} = + Cadet.Assessments.all_submissions_by_grader_for_index(avenger_cr, true, true) - if length(ungraded_submissions) < ungraded_threshold do + if Enum.count(ungraded_submissions) < ungraded_threshold do IO.puts("[AVENGER_BACKLOG] below threshold!") else IO.puts("[AVENGER_BACKLOG] SENDING_OUT") diff --git a/test/cadet/email_test.exs b/test/cadet/email_test.exs index 462daad65..2ee826979 100644 --- a/test/cadet/email_test.exs +++ b/test/cadet/email_test.exs @@ -24,10 +24,8 @@ defmodule Cadet.EmailTest do avenger_user = insert(:user, %{email: "test@gmail.com"}) avenger = insert(:course_registration, %{user: avenger_user, role: :staff}) - ungraded_submissions = - Jason.decode!( - elem(Cadet.Assessments.all_submissions_by_grader_for_index(avenger, true, true), 1) - ) + {:ok, %{submissions: ungraded_submissions}} = + Cadet.Assessments.all_submissions_by_grader_for_index(avenger, true, true) email = Email.avenger_backlog_email("avenger_backlog", avenger_user, ungraded_submissions) diff --git a/test/cadet/jobs/notification_worker/notification_worker_test.exs b/test/cadet/jobs/notification_worker/notification_worker_test.exs index 41606d4ce..626c46cac 100644 --- a/test/cadet/jobs/notification_worker/notification_worker_test.exs +++ b/test/cadet/jobs/notification_worker/notification_worker_test.exs @@ -18,10 +18,8 @@ defmodule Cadet.NotificationWorker.NotificationWorkerTest do submission = List.first(List.first(data.mcq_answers)).submission # setup for avenger backlog - ungraded_submissions = - Jason.decode!( - elem(Cadet.Assessments.all_submissions_by_grader_for_index(avenger_cr, true, true), 1) - ) + {:ok, %{submissions: ungraded_submissions}} = + Cadet.Assessments.all_submissions_by_grader_for_index(avenger_cr, true, true) Repo.update_all(NotificationType, set: [is_enabled: true]) Repo.update_all(NotificationConfig, set: [is_enabled: true]) From 76467670a4d5775098948720ede40ed2ddccd395 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 04:47:52 +0800 Subject: [PATCH 8/9] Remove old query --- lib/cadet/assessments/assessments.ex | 106 ++------------------------- 1 file changed, 7 insertions(+), 99 deletions(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 9f736c9dd..ce2afe219 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1242,118 +1242,26 @@ defmodule Cadet.Assessments do def all_submissions_by_grader_for_index( grader = %CourseRegistration{course_id: course_id}, group_only \\ false, - ungraded_only \\ false + _ungraded_only \\ false ) do show_all = not group_only - group_where = - if show_all, - do: "", - else: - "where s.student_id in (select cr.id from course_registrations cr inner join groups g on cr.group_id = g.id where g.leader_id = $1) or s.student_id = $1" - group_filter = if show_all, do: "", else: "AND s.student_id IN (SELECT cr.id FROM course_registrations AS cr INNER JOIN groups AS g ON cr.group_id = g.id WHERE g.leader_id = #{grader.id}) OR s.student_id = #{grader.id}" - ungraded_where = - if ungraded_only, - do: "where s.\"gradedCount\" < assts.\"questionCount\"", - else: "" - - params = if show_all, do: [], else: [grader.id] + # TODO: Restore ungraded filtering + # ... or more likely, decouple email logic from this function + # ungraded_where = + # if ungraded_only, + # do: "where s.\"gradedCount\" < assts.\"questionCount\"", + # else: "" # We bypass Ecto here and use a raw query to generate JSON directly from # PostgreSQL, because doing it in Elixir/Erlang is too inefficient. - # TODO: Remove old query - case Repo.query( - """ - select json_agg(q)::TEXT from - ( - select - s.id, - s.status, - s."unsubmittedAt", - s.xp, - s."xpAdjustment", - s."xpBonus", - s."gradedCount", - assts.jsn as assessment, - students.jsn as student, - unsubmitters.jsn as "unsubmittedBy" - from - (select - s.id, - s.student_id, - s.assessment_id, - s.status, - s.unsubmitted_at as "unsubmittedAt", - s.unsubmitted_by_id, - sum(ans.xp) as xp, - sum(ans.xp_adjustment) as "xpAdjustment", - s.xp_bonus as "xpBonus", - count(ans.id) filter (where ans.grader_id is not null) as "gradedCount" - from submissions s - left join - answers ans on s.id = ans.submission_id - #{group_where} - group by s.id) s - inner join - (select - a.id, a."questionCount", to_json(a) as jsn - from - (select - a.id, - a.title, - a.number as "assessmentNumber", - bool_or(ac.is_manually_graded) as "isManuallyGraded", - max(ac.type) as "type", - sum(q.max_xp) as "maxXp", - count(q.id) as "questionCount" - from assessments a - left join - questions q on a.id = q.assessment_id - inner join - assessment_configs ac on ac.id = a.config_id - where a.course_id = #{course_id} - group by a.id) a) assts on assts.id = s.assessment_id - inner join - (select - cr.id, to_json(cr) as jsn - from - (select - cr.id, - u.name as "name", - u.username as "username", - g.name as "groupName", - g.leader_id as "groupLeaderId" - from course_registrations cr - left join - groups g on g.id = cr.group_id - inner join - users u on u.id = cr.user_id) cr) students on students.id = s.student_id - left join - (select - cr.id, to_json(cr) as jsn - from - (select - cr.id, - u.name - from course_registrations cr - inner join - users u on u.id = cr.user_id) cr) unsubmitters on s.unsubmitted_by_id = unsubmitters.id - #{ungraded_where} - ) q - """, - params - ) do - {:ok, %{rows: [[nil]]}} -> {:ok, "[]"} - {:ok, %{rows: [[json]]}} -> {:ok, json} - end - # TODO: Implement filtering submissions = case Repo.query(""" From b51a598a9ae8d2dee27bac5bb36c2a04c6cf24b4 Mon Sep 17 00:00:00 2001 From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com> Date: Tue, 7 Nov 2023 05:12:38 +0800 Subject: [PATCH 9/9] Remove comment --- lib/cadet/assessments/assessments.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index ce2afe219..d8f9bc67f 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1262,7 +1262,6 @@ defmodule Cadet.Assessments do # We bypass Ecto here and use a raw query to generate JSON directly from # PostgreSQL, because doing it in Elixir/Erlang is too inefficient. - # TODO: Implement filtering submissions = case Repo.query(""" SELECT