Skip to content

Commit

Permalink
Manually assign entries for voting (#1097)
Browse files Browse the repository at this point in the history
* rename hasTokenCounter to hasVotingFeature under assessmentConfig

* Added hasVotingFeatures to assessment config

* Added hasVotingFeature to assessmentConfig

* Added hasTokenCounter and hasVotingFeatures to assessments

* hasTokenCounter and hasVotingFeatures to be updated to ones in assessment config when uploaded

* Added hasTokenCounter and hasVotingFeatures to be shown when assessment is requested

* Added hasTokenCounter into swaggers

* Changed test cases to include hasVotingFeatures and hasTokenCounter

* Added a way to change hasTokenCounter and hasVotingFeatures from the frontEnd

* fixed format

* fixed format

* fixed alias format

* added new function to delete voteSubmissions and call InsertVoting

* Added ability to reassign voting

* rename update_voting to reassign_voting for clarity

* added test case for reassign_voting

* rename reassignEntriesForVoting to assignEntriesForVoting

* added isVotingPublished to AssessmentOverview and changed testCases to match

* fixed format

* Moved is_voting_published to be a function in assessments

* Prevent deletions of submissions if voting has not been published

* fix format

* fix bug where submission is deleted regardless of reassigning_voting status

* changed label for better clarity

* Rename value for clarity and consistency

---------

Co-authored-by: Richard Dominick <[email protected]>
  • Loading branch information
DesSnowy and RichDom2185 authored Apr 6, 2024
1 parent 59fe997 commit 556f7ad
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 8 deletions.
52 changes: 52 additions & 0 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,58 @@ defmodule Cadet.Assessments do
Question.changeset(%Question{}, params_with_assessment_id)
end

def reassign_voting(assessment_id, is_reassigning_voting) do
if is_reassigning_voting do
if is_voting_published(assessment_id) do
Submission
|> where(assessment_id: ^assessment_id)
|> delete_submission_association(assessment_id)

Question
|> where(assessment_id: ^assessment_id)
|> Repo.all()
|> Enum.each(fn q ->
delete_submission_votes_association(q)
end)
end

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

unpublished_voting_questions =
Question
|> where(type: :voting)
|> where([q], q.id not in ^voting_assigned_question_ids)
|> where(assessment_id: ^assessment_id)
|> join(:inner, [q], asst in assoc(q, :assessment))
|> select([q, asst], %{course_id: asst.course_id, question: q.question, id: q.id})
|> Repo.all()

for q <- unpublished_voting_questions do
insert_voting(q.course_id, q.question["contest_number"], q.id)
end

{:ok, "voting assigned"}
else
{:ok, "no change to voting"}
end
end

def 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?()
end

def update_final_contest_entries do
# 1435 = 1 day - 5 minutes
if Log.log_execution("update_final_contest_entries", Duration.from_minutes(1435)) do
Expand Down
11 changes: 10 additions & 1 deletion lib/cadet_web/admin_controllers/admin_assessments_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ defmodule CadetWeb.AdminAssessmentsController do
max_team_size = params |> Map.get("maxTeamSize")
has_token_counter = params |> Map.get("hasTokenCounter")
has_voting_features = params |> Map.get("hasVotingFeatures")
assign_entries_for_voting = params |> Map.get("assignEntriesForVoting")

updated_assessment =
if is_nil(is_published) do
Expand Down Expand Up @@ -114,8 +115,16 @@ defmodule CadetWeb.AdminAssessmentsController do
Map.put(updated_assessment, :has_voting_features, has_voting_features)
end

is_reassigning_voting =
if is_nil(assign_entries_for_voting) do
false
else
assign_entries_for_voting
end

with {:ok, assessment} <- check_dates(open_at, close_at, updated_assessment),
{:ok, _nil} <- Assessments.update_assessment(assessment_id, assessment) do
{:ok, _nil} <- Assessments.update_assessment(assessment_id, assessment),
{:ok, _nil} <- Assessments.reassign_voting(assessment_id, is_reassigning_voting) do
text(conn, "OK")
else
{:error, {status, message}} ->
Expand Down
19 changes: 18 additions & 1 deletion lib/cadet_web/admin_views/admin_assessments_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ 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 @@ -31,7 +34,8 @@ defmodule CadetWeb.AdminAssessmentsView do
earlySubmissionXp: & &1.config.early_submission_xp,
maxTeamSize: :max_team_size,
hasVotingFeatures: :has_voting_features,
hasTokenCounter: :has_token_counter
hasTokenCounter: :has_token_counter,
isVotingPublished: &is_voting_assigned(&1.id)
})
end

