Skip to content

Commit

Permalink
Remove unnecessary trips to the DB (#1189)
Browse files Browse the repository at this point in the history
* Use subquery to get submission IDs

* Add TODO

* Fix n+1 query problem

* Fix typo

* Fix tests

* Comment Sentry DSN in example secrets

* Fix admin assessments controller

* fix: Fix n + 1 query in assessments_view

* Fix format

* Fix format
  • Loading branch information
RichDom2185 authored Sep 20, 2024
1 parent 6d33c09 commit b02b43c
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 56 deletions.
4 changes: 2 additions & 2 deletions config/dev.secrets.exs.example
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ config :cadet,
# optional, passed to [HTTPoison.Request](https://hexdocs.pm/httpoison/HTTPoison.Request.html) options
http_options: [recv_timeout: 170_0000]

config :sentry,
dsn: "https://public_key/sentry.io/somethingsomething"
# config :sentry,
# dsn: "https://public_key/sentry.io/somethingsomething"

# # Additional configuration for SAML authentication
# # For more details, see https://github.com/handnot2/samly
Expand Down
51 changes: 38 additions & 13 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ defmodule Cadet.Assessments do
Submission
|> where(
[s],
s.id in ^submission_ids
s.id in subquery(submission_ids)
)
|> where(is_grading_published: true)
|> join(:inner, [s], a in Answer, on: s.id == a.submission_id)
Expand Down Expand Up @@ -337,7 +337,7 @@ defmodule Cadet.Assessments do
|> join(:left, [s], ans in Answer, on: ans.submission_id == s.id)
|> where(
[s],
s.id in ^submission_ids
s.id in subquery(submission_ids)
)
|> group_by([s], s.assessment_id)
|> select([s, ans], %{
Expand All @@ -351,7 +351,7 @@ defmodule Cadet.Assessments do
Submission
|> where(
[s],
s.id in ^submission_ids
s.id in subquery(submission_ids)
)
|> select([s], [:assessment_id, :status, :is_grading_published])

Expand Down Expand Up @@ -382,21 +382,46 @@ defmodule Cadet.Assessments do
end

defp get_submission_ids(cr_id, teams) do
query =
from(s in Submission,
where: s.student_id == ^cr_id or s.team_id in ^Enum.map(teams, & &1.id),
select: s.id
)
from(s in Submission,
where: s.student_id == ^cr_id or s.team_id in ^Enum.map(teams, & &1.id),
select: s.id
)
end

Repo.all(query)
defp is_voting_assigned(assessment_ids) do
voting_assigned_question_ids =
SubmissionVotes
|> select([v], v.question_id)
|> Repo.all()

# Map of assessment_id to boolean
voting_assigned_assessment_ids =
Question
|> where(type: :voting)
|> where([q], q.id in ^voting_assigned_question_ids)
|> where([q], q.assessment_id in ^assessment_ids)
|> select([q], q.assessment_id)
|> distinct(true)
|> Repo.all()

Enum.reduce(assessment_ids, %{}, fn id, acc ->
Map.put(acc, id, Enum.member?(voting_assigned_assessment_ids, id))
end)
end

@doc """
A helper function which removes grading information from all assessments
if it's grading is not published.
"""
def format_all_assessments(assessments) do
is_voting_assigned_map =
assessments
|> Enum.map(& &1.id)
|> is_voting_assigned()

Enum.map(assessments, fn a ->
a = Map.put(a, :is_voting_published, Map.get(is_voting_assigned_map, a.id, false))

if a.is_grading_published do
a
else
Expand Down Expand Up @@ -653,17 +678,16 @@ defmodule Cadet.Assessments do
end
end

def is_voting_published(assessment_id) do
defp is_voting_published(assessment_id) do
voting_assigned_question_ids =
SubmissionVotes
|> select([v], v.question_id)
|> Repo.all()

Question
|> where(type: :voting)
|> where(assessment_id: ^assessment_id)
|> where([q], q.id in ^voting_assigned_question_ids)
|> Repo.exists?()
|> where([q], q.id in subquery(voting_assigned_question_ids))
|> Repo.exists?() || false
end

def update_final_contest_entries do
Expand Down Expand Up @@ -1442,6 +1466,7 @@ defmodule Cadet.Assessments do

@spec update_xp_bonus(Submission.t()) ::
{:ok, Submission.t()} | {:error, Ecto.Changeset.t()}
# TODO: Should destructure and pattern match on the function
defp update_xp_bonus(submission = %Submission{id: submission_id}) do
# to ensure backwards compatibility
if submission.xp_bonus == 0 do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defmodule CadetWeb.AdminAssessmentsController do
def index(conn, %{"course_reg_id" => course_reg_id}) do
course_reg = Repo.get(CourseRegistration, course_reg_id)
{:ok, assessments} = Assessments.all_assessments(course_reg)

assessments = Assessments.format_all_assessments(assessments)
render(conn, "index.json", assessments: assessments)
end

Expand Down
18 changes: 1 addition & 17 deletions lib/cadet_web/admin_views/admin_assessments_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ defmodule CadetWeb.AdminAssessmentsView do
use CadetWeb, :view
use Timex
import CadetWeb.AssessmentsHelpers
import Ecto.Query
alias Cadet.Assessments.{Question, SubmissionVotes}
alias Cadet.Repo

def render("index.json", %{assessments: assessments}) do
render_many(assessments, CadetWeb.AdminAssessmentsView, "overview.json", as: :assessment)
Expand Down Expand Up @@ -35,7 +32,7 @@ defmodule CadetWeb.AdminAssessmentsView do
maxTeamSize: :max_team_size,
hasVotingFeatures: :has_voting_features,
hasTokenCounter: :has_token_counter,
isVotingPublished: &is_voting_assigned(&1.id)
isVotingPublished: :is_voting_published
})
end

Expand Down Expand Up @@ -84,17 +81,4 @@ defmodule CadetWeb.AdminAssessmentsView do
defp password_protected?(nil), do: false

defp password_protected?(_), do: true

defp is_voting_assigned(assessment_id) do
voting_assigned_question_ids =
SubmissionVotes
|> select([v], v.question_id)
|> Repo.all()

Question
|> where(type: :voting)
|> where(assessment_id: ^assessment_id)
|> where([q], q.id in ^voting_assigned_question_ids)
|> Repo.exists?()
end
end
18 changes: 1 addition & 17 deletions lib/cadet_web/views/assessments_view.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
defmodule CadetWeb.AssessmentsView do
use CadetWeb, :view
use Timex
import Ecto.Query
alias Cadet.Assessments.{Question, SubmissionVotes}
alias Cadet.Repo

import CadetWeb.AssessmentsHelpers

Expand Down Expand Up @@ -37,7 +34,7 @@ defmodule CadetWeb.AssessmentsView do
maxTeamSize: :max_team_size,
hasVotingFeatures: :has_voting_features,
hasTokenCounter: :has_token_counter,
isVotingPublished: &is_voting_assigned(&1.id),
isVotingPublished: :is_voting_published,
hoursBeforeEarlyXpDecay: & &1.config.hours_before_early_xp_decay
})
end
Expand Down Expand Up @@ -87,17 +84,4 @@ defmodule CadetWeb.AssessmentsView do
defp password_protected?(nil), do: false

