Skip to content

Commit 9d6ff07

Browse files
authored
Merge branch 'master' into sv
2 parents 686c135 + fdeb111 commit 9d6ff07

File tree

4 files changed

+389
-100
lines changed

4 files changed

+389
-100
lines changed

config/config.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ config :cadet, Cadet.Jobs.Scheduler,
2323
# Compute contest leaderboard that close in the previous day at 00:01
2424
{"1 0 * * *", {Cadet.Assessments, :update_final_contest_leaderboards, []}},
2525
# Compute rolling leaderboard every 2 hours
26-
{"0 */2 * * *", {Cadet.Assessments, :update_rolling_contest_leaderboards, []}}
26+
{"0 */2 * * *", {Cadet.Assessments, :update_rolling_contest_leaderboards, []}},
27+
# Collate contest entries that close in the previous day at 00:01
28+
{"1 0 * * *", {Cadet.Assessments, :update_final_contest_entries, []}}
2729
]
2830

2931
# Configures the endpoint

lib/cadet/assessments/assessments.ex

Lines changed: 135 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ defmodule Cadet.Assessments do
2323
alias Cadet.ProgramAnalysis.Lexer
2424
alias Ecto.Multi
2525
alias Cadet.Incentives.Achievements
26+
alias Timex.Duration
2627

2728
require Decimal
2829

@@ -500,6 +501,35 @@ defmodule Cadet.Assessments do
500501
Question.changeset(%Question{}, params_with_assessment_id)
501502
end
502503

504+
def update_final_contest_entries do
505+
# 1435 = 1 day - 5 minutes
506+
if Log.log_execution("update_final_contest_entries", Duration.from_minutes(1435)) do
507+
Logger.info("Started update of contest entry pools")
508+
questions = fetch_unassigned_voting_questions()
509+
510+
for q <- questions do
511+
insert_voting(q.course_id, q.question["contest_number"], q.question_id)
512+
end
513+
514+
Logger.info("Successfully update contest entry pools")
515+
end
516+
end
517+
518+
# fetch voting questions where entries have not been assigned
519+
def fetch_unassigned_voting_questions do
520+
voting_assigned_question_ids =
521+
SubmissionVotes
522+
|> select([v], v.question_id)
523+
|> Repo.all()
524+
525+
Question
526+
|> where(type: :voting)
527+
|> where([q], q.id not in ^voting_assigned_question_ids)
528+
|> join(:inner, [q], asst in assoc(q, :assessment))
529+
|> select([q, asst], %{course_id: asst.course_id, question: q.question, question_id: q.id})
530+
|> Repo.all()
531+
end
532+
503533
@doc """
504534
Generates and assigns contest entries for users with given usernames.
505535
"""
@@ -522,102 +552,119 @@ defmodule Cadet.Assessments do
522552

