@@ -1006,6 +1006,18 @@ defmodule Cadet.Assessments do
10061006 # fetch top 10 contest voting entries with the contest question id
10071007 question_id = fetch_associated_contest_question_id ( course_id , q )
10081008
1009+ # fetch top 10 contest coting entries with contest question id based on popular score
1010+ popular_results =
1011+ if is_nil ( question_id ) do
1012+ [ ]
1013+ else
1014+ if leaderboard_open? ( assessment , q ) or role in @ open_all_assessment_roles do
1015+ fetch_top_popular_score_answers ( question_id , 10 )
1016+ else
1017+ [ ]
1018+ end
1019+ end
1020+
10091021 leaderboard_results =
10101022 if is_nil ( question_id ) do
10111023 [ ]
@@ -1025,6 +1037,10 @@ defmodule Cadet.Assessments do
10251037 :contest_leaderboard ,
10261038 leaderboard_results
10271039 )
1040+ |> Map . put (
1041+ :popular_leaderboard ,
1042+ popular_results
1043+ )
10281044
10291045 Map . put ( q , :question , voting_question )
10301046 else
@@ -1097,6 +1113,37 @@ defmodule Cadet.Assessments do
10971113 |> Repo . all ( )
10981114 end
10991115
1116+ @ doc """
1117+ Fetches top answers for the given question, based on the contest popular_score
1118+
1119+ Used for contest leaderboard fetching
1120+ """
1121+ def fetch_top_popular_score_answers ( question_id , number_of_answers ) do
1122+ Answer
1123+ |> where ( question_id: ^ question_id )
1124+ |> where (
1125+ [ a ] ,
1126+ fragment (
1127+ "?->>'code' like ?" ,
1128+ a . answer ,
1129+ "%return%"
1130+ )
1131+ )
1132+ |> order_by ( desc: :popular_score )
1133+ |> join ( :left , [ a ] , s in assoc ( a , :submission ) )
1134+ |> join ( :left , [ a , s ] , student in assoc ( s , :student ) )
1135+ |> join ( :inner , [ a , s , student ] , student_user in assoc ( student , :user ) )
1136+ |> where ( [ a , s , student ] , student . role == "student" )
1137+ |> select ( [ a , s , student , student_user ] , % {
1138+ submission_id: a . submission_id ,
1139+ answer: a . answer ,
1140+ popular_score: a . popular_score ,
1141+ student_name: student_user . name
1142+ } )
1143+ |> limit ( ^ number_of_answers )
1144+ |> Repo . all ( )
1145+ end
1146+
11001147 @ doc """
11011148 Computes rolling leaderboard for contest votes that are still open.
11021149 """
@@ -1181,6 +1228,7 @@ defmodule Cadet.Assessments do
11811228 |> Repo . get_by ( id: contest_voting_question_id )
11821229
11831230 entry_scores = map_eligible_votes_to_entry_score ( eligible_votes , token_divider )
1231+ normalized_scores = map_eligible_votes_to_popular_score ( eligible_votes , token_divider )
11841232
11851233 entry_scores
11861234 |> Enum . map ( fn { ans_id , relative_score } ->
@@ -1195,6 +1243,20 @@ defmodule Cadet.Assessments do
11951243 end )
11961244 |> Enum . reduce ( Multi . new ( ) , & Multi . append / 2 )
11971245 |> Repo . transaction ( )
1246+
1247+ normalized_scores
1248+ |> Enum . map ( fn { ans_id , popular_score } ->
1249+ % Answer { id: ans_id }
1250+ |> Answer . popular_score_update_changeset ( % {
1251+ popular_score: popular_score
1252+ } )
1253+ end )
1254+ |> Enum . map ( fn changeset ->
1255+ op_key = "answer_#{ changeset . data . id } "
1256+ Multi . update ( Multi . new ( ) , op_key , changeset )
1257+ end )
1258+ |> Enum . reduce ( Multi . new ( ) , & Multi . append / 2 )
1259+ |> Repo . transaction ( )
11981260 end
11991261
12001262 defp map_eligible_votes_to_entry_score ( eligible_votes , token_divider ) do
@@ -1220,14 +1282,46 @@ defmodule Cadet.Assessments do
12201282 )
12211283 end
12221284
1285+ defp map_eligible_votes_to_popular_score ( eligible_votes , token_divider ) do
1286+ # converts eligible votes to the {total cumulative score, number of votes, tokens}
1287+ entry_vote_data =
1288+ Enum . reduce ( eligible_votes , % { } , fn % { ans_id: ans_id , score: score , ans: ans } , tracker ->
1289+ { prev_score , prev_count , _ans_tokens } = Map . get ( tracker , ans_id , { 0 , 0 , 0 } )
1290+
1291+ Map . put (
1292+ tracker ,
1293+ ans_id ,
1294+ # assume each voter is assigned 10 entries which will make it fair.
1295+ { prev_score + score , prev_count + 1 , Lexer . count_tokens ( ans ) }
1296+ )
1297+ end )
1298+
1299+ # calculate the score based on formula {ans_id, score}
1300+ Enum . map (
1301+ entry_vote_data ,
1302+ fn { ans_id , { sum_of_scores , number_of_voters , tokens } } ->
1303+ { ans_id ,
1304+ calculate_normalized_score ( sum_of_scores , number_of_voters , tokens , token_divider ) }
1305+ end
1306+ )
1307+ end
1308+
12231309 # Calculate the score based on formula
12241310 # score(v,t) = v - 2^(t/token_divider) where v is the normalized_voting_score
12251311 # normalized_voting_score = sum_of_scores / number_of_voters / 10 * 100
12261312 defp calculate_formula_score ( sum_of_scores , number_of_voters , tokens , token_divider ) do
1227- normalized_voting_score = sum_of_scores / number_of_voters / 10 * 100
1313+ normalized_voting_score =
1314+ calculate_normalized_score ( sum_of_scores , number_of_voters , tokens , token_divider )
1315+
12281316 normalized_voting_score - :math . pow ( 2 , min ( 1023.5 , tokens / token_divider ) )
12291317 end
12301318
1319+ # Calculate the normalized score based on formula
1320+ # normalized_voting_score = sum_of_scores / number_of_voters / 10 * 100
1321+ defp calculate_normalized_score ( sum_of_scores , number_of_voters , _tokens , _token_divider ) do
1322+ sum_of_scores / number_of_voters / 10 * 100
1323+ end
1324+
12311325 @ doc """
12321326 Function returning submissions under a grader. This function returns only the
12331327 fields that are exposed in the /grading endpoint.
@@ -1509,7 +1603,8 @@ defmodule Cadet.Assessments do
15091603 |> Enum . map ( fn ans ->
15101604 if ans . question . type == :voting do
15111605 empty_contest_entries = Map . put ( ans . question . question , :contest_entries , [ ] )
1512- empty_contest_leaderboard = Map . put ( empty_contest_entries , :contest_leaderboard , [ ] )
1606+ empty_popular_leaderboard = Map . put ( empty_contest_entries , :popular_leaderboard , [ ] )
1607+ empty_contest_leaderboard = Map . put ( empty_popular_leaderboard , :contest_leaderboard , [ ] )
15131608 question = Map . put ( ans . question , :question , empty_contest_leaderboard )
15141609 Map . put ( ans , :question , question )
15151610 else
0 commit comments