defp password_protected?(_), do: true

defp is_voting_assigned(assessment_id) do
voting_assigned_question_ids =
SubmissionVotes
|> select([v], v.question_id)
|> Repo.all()

Question
|> where(type: :voting)
|> where(assessment_id: ^assessment_id)
|> where([q], q.id in ^voting_assigned_question_ids)
|> Repo.exists?()
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ defmodule CadetWeb.AdminAssessmentsControllerTest do
"isPublished" => &1.is_published,
"gradedCount" => 0,
"questionCount" => 9,
"xp" => (800 + 500 + 100) * 3,
"xp" => if(&1.is_grading_published, do: (800 + 500 + 100) * 3, else: 0),
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id)
"isVotingPublished" => false
}
)

Expand Down Expand Up @@ -145,7 +145,7 @@ defmodule CadetWeb.AdminAssessmentsControllerTest do
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id)
"isVotingPublished" => false
}
)

Expand Down
6 changes: 3 additions & 3 deletions test/cadet_web/controllers/assessments_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ defmodule CadetWeb.AssessmentsControllerTest do
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id),
"isVotingPublished" => false,
"hoursBeforeEarlyXpDecay" => &1.config.hours_before_early_xp_decay
}
)
Expand Down Expand Up @@ -174,7 +174,7 @@ defmodule CadetWeb.AssessmentsControllerTest do
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id),
"isVotingPublished" => false,
"hoursBeforeEarlyXpDecay" => &1.config.hours_before_early_xp_decay
}
)
Expand Down Expand Up @@ -288,7 +288,7 @@ defmodule CadetWeb.AssessmentsControllerTest do
"questionCount" => 9,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id),
"isVotingPublished" => false,
"earlySubmissionXp" => &1.config.early_submission_xp,
"isGradingPublished" => nil,
"hoursBeforeEarlyXpDecay" => &1.config.hours_before_early_xp_decay,
Expand Down

0 comments on commit b02b43c

Please sign in to comment.