Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
forbidals
/
admin_panel
/
app
/
Http
/
Controllers
/
Api
:
StudentApiController.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Resources\TimetableCollection; use App\Http\Resources\UserDataResource; use App\Models\AssignmentSubmission; use App\Models\OnlineExamCommon; use App\Models\School; use App\Models\Students; use App\Models\User; use Carbon\Carbon; use App\Models\DiaryCategory; use App\Repositories\Announcement\AnnouncementInterface; use App\Repositories\Assignment\AssignmentInterface; use App\Repositories\AssignmentSubmission\AssignmentSubmissionInterface; use App\Repositories\Attendance\AttendanceInterface; use App\Repositories\DiaryStudent\DiaryStudentInterface; use App\Repositories\Exam\ExamInterface; use App\Repositories\ExamResult\ExamResultInterface; use App\Repositories\Files\FilesInterface; use App\Repositories\Holiday\HolidayInterface; use App\Repositories\Lessons\LessonsInterface; use App\Repositories\OnlineExam\OnlineExamInterface; use App\Repositories\OnlineExamCommon\OnlineExamCommonInterface; use App\Repositories\OnlineExamQuestionChoice\OnlineExamQuestionChoiceInterface; use App\Repositories\OnlineExamStudentAnswer\OnlineExamStudentAnswerInterface; use App\Repositories\SessionYear\SessionYearInterface; use App\Repositories\Sliders\SlidersInterface; use App\Repositories\Student\StudentInterface; use App\Repositories\StudentOnlineExamStatus\StudentOnlineExamStatusInterface; use App\Repositories\StudentSubject\StudentSubjectInterface; use App\Repositories\SubjectTeacher\SubjectTeacherInterface; use App\Repositories\Timetable\TimetableInterface; use App\Repositories\Topics\TopicsInterface; use App\Repositories\User\UserInterface; use App\Models\OnlineExamQuestionOption; use App\Rules\MaxFileSize; use App\Services\CachingService; use App\Services\FeaturesService; use App\Services\FcmTokenService; use App\Services\ResponseService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; use JetBrains\PhpStorm\NoReturn; use App\Traits\DateFormatTrait; use Throwable; class StudentApiController extends Controller { use DateFormatTrait; private StudentInterface $student; private UserInterface $user; private AssignmentInterface $assignment; private AssignmentSubmissionInterface $assignmentSubmission; private FilesInterface $files; private CachingService $cache; private StudentSubjectInterface $studentSubject; private TimetableInterface $timetable; private ExamInterface $exam; private ExamResultInterface $examResult; private LessonsInterface $lesson; private TopicsInterface $lessonTopic; private AttendanceInterface $attendance; private HolidayInterface $holiday; private SessionYearInterface $sessionYear; private SubjectTeacherInterface $subjectTeacher; private AnnouncementInterface $announcement; private OnlineExamInterface $onlineExam; private StudentOnlineExamStatusInterface $studentOnlineExamStatus; private OnlineExamQuestionChoiceInterface $onlineExamQuestionChoice; private OnlineExamStudentAnswerInterface $onlineExamStudentAnswer; private OnlineExamCommonInterface $onlineExamCommon; private SlidersInterface $sliders; private FeaturesService $featureService; private DiaryStudentInterface $diaryStudent; public function __construct(StudentInterface $student, UserInterface $user, AssignmentInterface $assignment, AssignmentSubmissionInterface $assignmentSubmission, FilesInterface $files, CachingService $cache, StudentSubjectInterface $studentSubject, TimetableInterface $timetable, ExamInterface $exam, ExamResultInterface $examResult, LessonsInterface $lesson, TopicsInterface $lessonTopic, AttendanceInterface $attendance, HolidayInterface $holiday, SessionYearInterface $sessionYear, SubjectTeacherInterface $subjectTeacher, AnnouncementInterface $announcement, OnlineExamInterface $onlineExam, StudentOnlineExamStatusInterface $studentOnlineExamStatus, OnlineExamQuestionChoiceInterface $onlineExamQuestionChoice, OnlineExamStudentAnswerInterface $onlineExamStudentAnswer, SlidersInterface $sliders, FeaturesService $featuresService, OnlineExamCommonInterface $onlineExamCommon, DiaryStudentInterface $diaryStudent) { $this->student = $student; $this->user = $user; $this->assignment = $assignment; $this->assignmentSubmission = $assignmentSubmission; $this->files = $files; $this->cache = $cache; $this->studentSubject = $studentSubject; $this->timetable = $timetable; $this->exam = $exam; $this->examResult = $examResult; $this->lesson = $lesson; $this->lessonTopic = $lessonTopic; $this->attendance = $attendance; $this->holiday = $holiday; $this->sessionYear = $sessionYear; $this->subjectTeacher = $subjectTeacher; $this->announcement = $announcement; $this->onlineExam = $onlineExam; $this->studentOnlineExamStatus = $studentOnlineExamStatus; $this->onlineExamQuestionChoice = $onlineExamQuestionChoice; $this->onlineExamStudentAnswer = $onlineExamStudentAnswer; $this->sliders = $sliders; $this->featureService = $featuresService; $this->onlineExamCommon = $onlineExamCommon; $this->diaryStudent = $diaryStudent; } #[NoReturn] public function login(Request $request) { $validator = Validator::make($request->all(), [ 'gr_number' => 'required', 'password' => 'required', 'school_code' => 'required|alpha_num', ], [ 'gr_number.required' => 'The GR number is required.', 'password.required' => 'The password is required.', 'school_code.required' => 'The school code is required.', 'school_code.alpha_num' => 'The school code must contain only letters and numbers.', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } $school = School::on('mysql')->where('code', $request->school_code)->first(); if ($school) { DB::setDefaultConnection('school'); Config::set('database.connections.school.database', $school->database_name); DB::purge('school'); DB::connection('school')->reconnect(); DB::setDefaultConnection('school'); } else { ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); } $user = User::withTrashed() ->where('email', $request->gr_number) ->has('student') ->first(); $seesionYear = $this->sessionYear->builder()->where('default', 1)->first(); if ($user && Hash::check($request->password, $user->password)) { if ($user->trashed()) { // User is soft-deleted, handle accordingly ResponseService::errorResponse(trans('your_account_has_been_deactivated_please_contact_admin'), null, config('constants.RESPONSE_CODE.INACTIVATED_USER')); } $student = Students::where('user_id', $user->id)->first(); if ($student->session_year_id != $seesionYear->id) { ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); } } if (Auth::attempt(['email' => $request->gr_number, 'password' => $request->password, 'status' => 1])) { //Here Email Field is referenced as a GR Number for Student $auth = Auth::user()->load('student'); if (!$auth->student) { // logout Auth::logout(); ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); } // Check role // $auth->assignRole('Student'); // if (!$auth->hasRole('Student')) { // ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); // } // Check school status is activated or not if ($auth->school->status == 0) { ResponseService::errorResponse('Your account has been deactivated', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); } $token = $auth->createToken($auth->first_name)->plainTextToken; $user = $auth->load([ 'student.class_section' => function ($q) { $q->with('section', 'class', 'class.shift', 'medium'); }, 'student.guardian', 'school', 'fcmTokens' ]); // child.user', 'child.class_section.class', 'child.class_section.section', 'child.class_section.medium', 'child.user.school // $user = $auth->load(['student.guardian.child' => function($q) { // $q->with('user.school','class_section.class','class_section.section', 'class_section.medium'); // }]); // Store FCM tokens using FcmTokenService $fcmTokenService = app(FcmTokenService::class); if ($request->fcm_id) { $fcmTokenService->storeOrUpdateToken( $auth, $request->fcm_id, $request->device_type ?? 'android', $request->device_id ?? null ); } if ($request->web_fcm) { $fcmTokenService->storeOrUpdateToken( $auth, $request->web_fcm, 'web', $request->device_id ?? null ); } $extraData = [ 'fcm_id' => $request->fcm_id, 'web_fcm' => $request->web_fcm, 'device_type' => $request->device_type, ]; $user->extra_data = $extraData; ResponseService::successResponse('User logged-in!', new UserDataResource($user), ['error' => false, 'token' => $token]); } ResponseService::errorResponse('Invalid Login Credentials', null, config('constants.RESPONSE_CODE.INVALID_LOGIN')); } public function forgotPassword(Request $request) { $validator = Validator::make($request->all(), [ 'gr_no' => 'required', 'dob' => 'required|date', 'school_code' => 'required' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $schoolCode = $request->school_code; if ($schoolCode) { $school = School::on('mysql')->where('code', $schoolCode)->first(); if ($school) { DB::setDefaultConnection('school'); Config::set('database.connections.school.database', $school->database_name); DB::purge('school'); DB::connection('school')->reconnect(); DB::setDefaultConnection('school'); $user = $this->user->builder()->whereHas('student', function ($query) use ($request) { $query->where('admission_no', $request->gr_no); })->whereDate('dob', '=', date('Y-m-d', strtotime($request->dob)))->first(); // dd($user); if ($user) { /*NOTE : Revert this if needed */ //$this->user->update($user->id, ['reset_request' => 1,'school_id' => $user->school_id]); $this->user->update($user->id, ['reset_request' => 1, 'school_id' => $user->school_id]); ResponseService::successResponse("Request Send Successfully"); } else { ResponseService::errorResponse("Invalid user Details", null, config('constants.RESPONSE_CODE.INVALID_USER_DETAILS')); } } else { return response()->json(['error' => true, 'message' => 'Invalid school code'], 200); } } else { return response()->json(['error' => true, 'message' => 'Unauthenticated'], 200); } } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function classSubjects(Request $request) { try { $user = $request->user(); $subjects = $user->student->currentSemesterClassSubjects(); ResponseService::successResponse('Class Subject Fetched Successfully.', $subjects); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function subjects(Request $request) { try { $user = $request->user(); $subjects = $user->student->currentSemesterSubjects(); ResponseService::successResponse('Student Subject Fetched Successfully.', $subjects); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function selectSubjects(Request $request) { $validator = Validator::make($request->all(), [ 'subject_group.*.id' => 'required', 'subject_group.*.class_subject_id' => 'required|array', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { DB::beginTransaction(); $sessionYear = $this->cache->getDefaultSessionYear(); // Default Session Year From Cache $student = $request->user()->student; // Logged in Student Details $classSection = $student->class_section; // Class Section Data $studentSubject = array(); // Loop to Subject Group foreach ($request->subject_group as $subjectGroup) { // Loop to Subject's ID foreach ($subjectGroup['class_subject_id'] as $classSubjectId) { // Create Two Dimensional Student Subject Array $studentSubject[] = array( 'student_id' => $student->user_id, 'class_subject_id' => $classSubjectId, 'class_section_id' => $classSection->id, 'session_year_id' => $sessionYear->id, ); } } // Update OR Create Data $this->studentSubject->upsert($studentSubject, ['student_id', 'class_subject_id', 'class_section_id', 'session_year_id'], ['student_id', 'class_subject_id', 'class_section_id', 'session_year_id',]); DB::commit(); ResponseService::successResponse("Subject Selected Successfully"); } catch (Throwable $e) { DB::rollBack(); ResponseService::logErrorResponse($e, "StudentApiController :- selectSubject Method"); ResponseService::errorResponse(); } } public function getGuardianDetails(Request $request) { try { $student = $request->user()->student->load(['guardian']); $data = array( 'guardian' => (!empty($student->guardian)) ? $student->guardian : (object) [] ); ResponseService::successResponse("Guardian Details Fetched Successfully", $data); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getTimetable(Request $request) { try { $student = $request->user()->student; $studentSubjects = $student->currentSemesterSubjects(); $core_subjects = $studentSubjects["core_subject"]->pluck('id')->toArray(); $elective_subjects = $studentSubjects["elective_subject"] ?? []; if ($elective_subjects) { $elective_subjects = $elective_subjects->pluck('class_subject.subject.id')->toArray(); } $subjectIds = array_merge($core_subjects, $elective_subjects); $timetable = $this->timetable->builder() ->where('class_section_id', $student->class_section_id) ->where(function ($query) use ($subjectIds) { $query->whereIn('subject_id', $subjectIds) ->orWhereNull('subject_id'); }) ->where(function ($query) use ($subjectIds) { $query->whereHas('subject_teacher', function ($q) use ($subjectIds) { $q->whereIn('subject_id', $subjectIds); }) ->orWhereDoesntHave('subject_teacher'); }) ->with([ 'subject_teacher.subject:id,name,type,code,bg_color,image', 'subject_teacher.teacher:id,first_name,last_name' ]) ->orderBy('start_time') ->get(); ResponseService::successResponse("Timetable Fetched Successfully", new TimetableCollection($timetable)); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getLessons(Request $request) { $validator = Validator::make($request->all(), [ 'lesson_id' => 'nullable|numeric', 'class_subject_id' => 'required', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = $request->user()->student; $lessonQuery = $this->lesson->builder()->whereHas('lesson_commons', function ($q) use ($request, $student) { $q->whereHas('syllabus.class_subject', function ($q) use ($request) { $q->where('id', $request->class_subject_id); })->where('class_section_id', $student->class_section_id); }) ->with([ 'topic' => function ($q) { $q->orderBy('id', 'DESC'); }, 'file' ]); if ($request->lesson_id) { $lessonQuery->where('id', $request->lesson_id); } $lessonData = $lessonQuery->orderBy('id', 'DESC')->get(); ResponseService::successResponse("Lessons Fetched Successfully", $lessonData); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getLessonTopics(Request $request) { $validator = Validator::make($request->all(), [ 'lesson_id' => 'required|numeric', 'topic_id' => 'nullable|numeric', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $lessonTopicQuery = $this->lessonTopic->builder()->with('lesson.lesson_commons')->where('lesson_id', $request->lesson_id)->with('file'); if ($request->topic_id) { $lessonTopicQuery->where('id', $request->topic_id); } $lessonTopicData = $lessonTopicQuery->get(); ResponseService::successResponse("Topics Fetched Successfully", $lessonTopicData); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController :- getLessonTopics Method"); ResponseService::errorResponse(); } } public function getAssignments(Request $request) { $validator = Validator::make($request->all(), [ 'assignment_id' => 'nullable|numeric', 'class_subject_id' => 'nullable|numeric', 'is_submitted' => 'nullable|numeric', 'search' => 'nullable|string', 'sort' => 'nullable|string', ]); if ($validator->fails()) { return ResponseService::validationError($validator->errors()->first()); } try { /* ------------------------------------------------- | 1. Resolve student + class + class_section -------------------------------------------------*/ $student = $request->user()->student; if ( !$student || !$student->class_section_id || !$student->class_section || !$student->class_section->class_id ) { return ResponseService::errorResponse('Invalid student or class context'); } $classSectionId = $student->class_section_id; $classId = $student->class_section->class_id; /* ------------------------------------------------- | 2. Resolve allowed class_subject_ids -------------------------------------------------*/ $allowedClassSubjectIds = DB::table('class_subjects') ->where('class_id', $classId) ->where(function ($q) use ($student) { // Compulsory → always allowed $q->where('type', 'Compulsory') // Elective → only if assigned ->orWhere(function ($q) use ($student) { $q->where('type', 'Elective') ->whereExists(function ($sub) use ($student) { $sub->select(DB::raw(1)) ->from('student_subjects') ->whereColumn( 'student_subjects.class_subject_id', 'class_subjects.id' ) ->where('student_subjects.student_id', $student->user_id); }); }); }) ->pluck('id'); /* ------------------------------------------------- | 3. Base assignment query -------------------------------------------------*/ $data = $this->assignment->builder() ->with([ 'file', 'class_subject.subject', 'submission' => function ($query) use ($student) { $query->where('student_id', $student->user_id) ->with('file'); } ]) ->whereHas('assignment_commons', function ($query) use ($classSectionId, $allowedClassSubjectIds) { $query->where('class_section_id', $classSectionId) ->whereIn('class_subject_id', $allowedClassSubjectIds); }); /* ------------------------------------------------- | 4. Search -------------------------------------------------*/ if (!empty($request->search)) { $search = $request->search; $data->where(function ($query) use ($search) { $query->where('name', 'LIKE', "%{$search}%") ->orWhere('instructions', 'LIKE', "%{$search}%") ->orWhereHas('class_subject.subject', function ($q) use ($search) { $q->where('name', 'LIKE', "%{$search}%"); }); }); } /* ------------------------------------------------- | 5. Filters -------------------------------------------------*/ if ($request->assignment_id) { $data->where('id', $request->assignment_id); } // Optional class_subject filter (still validated server-side) if ($request->class_subject_id) { $data->whereHas('assignment_commons', function ($q) use ($request, $allowedClassSubjectIds) { $q->where('class_subject_id', $request->class_subject_id) ->whereIn('class_subject_id', $allowedClassSubjectIds); }); } // Submission filter if (isset($request->is_submitted)) { if ((int) $request->is_submitted === 1) { $data->whereHas('submission', function ($q) use ($student) { $q->where('student_id', $student->user_id); }); } else { $data->whereDoesntHave('submission', function ($q) use ($student) { $q->where('student_id', $student->user_id); }); } } /* ------------------------------------------------- | 6. Sorting -------------------------------------------------*/ switch ($request->sort) { case 'assigned_latest': $data->orderBy('created_at', 'desc'); break; case 'assigned_oldest': $data->orderBy('created_at', 'asc'); break; case 'due_latest': $data->orderBy('due_date', 'desc'); break; case 'due_oldest': $data->orderBy('due_date', 'asc'); break; default: $data->orderBy('id', 'desc'); } /* ------------------------------------------------- | 7. Response -------------------------------------------------*/ $result = $data->paginate(15); ResponseService::successResponse( 'Assignments Fetched Successfully', $result ); } catch (Throwable $e) { ResponseService::logErrorResponse( $e, 'Student Api Controller -> getAssignments' ); ResponseService::errorResponse(); } } public function submitAssignment(Request $request) { $file_upload_size_limit = $this->cache->getSystemSettings('file_upload_size_limit'); $validator = Validator::make($request->all(), [ 'assignment_id' => 'required|numeric', 'subject_id' => 'nullable|numeric', // Either files OR add_url is required 'files' => 'required_without:add_url|array', 'files.*' => [ 'mimes:jpeg,png,jpg,gif,svg,webp,pdf,doc,docx,xml', 'max:' . ($file_upload_size_limit * 1024), // MB → KB ], 'add_url' => 'required_without:files|nullable|url', ], [ 'files.required_without' => __('Please upload a file or provide a URL.'), 'add_url.required_without' => __('Please upload a file or provide a URL.'), 'files.*.max' => __('The uploaded file must be less than :size MB.', [ 'size' => $file_upload_size_limit, ]), ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } if (!$this->assignment->builder()->where('id', $request->assignment_id)->exists()) { ResponseService::errorResponse("Assignment not found"); } try { DB::beginTransaction(); $assignmentSubmissionData = array(); $student = $request->user()->student; $sessionYear = $this->cache->getDefaultSessionYear(); $assignment = $this->assignment->builder()->where('id', $request->assignment_id)->first(); $assignmentSubmissionQuery = $this->assignmentSubmission->builder()->where(['assignment_id' => $assignment->id, 'student_id' => $student->user_id])->first(); if (empty($assignmentSubmissionQuery)) { $assignmentSubmissionData = array( 'assignment_id' => $request->assignment_id, 'student_id' => $student->user_id, 'session_year_id' => $sessionYear->id ); } else if (($assignmentSubmissionQuery->status == 2 && $assignment->resubmission) || $assignmentSubmissionQuery->status == 3) { // if assignment submission is rejected and // Assignment has resubmission allowed then change the status to resubmitted $assignmentSubmissionData = array( 'id' => $assignmentSubmissionQuery->id, 'status' => 3 ); // Check Old Files and Delete it if ($assignmentSubmissionQuery->file) { foreach ($assignmentSubmissionQuery->file as $file) { if (Storage::disk('public')->exists($file->getRawOriginal('file_url'))) { Storage::disk('public')->delete($file->getRawOriginal('file_url')); } } } $assignmentSubmissionQuery->file()->delete(); } else { ResponseService::errorResponse("You already have submitted your assignment.", null, config('constants.RESPONSE_CODE.ASSIGNMENT_ALREADY_SUBMITTED')); } $assignmentSubmission = $this->assignmentSubmission->updateOrCreate(['id' => $assignmentSubmissionData['id'] ?? null], $assignmentSubmissionData); //If File Exists if ($request->hasFile('files')) { $fileData = array(); // Empty FileData Array // Create A File Model Instance $assignmentSubmissionModelAssociate = $this->files->model()->modal()->associate($assignmentSubmission); // Get the Association Values of File with Assignment Submission foreach ($request->file('files') as $file_upload) { // Create Temp File Data Array $tempFileData = array( 'modal_type' => $assignmentSubmissionModelAssociate->modal_type, 'modal_id' => $assignmentSubmissionModelAssociate->modal_id, 'file_name' => $file_upload->getClientOriginalName(), 'type' => 1, 'file_url' => $file_upload ); $fileData[] = $tempFileData; // Store Temp File Data in Multi-Dimensional File Data Array } $this->files->createBulk($fileData); // Store File Data } if ($request->add_url) { $urlData = []; $urls = is_array($request->add_url) ? $request->add_url : [$request->add_url]; foreach ($urls as $url) { $urlParts = parse_url($url); $fileName = basename($urlParts['path'] ?? '/'); $assignmentSubmissionModelAssociate = $this->files->model()->modal()->associate($assignmentSubmission); $tempUrlData = array( 'modal_type' => $assignmentSubmissionModelAssociate->modal_type, 'modal_id' => $assignmentSubmissionModelAssociate->modal_id, 'file_name' => $fileName, 'type' => 4, 'file_url' => $url, ); $urlData[] = $tempUrlData; } // Store the URL data $this->files->createBulk($urlData); } $submittedAssignment = $this->assignmentSubmission->builder()->where('id', $assignmentSubmission->id)->with('file')->get(); DB::commit(); ResponseService::successResponse("Assignments Submitted Successfully", $submittedAssignment); } catch (Throwable $e) { DB::rollback(); ResponseService::logErrorResponse($e, "Student Api Controller -> submitAssignment Method"); ResponseService::errorResponse(); } } public function deleteAssignmentSubmission(Request $request) { $validator = Validator::make($request->all(), [ 'assignment_submission_id' => 'required|numeric', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { DB::beginTransaction(); $student = $request->user(); $assignment_submission = AssignmentSubmission::where('id', $request->assignment_submission_id)->where('student_id', $student->id)->with('file')->first(); if (!empty($assignment_submission) && $assignment_submission->status == 0) { foreach ($assignment_submission->file as $file) { if (Storage::disk('public')->exists($file->file_url)) { Storage::disk('public')->delete($file->file_url); } } $assignment_submission->file()->delete(); $assignment_submission->delete(); DB::commit(); ResponseService::successResponse("Assignments Deleted Successfully"); } else { ResponseService::errorResponse("You can not delete assignment"); } } catch (Throwable $e) { DB::rollBack(); ResponseService::logErrorResponse($e, "Student Api Controller -> deleteAssignmentSubmission Method"); ResponseService::errorResponse(); } } public function getAttendance(Request $request) { $validator = Validator::make($request->all(), [ 'month' => 'nullable|numeric', 'year' => 'nullable|numeric', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = $request->user()->student; $sessionYear = $this->cache->getDefaultSessionYear(); $attendance = $this->attendance->builder()->where(['student_id' => $student->user_id, 'session_year_id' => $sessionYear->id]); $holidays = $this->holiday->builder(); $session_year_data = $this->sessionYear->findById($sessionYear->id); if (isset($request->month)) { $attendance = $attendance->whereMonth('date', $request->month); $holidays = $holidays->whereMonth('date', $request->month); } if (isset($request->year)) { $attendance = $attendance->whereYear('date', $request->year); $holidays = $holidays->whereYear('date', $request->year); } $attendance = $attendance->get(); $holidays = $holidays->get(); $data = ['attendance' => $attendance, 'holidays' => $holidays, 'session_year' => $session_year_data]; ResponseService::successResponse("Attendance Details Fetched Successfully", $data); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "Student Api Controller -> getAttendance Method"); ResponseService::errorResponse(); } } public function getAnnouncements(Request $request) { $validator = Validator::make($request->all(), [ 'type' => 'nullable|in:subject,noticeboard,class', 'class_subject_id' => 'required_if:type,subject|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = $request->user()->student; $classSectionId = $student->class_section->id; $sessionYear = $this->cache->getDefaultSessionYear(); if (isset($request->type) && $request->type == "subject") { // TODO : There might be some mistake in this code $table = $this->subjectTeacher->builder()->where(['class_section_id' => $student->class_section_id, 'class_subject_id' => $request->class_subject_id])->pluck('id'); if ($table === null) { ResponseService::errorResponse("Invalid Subject ID", null, config('constants.RESPONSE_CODE.INVALID_SUBJECT_ID')); } } $announcementData = $this->announcement->builder()->with('file', 'announcement_class')->where('session_year_id', $sessionYear->id); if (isset($request->type) && $request->type == "class") { $announcementData = $announcementData->whereHas('announcement_class', function ($query) use ($classSectionId) { $query->where(['class_section_id' => $classSectionId, 'class_subject_id' => null]); }); } if (isset($request->type) && $request->type == "subject") { $announcementData = $announcementData->whereHas('announcement_class', function ($query) use ($classSectionId, $request) { $query->where(['class_section_id' => $classSectionId, 'class_subject_id' => $request->class_subject_id]); }); } $announcementData = $announcementData->orderBy('id', 'desc')->paginate(15); ResponseService::successResponse("Announcement Details Fetched Successfully", $announcementData); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController :- getAnnouncements Method"); ResponseService::errorResponse(); } } public function getExamList(Request $request) { try { $studentId = Auth::user()->student->id; $studentData = Auth::user()->student; $student = $this->student->findById($studentId, ['*'], ['class_section']); $classId = $student->class_section->class_id; $studentSubjects = $studentData->currentSemesterSubjects(); $core_subjects = $studentSubjects['core_subject']->pluck('id')->toArray(); $elective_subjects = $studentSubjects['elective_subject'] ?? []; if ($elective_subjects) { $elective_subjects = $elective_subjects->pluck('class_subject.subject.id')->toArray(); } $subjectIds = array_merge($core_subjects, $elective_subjects); $currentSessionYear = $this->cache->getDefaultSessionYear(); $exam = $this->exam->builder() ->where([ 'class_id' => $classId, 'session_year_id' => $currentSessionYear->id ]) ->whereHas('timetable', function ($query) use ($subjectIds) { $query->owner() ->whereHas('class_subject', function ($q) use ($subjectIds) { $q->whereIn('subject_id', $subjectIds); // filter by actual subject }); }) ->with([ 'timetable' => function ($query) use ($subjectIds) { $query->owner() ->whereHas('class_subject', function ($q) use ($subjectIds) { $q->whereIn('subject_id', $subjectIds); // filter by actual subject }) ->selectRaw('*, SUM(total_marks) as total_marks') ->groupBy('exam_id'); } ]) ->orderBy('id', 'DESC') ->get(); $exam_data = array(); foreach ($exam as $data) { if (isset($request->status) && $request->status != $data->exam_status && $request->status != 3) { continue; } $exam_data[] = [ 'id' => $data->id, 'name' => $data->name, 'description' => $data->description, 'publish' => $data->publish, 'session_year' => $data->session_year->name, 'exam_starting_date' => $data->start_date, 'exam_ending_date' => $data->end_date, 'exam_status' => $data->exam_status, ]; } ResponseService::successResponse("", $exam_data); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController :- getExamList Method"); ResponseService::errorResponse(); } } public function getExamDetails(Request $request) { $validator = Validator::make($request->all(), [ 'exam_id' => 'required|nullable' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $studentData = Auth::user()->student; $classId = $studentData->class_section->class_id; $studentSubjects = $studentData->currentSemesterSubjects(); $core_subjects = $studentSubjects['core_subject']->pluck('id')->toArray(); $elective_subjects = $studentSubjects['elective_subject'] ?? []; if ($elective_subjects) { $elective_subjects = $elective_subjects->pluck('class_subject.subject.id')->toArray(); } $subjectIds = array_merge($core_subjects, $elective_subjects); // Fetch exam and filter timetable by student's subjects $examData = $this->exam->builder() ->where([ 'id' => $request->exam_id, 'class_id' => $classId ]) ->with([ 'timetable' => function ($query) use ($subjectIds) { $query->owner() ->with(['class_subject.subject']) ->whereHas('class_subject', function ($q) use ($subjectIds) { $q->whereIn('subject_id', $subjectIds); // filter by actual subject }) ->orderBy('date'); } ]) ->first(); if (!$examData) { ResponseService::successResponse("", []); } foreach ($examData->timetable as $data) { $exam_data[] = array( 'exam_timetable_id' => $data->id, 'total_marks' => $data->total_marks, 'passing_marks' => $data->passing_marks, 'date' => $data->date, 'starting_time' => $data->start_time, 'ending_time' => $data->end_time, 'subject' => array( 'id' => $data->class_subject->subject->id, 'class_subject_id' => $data->class_subject_id, 'name' => $data->class_subject->subject->name, 'bg_color' => $data->class_subject->subject->bg_color, 'image' => $data->class_subject->subject->image, 'type' => $data->class_subject->subject->type, ) ); } ResponseService::successResponse("", $exam_data ?? []); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController :- getExamDetails Method"); ResponseService::errorResponse(); } } public function getExamMarks(Request $request) { try { $studentData = Auth::user()->student->load('class_section.class:id,name,shift_id', 'class_section.class.shift:id,name', 'class_section.section:id,name', 'class_section.medium:id,name'); // Exam Result Data $examResultDB = $this->examResult->builder()->with([ 'user' => function ($q) { $q->select('id', 'first_name', 'last_name')->with('student:id,user_id,roll_number'); }, 'exam.timetable:id,exam_id,start_time,end_time', 'session_year', 'exam.marks' => function ($q) use ($studentData) { $q->where('student_id', $studentData->user_id) ->with([ 'class_subject' => function ($q) { $q->withTrashed()->with([ 'subject' => function ($q) { $q->withTrashed(); } ]); } ]); } ])->where('student_id', $studentData->user_id); if ($request->result_id) { $examResultDB = $examResultDB->where('id', $request->result_id); } if ($request->session_year_id) { $examResultDB = $examResultDB->where('session_year_id', $request->session_year_id); } $examResultDB = $examResultDB->get(); // Check that Exam Result DB is not empty if (count($examResultDB)) { foreach ($examResultDB as $examResultData) { $exam_result = array( 'result_id' => $examResultData->id, 'exam_id' => $examResultData->exam_id, 'exam_name' => $examResultData->exam->name, 'class_name' => $studentData->class_section->full_name, 'student_name' => $examResultData->user->full_name, 'exam_date' => $examResultData->exam->start_date, 'total_marks' => $examResultData->total_marks, 'obtained_marks' => $examResultData->obtained_marks, 'percentage' => $examResultData->percentage, 'grade' => $examResultData->grade, 'session_year' => $examResultData->session_year->name, ); $exam_marks = array(); foreach ($examResultData->exam->marks as $marks) { $exam_marks[] = array( 'marks_id' => $marks->id, 'subject_name' => $marks->class_subject->subject->name, 'subject_type' => $marks->class_subject->subject->type, 'total_marks' => $marks->timetable->total_marks, 'passing_marks' => $marks->timetable->passing_marks, 'obtained_marks' => $marks->obtained_marks, 'teacher_review' => $marks->teacher_review, 'grade' => $marks->grade, ); } $data[] = array( 'result' => $exam_result, 'exam_marks' => $exam_marks, ); } ResponseService::successResponse("Exam Result Fetched Successfully", $data ?? null); } else { ResponseService::successResponse("Exam Result Fetched Successfully", []); } } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getProfileDetails() { try { $studentData = Auth::user()->load([ 'student' => function ($query) { $query->with([ 'class_section' => function ($query) { $query->with('section', 'class', 'medium', 'class.shift', 'class.stream'); } ], 'guardian'); }, 'extra_student_details.form_field', 'school' ]); $extraDetails = $studentData->extra_student_details->map(function ($item) { $field = $item->form_field; $value = $item->data; if ($field) { // Dropdown or Radio: replace numeric/index key with label if (in_array($field->type, ['dropdown'])) { $options = $field->default_values ?? []; $value = $options[$value] ?? $value; } // Checkbox: decode JSON array, keep valid options if ($field->type === 'checkbox') { $selected = json_decode($value, true) ?: []; $options = $field->default_values ?? []; $value = json_encode(array_values(array_intersect($selected, $options))); } } return [ 'id' => $item->id, 'user_id' => $item->user_id, 'form_field_id' => $item->form_field_id, 'data' => $value, 'school_id' => $item->school_id, 'created_at' => $item->created_at, 'updated_at' => $item->updated_at, 'deleted_at' => $item->deleted_at, 'file_url' => $item->file_url ?? null, 'form_field' => $field ? [ 'id' => $field->id, 'name' => $field->name, 'type' => $field->type, 'is_required' => $field->is_required, 'default_values' => $field->default_values, 'school_id' => $field->school_id, 'user_type' => $field->user_type, 'rank' => $field->rank, 'display_on_id' => $field->display_on_id, 'created_at' => $field->created_at, 'updated_at' => $field->updated_at, 'deleted_at' => $field->deleted_at, ] : null, ]; }); $data = array( 'id' => $studentData->id, 'first_name' => $studentData->first_name, 'last_name' => $studentData->last_name, 'mobile' => $studentData->mobile, 'roll_number' => $studentData->student->roll_number, 'admission_no' => $studentData->student->admission_no, 'admission_date' => $studentData->student->admission_date, 'gender' => $studentData->gender, 'image' => $studentData->image, 'dob' => $studentData->dob, 'current_address' => $studentData->current_address, 'permanent_address' => $studentData->permanent_address, 'occupation' => $studentData->occupation, 'status' => $studentData->status, 'fcm_id' => $studentData->fcm_id, 'school_id' => $studentData->school_id, 'session_year_id' => $studentData->student->session_year_id, 'email_verified_at' => $studentData->email_verified_at, 'created_at' => $studentData->created_at, 'updated_at' => $studentData->updated_at, 'class_section' => $studentData->student->class_section, 'guardian' => $studentData->student->guardian, 'extra_details' => $extraDetails, 'school' => $studentData->school, ); ResponseService::successResponse('Data Fetched Successfully', $data); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getSessionYear() { try { $sessionYear = $this->cache->getDefaultSessionYear(); ResponseService::successResponse("Session Year Fetched Successfully", $sessionYear); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getOnlineExamList(Request $request) { $validator = Validator::make($request->all(), [ 'class_subject_id' => 'nullable|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; $classSectionId = $student->class_section->id; $sessionYear = $this->cache->getDefaultSessionYear(); $classSubjectId = []; if ($request->class_subject_id) { $classSubjectId[] = $request->class_subject_id; } else { $studentSubjects = $student->currentSemesterSubjects(); $coreSubject = $studentSubjects['core_subject']->toArray(); $electiveSubject = []; if (isset($studentSubjects['elective_subject'])) { $electiveSubject = is_array($studentSubjects['elective_subject']) ? $studentSubjects['elective_subject'] : $studentSubjects['elective_subject']->toArray(); } $classSubjectId = collect($coreSubject)->pluck('class_subject_id')->toArray(); $elective_subject_ids = collect($electiveSubject)->pluck('class_subject_id')->toArray(); $classSubjectId = array_merge($classSubjectId, $elective_subject_ids); } if (env('DEMO_MODE')) { $check_student_status = $this->studentOnlineExamStatus->builder()->where('student_id', $student->user_id); if ($check_student_status->count()) { $status_id = $check_student_status->pluck('id'); $this->studentOnlineExamStatus->builder()->whereIn('id', $status_id)->delete(); } $check_student_answers = $this->onlineExamStudentAnswer->builder()->where('student_id', $student->user_id); if ($check_student_answers->count()) { $status_id = $check_student_answers->pluck('id'); $this->onlineExamStudentAnswer->builder()->whereIn('id', $status_id)->delete(); } } $onlineExamData = $this->onlineExam->builder() ->whereHas('online_exam_commons', function ($query) use ($student) { $query->where('class_section_id', $student->class_section_id); }) ->where(['session_year_id' => $sessionYear->id]) ->where('end_date', '>=', now()) ->has('question_choice') ->with('class_subject', 'question_choice:id,online_exam_id,marks') ->whereDoesntHave('student_attempt', function ($q) use ($student) { $q->where('student_id', $student->user_id); }) ->when($classSubjectId, function ($query, $classSubjectId) { return $query->whereIn('class_subject_id', $classSubjectId); }) ->orderBy('start_date', 'desc') ->paginate(15); ResponseService::successResponse('Data Fetched Successfully', $onlineExamData); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getOnlineExamQuestions(Request $request) { $validator = Validator::make($request->all(), [ 'exam_id' => 'required', 'exam_key' => 'required', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; // Checks Student Exam Status if ( $this->studentOnlineExamStatus->builder() ->where(['online_exam_id' => $request->exam_id, 'student_id' => $student->user_id]) ->exists() ) { ResponseService::errorResponse('Student already attempted exam', null, config('constants.RESPONSE_CODE.STUDENT_ALREADY_ATTEMPTED_EXAM')); } $onlineExamCommon = OnlineExamCommon::with(['online_exam']) ->where('online_exam_id', $request->exam_id) ->first(); if (!$onlineExamCommon) { return ResponseService::errorResponse("Exam configuration not found"); } // Check if the associated online_exam exists and matches request parameters $onlineExam = $onlineExamCommon->online_exam; if ( !$onlineExam || $onlineExam->id != $request->exam_id || $onlineExam->exam_key != $request->exam_key ) { return ResponseService::errorResponse("Invalid Exam Key"); } // Check if the exam has started $currentDateTime = now(); // Get the current date and time if ($currentDateTime < $onlineExam->start_date_iso) { return ResponseService::errorResponse("Exam not started yet"); } // Add Student Status Entry $this->studentOnlineExamStatus->create([ 'student_id' => $student->user_id, 'online_exam_id' => $request->exam_id, 'status' => 1, ]); $onlineExamQuestionChoice = $this->onlineExamQuestionChoice->builder(); // Get Total Questions $totalQuestions = $onlineExamQuestionChoice->where('online_exam_id', $request->exam_id)->count(); // Get Questions Data $examQuestionData = $onlineExamQuestionChoice->where('online_exam_id', $request->exam_id)->with('questions')->get(); $totalMarks = 0; $questionData = []; foreach ($examQuestionData as $examQuestion) { $totalMarks += $examQuestion->marks; // Make Options Array $optionData = $examQuestion->questions->options->map(function ($optionsData) { return [ 'id' => $optionsData->id, 'option' => htmlspecialchars_decode($optionsData->option), //'is_answer' => $optionsData->is_answer == 1 ? 1 : 0 ]; }); // Make Question Array Data $questionData[] = [ 'id' => $examQuestion->id, 'question' => htmlspecialchars_decode($examQuestion->questions->question), 'options' => $optionData, 'marks' => $examQuestion->marks, 'image' => $examQuestion->questions->image_url, 'note' => $examQuestion->questions->note, ]; } ResponseService::successResponse('Data Fetched Successfully', $questionData, [ 'total_questions' => $totalQuestions, 'total_marks' => $totalMarks ]); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function submitOnlineExamAnswers(Request $request) { $validator = Validator::make($request->all(), [ 'online_exam_id' => 'required|numeric', 'answers_data' => 'nullable|array', //'answers_data.*.question_id' => 'required|numeric', ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; DB::beginTransaction(); // Check Online Exam Exists or not //$onlineExamData = $this->onlineExam->findById($request->online_exam_id); //if (!$onlineExamData) { //ResponseService::errorResponse('Invalid online exam id'); //} // Clean existing answers for fresh submission $this->onlineExamStudentAnswer->builder()->where(['student_id' => $student->user_id, 'online_exam_id' => $request->online_exam_id])->delete(); $answers = []; foreach ($request->answers_data ?? [] as $answerData) { // checks the question exists with provided exam id $questionChoice = $this->onlineExamQuestionChoice->findById($answerData['question_id']); if (!$questionChoice || $questionChoice->online_exam_id != $request->online_exam_id) { ResponseService::errorResponse('Invalid question id'); } // Remove duplicates from submitted options for this question $uniqueOptionIds = array_unique($answerData['option_id']); foreach ($uniqueOptionIds as $optionId) { // add the data of answers $onlineExamQuestionOption = OnlineExamQuestionOption::find($optionId); $answers[] = [ 'student_id' => $student->user_id, 'online_exam_id' => $request->online_exam_id, 'question_id' => $answerData['question_id'], 'option_id' => $optionId, 'true_answer' => $onlineExamQuestionOption->is_answer == 1 ? 1 : 0, 'question_marks' => $questionChoice->marks, 'submitted_date' => now()->toDateString(), ]; } } // Debug: Log what we're about to store \Log::info('About to store answers:', [ 'exam_id' => $request->online_exam_id, 'student_id' => $student->user_id, 'answers_count' => count($answers), 'answers' => $answers ]); if (count($answers) > 0) { $this->onlineExamStudentAnswer->createBulk($answers); } // Update student exam status $this->studentOnlineExamStatus->updateOrCreate( ['student_id' => $student->user_id, 'online_exam_id' => $request->online_exam_id], ['status' => 2] ); DB::commit(); ResponseService::successResponse('Data Stored Successfully'); } catch (Throwable $e) { DB::rollback(); ResponseService::logErrorResponse($e, "StudentApiController submitOnlineExamAnswers Method"); ResponseService::errorResponse(); } } public function getOnlineExamResultList(Request $request) { $validator = Validator::make($request->all(), [ 'class_subject_id' => 'nullable|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; $classSectionId = $student->class_section_id; if ($request->session_year_id) { $sessionYear = $request->session_year_id; } else { $sessionYear = $this->cache->getDefaultSessionYear(); $sessionYear = $sessionYear->id; } $classSubjectId = []; if ($request->class_subject_id) { $classSubjectId[] = $request->class_subject_id; } else { $studentSubjects = $student->currentSemesterSubjects(); $coreSubject = $studentSubjects['core_subject']->toArray(); $electiveSubject = []; if (isset($studentSubjects['elective_subject'])) { $electiveSubject = is_array($studentSubjects['elective_subject']) ? $studentSubjects['elective_subject'] : $studentSubjects['elective_subject']->toArray(); } $classSubjectId = collect($coreSubject)->pluck('class_subject_id')->toArray(); $elective_subject_ids = collect($electiveSubject)->pluck('class_subject_id')->toArray(); $classSubjectId = array_merge($classSubjectId, $elective_subject_ids); } // Get Online Exam Data Where Logged in Student have attempted data and Relation Data with Question Choice , Student's answer with user submitted question with question and its option $query = $this->onlineExam->builder() ->when($request->class_subject_id, function ($query) use ($request, $classSectionId, $classSubjectId) { $query->whereHas('online_exam_commons', function ($q) use ($classSectionId, $classSubjectId) { $q->whereIn('class_subject_id', $classSubjectId) ->where('class_section_id', $classSectionId); }); }) ->where(['session_year_id' => $sessionYear]) ->whereHas('student_attempt', function ($q) use ($student) { $q->where('student_id', $student->user_id) ->where('status', 2); }) ->with('student_attempt') ->with([ 'student_answers' => function ($q) { $q->where('student_id', Auth::user()->id) ->with('user_submitted_questions.questions:id', 'user_submitted_questions.questions.options:id,question_id,is_answer'); } ]) ->with('question_choice:id,online_exam_id,marks', 'class_subject.subject:id,name,type,code,bg_color,image') ->orderBy('id', 'desc'); $onlineExamData = $query->paginate(15)->toArray(); // dd($onlineExamData); $examListData = array(); // Initialized Empty examListData Array // dd($onlineExamData); // Loop through Exam data foreach ($onlineExamData['data'] as $data) { // Reset for each exam $examSubmittedDate = null; $examSubmittedCreatedDate = null; // Get Total Marks of Particular Exam $totalMarks = collect($data['question_choice'])->sum('marks'); // Initialized totalObtainedMarks with 0 $totalObtainedMarks = 0; // Group Student's Answers by question_id $grouped_answers = []; foreach ($data['student_answers'] as $student_answer) { $examSubmittedDate = date('Y-m-d', strtotime($student_answer['submitted_date'])); $examSubmittedCreatedDate = date('d-m-Y H:i', strtotime($student_answer['created_at'])); if (isset($student_answer['question_marks'])) { if (isset($student_answer['true_answer']) && $student_answer['true_answer'] == 1) { $totalObtainedMarks += $student_answer['question_marks']; } } else { $grouped_answers[$student_answer['question_id']][] = $student_answer; } } // Fallback to student_attempt created_at if examSubmittedCreatedDate is not set if (!$examSubmittedCreatedDate && isset($data['student_attempt'][0]['created_at'])) { $examSubmittedCreatedDate = date('d-m-Y H:i', strtotime($data['student_attempt'][0]['created_at'])); } if ($grouped_answers && $grouped_answers != []) { foreach ($grouped_answers as $student_answers) { // Filter the options whose is_answer values is 1 $correct_option_ids = array_filter($student_answers[0]['user_submitted_questions']['questions']['options'], static function ($option) { return $option['is_answer'] == 1; }); // Get All Correct Options $correct_option_ids = array_column($correct_option_ids, 'id'); // Get Student's Correct Options $student_option_ids = array_column($student_answers, 'option_id'); // Check if the student's answers exactly match the correct answers then add marks with totalObtainedMarks if (!array_diff($correct_option_ids, $student_option_ids) && !array_diff($student_option_ids, $correct_option_ids)) { $totalObtainedMarks += $student_answers[0]['user_submitted_questions']['marks']; } } } // Loop through Student's Grouped answers // Make Exam List Data $examListData[] = array( 'online_exam_id' => $data['id'], 'subject' => array( 'id' => $data['class_subject']['subject']['id'], 'name' => $data['class_subject']['subject']['name'] . ' - ' . $data['class_subject']['subject']['type'], ), 'title' => $data['title'], 'obtained_marks' => $totalObtainedMarks, 'total_marks' => $totalMarks ?? "0", 'exam_submitted_date' => $examSubmittedCreatedDate, // 'exam_submitted_date' => $examSubmittedDate ?? carbon::createFromFormat($settings['date_format'] . ' ' . $settings['time_format'], $data['student_attempt'][0]['created_at']) ); } $examList = array( 'current_page' => $onlineExamData['current_page'], 'data' => $examListData, 'from' => $onlineExamData['from'], 'last_page' => $onlineExamData['last_page'], 'per_page' => $onlineExamData['per_page'], 'to' => $onlineExamData['to'], 'total' => $onlineExamData['total'], ); ResponseService::successResponse("", $examList); } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController :- getOnlineExamList Method"); ResponseService::errorResponse(); } } public function getOnlineExamResult(Request $request) { $validator = Validator::make($request->all(), [ 'online_exam_id' => 'required|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; $onlineExam = $this->onlineExam->builder() ->where('id', $request->online_exam_id) ->whereHas('student_attempt', function ($q) use ($student) { $q->where('student_id', $student->user_id); }) ->with([ 'question_choice:id,online_exam_id,marks', 'class_subject.subject:id,name', ]) ->with([ 'student_answers' => function ($q) { $q->where('student_id', Auth::user()->id) ->with('user_submitted_questions.questions:id', 'user_submitted_questions.questions.options:id,question_id,is_answer'); } ]) ->first(); if (isset($onlineExam) && $onlineExam != null) { $totalMarks = $onlineExam->question_choice->sum('marks'); $totalQuestions = $onlineExam->question_choice->count(); $correctQuestionData = array(); $inCorrectQuestionData = array(); $correctQuestions = 0; $incorrectQuestions = 0; $totalObtainedMarks = "0"; $correctQuestionIds = []; // Group Student's Answers by question_id $grouped_answers = []; foreach ($onlineExam->student_answers as $student_answer) { if (isset($student_answer->question_marks)) { if ($student_answer->true_answer == 1) { $correctQuestions++; $correctQuestionData[] = [ 'question_id' => $student_answer->question_id, 'marks' => $student_answer->question_marks ]; $correctQuestionIds[] = $student_answer->question_id; $totalObtainedMarks += $student_answer->question_marks; } } else { $grouped_answers[$student_answer['question_id']][] = $student_answer->toArray(); } } $incorrectQuestions = $totalQuestions - $correctQuestions; if (!empty($correctQuestionIds)) { $incorrectQuestionsData = $onlineExam->question_choice ->whereNotIn('id', $correctQuestionIds); } else { $incorrectQuestionsData = $onlineExam->question_choice; } foreach ($incorrectQuestionsData as $incorrectData) { $inCorrectQuestionData[] = [ 'question_id' => $incorrectData->id, 'marks' => $incorrectData->marks ]; } // Initialized the variables // Loop through Student's Grouped answers if (isset($grouped_answers) && $grouped_answers != null) { foreach ($grouped_answers as $student_answers) { // Filter the options whose is_answer values is 1 $correct_option_ids = array_filter($student_answers[0]['user_submitted_questions']['questions']['options'], static function ($option) { return $option['is_answer'] == 1; }); // Get All Correct Options $correct_option_ids = array_column($correct_option_ids, 'id'); // Get Student's Correct Options $student_option_ids = array_column($student_answers, 'option_id'); // Check if the student's answers exactly match the correct answers then add marks with totalObtainedMarks if (!array_diff($correct_option_ids, $student_option_ids) && !array_diff($student_option_ids, $correct_option_ids)) { // Sum Question marks with ObtainedMarks $totalObtainedMarks += $student_answers[0]['user_submitted_questions']['marks']; // Get Correct Questions Ids $correctQuestionIds[] = $student_answers[0]['user_submitted_questions']['id']; // Increment Correct Question by 1 ++$correctQuestions; // Correct Question Data $correctQuestionData[] = array( 'question_id' => $student_answers[0]['user_submitted_questions']['id'], 'marks' => $student_answers[0]['user_submitted_questions']['marks'] ); } } // Check correctQuestionIds exists and not empty if (!empty($correctQuestionIds)) { // Get Incorrect Questions Excluding Correct answer using correctQuestionIds $incorrectQuestionsData = $onlineExam->question_choice->whereNotIn('id', $correctQuestionIds); } else { // Get All Question Choice as incorrectQuestionsData $incorrectQuestionsData = $onlineExam->question_choice; } // Total Incorrect Questions $incorrectQuestions = $incorrectQuestionsData->count(); // Incorrect Question Data $inCorrectQuestionData = array(); foreach ($incorrectQuestionsData as $incorrectData) { $inCorrectQuestionData[] = array( 'question_id' => $incorrectData->id, 'marks' => $incorrectData->marks ); } } // Find the exam submitted date (assume it's the latest updated_at from student's answers, or fallback to end_date) // $examSubmittedDate = $onlineExam->end_date; $examSubmittedDate = null; if ($onlineExam->student_answers && count($onlineExam->student_answers) > 0) { // Get the latest updated_at or created_at from answers $examSubmittedDate = collect($onlineExam->student_answers)->max(function ($answer) { return $answer->updated_at ?? $answer->created_at; }); try { // $examSubmittedDate = Carbon::createFromFormat('d/m/Y H:i', $examSubmittedDate)->format('Y-m-d H:i:s'); } catch (\Exception $e) { try { // $examSubmittedDate = Carbon::parse($examSubmittedDate)->format('Y-m-d H:i:s'); } catch (\Exception $e2) { $examSubmittedDate = null; // fallback if both parsers fail } } } // Get online exam title and subject name $examTitle = $onlineExam->title ?? ''; $subjectName = $onlineExam->class_subject->subject->name; // Final Array Data with additional info $onlineExamResult = array( 'online_exam_name' => $examTitle, 'subject_name' => $subjectName, 'exam_submitted_date' => $examSubmittedDate, 'total_questions' => $totalQuestions, 'correct_answers' => array( 'total_questions' => $correctQuestions, 'question_data' => $correctQuestionData ), 'in_correct_answers' => array( 'total_questions' => $incorrectQuestions, 'question_data' => $inCorrectQuestionData ), 'total_obtained_marks' => $totalObtainedMarks, 'total_marks' => $totalMarks ?? '0' ); ResponseService::successResponse("", $onlineExamResult); } else { ResponseService::successResponse("", []); } } catch (Throwable $e) { ResponseService::logErrorResponse($e, "StudentApiController getOnlineExamResult Method"); ResponseService::errorResponse(); } } public function getOnlineExamReport(Request $request) { $validator = Validator::make($request->all(), [ 'class_subject_id' => 'nullable|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; $sessionYear = $this->cache->getDefaultSessionYear(); $onlineExams = $this->onlineExam->builder() ->has('question_choice') ->whereHas('online_exam_commons', function ($q) use ($request, $student) { $q->where('class_subject_id', $request->class_subject_id) ->where('class_section_id', $student->class_section_id); }) ->where(['session_year_id' => $sessionYear->id]) ->with([ 'student_answers' => function ($q) use ($student) { $q->where('student_id', $student->user_id) ->with('user_submitted_questions.questions:id', 'user_submitted_questions.questions.options:id,question_id,is_answer'); } ])->with('question_choice:id,online_exam_id,marks', 'class_subject.subject:id,name,type,code,bg_color,image') ->paginate(null); if ($onlineExams->count() > 0) { $totalExamIds = $onlineExams->pluck('id')->toArray(); $totalExamsAttempted = 0; $totalExamsAttempted = $this->studentOnlineExamStatus->builder()->where('student_id', $student->user_id)->whereIn('online_exam_id', $totalExamIds)->where('status', 2)->count(); $examList = array(); foreach ($onlineExams->toArray()['data'] as $onlineExam) { $totalMarks = collect($onlineExam['question_choice'])->sum('marks'); // Initialized totalObtainedMarks with 0 $totalObtainedMarks = "0"; // Group Student's Answers by question_id $grouped_answers = []; foreach ($onlineExam['student_answers'] as $student_answer) { if (isset($student_answer['question_marks'])) { if ($student_answer['true_answer'] == 1) { $totalObtainedMarks += $student_answer['question_marks']; } } else { $grouped_answers[$student_answer['question_id']][] = $student_answer; } } // Loop through Student's Grouped answers if (isset($grouped_answers) && count($grouped_answers) > 0) { foreach ($grouped_answers as $student_answers) { // Filter the options whose is_answer values is 1 $correct_option_ids = array_filter($student_answers[0]['user_submitted_questions']['questions']['options'], static function ($option) { return $option['is_answer'] == 1; }); // Get All Correct Options $correct_option_ids = array_column($correct_option_ids, 'id'); // Get Student's Correct Options $student_option_ids = array_column($student_answers, 'option_id'); // Convert to integers and sort for proper comparison $correct_option_ids = array_map('intval', $correct_option_ids); $student_option_ids = array_map('intval', $student_option_ids); sort($correct_option_ids); sort($student_option_ids); // Check if the student's answers exactly match the correct answers then add marks with totalObtainedMarks if ($correct_option_ids === $student_option_ids) { $totalObtainedMarks += $student_answers[0]['user_submitted_questions']['marks']; } } } // Add exam to the list $examList[] = [ 'online_exam_id' => $onlineExam['id'], 'title' => $onlineExam['title'], 'obtained_marks' => (string) $totalObtainedMarks, 'total_marks' => (string) $totalMarks, ]; } $totalExamsMarks = collect($examList)->sum('total_marks'); $totalExamsObtainedMarks = collect($examList)->sum('obtained_marks'); // dd($totalExamsMarks); // Calculate Percentage if ($totalMarks > 0) { // Avoid division by zero error $percentage = number_format(($totalExamsObtainedMarks * 100) / max($totalExamsMarks, 1), 2); } else { // If total marks is zero, then percentage is also zero $percentage = 0; } // Build the final data array $onlineExamReportData = array( 'total_exams' => count($totalExamIds), 'attempted' => $totalExamsAttempted, 'missed_exams' => count($totalExamIds) - $totalExamsAttempted, 'total_marks' => (string) $totalExamsMarks, 'total_obtained_marks' => (string) $totalExamsObtainedMarks, 'percentage' => (string) $percentage, 'exam_list' => [ 'current_page' => (string) $onlineExams->currentPage(), 'data' => array_values($examList), 'from' => (string) $onlineExams->firstItem(), 'last_page' => (string) $onlineExams->lastPage(), 'per_page' => (string) $onlineExams->perPage(), 'to' => (string) $onlineExams->lastItem(), 'total' => (string) $onlineExams->total(), ], ); } else { $onlineExamReportData = []; } // Return the response ResponseService::successResponse("", $onlineExamReportData); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getAssignmentReport(Request $request) { $validator = Validator::make($request->all(), [ 'class_subject_id' => 'nullable|numeric' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Auth::user()->student; $sessionYear = $this->cache->getDefaultSessionYear(); // Assignment Data $assignments = $this->assignment->builder() ->where(['session_year_id' => $sessionYear->id]) ->whereNotNull('points') ->whereHas('assignment_commons', function ($q) use ($student, $request) { $q->where('class_section_id', $student->class_section_id) ->where('class_subject_id', $request->class_subject_id); }) ->get(); // Get the assignment submissions $submitted_assignment_ids = $this->assignmentSubmission->builder()->where('student_id', $student->user_id)->whereIn('assignment_id', $assignments->pluck('id'))->pluck('assignment_id'); // Calculate various statistics $total_assignments = $assignments->count(); $total_submitted_assignments = $submitted_assignment_ids->count(); $total_assignment_submitted_points = $assignments->sum('points'); $total_points_obtained = $this->assignmentSubmission->builder()->whereIn('assignment_id', $submitted_assignment_ids)->sum('points'); // Calculate the percentage $percentage = $total_assignment_submitted_points ? number_format(($total_points_obtained * 100) / $total_assignment_submitted_points, 2) : 0; // Get the submitted assignment data with points (using pagination manually) $perPage = 15; $currentPage = $request->input('page', 1); $offset = ($currentPage - 1) * $perPage; $submitted_assignment_data_with_points = $assignments->filter(function ($assignment) use ($submitted_assignment_ids) { return $assignment->points !== null && $submitted_assignment_ids->contains($assignment->id); })->slice($offset, $perPage)->map(function ($assignment) { return [ 'assignment_id' => $assignment->id, 'assignment_name' => $assignment->name, 'obtained_points' => optional($assignment->submission)->points ?? 0, 'total_points' => $assignment->points ]; }); $assignment_report = [ 'assignments' => $total_assignments, 'submitted_assignments' => $total_submitted_assignments, 'unsubmitted_assignments' => $total_assignments - $total_submitted_assignments, 'total_points' => $total_assignment_submitted_points, 'total_obtained_points' => $total_points_obtained, 'percentage' => $percentage, 'submitted_assignment_with_points_data' => [ 'current_page' => $currentPage, 'data' => array_values($submitted_assignment_data_with_points->toArray()), 'from' => $offset + 1, 'to' => $offset + $submitted_assignment_data_with_points->count(), 'per_page' => $perPage, 'total' => $total_submitted_assignments, 'last_page' => ceil($total_submitted_assignments / $perPage), ], ]; ResponseService::successResponse("Data Fetched Successfully", $assignment_report); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getSchoolSettings() { try { if (Auth::user()) { $settings = $this->cache->getSchoolSettings(); $sessionYear = $this->cache->getDefaultSessionYear(); $semester = $this->cache->getDefaultSemesterData(); $features = FeaturesService::getFeatures(); $data = [ 'school_id' => Auth::user()->school_id, 'session_year' => $sessionYear, 'semester' => $semester, 'settings' => $settings, 'features' => (count($features) > 0) ? $features : (object) [] ]; ResponseService::successResponse('Settings Fetched Successfully.', $data); } else { ResponseService::errorResponse(trans('your_account_has_been_deactivated_please_contact_admin'), null, config('constants.RESPONSE_CODE.INACTIVATED_USER')); } } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getSliders() { try { $studentData = Auth::user(); $data = $this->sliders->builder()->where('school_id', $studentData->school_id)->whereIn('type', [1, 3])->get(); ResponseService::successResponse("Sliders Fetched Successfully", $data); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getStudentDiaries() { try { $student = Students::where('user_id', Auth::id())->first(); if (!$student) { return response()->json(['message' => 'Student record not found.'], 404); } $diaryStudents = $this->diaryStudent->builder() ->where('student_id', $student->id) ->with([ 'diary' => function ($query) { $query->with(['diary_category', 'session_year', 'subject']); } ]) ->get(); ResponseService::successResponse("Student Diaries Fetched Successfully", $diaryStudents); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function showStudentDiaryDetail(Request $request) { $validator = Validator::make($request->all(), [ 'id' => 'required|nullable' ]); if ($validator->fails()) { ResponseService::validationError($validator->errors()->first()); } try { $student = Students::where('user_id', Auth::id())->first(); if (!$student) { return response()->json(['message' => 'Student record not found.'], 404); } $diaryStudents = $this->diaryStudent->builder() ->where('id', $request->id) ->where('student_id', $student->id) ->with([ 'diary' => function ($query) { $query->with(['diary_category', 'session_year', 'subject']); } ]) ->firstOrFail(); ResponseService::successResponse("Student Diary Fetched Successfully", $diaryStudents); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getStudentDiaryCategories() { try { $student = Auth::user(); // Get unique diary_category_ids for the student's diaries $categoryIds = $this->diaryStudent ->builder() ->where('student_id', $student->id) ->with([ 'diary' => function ($query) { $query->withTrashed(); } ]) ->get() ->pluck('diary.diary_category_id') ->unique() ->filter() ->values(); // Fetch the DiaryCategory models with these IDs $categories = DiaryCategory::withTrashed() ->whereIn('id', $categoryIds) ->get(); ResponseService::successResponse("Student Diary Categories Fetched Successfully", $categories); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } public function getWebSliders() { try { $studentData = Auth::user(); $data = $this->sliders->builder()->where('school_id', $studentData->school_id)->whereIn('type', [1, 3])->get(); ResponseService::successResponse("Sliders Fetched Successfully", $data); } catch (Throwable $e) { ResponseService::logErrorResponse($e); ResponseService::errorResponse(); } } }