Skip to content

Commit

Permalink
Add token divider field to contest voting XML file (#1034)
Browse files Browse the repository at this point in the history
* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Merge branch 'master' of https://github.com/kjw142857/backend-ContestVotingUpdate

* 1

* commit

* commit

* Update config.exs

* Update assessments.ex

* commit

* commit

* commit

* Add files via upload

* Update assessments_test.exs

* Change modifier to 70 for Game of Tones contest

Change the token penalty modifier to 70 for Contest C6 Game of Tones, NUS SA 23/24 Sem 1.

Note: In the future, proposed implementation is to have the token penalty modifier inserted in the contest voting file as a parameter at the point of upload.

* Update assessments_test.exs

* Fix incorrect denominator to 80

* commit

* Update assessments.ex

* Update voting_question.ex

* Update xml_parser.ex

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update voting_question.ex

* Add files via upload

* Add files via upload

* Update mix.lock

* Revert "Update mix.lock"

This reverts commit e6dc124.

* Insert validation that token divider must be > 0

---------

Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
  • Loading branch information
kjw142857 and RichDom2185 authored Dec 8, 2023
1 parent df78afa commit 3b35b80
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 23 deletions.
18 changes: 11 additions & 7 deletions lib/cadet/assessments/assessments.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,12 @@ defmodule Cadet.Assessments do
)
|> Repo.all()

entry_scores = map_eligible_votes_to_entry_score(eligible_votes)
token_divider =
Question
|> select([q], q.question["token_divider"])
|> Repo.get_by(id: contest_voting_question_id)

entry_scores = map_eligible_votes_to_entry_score(eligible_votes, token_divider)