523553
{:error, error_changeset}
524554
else
525-
# Returns contest submission ids with answers that contain "return"
526-
contest_submission_ids =
527-
Submission
528-
|> join(:inner, [s], ans in assoc(s, :answers))
529-
|> join(:inner, [s, ans], cr in assoc(s, :student))
530-
|> where([s, ans, cr], cr.role == "student")
531-
|> where([s, _], s.assessment_id == ^contest_assessment.id and s.status == "submitted")
532-
|> where(
533-
[_, ans, cr],
534-
fragment(
535-
"?->>'code' like ?",
536-
ans.answer,
537-
"%return%"
538-
)
555+
if Timex.compare(contest_assessment.close_at, Timex.now()) < 0 do
556+
compile_entries(course_id, contest_assessment, question_id)
557+
else
558+
# contest has not closed, do nothing
559+
{:ok, nil}
560+
end
561+
end
562+
end
563+
564+
def compile_entries(
565+
course_id,
566+
contest_assessment,
567+
question_id
568+
) do
569+
# Returns contest submission ids with answers that contain "return"
570+
contest_submission_ids =
571+
Submission
572+
|> join(:inner, [s], ans in assoc(s, :answers))
573+
|> join(:inner, [s, ans], cr in assoc(s, :student))
574+
|> where([s, ans, cr], cr.role == "student")
575+
|> where([s, _], s.assessment_id == ^contest_assessment.id and s.status == "submitted")
576+
|> where(
577+
[_, ans, cr],
578+
fragment(
579+
"?->>'code' like ?",
580+
ans.answer,
581+
"%return%"
539582
)
540-
|> select([s, _ans], {s.student_id, s.id})
541-
|> Repo.all()
542-
|> Enum.into(%{})
583+
)
584+
|> select([s, _ans], {s.student_id, s.id})
585+
|> Repo.all()
586+
|> Enum.into(%{})
543587

544-
contest_submission_ids_length = Enum.count(contest_submission_ids)
588+
contest_submission_ids_length = Enum.count(contest_submission_ids)
545589

546-
voter_ids =
547-
CourseRegistration
548-
|> where(role: "student", course_id: ^course_id)
549-
|> select([cr], cr.id)
550-
|> Repo.all()
590+
voter_ids =
591+
CourseRegistration
592+
|> where(role: "student", course_id: ^course_id)
593+
|> select([cr], cr.id)
594+
|> Repo.all()
551595

552-
votes_per_user = min(contest_submission_ids_length, 10)
596+
votes_per_user = min(contest_submission_ids_length, 10)
553597

554-
votes_per_submission =
555-
if Enum.empty?(contest_submission_ids) do
556-
0
557-
else
558-
trunc(Float.ceil(votes_per_user * length(voter_ids) / contest_submission_ids_length))
559-
end
598+
votes_per_submission =
599+
if Enum.empty?(contest_submission_ids) do
600+
0
601+
else
602+
trunc(Float.ceil(votes_per_user * length(voter_ids) / contest_submission_ids_length))
603+
end
560604

561-
submission_id_list =
562-
contest_submission_ids
563-
|> Enum.map(fn {_, s_id} -> s_id end)
564-
|> Enum.shuffle()
565-
|> List.duplicate(votes_per_submission)
566-
|> List.flatten()
567-
568-
{_submission_map, submission_votes_changesets} =
569-
voter_ids
570-
|> Enum.reduce({submission_id_list, []}, fn voter_id, acc ->
571-
{submission_list, submission_votes} = acc
572-
573-
user_contest_submission_id = Map.get(contest_submission_ids, voter_id)
574-
575-
{votes, rest} =
576-
submission_list
577-
|> Enum.reduce_while({MapSet.new(), submission_list}, fn s_id, acc ->
578-
{user_votes, submissions} = acc
579-
580-
max_votes =
581-
if votes_per_user == contest_submission_ids_length and
582-
not is_nil(user_contest_submission_id) do
583-
# no. of submssions is less than 10. Unable to find
584-
votes_per_user - 1
585-
else
586-
votes_per_user
587-
end
588-
589-
if MapSet.size(user_votes) < max_votes do
590-
if s_id != user_contest_submission_id and not MapSet.member?(user_votes, s_id) do
591-
new_user_votes = MapSet.put(user_votes, s_id)
592-
new_submissions = List.delete(submissions, s_id)
593-
{:cont, {new_user_votes, new_submissions}}
594-
else
595-
{:cont, {user_votes, submissions}}
596-
end
605+
submission_id_list =
606+
contest_submission_ids
607+
|> Enum.map(fn {_, s_id} -> s_id end)
608+
|> Enum.shuffle()
609+
|> List.duplicate(votes_per_submission)
610+
|> List.flatten()
611+
612+
{_submission_map, submission_votes_changesets} =
613+
voter_ids
614+
|> Enum.reduce({submission_id_list, []}, fn voter_id, acc ->
615+
{submission_list, submission_votes} = acc
616+
617+
user_contest_submission_id = Map.get(contest_submission_ids, voter_id)
618+
619+
{votes, rest} =
620+
submission_list
621+
|> Enum.reduce_while({MapSet.new(), submission_list}, fn s_id, acc ->
622+
{user_votes, submissions} = acc
623+
624+
max_votes =
625+
if votes_per_user == contest_submission_ids_length and
626+
not is_nil(user_contest_submission_id) do
627+
# no. of submssions is less than 10. Unable to find
628+
votes_per_user - 1
597629
else
598-
{:halt, acc}
630+
votes_per_user
599631
end
600-
end)
601632

602-
votes = MapSet.to_list(votes)
603-
604-
new_submission_votes =
605-
votes
606-
|> Enum.map(fn s_id ->
607-
%SubmissionVotes{voter_id: voter_id, submission_id: s_id, question_id: question_id}
608-
end)
609-
|> Enum.concat(submission_votes)
610-
611-
{rest, new_submission_votes}
612-
end)
613-
614-
submission_votes_changesets
615-
|> Enum.with_index()
616-
|> Enum.reduce(Multi.new(), fn {changeset, index}, multi ->
617-
Multi.insert(multi, Integer.to_string(index), changeset)
633+
if MapSet.size(user_votes) < max_votes do
634+
if s_id != user_contest_submission_id and not MapSet.member?(user_votes, s_id) do
635+
new_user_votes = MapSet.put(user_votes, s_id)
636+
new_submissions = List.delete(submissions, s_id)
637+
{:cont, {new_user_votes, new_submissions}}
638+
else
639+
{:cont, {user_votes, submissions}}
640+
end
641+
else
642+
{:halt, acc}
643+
end
644+
end)
645+
646+
votes = MapSet.to_list(votes)
647+
648+
new_submission_votes =
649+
votes
650+
|> Enum.map(fn s_id ->
651+
%SubmissionVotes{
652+
voter_id: voter_id,
653+
submission_id: s_id,
654+
question_id: question_id
655+
}
656+
end)
657+
|> Enum.concat(submission_votes)
658+
659+
{rest, new_submission_votes}
618660
end)
619-
|> Repo.transaction()
620-
end
661+
662+
submission_votes_changesets
663+
|> Enum.with_index()
664+
|> Enum.reduce(Multi.new(), fn {changeset, index}, multi ->
665+
Multi.insert(multi, Integer.to_string(index), changeset)
666+
end)
667+
|> Repo.transaction()
621668
end
622669

623670
def update_assessment(id, params) when is_ecto_id(id) do
@@ -1026,7 +1073,7 @@ defmodule Cadet.Assessments do
10261073
"""
10271074
def update_rolling_contest_leaderboards do
10281075
# 115 = 2 hours - 5 minutes is default.
1029-
if Log.log_execution("update_rolling_contest_leaderboards", Timex.Duration.from_minutes(115)) do
1076+
if Log.log_execution("update_rolling_contest_leaderboards", Duration.from_minutes(115)) do
10301077
Logger.info("Started update_rolling_contest_leaderboards")
10311078

10321079
voting_questions_to_update = fetch_active_voting_questions()
@@ -1053,7 +1100,7 @@ defmodule Cadet.Assessments do
10531100
"""
10541101
def update_final_contest_leaderboards do
10551102
# 1435 = 24 hours - 5 minutes
1056-
if Log.log_execution("update_final_contest_leaderboards", Timex.Duration.from_minutes(1435)) do
1103+
if Log.log_execution("update_final_contest_leaderboards", Duration.from_minutes(1435)) do
10571104
Logger.info("Started update_final_contest_leaderboards")
10581105

10591106
voting_questions_to_update = fetch_voting_questions_due_yesterday()

mix.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@
4747
"gen_stage": {:hex, :gen_stage, "1.1.2", "b1656cd4ba431ed02c5656fe10cb5423820847113a07218da68eae5d6a260c23", [:mix], [], "hexpm", "9e39af23140f704e2b07a3e29d8f05fd21c2aaf4088ff43cb82be4b9e3148d02"},
4848
"gettext": {:hex, :gettext, "0.22.3", "c8273e78db4a0bb6fba7e9f0fd881112f349a3117f7f7c598fa18c66c888e524", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "935f23447713954a6866f1bb28c3a878c4c011e802bcd68a726f5e558e4b64bd"},
4949
"git_hooks": {:hex, :git_hooks, "0.7.3", "09489e94d88dfc767662e22aff2b6208bd7cf555a19dd0e1477cca4683ce0701", [:mix], [{:blankable, "~> 1.0.0", [hex: :blankable, repo: "hexpm", optional: false]}, {:recase, "~> 0.7.0", [hex: :recase, repo: "hexpm", optional: false]}], "hexpm", "d6ddedeb4d3a8602bc3f84e087a38f6150a86d9e790628ed8bc70e6d90681659"},
50-
"guardian": {:hex, :guardian, "2.3.1", "2b2d78dc399a7df182d739ddc0e566d88723299bfac20be36255e2d052fd215d", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bbe241f9ca1b09fad916ad42d6049d2600bbc688aba5b3c4a6c82592a54274c3"},
50+
"guardian": {:hex, :guardian, "2.3.2", "78003504b987f2b189d76ccf9496ceaa6a454bb2763627702233f31eb7212881", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b189ff38cd46a22a8a824866a6867ca8722942347f13c33f7d23126af8821b52"},
5151
"guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"},
5252
"hackney": {:hex, :hackney, "1.18.2", "d7ff544ddae5e1cb49e9cf7fa4e356d7f41b283989a1c304bfc47a8cc1cf966f", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "af94d5c9f97857db257090a4a10e5426ecb6f4918aa5cc666798566ae14b65fd"},
5353
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
5454
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
5555
"inch_ex": {:hex, :inch_ex, "2.1.0-rc.1", "7642a8902c0d2ed5d9b5754b2fc88fedf630500d630fc03db7caca2e92dedb36", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4ceee988760f9382d1c1d0b93ea5875727f6071693e89a0a3c49c456ef1be75d"},
5656
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
57-
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
57+
"jose": {:hex, :jose, "1.11.6", "613fda82552128aa6fb804682e3a616f4bc15565a048dabd05b1ebd5827ed965", [:mix, :rebar3], [], "hexpm", "6275cb75504f9c1e60eeacb771adfeee4905a9e182103aa59b53fed651ff9738"},
5858
"jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"},
5959
"mail": {:hex, :mail, "0.2.3", "2c6bb5f8a5f74845fa50ecd0fb45ea16b164026f285f45104f1c4c078cd616d4", [:mix], [], "hexpm", "932b398fa9c69fdf290d7ff63175826e0f1e24414d5b0763bb00a2acfc6c6bf5"},
6060
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},

0 commit comments

Comments
 (0)