diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index acffe823d..1b338f9a9 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -2297,17 +2297,29 @@ defmodule Cadet.Assessments do _requesting_user = %CourseRegistration{id: grader_id} ) when is_ecto_id(submission_id) and is_ecto_id(question_id) do - answer = + questions = + Question + |> where(question_id: ^question_id) + |> order_by(:display_order) + |> Repo.all() + + answers = Answer - |> where(submission_id: ^submission_id, question_id: ^question_id) + |> where(submission_id: ^submission_id) |> preload([:question, :submission]) - |> Repo.one() + |> order_by(:question_id) + |> Repo.all() + + # the answer we want to target + answer = + answers + |> Enum.find(fn answer -> answer.question_id == question_id end) with {:get, answer} when not is_nil(answer) <- {:get, answer}, {:status, true} <- {:status, answer.submission.student_id == grader_id or answer.submission.status == :submitted} do - GradingJob.grade_answer(answer, answer.question, true) + GradingJob.grade_answer(answer.question, answers, answer, questions, true) {:ok, nil} else {:get, nil} -> diff --git a/lib/cadet/jobs/autograder/grading_job.ex b/lib/cadet/jobs/autograder/grading_job.ex index 78e41bf2f..1ff832641 100644 --- a/lib/cadet/jobs/autograder/grading_job.ex +++ b/lib/cadet/jobs/autograder/grading_job.ex @@ -123,10 +123,16 @@ defmodule Cadet.Autograder.GradingJob do end end - def grade_answer(answer = %Answer{}, question = %Question{type: type}, overwrite \\ false) do + def grade_answer( + question = %Question{type: type}, + answers, + answer = %Answer{}, + questions, + overwrite \\ false + ) do case type do :programming -> - Utilities.dispatch_programming_answer(answer, question, overwrite) + Utilities.dispatch_programming_answer(question, answers, answer, questions, overwrite) :mcq -> grade_mcq_answer(answer, question) @@ -210,7 +216,7 @@ defmodule Cadet.Autograder.GradingJob do defp grade_submission_question_answer_lists( submission_id, - [question = %Question{} | question_tail], + questions = [question = %Question{} | question_tail], answers = [answer = %Answer{} | answer_tail], regrade, overwrite @@ -218,7 +224,7 @@ defmodule Cadet.Autograder.GradingJob do when is_boolean(regrade) and is_boolean(overwrite) and is_ecto_id(submission_id) do if question.id == answer.question_id do if regrade || answer.autograding_status in [:none, :failed] do - grade_answer(answer, question, overwrite) + grade_answer(question, answers, answer, questions, overwrite) end grade_submission_question_answer_lists( diff --git a/lib/cadet/jobs/autograder/lambda_worker.ex b/lib/cadet/jobs/autograder/lambda_worker.ex index a2a6d3f7a..2a9e326e7 100644 --- a/lib/cadet/jobs/autograder/lambda_worker.ex +++ b/lib/cadet/jobs/autograder/lambda_worker.ex @@ -12,12 +12,25 @@ defmodule Cadet.Autograder.LambdaWorker do alias Cadet.Autograder.ResultStoreWorker alias Cadet.Assessments.{Answer, Question} + @type entrypoint_file :: String.t() + @doc """ This Que callback transforms an input of %{question: %Question{}, answer: %Answer{}} into the correct shape to dispatch to lambda, waits for the response, parses it, and enqueues a storage job. """ - def perform(params = %{answer: answer = %Answer{}, question: %Question{}}) do + def perform(%{ + base_question: base_question = %Question{}, + questions: questions, + answers: answers, + answer: answer = %Answer{} + }) do + params = %{ + base_question: base_question, + questions: questions, + answers: answers + } + lambda_params = build_request_params(params) if Enum.empty?(lambda_params.testcases) do @@ -42,7 +55,15 @@ defmodule Cadet.Autograder.LambdaWorker do end end - def on_failure(%{answer: answer = %Answer{}, question: %Question{}}, error) do + def on_failure( + %{ + base_question: base_question = %Question{}, + questions: questions, + answers: answers, + answer: answer = %Answer{} + }, + error + ) do error_message = "Failed to get autograder result. answer_id: #{answer.id}, error: #{inspect(error, pretty: true)}" @@ -74,28 +95,48 @@ defmodule Cadet.Autograder.LambdaWorker do ) end - def build_request_params(%{question: question = %Question{}, answer: answer = %Answer{}}) do - question_content = question.question - + # base_question is the actual question that this request grades on + def build_request_params(%{ + base_question: base_question = %Question{}, + questions: questions, + answers: answers + }) do {_, upcased_name_external} = - question.grading_library.external + base_question.grading_library.external |> Map.from_struct() |> Map.get_and_update( :name, &{&1, &1 |> String.upcase()} ) + filesContent = + Enum.zip(questions, answers) + |> Enum.reduce(%{}, fn {question, answer}, acc -> + question_content = question.question + file_name = Integer.to_string(question.display_order) <> ".js" + + final_answer_content = + (Map.get(question_content, "prepend") || "") <> + (Map.get(answer.answer, "code") || "") <> + (Map.get(question_content, "postpend") || "") + + Map.put(acc, file_name, final_answer_content) + end) + + base_question_content = base_question.question + # test %{ - prependProgram: Map.get(question_content, "prepend", ""), - studentProgram: Map.get(answer.answer, "code"), - postpendProgram: Map.get(question_content, "postpend", ""), + files: filesContent, + # entrypointFile is the base question's question number + entrypointFile: Integer.to_string(base_question_content.display_order) <> ".js", testcases: - Map.get(question_content, "public", []) ++ - Map.get(question_content, "opaque", []) ++ Map.get(question_content, "secret", []), + Map.get(base_question_content, "public", []) ++ + Map.get(base_question_content, "opaque", []) ++ + Map.get(base_question_content, "secret", []), library: %{ - chapter: question.grading_library.chapter, + chapter: base_question.grading_library.chapter, external: upcased_name_external, - globals: Enum.map(question.grading_library.globals, fn {k, v} -> [k, v] end) + globals: Enum.map(base_question.grading_library.globals, fn {k, v} -> [k, v] end) } } end diff --git a/lib/cadet/jobs/autograder/utilities.ex b/lib/cadet/jobs/autograder/utilities.ex index 32208e0cf..a7367f694 100644 --- a/lib/cadet/jobs/autograder/utilities.ex +++ b/lib/cadet/jobs/autograder/utilities.ex @@ -11,7 +11,13 @@ defmodule Cadet.Autograder.Utilities do alias Cadet.Accounts.CourseRegistration alias Cadet.Assessments.{Answer, Assessment, Question, Submission} - def dispatch_programming_answer(answer = %Answer{}, question = %Question{}, overwrite \\ false) do + def dispatch_programming_answer( + base_question = %Question{}, + answers, + answer = %Answer{}, + questions, + overwrite \\ false + ) do # This should never fail answer = answer @@ -19,8 +25,10 @@ defmodule Cadet.Autograder.Utilities do |> Repo.update!() Que.add(Cadet.Autograder.LambdaWorker, %{ - question: question, + base_question: base_question, answer: answer, + questions: questions, + answers: answers, overwrite: overwrite }) end