entry_scores
|> Enum.map(fn {ans_id, relative_score} ->
Expand All @@ -1192,7 +1197,7 @@ defmodule Cadet.Assessments do
|> Repo.transaction()
end

defp map_eligible_votes_to_entry_score(eligible_votes) do
defp map_eligible_votes_to_entry_score(eligible_votes, token_divider) do
# converts eligible votes to the {total cumulative score, number of votes, tokens}
entry_vote_data =
Enum.reduce(eligible_votes, %{}, fn %{ans_id: ans_id, score: score, ans: ans}, tracker ->
Expand All @@ -1210,18 +1215,17 @@ defmodule Cadet.Assessments do
Enum.map(
entry_vote_data,
fn {ans_id, {sum_of_scores, number_of_voters, tokens}} ->
{ans_id, calculate_formula_score(sum_of_scores, number_of_voters, tokens)}
{ans_id, calculate_formula_score(sum_of_scores, number_of_voters, tokens, token_divider)}
end
)
end

# Calculate the score based on formula
# score(v,t) = v - 2^(t/n) where v is the normalized_voting_score and n is the modifier
# n = 50 for C3 & C4, n = 80 for C6
# score(v,t) = v - 2^(t/token_divider) where v is the normalized_voting_score
# normalized_voting_score = sum_of_scores / number_of_voters / 10 * 100
defp calculate_formula_score(sum_of_scores, number_of_voters, tokens) do
defp calculate_formula_score(sum_of_scores, number_of_voters, tokens, token_divider) do
normalized_voting_score = sum_of_scores / number_of_voters / 10 * 100
normalized_voting_score - :math.pow(2, min(1023.5, tokens / 80))
normalized_voting_score - :math.pow(2, min(1023.5, tokens / token_divider))
end

@doc """
Expand Down
4 changes: 3 additions & 1 deletion lib/cadet/assessments/question_types/voting_question.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ defmodule Cadet.Assessments.QuestionTypes.VotingQuestion do
field(:template, :string)
field(:contest_number, :string)
field(:reveal_hours, :integer)
field(:token_divider, :integer)
end

@required_fields ~w(content contest_number reveal_hours)a
@required_fields ~w(content contest_number reveal_hours token_divider)a
@optional_fields ~w(prepend template)a

def changeset(question, params \\ %{}) do
question
|> cast(params, @required_fields ++ @optional_fields)
|> validate_required(@required_fields)
|> validate_number(:token_divider, greater_than: 0)
end
end
3 changes: 2 additions & 1 deletion lib/cadet/jobs/xml_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ defmodule Cadet.Updater.XMLParser do
|> xpath(
~x"./VOTING"e,
contest_number: ~x"./@assessment_number"s,
reveal_hours: ~x"./@reveal_hours"i
reveal_hours: ~x"./@reveal_hours"i,
token_divider: ~x"./@token_divider"i
)
)
end
Expand Down
16 changes: 8 additions & 8 deletions test/cadet/assessments/assessments_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ defmodule Cadet.AssessmentsTest do
question: %{
content: Faker.Pokemon.name(),
contest_number: assessment.number,
reveal_hours: 48
reveal_hours: 48,
token_divider: 50
}
},
assessment.id
Expand Down Expand Up @@ -646,13 +647,13 @@ defmodule Cadet.AssessmentsTest do

top_x_ans = Assessments.fetch_top_relative_score_answers(question_id, 5)

assert get_answer_relative_scores(top_x_ans) == expected_top_relative_scores(5)
assert get_answer_relative_scores(top_x_ans) == expected_top_relative_scores(5, 50)

x = 3
top_x_ans = Assessments.fetch_top_relative_score_answers(question_id, x)

# verify that top x ans are queried correctly
assert get_answer_relative_scores(top_x_ans) == expected_top_relative_scores(3)
assert get_answer_relative_scores(top_x_ans) == expected_top_relative_scores(3, 50)
end
end

Expand Down Expand Up @@ -886,7 +887,7 @@ defmodule Cadet.AssessmentsTest do

assert get_answer_relative_scores(
Assessments.fetch_top_relative_score_answers(yesterday_question.id, 5)
) == expected_top_relative_scores(5)
) == expected_top_relative_scores(5, 50)
end

test "update_rolling_contest_leaderboards correcly updates leaderboards which voting is active",
Expand All @@ -908,7 +909,7 @@ defmodule Cadet.AssessmentsTest do

assert get_answer_relative_scores(
Assessments.fetch_top_relative_score_answers(current_question.id, 5)
) == expected_top_relative_scores(5)
) == expected_top_relative_scores(5, 50)
end
end

Expand Down Expand Up @@ -1707,12 +1708,11 @@ defmodule Cadet.AssessmentsTest do
questions |> Enum.map(fn q -> q.id end) |> Enum.sort()
end

defp expected_top_relative_scores(top_x) do
# test with token penalty of 80
defp expected_top_relative_scores(top_x, token_divider) do
# "return 0;" in the factory has 3 token
10..0
|> Enum.to_list()
|> Enum.map(fn score -> 10 * score - :math.pow(2, 3 / 80) end)
|> Enum.map(fn score -> 10 * score - :math.pow(2, 3 / token_divider) end)
|> Enum.take(top_x)
end
end
3 changes: 2 additions & 1 deletion test/cadet/assessments/question_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ defmodule Cadet.Assessments.QuestionTest do
question: %{
content: Faker.Pokemon.name(),
contest_number: assessment.number,
reveal_hours: 48
reveal_hours: 48,
token_divider: 50
}
}

Expand Down
23 changes: 22 additions & 1 deletion test/cadet/assessments/question_types/voting_question_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ defmodule Cadet.Assessments.QuestionTypes.VotingQuestionTest do
%{
content: "content",
contest_number: "C4",
reveal_hours: 48
reveal_hours: 48,
token_divider: 50
},
:valid
)
Expand All @@ -22,6 +23,26 @@ defmodule Cadet.Assessments.QuestionTypes.VotingQuestionTest do
},
:invalid
)

assert_changeset(
%{
content: "content",
contest_number: "C3",
reveal_hours: 48,
token_divider: -1
},
:invalid
)

assert_changeset(
%{
content: "content",
contest_number: "C6",
reveal_hours: 48,
token_divider: 0
},
:invalid
)
end
end
end
6 changes: 4 additions & 2 deletions test/factories/assessments/question_factory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ defmodule Cadet.Assessments.QuestionFactory do
prepend: Faker.Pokemon.location(),
template: Faker.Lorem.Shakespeare.as_you_like_it(),
contest_number: contest_assessment.number,
reveal_hours: 48
reveal_hours: 48,
token_divider: 50
}
}
end
Expand All @@ -106,7 +107,8 @@ defmodule Cadet.Assessments.QuestionFactory do
prepend: Faker.Pokemon.location(),
template: Faker.Lorem.Shakespeare.as_you_like_it(),
contest_number: contest_assessment.number,
reveal_hours: 48
reveal_hours: 48,
token_divider: 50
}
end
end
Expand Down
5 changes: 3 additions & 2 deletions test/support/xml_generator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ defmodule Cadet.Test.XMLGenerator do
voting_field =
voting(%{
reveal_hours: question.question.reveal_hours,
assessment_number: question.question.contest_number
assessment_number: question.question.contest_number,
token_divider: question.question.token_divider
})

[
Expand All @@ -167,7 +168,7 @@ defmodule Cadet.Test.XMLGenerator do
end

defp voting(raw_attr) do
{"VOTING", map_permit_keys(raw_attr, ~w(assessment_number reveal_hours)a)}
{"VOTING", map_permit_keys(raw_attr, ~w(assessment_number reveal_hours token_divider)a)}
end

defp deployment(raw_attrs, children) do
Expand Down

0 comments on commit 3b35b80

Please sign in to comment.