diff --git a/lib/cadet/assessments/assessments.ex b/lib/cadet/assessments/assessments.ex index 6bb932160..55b413fe5 100644 --- a/lib/cadet/assessments/assessments.ex +++ b/lib/cadet/assessments/assessments.ex @@ -1345,7 +1345,8 @@ defmodule Cadet.Assessments do end @spec get_answers_in_submission(integer() | String.t()) :: - {:ok, [Answer.t()]} | {:error, {:bad_request, String.t()}} + {:ok, {[Answer.t()], Assessment.t()}} + | {:error, {:bad_request, String.t()}} def get_answers_in_submission(id) when is_ecto_id(id) do answer_query = Answer @@ -1382,7 +1383,9 @@ defmodule Cadet.Assessments do if answers == [] do {:error, {:bad_request, "Submission is not found."}} else - {:ok, answers} + assessment_id = Submission |> where(id: ^id) |> select([s], s.assessment_id) |> Repo.one() + assessment = Assessment |> where(id: ^assessment_id) |> Repo.one() + {:ok, {answers, assessment}} end end diff --git a/lib/cadet_web/admin_controllers/admin_grading_controller.ex b/lib/cadet_web/admin_controllers/admin_grading_controller.ex index caa9771a1..24b915371 100644 --- a/lib/cadet_web/admin_controllers/admin_grading_controller.ex +++ b/lib/cadet_web/admin_controllers/admin_grading_controller.ex @@ -24,8 +24,8 @@ defmodule CadetWeb.AdminGradingController do def show(conn, %{"submissionid" => submission_id}) when is_ecto_id(submission_id) do case Assessments.get_answers_in_submission(submission_id) do - {:ok, answers} -> - render(conn, "show.json", answers: answers) + {:ok, {answers, assessment}} -> + render(conn, "show.json", answers: answers, assessment: assessment) {:error, {status, message}} -> conn diff --git a/lib/cadet_web/admin_views/admin_grading_view.ex b/lib/cadet_web/admin_views/admin_grading_view.ex index 128bfb59d..c9418ef9a 100644 --- a/lib/cadet_web/admin_views/admin_grading_view.ex +++ b/lib/cadet_web/admin_views/admin_grading_view.ex @@ -3,8 +3,25 @@ defmodule CadetWeb.AdminGradingView do import CadetWeb.AssessmentsHelpers - def render("show.json", %{answers: answers}) do - render_many(answers, CadetWeb.AdminGradingView, "grading_info.json", as: :answer) + def render("show.json", %{answers: answers, assessment: assessment}) do + %{ + assessment: + render_one(assessment, CadetWeb.AdminGradingView, "assessment.json", as: :assessment), + answers: render_many(answers, CadetWeb.AdminGradingView, "grading_info.json", as: :answer) + } + end + + def render("assessment.json", %{assessment: assessment}) do + %{ + id: assessment.id, + title: assessment.title, + summaryShort: assessment.summary_short, + summaryLong: assessment.summary_long, + coverPicture: assessment.cover_picture, + number: assessment.number, + story: assessment.story, + reading: assessment.reading + } end def render("gradingsummaries.json", %{ diff --git a/test/cadet_web/admin_controllers/admin_grading_controller_test.exs b/test/cadet_web/admin_controllers/admin_grading_controller_test.exs index e2491051f..a42131c19 100644 --- a/test/cadet_web/admin_controllers/admin_grading_controller_test.exs +++ b/test/cadet_web/admin_controllers/admin_grading_controller_test.exs @@ -219,180 +219,193 @@ defmodule CadetWeb.AdminGradingControllerTest do course: course, grader: grader, submissions: submissions, - answers: answers + answers: answers, + mission: assessment } = seed_db(conn) submission = List.first(submissions) conn = get(conn, build_url(course.id, submission.id)) - expected = - answers - |> Enum.filter(&(&1.submission.id == submission.id)) - |> Enum.sort_by(& &1.question.display_order) - |> Enum.map( - &case &1.question.type do - :programming -> - %{ - "question" => %{ - "prepend" => &1.question.question.prepend, - "postpend" => &1.question.question.postpend, - "testcases" => - Enum.map( - &1.question.question.public, - fn testcase -> - for {k, v} <- testcase, - into: %{"type" => "public"}, - do: {Atom.to_string(k), v} - end - ) ++ + expected = %{ + "assessment" => %{ + "id" => assessment.id, + "title" => assessment.title, + "summaryShort" => assessment.summary_short, + "summaryLong" => assessment.summary_long, + "coverPicture" => assessment.cover_picture, + "number" => assessment.number, + "story" => assessment.story, + "reading" => assessment.reading + }, + "answers" => + answers + |> Enum.filter(&(&1.submission.id == submission.id)) + |> Enum.sort_by(& &1.question.display_order) + |> Enum.map( + &case &1.question.type do + :programming -> + %{ + "question" => %{ + "prepend" => &1.question.question.prepend, + "postpend" => &1.question.question.postpend, + "testcases" => Enum.map( - &1.question.question.opaque, + &1.question.question.public, fn testcase -> for {k, v} <- testcase, - into: %{"type" => "opaque"}, + into: %{"type" => "public"}, do: {Atom.to_string(k), v} end ) ++ - Enum.map( - &1.question.question.secret, - fn testcase -> - for {k, v} <- testcase, - into: %{"type" => "secret"}, - do: {Atom.to_string(k), v} - end - ), - "solutionTemplate" => &1.question.question.template, - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + Enum.map( + &1.question.question.opaque, + fn testcase -> + for {k, v} <- testcase, + into: %{"type" => "opaque"}, + do: {Atom.to_string(k), v} + end + ) ++ + Enum.map( + &1.question.question.secret, + fn testcase -> + for {k, v} <- testcase, + into: %{"type" => "secret"}, + do: {Atom.to_string(k), v} + end + ), + "solutionTemplate" => &1.question.question.template, + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "maxXp" => &1.question.max_xp, + "content" => &1.question.question.content, + "answer" => &1.answer.code, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results }, - "maxXp" => &1.question.max_xp, - "content" => &1.question.question.content, - "answer" => &1.answer.code, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results - }, - "solution" => &1.question.question.solution, - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "solution" => &1.question.question.solution, + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + } } - } - - :mcq -> - %{ - "question" => %{ - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + + :mcq -> + %{ + "question" => %{ + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "maxXp" => &1.question.max_xp, + "content" => &1.question.question.content, + "answer" => &1.answer.choice_id, + "choices" => + for choice <- &1.question.question.choices do + %{ + "content" => choice.content, + "hint" => choice.hint, + "id" => choice.choice_id + } + end, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results }, - "maxXp" => &1.question.max_xp, - "content" => &1.question.question.content, - "answer" => &1.answer.choice_id, - "choices" => - for choice <- &1.question.question.choices do - %{ - "content" => choice.content, - "hint" => choice.hint, - "id" => choice.choice_id - } - end, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results - }, - "solution" => "", - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "solution" => "", + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + } } - } - - :voting -> - %{ - "question" => %{ - "prepend" => &1.question.question.prepend, - "solutionTemplate" => &1.question.question.template, - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + + :voting -> + %{ + "question" => %{ + "prepend" => &1.question.question.prepend, + "solutionTemplate" => &1.question.question.template, + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "maxXp" => &1.question.max_xp, + "content" => &1.question.question.content, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results, + "answer" => nil, + "contestEntries" => [], + "scoreLeaderboard" => [] }, - "maxXp" => &1.question.max_xp, - "content" => &1.question.question.content, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results, - "answer" => nil, - "contestEntries" => [], - "scoreLeaderboard" => [] - }, - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id - }, - "solution" => "" - } - end - ) + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + }, + "solution" => "" + } + end + ) + } assert expected == json_response(conn, 200) end @@ -866,180 +879,192 @@ defmodule CadetWeb.AdminGradingControllerTest do course: course, grader: grader, submissions: submissions, - answers: answers + answers: answers, + mission: assessment } = seed_db(conn) submission = List.first(submissions) - conn = get(conn, build_url(course.id, submission.id)) - expected = - answers - |> Enum.filter(&(&1.submission.id == submission.id)) - |> Enum.sort_by(& &1.question.display_order) - |> Enum.map( - &case &1.question.type do - :programming -> - %{ - "question" => %{ - "prepend" => &1.question.question.prepend, - "postpend" => &1.question.question.postpend, - "testcases" => - Enum.map( - &1.question.question.public, - fn testcase -> - for {k, v} <- testcase, - into: %{"type" => "public"}, - do: {Atom.to_string(k), v} - end - ) ++ + expected = %{ + "assessment" => %{ + "id" => assessment.id, + "title" => assessment.title, + "summaryShort" => assessment.summary_short, + "summaryLong" => assessment.summary_long, + "coverPicture" => assessment.cover_picture, + "number" => assessment.number, + "story" => assessment.story, + "reading" => assessment.reading + }, + "answers" => + answers + |> Enum.filter(&(&1.submission.id == submission.id)) + |> Enum.sort_by(& &1.question.display_order) + |> Enum.map( + &case &1.question.type do + :programming -> + %{ + "question" => %{ + "prepend" => &1.question.question.prepend, + "postpend" => &1.question.question.postpend, + "testcases" => Enum.map( - &1.question.question.opaque, + &1.question.question.public, fn testcase -> for {k, v} <- testcase, - into: %{"type" => "opaque"}, + into: %{"type" => "public"}, do: {Atom.to_string(k), v} end ) ++ - Enum.map( - &1.question.question.secret, - fn testcase -> - for {k, v} <- testcase, - into: %{"type" => "secret"}, - do: {Atom.to_string(k), v} - end - ), - "solutionTemplate" => &1.question.question.template, - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + Enum.map( + &1.question.question.opaque, + fn testcase -> + for {k, v} <- testcase, + into: %{"type" => "opaque"}, + do: {Atom.to_string(k), v} + end + ) ++ + Enum.map( + &1.question.question.secret, + fn testcase -> + for {k, v} <- testcase, + into: %{"type" => "secret"}, + do: {Atom.to_string(k), v} + end + ), + "solutionTemplate" => &1.question.question.template, + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "maxXp" => &1.question.max_xp, + "content" => &1.question.question.content, + "answer" => &1.answer.code, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results }, - "maxXp" => &1.question.max_xp, - "content" => &1.question.question.content, - "answer" => &1.answer.code, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results - }, - "solution" => &1.question.question.solution, - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "solution" => &1.question.question.solution, + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + } } - } - - :mcq -> - %{ - "question" => %{ - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + + :mcq -> + %{ + "question" => %{ + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "content" => &1.question.question.content, + "answer" => &1.answer.choice_id, + "maxXp" => &1.question.max_xp, + "choices" => + for choice <- &1.question.question.choices do + %{ + "content" => choice.content, + "hint" => choice.hint, + "id" => choice.choice_id + } + end, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results }, - "content" => &1.question.question.content, - "answer" => &1.answer.choice_id, - "maxXp" => &1.question.max_xp, - "choices" => - for choice <- &1.question.question.choices do - %{ - "content" => choice.content, - "hint" => choice.hint, - "id" => choice.choice_id - } - end, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results - }, - "solution" => "", - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "solution" => "", + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + } } - } - - :voting -> - %{ - "question" => %{ - "prepend" => &1.question.question.prepend, - "solutionTemplate" => &1.question.question.template, - "type" => "#{&1.question.type}", - "blocking" => &1.question.blocking, - "id" => &1.question.id, - "library" => %{ - "chapter" => &1.question.library.chapter, - "globals" => &1.question.library.globals, - "external" => %{ - "name" => "#{&1.question.library.external.name}", - "symbols" => &1.question.library.external.symbols + + :voting -> + %{ + "question" => %{ + "prepend" => &1.question.question.prepend, + "solutionTemplate" => &1.question.question.template, + "type" => "#{&1.question.type}", + "blocking" => &1.question.blocking, + "id" => &1.question.id, + "library" => %{ + "chapter" => &1.question.library.chapter, + "globals" => &1.question.library.globals, + "external" => %{ + "name" => "#{&1.question.library.external.name}", + "symbols" => &1.question.library.external.symbols + }, + "execTimeMs" => &1.question.library.exec_time_ms, + "variant" => &1.question.library.variant }, - "execTimeMs" => &1.question.library.exec_time_ms, - "variant" => &1.question.library.variant + "maxXp" => &1.question.max_xp, + "content" => &1.question.question.content, + "autogradingStatus" => Atom.to_string(&1.autograding_status), + "autogradingResults" => &1.autograding_results, + "answer" => nil, + "contestEntries" => [], + "scoreLeaderboard" => [] }, - "maxXp" => &1.question.max_xp, - "content" => &1.question.question.content, - "autogradingStatus" => Atom.to_string(&1.autograding_status), - "autogradingResults" => &1.autograding_results, - "answer" => nil, - "contestEntries" => [], - "scoreLeaderboard" => [] - }, - "grade" => %{ - "xp" => &1.xp, - "xpAdjustment" => &1.xp_adjustment, - "grader" => %{ - "name" => grader.user.name, - "id" => grader.id + "grade" => %{ + "xp" => &1.xp, + "xpAdjustment" => &1.xp_adjustment, + "grader" => %{ + "name" => grader.user.name, + "id" => grader.id + }, + "gradedAt" => format_datetime(&1.updated_at), + "comments" => &1.comments }, - "gradedAt" => format_datetime(&1.updated_at), - "comments" => &1.comments - }, - "student" => %{ - "name" => &1.submission.student.user.name, - "username" => &1.submission.student.user.username, - "id" => &1.submission.student.id - }, - "solution" => "" - } - end - ) + "student" => %{ + "name" => &1.submission.student.user.name, + "username" => &1.submission.student.user.username, + "id" => &1.submission.student.id + }, + "solution" => "" + } + end + ) + } assert expected == json_response(conn, 200) end