user(); $userTests = UserTest::where('user_id', $user->id) ->orderBy('id', 'desc') ->with(['user', 'category', 'answers.question']) ->get(); return UserTestResource::collection($userTests); } /** * @OA\Post( * path="/api/user-tests", * summary="Create a new user test", * description="Generate a new UserTest for the authenticated user with questions from a specific category and difficulty range.", * tags={"UserTests"}, * security={{"bearerAuth":{}}}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"category_id"}, * @OA\Property(property="category_id", type="integer", example=3, description="Category from which to select questions"), * @OA\Property(property="min_difficulty", type="integer", nullable=true, example=0, description="Minimum difficulty of questions"), * @OA\Property(property="max_difficulty", type="integer", nullable=true, example=10, description="Maximum difficulty of questions") * ) * ), * @OA\Response( * response=422, * description="Validation error or no questions found" * ) * ) */ public function store(Request $request) { $this->authorize('create', UserTest::class); $fields = $request->validate([ 'category_id' => 'required|exists:categories,id', 'min_difficulty' => 'nullable|integer', 'max_difficulty' => 'nullable|integer', ]); $minDifficulty = $fields['min_difficulty'] ?? 0; $maxDifficulty = $fields['max_difficulty'] ?? 10; $questions = Question::where('category_id', $fields['category_id']) ->whereBetween('difficulty', [$minDifficulty, $maxDifficulty]) ->inRandomOrder() ->take(10) ->get(); if ($questions->isEmpty()) { return response()->json(['message' => 'No questions found for this criteria'], 422); } $userTest = new UserTest(); $userTest->category_id = $fields['category_id']; $userTest->user_id = $request->user()->id; $userTest->closed_at = now()->addDay(); $userTest->save(); $userTest->generateEmptyAnswers($questions); return new UserTestResource($userTest); } /** * @OA\Post( * path="/api/user-tests/by-test", * summary="Create a UserTest based on an existing Test", * description="Generates a UserTest from an existing Test, including all its questions. Only available if the Test is available.", * tags={"UserTests"}, * security={{"bearerAuth":{}}}, * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"test_id"}, * @OA\Property(property="test_id", type="integer", example=1, description="ID of the existing Test to create a UserTest from") * ) * ), * @OA\Response( * response=200, * description="UserTest created successfully", * @OA\JsonContent(ref="#/components/schemas/UserTestResource") * ), * @OA\Response( * response=409, * description="Test is not available at this time" * ) * ) */ public function storeByTest(Request $request) { $this->authorize('create', UserTest::class); $fields = $request->validate([ 'test_id' => 'required|exists:tests,id' ]); $test = Test::findOrFail($fields['test_id']); if(!$test->isAvailable()) return response(['message' => 'This test isn\'t available now!'], 409); $test->load(['questions']); $userTest = new UserTest(); $userTest->category_id = $test->category_id; $userTest->test_id = $test->id; $userTest->closed_at = $test->closed_at; $userTest->user_id = $request->user()->id; $userTest->save(); $userTest->generateEmptyAnswers($test->questions->toArray()); return new UserTestResource($userTest); } /** * @OA\Post( * path="/api/user-test-answers/{answer}/submit", * summary="Submit an answer for a UserTestAnswer", * description="Records the user's answer for a specific UserTestAnswer. The answer must match the question type (string for text, integer for single/multiple choice).", * tags={"UserTestAnswers"}, * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="answer", * in="path", * required=true, * description="ID of the UserTestAnswer to submit", * @OA\Schema(type="integer") * ), * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"answer"}, * @OA\Property( * property="answer", * type="array", * description="Array of answers. Should be string(s) if the question type is 'text' or integer(s) otherwise", * @OA\Items( * oneOf={ * @OA\Schema(type="string"), * @OA\Schema(type="integer") * } * ) * ) * ) * ), * @OA\Response( * response=200, * description="Answer recorded successfully", * @OA\JsonContent( * @OA\Property(property="message", type="string", example="Answer is recorded") * ) * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=422, * description="Validation error (wrong type or empty array)" * ), * @OA\Response( * response=403, * description="Forbidden (user cannot edit this answer)" * ) * ) */ public function answerByTest(Request $request, UserTestAnswer $answer) { $answer->load(['userTest', 'question']); $answerType = $answer->question->type == 'text' ? 'string' : 'integer'; $this->authorize('canBeEdit', $answer->userTest); $fields = $request->validate([ 'answer' => 'required|array|min:1', 'answer.*' => 'required|' . $answerType ]); $answer->answer = $fields['answer']; $answer->save(); return ['message' => 'Answer is recorded']; } /** * @OA\Get( * path="/api/user-test-answers/{answer}", * summary="Get a UserTestAnswer", * description="Retrieve a specific UserTestAnswer by ID. Only the owner or authorized user can view it.", * tags={"UserTestAnswers"}, * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="answer", * in="path", * required=true, * description="ID of the UserTestAnswer to retrieve", * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="UserTestAnswer retrieved successfully", * @OA\JsonContent(ref="#/components/schemas/UserTestAnswerResource") * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=403, * description="Forbidden: user cannot view this answer" * ), * @OA\Response( * response=404, * description="UserTestAnswer not found" * ) * ) */ public function showAnswer(UserTestAnswer $answer) { $answer->load(['userTest', 'question']); $this->authorize('view', $answer->userTest); return new UserTestAnswerResource($answer); } /** * @OA\Post( * path="/api/user-tests/{userTest}/complete", * summary="Complete a UserTest", * description="Marks a UserTest as completed, calculates the score based on answers, and prevents further editing.", * tags={"UserTests"}, * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="userTest", * in="path", * required=true, * description="ID of the UserTest to complete", * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="Test successfully completed", * @OA\JsonContent( * @OA\Property(property="message", type="string", example="Test is completed") * ) * ), * @OA\Response( * response=403, * description="Forbidden: user cannot edit this test" * ) * ) */ public function completeTest(UserTest $userTest) { $this->authorize('canBeEdit', $userTest); $userTest->load(['category', 'answers.question', 'user']); $userTest->completeTest(); return ['message' => 'Test is completed']; } /** * @OA\Get( * path="/api/user-tests/{userTest}", * summary="Get a single UserTest", * description="Retrieve a UserTest with its answers, question details, category, and user info.", * tags={"UserTests"}, * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="userTest", * in="path", * required=true, * description="ID of the UserTest to retrieve", * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="UserTest details", * @OA\JsonContent(ref="#/components/schemas/UserTestResource") * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=403, * description="Forbidden: user cannot view this test" * ), * @OA\Response( * response=404, * description="UserTest not found" * ) * ) */ public function show(UserTest $userTest) { $this->authorize('view', $userTest); $userTest->load(['user', 'category', 'answers.question']); return new UserTestResource($userTest); } /** * @OA\Delete( * path="/api/user-tests/{userTest}", * summary="Delete a UserTest", * description="Delete a UserTest by ID. Only the owner or admin can delete it.", * tags={"UserTests"}, * security={{"bearerAuth":{}}}, * @OA\Parameter( * name="userTest", * in="path", * required=true, * description="ID of the UserTest to delete", * @OA\Schema(type="integer") * ), * @OA\Response( * response=200, * description="UserTest deleted successfully", * @OA\JsonContent( * type="object", * @OA\Property(property="message", type="string", example="The UserTest was deleted") * ) * ), * @OA\Response( * response=401, * description="Unauthorized" * ), * @OA\Response( * response=403, * description="Forbidden: user cannot delete this test" * ), * @OA\Response( * response=404, * description="UserTest not found" * ) * ) */ public function destroy(Request $request, UserTest $userTest) { $this->authorize('delete', $userTest); $userTest->load('user'); $username = $userTest->user ? $userTest->user->username : 'unknown'; $userTestId = $userTest->id; $userTest->delete(); Log::writeLog("UserTest #$userTestId ($username)' is deleted by " . $request->user()->username); return ['message' => 'The UserTest was deleted']; } }