diff --git a/lib/cadet_web/controllers/answer_controller.ex b/lib/cadet_web/controllers/answer_controller.ex new file mode 100644 index 000000000..05df41f0f --- /dev/null +++ b/lib/cadet_web/controllers/answer_controller.ex @@ -0,0 +1,42 @@ +defmodule CadetWeb.AnswerController do + use CadetWeb, :controller + + use PhoenixSwagger + + swagger_path :submit do + post("/assessments/question/{questionId}/submit") + + summary("Submit an answer to a question.") + + description( + "For MCQ, answer contains choice_id. For programming question, this is a string containing the student's code." + ) + + security([%{JWT: []}]) + + consumes("application/json") + + parameters do + answer(:body, Schema.ref(:Answer), "answer", required: true) + end + + response(200, "OK") + response(400, "Missing parameter(s) or wrong answer type") + response(401, "Unauthorised") + end + + def swagger_definitions do + %{ + Answer: + swagger_schema do + properties do + answer( + :string_or_int, + "answer of appropriate type depending on question type", + required: true + ) + end + end + } + end +end diff --git a/lib/cadet_web/controllers/assessments_controller.ex b/lib/cadet_web/controllers/assessments_controller.ex new file mode 100644 index 000000000..c22033c82 --- /dev/null +++ b/lib/cadet_web/controllers/assessments_controller.ex @@ -0,0 +1,164 @@ +defmodule CadetWeb.AssessmentsController do + use CadetWeb, :controller + + use PhoenixSwagger + + swagger_path :index do + get("/assessments") + + summary("Get a list of all assessments") + + security([%{JWT: []}]) + + produces("application/json") + + response(200, "OK", Schema.ref(:AssessmentsList)) + response(401, "Unauthorised") + end + + swagger_path :show do + get("/assessments/{assessmentId}") + + summary("Get information about one particular assessment.") + + security([%{JWT: []}]) + + produces("application/json") + + parameters do + assessmentId(:path, :integer, "assessment id", required: true) + end + + response(200, "OK", Schema.ref(:Assessment)) + response(400, "Missing parameter(s) or invalid assessmentId") + response(401, "Unauthorised") + end + + def swagger_definitions do + %{ + AssessmentsList: + swagger_schema do + description("A list of all assessments") + type(:array) + items(Schema.ref(:AssessmentOverview)) + end, + AssessmentOverview: + swagger_schema do + properties do + id(:integer, "The assessment id", required: true) + title(:string, "The title of the assessment", required: true) + type(:string, "Either mission/sidequest/path/contest", required: true) + summary_short(:string, "Short summary", required: true) + open_at(:string, "The opening date", format: "date-time", required: true) + close_at(:string, "The closing date", format: "date-time", required: true) + + max_xp( + :integer, + "The maximum amount of XP to be earned from this assessment", + required: true + ) + + cover_picture(:string, "The URL to the cover picture", required: true) + end + end, + Assessment: + swagger_schema do + properties do + id(:integer, "The assessment id", required: true) + title(:string, "The title of the assessment", required: true) + type(:string, "Either mission/sidequest/path/contest", required: true) + summary_long(:string, "Long summary", required: true) + mission_pdf(:string, "The URL to the assessment pdf") + + questions(Schema.ref(:Questions), "The list of questions for this assessment") + end + end, + Questions: + swagger_schema do + description("A list of questions") + type(:array) + items(Schema.ref(:Question)) + end, + Question: + swagger_schema do + properties do + questionId(:integer, "The question id", required: true) + questionType(:string, "The question type (mcq/programming)", required: true) + content(:string, "The question content", required: true) + + choices( + Schema.new do + type(:array) + items(Schema.ref(:MCQChoice)) + end, + "MCQ choices if question type is mcq" + ) + + solution(:integer, "Solution to a mcq question if it belongs to path assessment") + + answer( + :string_or_integer, + "Previous answer for this quesiton (string/int) depending on question type", + required: true + ) + + library( + Schema.ref(:Library), + "The library used for this question (programming questions only)" + ) + + solution_template(:string, "Solution template for programming questions") + end + end, + MCQChoice: + swagger_schema do + properties do + content(:string, "The choice content", required: true) + hint(:string, "The hint", required: true) + end + end, + Library: + swagger_schema do + properties do + chapter(:integer) + + globals( + Schema.new do + type(:array) + + items( + Schema.new do + type(:string) + end + ) + end + ) + + externals( + Schema.new do + type(:array) + + items( + Schema.new do + type(:string) + end + ) + end + ) + + files( + Schema.new do + type(:array) + + items( + Schema.new do + type(:string) + end + ) + end + ) + end + end + } + end +end diff --git a/lib/cadet_web/controllers/grading_controller.ex b/lib/cadet_web/controllers/grading_controller.ex new file mode 100644 index 000000000..eb10e796a --- /dev/null +++ b/lib/cadet_web/controllers/grading_controller.ex @@ -0,0 +1,106 @@ +defmodule CadetWeb.GradingController do + use CadetWeb, :controller + + use PhoenixSwagger + + swagger_path :index do + get("/grading") + + summary("Get a list of all submissions with current user as the grader. ") + + security([%{JWT: []}]) + + produces("application/json") + + parameters do + filter(:query, :string, "Filter only specific types e.g. done/pending") + end + + response(200, "OK", Schema.ref(:Submissions)) + response(401, "Unauthorised") + end + + swagger_path :show do + get("/grading/{submissionId}") + + summary("Get information about a specific submission to be graded.") + + security([%{JWT: []}]) + + produces("application/json") + + parameters do + submissionId(:path, :integer, "submission id", required: true) + end + + response(200, "OK", Schema.ref(:GradingInfo)) + response(400, "Invalid or missing parameter(s) or submission and/or question not found") + response(401, "Unauthorised") + end + + swagger_path :update do + post("/grading/{submissionId}/{questionId}") + + summary( + "Update comment and/or marks given to the answer of a particular querstion in a submission" + ) + + security([%{JWT: []}]) + + consumes("application/json") + produces("application/json") + + parameters do + submissionId(:path, :integer, "submission id", required: true) + questionId(:path, :integer, "question id", required: true) + grading(:body, Schema.ref(:Grade), "comment given for a question", required: true) + end + + response(200, "OK") + response(400, "Invalid or missing parameter(s) or submission and/or question not found") + response(401, "Unauthorised") + end + + def swagger_definitions do + %{ + Submissions: + swagger_schema do + type(:array) + items(Schema.ref(:Submission)) + end, + Submission: + swagger_schema do + properties do + submissionId(:integer, "submission id", required: true) + missionId(:integer, "mission id", required: true) + studentId(:integer, "student id", required: true) + end + end, + GradingInfo: + swagger_schema do + description( + "A list of questions with submitted answers and previous grading info if available" + ) + + type(:array) + + items( + Schema.new do + properties do + question(Schema.ref(:Question)) + grade(Schema.ref(:Grade)) + max_xp(:integer, "the max xp that can be given to this question", required: true) + end + end + ) + end, + Grade: + swagger_schema do + properties do + comment(:string, "comment given") + xp(:integer, "xp given") + end + end + } + end +end diff --git a/lib/cadet_web/router.ex b/lib/cadet_web/router.ex index a722aa289..9f5fe8b0b 100644 --- a/lib/cadet_web/router.ex +++ b/lib/cadet_web/router.ex @@ -28,6 +28,13 @@ defmodule CadetWeb.Router do # Authenticated Pages scope "/", CadetWeb do pipe_through([:api, :auth, :ensure_auth]) + + resources("/assessments", AssessmentsController, only: [:index, :show]) + post("/assessments/question/:questionid/submit", AnswerController, :submit) + + get("/grading", GradingController, :index) + get("/grading/:submissionid", GradingController, :show) + post("/grading/:submissionid/:questionid", GradingController, :update) end # Other scopes may use custom stacks. diff --git a/test/cadet_web/controllers/answer_controller_test.exs b/test/cadet_web/controllers/answer_controller_test.exs new file mode 100644 index 000000000..1fbe4f8e1 --- /dev/null +++ b/test/cadet_web/controllers/answer_controller_test.exs @@ -0,0 +1,10 @@ +defmodule CadetWeb.AnswerControllerTest do + use CadetWeb.ConnCase + + alias CadetWeb.AnswerController + + test "swagger" do + AnswerController.swagger_definitions() + AnswerController.swagger_path_submit(nil) + end +end diff --git a/test/cadet_web/controllers/assessments_controller_test.exs b/test/cadet_web/controllers/assessments_controller_test.exs new file mode 100644 index 000000000..2d7963210 --- /dev/null +++ b/test/cadet_web/controllers/assessments_controller_test.exs @@ -0,0 +1,11 @@ +defmodule CadetWeb.AssessmentsControllerTest do + use CadetWeb.ConnCase + + alias CadetWeb.AssessmentsController + + test "swagger" do + AssessmentsController.swagger_definitions() + AssessmentsController.swagger_path_index(nil) + AssessmentsController.swagger_path_show(nil) + end +end diff --git a/test/cadet_web/controllers/grading_controller_test.exs b/test/cadet_web/controllers/grading_controller_test.exs new file mode 100644 index 000000000..f588af8ce --- /dev/null +++ b/test/cadet_web/controllers/grading_controller_test.exs @@ -0,0 +1,12 @@ +defmodule CadetWeb.GradingControllerTest do + use CadetWeb.ConnCase + + alias CadetWeb.GradingController + + test "swagger" do + GradingController.swagger_definitions() + GradingController.swagger_path_index(nil) + GradingController.swagger_path_show(nil) + GradingController.swagger_path_update(nil) + end +end