@@ -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 ( )
0 commit comments