Expand Down Expand Up @@ -65,4 +69,17 @@ 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
19 changes: 18 additions & 1 deletion lib/cadet_web/views/assessments_view.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
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 @@ -32,7 +35,8 @@ defmodule CadetWeb.AssessmentsView do
earlySubmissionXp: & &1.config.early_submission_xp,
maxTeamSize: :max_team_size,
hasVotingFeatures: :has_voting_features,
hasTokenCounter: :has_token_counter
hasTokenCounter: :has_token_counter,
isVotingPublished: &is_voting_assigned(&1.id)
})
end

Expand Down Expand Up @@ -66,4 +70,17 @@ 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
71 changes: 71 additions & 0 deletions test/cadet/assessments/assessments_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,77 @@ defmodule Cadet.AssessmentsTest do
assert SubmissionVotes |> where(question_id: ^question.id) |> Repo.all() |> length() == 0
end

test "function that reassign voting after voting is assigned" do
course = insert(:course)
config = insert(:assessment_config)
# contest assessment that has closed
closed_contest_assessment =
insert(:assessment,
is_published: true,
open_at: Timex.shift(Timex.now(), days: -5),
close_at: Timex.shift(Timex.now(), hours: -1),
course: course,
config: config
)

contest_question = insert(:programming_question, assessment: closed_contest_assessment)
voting_assessment = insert(:assessment, %{course: course})

question =
insert(:voting_question, %{
assessment: voting_assessment,
question:
build(:voting_question_content, contest_number: closed_contest_assessment.number)
})

students =
insert_list(6, :course_registration, %{
role: :student,
course: course
})

Enum.map(students, fn student ->
submission =
insert(:submission,
student: student,
assessment: contest_question.assessment,
status: "submitted"
)

insert(:answer,
answer: %{code: "return 2;"},
submission: submission,
question: contest_question
)
end)

unattempted_student = insert(:course_registration, %{role: :student, course: course})

# unattempted submission will automatically be submitted after the assessment closes.
unattempted_submission =
insert(:submission,
student: unattempted_student,
assessment: contest_question.assessment,
status: "submitted"
)

insert(:answer,
answer: %{
code: "// question was left blank by student"
},
submission: unattempted_submission,
question: contest_question
)

Assessments.insert_voting(course.id, contest_question.assessment.number, question.id)
Assessments.reassign_voting(voting_assessment.id, true)

# students with own contest submissions will vote for 5 entries
# students without own contest submissin will vote for 6 entries
assert SubmissionVotes |> where(question_id: ^question.id) |> Repo.all() |> length() ==
6 * 5 + 6
end

test "function that checks for closed contests and releases entries into voting pool" do
course = insert(:course)
config = insert(:assessment_config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule CadetWeb.AdminAssessmentsControllerTest do
import Ecto.Query
import ExUnit.CaptureLog

alias Cadet.Repo
alias Cadet.{Assessments, Repo}
alias Cadet.Accounts.CourseRegistration
alias Cadet.Assessments.{Assessment, Submission}
alias Cadet.Test.XMLGenerator
Expand Down Expand Up @@ -93,7 +93,8 @@ defmodule CadetWeb.AdminAssessmentsControllerTest do
"xp" => (800 + 500 + 100) * 3,
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id)
}
)

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

Expand Down
7 changes: 5 additions & 2 deletions test/cadet_web/controllers/assessments_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ defmodule CadetWeb.AssessmentsControllerTest do
"questionCount" => 9,
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id)
}
)

Expand Down Expand Up @@ -169,7 +170,8 @@ defmodule CadetWeb.AssessmentsControllerTest do
"questionCount" => 9,
"earlySubmissionXp" => &1.config.early_submission_xp,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id)
}
)

Expand Down Expand Up @@ -282,6 +284,7 @@ defmodule CadetWeb.AssessmentsControllerTest do
"questionCount" => 9,
"hasVotingFeatures" => &1.has_voting_features,
"hasTokenCounter" => &1.has_token_counter,
"isVotingPublished" => Assessments.is_voting_published(&1.id),
"earlySubmissionXp" => &1.config.early_submission_xp,
"isPublished" =>
if &1.config.type == hd(configs).type do
Expand Down

0 comments on commit 556f7ad

Please sign in to comment.