Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 20
CRAP
0.00% covered (danger)
0.00%
0 / 1067
CommonExamService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 20
46440.00
0.00% covered (danger)
0.00%
0 / 1067
 __construct
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 3
 __clone
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 getInstance
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 5
 checkIfExamControllerIsEnabled
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 7
 updateAcademicPaperMaximumMarkToContollerSide
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 31
 getV4StudentDetailsByFalseNumber
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 40
 getV4DigitalValuationStaffPermission
0.00% covered (danger)
0.00%
0 / 1
506.00
0.00% covered (danger)
0.00%
0 / 89
 checkStudentIsEligibleForCondonation
0.00% covered (danger)
0.00%
0 / 1
462.00
0.00% covered (danger)
0.00%
0 / 55
 getExamRegistrationDetailsByRequest
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 54
 getStatusOfWorkflowRequest
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 16
 getAllWorkflows
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 10
 getConsolidatedStudentData
0.00% covered (danger)
0.00%
0 / 1
1980.00
0.00% covered (danger)
0.00%
0 / 285
 getStudentExamRegistrationDetails
0.00% covered (danger)
0.00%
0 / 1
90.00
0.00% covered (danger)
0.00%
0 / 69
 checkStudentSemesterWiseAttendanceCriteriaForExamRegistration
0.00% covered (danger)
0.00%
0 / 1
702.00
0.00% covered (danger)
0.00%
0 / 64
 checkHaveMinimumAttendance
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 4
 isEligibleForCondonation
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 7
 getGroupedHeaderDetails
0.00% covered (danger)
0.00%
0 / 1
2256.00
0.00% covered (danger)
0.00%
0 / 245
 fetchExamRegisteredStudentsForTermPromotion
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 23
 getSubCourseRelationByPaperIds
0.00% covered (danger)
0.00%
0 / 1
30.00
0.00% covered (danger)
0.00%
0 / 37
 chekActiveMandatoryOnlineExam
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 21
<?php 
    namespace com\linways\core\ams\professional\service\examcontroller;
    use com\linways\core\ams\professional\exception\ProfessionalException;
    use com\linways\core\ams\professional\service\BaseService;
    use com\linways\core\ams\professional\toggles\PermissionToggle;
    use com\linways\core\ams\professional\service\AssessmentService;
    use com\linways\core\ams\professional\dto\SettingsConstents;
    use com\linways\core\ams\professional\constant\ExamType;
    use com\linways\core\ams\professional\service\CommonService;
    use com\linways\core\ams\professional\service\AttendanceService;
    use com\linways\core\ams\professional\service\v4Attendance\V4AttendanceService; 
    use com\linways\core\ams\professional\util\CommonUtil;
    use com\linways\core\ams\professional\mapper\examcontroller\ConsolidatedMarkReportServiceMapper;
    class CommonExamService extends BaseService
    {
        // /Condition 1 - Presence of a static member variable
        private static $_instance = null;
        private $mapper = [];
        // /Condition 2 - Locked down the constructor
        private function __construct()
        {
            $this->mapper = ConsolidatedMarkReportServiceMapper::getInstance()->getMapper();
        }
        // Prevent any oustide instantiation of this class
        // /Condition 3 - Prevent any object or instance of that class to be cloned
        private function __clone()
        {
        }
        // Prevent any copy of this object
        // /Condition 4 - Have a single globally accessible static method
        public static function getInstance()
        {
            if (!is_object(self::$_instance)) // or if( is_null(self::$_instance) ) or if( self::$_instance == null )
                self::$_instance = new self ();
            return self::$_instance;
        }
        
        /**
         * @return Boolean|null
         * @throws ProfessionalException
         * @author Krishnajith V
         */
        public function checkIfExamControllerIsEnabled() {
            try {
                $isExamControllerToggleEnabled = PermissionToggle::isEnabled("EXAMCONTROLLER") ? true : false;
            }catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
            return $isExamControllerToggleEnabled;
        }
        /**
         * update Academic Paper Maximum Mark To Contoller Side
         * @throws ProfessionalException
         * @author Krishnajith V
         */
        public function updateAcademicPaperMaximumMarkToContollerSide($request) {
            try {
                if($this->checkIfExamControllerIsEnabled()){
                    $academicPaperId = $this->realEscapeString($request->academicPaperId);
                    $externalMaxMark = $this->realEscapeString($request->externalMaxMark);
                    $assessmentIdQuery = "    SELECT distinct
                                                eers.am_assessment_id as assessmentId
                                            FROM
                                                ec_exam_registration_subject eers
                                            WHERE
                                                eers.cm_academic_paper_subjects_id = '$academicPaperId";
                    $assessmentIdsObj = $this->executeQueryForList($assessmentIdQuery);
                    $assessmentIds = $assessmentIdsObj ? $assessmentIdsObj->assessmentId : [];
                    if($assessmentIds){
                        $assessmentIdsWhereCondition = " ostm.am_assessment_id IN ('".implode("','",$assessmentIds)."') ";    
                        $attendedCountQuery = "    SELECT
                                                    ostm.id
                                                FROM
                                                    oe_student_total_mark ostm
                                                WHERE
                                                    $assessmentIdsWhereCondition ";
                        $userAttendedExamsCount = $this->executeQueryForList($attendedCountQuery);
                        if (count($userAttendedExamsCount) > 0) {
                            throw new ProfessionalException ( ProfessionalException::USER_ATTENDED, "Exams cannot be edited since in some exams students have been attended");
                        }
                        AssessmentService::getInstance()->updateMaxMarksInExams($assessmentIds,$externalMaxMark);
                    }
                }
            }catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        /**
         * Get studentDetails By false no ,assessmentId
         * @param int $request
         * @return object|NULL|\com\linways\base\util\$objectList[]
         * @throws ProfessionalException
         */
        public function getV4StudentDetailsByFalseNumber($request)
        {
            $request = $this->realEscapeObject($request);
            $sqlR = "";
            $sql = "";
            $regularExamId = "";
            if (!$request->examId || !$request->falseNumber) {
                return false;
            }
            try {
                //new digital valuation properties
                $digitalValuationProperties = CommonService::getInstance()->getSettings(SettingsConstents::EXAM_CONTROLLER, SettingsConstents::DIGITAL_VALUATION_PROPERTIES);
                $digitalValuationProperties = $digitalValuationProperties ? json_decode($digitalValuationProperties) : new \stdClass;
                if ($request->examType == ExamType::SUPPLY && $digitalValuationProperties->getRegularFalseNumberForSupply) {
                    //get regular false number for supply
                    $sqlR = "SELECT 
                        eer2.id as examRegId,eers2.am_assessment_id  as regularExamId 
                    FROM 
                        ec_exam_registration_subject eers 
                    INNER JOIN ec_exam_registration_batch eerb ON 
                        eerb.id = eers.ec_exam_registration_batch_id 
                    INNER JOIN ec_exam_registration eer ON 
                        eer.id = eerb.ec_exam_registration_id AND 
                        eer.`type` ='SUPPLEMENTARY'
                    INNER JOIN ec_exam_registration_batch eerb2 ON 
                        eerb2.groups_id = eerb.groups_id AND 
                        eerb2.academicTermId = eerb.academicTermId  
                    INNER JOIN ec_exam_registration eer2 ON 
                        eer2.id = eerb2.ec_exam_registration_id AND 
                        eer2.`type` ='REGULAR'
                    INNER JOIN ec_exam_registration_subject eers2 ON 
                        eers2.ec_exam_registration_batch_id = eerb2.id AND 
                        eers2.cm_academic_paper_subjects_id = eers.cm_academic_paper_subjects_id  
                    WHERE eers.am_assessment_id IN($request->examId)";
                    $regularExamId =  $this->executeQueryForObject($sqlR)->regularExamId;
                }
                $examId = $regularExamId ? $regularExamId : $request->examId;
                $sql = "SELECT esar.student_id AS userId from ec_student_assessment_registration esar WHERE esar.properties->>'$.falseNo'= '$request->falseNumber' AND esar.am_assessment_id ='$examId'";
                return $this->executeQueryForObject($sql);
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        /**
         * get staff permissions for assigned digital valuation count
         * @param string $request
         * @throws ProfessionalException
         * @return object|NULL|\com\linways\base\util\$objectList[]
         */
        public function getV4DigitalValuationStaffPermission($request)
        {
            $request = $this->realEscapeObject($request);
            $sql = "";
            $permissionSql = "";
            $permission = false;
            $condition = "";
            $registeredStudents = [];
            if(!$request->staffId || !$request->studentId || !$request->examId || !$request->valCount){
                return $permission;
            }
            try {
                if($request->revalId){
                    //To be handled at the time of revaluation
                    // $digitalValuationProperties = $this->getSettings(SettingsConstents::EXAM_CONTROLLER, SettingsConstents::DIGITAL_VALUATION_PROPERTIES);
                    // $digitalValuationProperties = $digitalValuationProperties ? current(json_decode($digitalValuationProperties)->revaluation) : "";
                    // //FOR SCRUTINY
                    // if($digitalValuationProperties->considerRevaluationType){
                    //     $request->markEntryType = $request->scrId ? "SCRUTINY":"REVALUATION";
                    //     $revaluationTypeId = (int)ExamRevaluationService::getInstance()->getRevaluationTypeIdByOeRequest($request)->revaluationTypeId;
                    //     $condition = $revaluationTypeId  ? " AND revaluationTypeId IN ($revaluationTypeId)" : "";
                    // }
                    $sql = "SELECT IF (esar.valuation_details IS NULL, JSON_OBJECT(), esar.valuation_details) AS valuationDetails FROM ec_student_assessment_registration esar WHERE am_assessment_id ='$request->examId' and student_id = $request->studentId AND ec_exam_registration_type IN ('REVALUATION') AND esar.revaluationType = $request->revalId";
                    $valuationDetail = $this->executeQueryForObject($sql)->valuationDetails;
                    $valuationDetail = json_decode($valuationDetail);
                    $currentValuationStaffDetails = reset(array_filter($valuationDetail->assignedValuationStaffs,function($value)use($request){
                        return $value->count == $request->valCount;
                    }));
                    if(!empty($currentValuationStaffDetails->addiitonalExamniners)){
                        if(in_array($request->staffId,$currentValuationStaffDetails->addiitonalExamniners)){
                            $registeredStudents[] = $currentValuationStaffDetails;
                        }
                    }
                }
                else if($request->rvwId){
                    // check permission for reviewer
                    $sql = "SELECT IF (esar.valuation_details IS NULL, JSON_OBJECT(), esar.valuation_details) AS valuationDetails FROM ec_student_assessment_registration esar WHERE am_assessment_id ='$request->examId' and student_id = $request->studentId AND ec_exam_registration_type IN ('REGULAR', 'SUPPLEMENTARY')";
                    $valuationDetail = $this->executeQueryForObject($sql)->valuationDetails;
                    $valuationDetail = json_decode($valuationDetail);
                    // check reviewer staff permission  
                    $digitalValuationTemplateQuery = "SELECT IF (er.rule IS NULL, JSON_OBJECT(), er.rule) AS rule FROM ec_rule er WHERE er.name ='EXAM_VALUATION_PROCEDURE_RULE' ";
                    $digitalValuationTemplateRule = $this->executeQueryForObject($digitalValuationTemplateQuery)->rule;
                    $digitalValuationTemplateRule = json_decode($digitalValuationTemplateRule);
                    if($digitalValuationTemplateRule->templateName == 'TEMPLATE_WITH_DIGITAL_VALUATION' ){
                        $subjectValuationDetailsQuery = "SELECT IF (eers.valuation_details IS NULL, JSON_OBJECT(), eers.valuation_details) AS valuationDetails 
                                                            FROM ec_exam_registration_subject eers 
                                                            INNER JOIN ec_exam_registration_batch eerb ON
                                                                eerb.id = eers.ec_exam_registration_batch_id
                                                            INNER JOIN ec_exam_registration eer ON
                                                                eer.id = eerb.ec_exam_registration_id WHERE eer.type IN ('REGULAR', 'SUPPLEMENTARY') AND eers.am_assessment_id = '$request->examId";
                        $subjectValuationDetails = $this->executeQueryForObject($subjectValuationDetailsQuery)->valuationDetails;
                        $subjectValuationDetails = json_decode($subjectValuationDetails);
                       
                        // if only one reviewer in current subject then not to be assign revaluver
                        $firstValuationStaff = reset(array_filter($subjectValuationDetails->valuationStaffs,function($value){
                            return $value->count == '1';
                        }));
                        $secondValuationStaff = reset(array_filter($subjectValuationDetails->valuationStaffs,function($value){
                            return $value->count == '2';
                        }));
                        if(count($secondValuationStaff->addiitonalExamniners) > 1){
                            $currentValuationDate = reset(array_filter($subjectValuationDetails->valuationDates,function($value)use($request){
                                return $value->count == '2' && $value->staffId == $request->staffId;
                            }));
                            // mapped valuers ids of current reviewer
                            $viewerIds = $currentValuationDate->viewerIds;
                            // fetch student assigned valuer details
                            $currentValuationStaffDetails = reset(array_filter($valuationDetail->assignedValuationStaffs,function($value)use($request){
                                return $value->count == '1';
                            }));
                            // check assigned valuer and mapped valuers are same
                            foreach($viewerIds as $viewerId){
                                if(!empty($currentValuationStaffDetails->addiitonalExamniners)){
                                    if(in_array($viewerId,$currentValuationStaffDetails->addiitonalExamniners)){
                                        $registeredStudents[] = $currentValuationStaffDetails;
                                    }
                                }
                            }
                        }
                        else{
                             // mapped valuers ids of first valuer 
                             $viewerIds = $firstValuationStaff->addiitonalExamniners;
                             // fetch student assigned valuer details
                             $currentValuationStaffDetails = reset(array_filter($valuationDetail->assignedValuationStaffs,function($value)use($request){
                                 return $value->count == '1';
                             }));
                             // check assigned valuer and mapped valuers are same
                             foreach($viewerIds as $viewerId){
                                 if(!empty($currentValuationStaffDetails->addiitonalExamniners)){
                                     if(in_array($viewerId,$currentValuationStaffDetails->addiitonalExamniners)){
                                         $registeredStudents[] = $currentValuationStaffDetails;
                                     }
                                 }
                             }
                        }
                    }
                }
                else{
                    //FOR REGULAR AND SUPPLY
                    $sql = "SELECT IF (esar.valuation_details IS NULL, JSON_OBJECT(), esar.valuation_details) AS valuationDetails FROM ec_student_assessment_registration esar WHERE am_assessment_id ='$request->examId' and student_id = $request->studentId AND ec_exam_registration_type IN ('REGULAR', 'SUPPLEMENTARY')";
                    $valuationDetail = $this->executeQueryForObject($sql)->valuationDetails;
                    $valuationDetail = json_decode($valuationDetail);
                    $currentValuationStaffDetails = reset(array_filter($valuationDetail->assignedValuationStaffs,function($value)use($request){
                        return $value->count == $request->valCount;
                    }));
                    if(!empty($currentValuationStaffDetails->addiitonalExamniners)){
                        if(in_array($request->staffId,$currentValuationStaffDetails->addiitonalExamniners)){
                            $registeredStudents[] = $currentValuationStaffDetails;
                        }
                    }
                }
                // $result =  $permissionSql ? $this->executeQueryForObject($permissionSql)->id :"";
                $permission = $registeredStudents ? true : false;
                return $permission;
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        /**
         * check student is eligible for condonation
         * @param string $studentRequestArray
         * @throws ProfessionalException
         * @return array
         */
        public function checkStudentIsEligibleForCondonation($studentRequestArray)
        {
            $studentRequestArray = $this->realEscapeArray($studentRequestArray);
            $studentObj = (object) $studentRequestArray;
            if (!$studentObj->studentId || !$studentObj->termId || !$studentObj->workFlowId) {
                return false;
            }
            try {
                $isExamRegistrationApplyRequest = $studentObj->isExamRegistrationApplyRequest;
                $examRegistration = $this->getExamRegistrationDetailsByRequest($studentObj);
                $examProperties = $examRegistration->properties ? json_decode($examRegistration->properties) : '';
                if( empty($examRegistration ) ||  empty($examRegistration->subjects)){
                    return false;
                }
                $minimumAttendancePercentage = $examProperties->minimumAttendancePercentage ? $examProperties->minimumAttendancePercentage : 0;
                $examProperties->genderWiseMinimumAttendance = (array) $examProperties->genderWiseMinimumAttendance;
                $minimumAttendancePercentage = $examProperties->genderWiseMinimumAttendance[strtoupper($studentObj->gender)] ? $examProperties->genderWiseMinimumAttendance[strtoupper($studentObj->gender)] : $minimumAttendancePercentage;
                $attendanceProperties = CommonService::getInstance()->getSettings(SettingsConstents::EXAM_CONTROLLER, SettingsConstents::CONDONATION_GENDER_WISE_ATTENDANCE_RANGE);
                $attendanceProperties = $attendanceProperties ? json_decode($attendanceProperties) : [];
                $attendanceProperties = (array) $attendanceProperties;
                $startRange = 0;
                $endRange = 0;
                $studentObj->attendanceDateTillDate = $examProperties->attendanceClosingDate;
                if ( $attendanceProperties[strtoupper($studentObj->gender)] ){
                    $startRange = $attendanceProperties[strtoupper($studentObj->gender)]->startRange;
                    $endRange = $attendanceProperties[strtoupper($studentObj->gender)]->endRange;
                }
                $studentAttendance = AttendanceService::getInstance()->getSubjectWiseAttendanceDetails($studentObj);
                $hasEligible = 0;
                $eligiblePapers = [];
                foreach ($studentAttendance->data as $subject){
                    if( !in_array($subject->academicPaperId, $examRegistration->subjects)){
                        continue;
                    }
                    if( $startRange && $endRange ){
                        if($subject->percentage >= $startRange && $subject->percentage < $endRange){
                            $hasEligible = 1;
                            $eligiblePapers[$subject->academicPaperId] = $subject->academicPaperId;
                        }
                    } else{
                        if($subject->percentage < $minimumAttendancePercentage){
                            $hasEligible = 1;
                            $eligiblePapers[$subject->academicPaperId] = $subject->academicPaperId;
                        }
                    }
                }
                if( $isExamRegistrationApplyRequest ){
                    return $eligiblePapers;
                }
                else{
                    return $hasEligible == 1 ? true : false;
                }
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        /**
         * get exam registration details by request
         * @param Object $studentRequest
         * @throws ProfessionalException
         * @return Object
         */
        public function getExamRegistrationDetailsByRequest($studentRequest)
        {
            $studentRequest = $this->realEscapeObject($studentRequest);
            $whereCond = "";
            if ( $studentRequest->workFlowId ){
                $whereCond = " AND eer.properties ->> '$.workFlowId' = '$studentRequest->workFlowId";
            }
            if ( $studentRequest->examRegistrationId ){
                $whereCond = " AND eer.id = '$studentRequest->examRegistrationId";
            }
            if ( !$studentRequest->isSemesterWiseAttenanceRequest ){
                $whereCond .= " AND aps.properties ->> '$.isExternal' = 1";
            }
            try {
                $sql = "SELECT 
                    eer.id,
                    eer.name, 
                    eers.cm_academic_paper_subjects_id AS paperSubjectId,
                    eer.properties,
                    eerb.properties as batchProperties,
                    aps.properties ->> '$.classType' AS classType
                FROM 
                    student_program_account spa 
                INNER JOIN ec_exam_registration_batch eerb ON
                    eerb.groups_id = spa.current_batch_id 
                INNER JOIN ec_exam_registration eer ON
                    eer.id = eerb.ec_exam_registration_id  
                INNER JOIN ec_exam_registration_subject eers ON 
                    eers.ec_exam_registration_batch_id = eerb.id  
                INNER JOIN  cm_academic_paper_subjects aps ON 
                    aps.id = eers.cm_academic_paper_subjects_id
                WHERE 
                    spa.student_id =$studentRequest->studentId AND eerb.academicTermId ='$studentRequest->termId' AND eer.trashed IS NULL
                     $whereCond";
                
                if ( $studentRequest->isSemesterWiseAttenanceRequest ){
                    return $this->executeQueryForObject($sql);
                }
                else{
                    $examSubject = $this->executeQueryForList($sql);
                }
                $examObj = new \stdClass();
                foreach ( $examSubject as $subject ){
                    if( $subject->classType == 'PRACTICAL'){
                        continue;
                    }
                    $examObj->id = $subject->id;
                    $examObj->name = $subject->name;
                    $examObj->properties = $subject->properties;
                    $examObj->subjects[$subject->paperSubjectId] = $subject->paperSubjectId;
                }
                return $examObj;
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        public function getStatusOfWorkflowRequest($studentId,$academicTermId,$workFlowId = '')
        {
            $studentId = $this->realEscapeString($studentId);
            $academicTermId = $this->realEscapeString($academicTermId);
            $workFlowId = $this->realEscapeString($workFlowId);
            $query = "SELECT `status` FROM wm_workflow_requests WHERE `student_id`='$studentId' AND `academic_term_id`='$academicTermId";
            if($workFlowId) {
                $query .= " AND `wm_workflow_id`='$workFlowId'";
            }
            $query .= " ORDER BY FIELD(`status`,'APPROVED','REJECTED','PENDING')";
            try{
                $data = $this->executeQueryForObject($query);
                return $data->status;
            }catch(\Exception $e) {
                throw new ProfessionalException($e->getCode(),$e->getMessage());
            }
        }
        public function getAllWorkflows ($userType = 'STUDENT')
        {
            $userType = $this->realEscapeString($userType);
            $query = "SELECT id,name, name AS text FROM wm_workflow WHERE is_active='1' AND type='$userType'";
            try{
                $workFlows = $this->executeQueryForList($query);
                return $workFlows;
            }catch(\Exception $e) {
                throw new ProfessionalException($e->getCode(),$e->getMessage());
            }
        }
        /**
      * get Consoliidated Student Data For academics Sem term registration
     * @param $request
     */
    public function getConsolidatedStudentData($request){
        $request = $this->realEscapeObject($request);
        try {
            // to get student exam registration and block status 
                $studentExamRegistrationReq = new \stdClass;
                $studentExamRegistrationReq->studentId = $request->studentId;
                $studentExamRegistrationReq->groupId = $request->groupId;
                $studentExamRegistrations = reset($this->getStudentExamRegistrationDetails($studentExamRegistrationReq));
                $notPublishedExamRegIds = [];
                foreach($studentExamRegistrations->exams as $key => $examRegistration){
                    // this case to check these cases 
                    // 1. result is published
                    // 2.student wise with held status
                    // 3.result blocking
                    if( !($examRegistration->batchProperties->isResultPublished ) ||  
                        (($examRegistration->batchProperties->isResultPublished) && (strtotime($examRegistration->batchProperties->publishingStartDate) > strtotime(date("Y-m-d H:i")))) || 
                        ( $examRegistration->isResultBlocked) || 
                        ($examRegistration->isResultWithHeld)) {
                        $notPublishedExamRegIds[] = $examRegistration->examRegistrationId;
                    }
                }
                $consolidatedStudentRequest = new \stdClass;
                $consolidatedStudentRequest->studentId = $request->studentId;
                $consolidatedStudentRequest->groupId = $request->groupId;
                if(empty( $consolidatedStudentRequest->studentId)){
                    throw new ProfessionalException(ProfessionalException::EMPTY_PARAMETERS,"Invaild Request.");
                }
                $whereQuery = "";
                if(!empty($consolidatedStudentRequest->groupId)) {
                    $groupIdString = is_array($consolidatedStudentRequest->groupId) ? implode("','",$consolidatedStudentRequest->groupId) : $consolidatedStudentRequest->groupId;
                    $whereQuery .= " AND g.id IN ('$groupIdString') ";
                }
                if(!empty($consolidatedStudentRequest->studentId)) {
                    $studentIdString = is_array($consolidatedStudentRequest->studentId) ? implode(",",$consolidatedStudentRequest->studentId) : $consolidatedStudentRequest->studentId;
                    $whereQuery .= " AND ecsmd.student_id IN ($studentIdString";
                }
                $query = "SELECT DISTINCT
                    ecsmd.student_id AS studentId,
                    ecsmd.groups_id AS groupsId,
                    atm.id AS termId,
                    ecsmd.cm_academic_paper_subjects_id AS paperSubjetId,
                    ecsmd.mark_details AS markDetails,
                    ecsmd.no_of_chances_taken AS noOfChancesTaken,
                    ecsmd.total_mark AS totalMarkObtained,
                    ecsmd.class,
                    ecsmd.grade,
                    ecsmd.failed_status AS failedStatus,
                    ecsmd.mark_history AS markHistory,
                    s.id as subjectId,
                    s.code AS code,
                    s.name AS name,
                    IF(caps.properties ->> '$.classType' = 'THEORY',1,0) AS isTheory,
                    IF(esar.properties->>'$.syllabusSubType' = 'MOOC',1,0) AS isMoocSubject,
                    esar.properties->>'$.studentAttendanceStatus' AS studentAttendanceStatus,
                    sc.subjectcatID AS categoryId,
                    sc.subjectcatName AS categoryName,
                    sc.subjectcatPriority AS categoryPriority,
                    sc.subjectcatCode AS subjectCategoryCode,
                    sc.parentID AS categoryParentId,
                    sc.use_bring_value AS useBringValue,
                    cc.categoryCode,
                    cs.`type` as syllabusType,
                    cs.id as syllabusId,
                    cclo.name as slot
                FROM
                    ec_consolidated_subject_mark_details ecsmd
                INNER JOIN student_program_account spa
                    ON spa.student_id =  ecsmd.student_id
                INNER JOIN student_program_batch_log spbl 
                    ON spbl.program_student_id = spa.id AND spbl.batch_group_id = ecsmd.groups_id AND spbl.properties->>'$.academicStatus' IN ('ACTIVE','COMPLETED')
                INNER JOIN `groups` g ON
                    g.id = spbl.batch_group_id
                INNER JOIN cm_academic_paper_subjects caps ON
                    caps.id = ecsmd.cm_academic_paper_subjects_id
                INNER JOIN cm_academic_paper cap ON
                    cap.id = caps.cm_academic_paper_id
                INNER JOIN cm_syllabus_academic_term_settings csats ON
                    csats.id = cap.cm_syllabus_academic_term_settings_id 
                INNER JOIN cm_syllabus cs ON
                    cs.id = csats.cm_syllabus_id
                INNER JOIN academic_term atm ON
                    atm.id = csats.academic_term_id AND atm.id = spbl.term_id
                INNER JOIN v4_ams_subject s ON
                    s.id = caps.ams_subject_id
                INNER JOIN ec_exam_registration_subject eers ON 
                    eers.cm_academic_paper_subjects_id = caps.id 
                INNER JOIN ec_student_assessment_registration esar ON
                    esar.student_id = ecsmd.student_id AND 
                    esar.am_assessment_id = eers.am_assessment_id AND 
                    esar.ec_exam_registration_type = 'REGULAR'
                LEFT JOIN subject_category sc ON
                    sc.subjectcatID = caps.properties->>'$.categoryId'
                LEFT JOIN categoryCode cc ON
                    cc.subject_category_id = caps.properties->>'$.subjectTypeId'
                    AND cc.subject_category_id = sc.subjectcatID
                    AND cc.course_type_id = CAST(g.properties->>'$.courseTypeId' AS CHAR)
                LEFT JOIN cm_common_list_object cclo ON
                    cap.slot_id = cclo.id AND cclo.type = 'SLOT'
                WHERE
                    1 = 1 AND ecsmd.is_active = 1 
                $whereQuery ORDER BY cclo.name ASC ";
                $subjectsMarkDetails = $this->executeQueryForList($query);
                array_walk($subjectsMarkDetails,function($subject,$key) use($request){
                    $subject->markDetails = json_decode($subject->markDetails);
                    $subject->markHistory = json_decode($subject->markHistory);
                    // $subject->markHistory = "";
                });
                // this case to handle publish exam registration cases
                foreach($subjectsMarkDetails as $subjectKey => $subject){
                    $subject->credit =  $subject->markDetails->credit;
                    $subject->excludeSubjectFromTotal =  $subject->markDetails->excludeSubjectFromTotal;
                    $subject->markHistory = array_filter($subject->markHistory,function($value) use($notPublishedExamRegIds) {
                        return !(in_array($value->examRegistrationId,$notPublishedExamRegIds));
                    });
                    foreach($subject->markHistory as $markHistory){
                        if($markHistory->hasRevaluationMark && in_array($markHistory->revaluationId,$notPublishedExamRegIds)){
                            $markHistory->failedStatus = $markHistory->withoutRevaluationIsFailed == 1 ? "FAILED" : "PASSED";
                            $markHistory->isFailed = $markHistory->failedStatus;
                            $markHistory->grade = $markHistory->withoutRevaluationGrade;
                        }
                    }    
                    $supplyHistory = array_search("SUPPLY", array_column( $subject->markHistory, "examMarkType"));
                    if($supplyHistory || $supplyHistory === 0){
                        $subject->supplyAttempted = 1;
                    }            
                    $regularHistory = array_filter($subject->markHistory,function($value){
                        return $value->examMarkType == "REGULAR";
                    });
                    if(empty($subject->markHistory) || empty($regularHistory)){
                        unset($subjectsMarkDetails[$subjectKey]);
                    }
                    usort($subject->markHistory, function($a, $b) {
                        return ($a->examYear."-".date("m", mktime(0, 0, 0, (int)$a->examMonth, 10))."-".$a->examMarkType) < ($b->examYear."-".date("m", mktime(0, 0, 0, (int)$b->examMonth, 10))."-".$b->examMarkType);
                    });
                    $subject->currentMarkHistory = reset($subject->markHistory);
                    $subject->markDetails = $subject->currentMarkHistory;
                    $subject->failedStatus = $subject->markDetails->resultStatus;
                    $subject->grade = $subject->markDetails->grade;
                    $subject->examMonth = $subject->markDetails->examMonth;
                    $subject->examYear = $subject->markDetails->examYear;
                    $subject->monthName = date("F", mktime(0, 0, 0, (int)$subject->examMonth, 10));
                    $subject->syllabusType = $subject->syllabusType;
                    $subject->syllabusId = $subject->syllabusId;
                    $subject->markDetails->credit = $subject->credit;
                    $subject->earnedCredit = $subject->failedStatus == 'FAILED' ? 0 : (int)$subject->markDetails->credit;
                    if ( $subject->markDetails->studentAttendanceStatus == 'FE' && $request->considerFeStudentGradeChange == 1){
                        $subject->grade = 'FE';
                        $subject->examMonth = '';
                        $subject->examYear = '';
                        $subject->monthName = '';
                        $subject->credit = '';
                    }
                    // if subject exclude from total then the grade become P or F
                    if( $subject->excludeSubjectFromTotal == 1){
                        $subject->grade = $subject->failedStatus == 'PASSED' ? 'P' : 'F';
                    }
                }
                
                
                $whereQuery = "";
                if(!empty($consolidatedStudentRequest->groupId)) {
                    $groupIdString = is_array($consolidatedStudentRequest->groupId) ? implode("','",$consolidatedStudentRequest->groupId) : $consolidatedStudentRequest->groupId;
                    $whereQuery .= " AND esmd.groups_id IN ('$groupIdString') ";
                }
                if(!empty($consolidatedStudentRequest->studentId)) {
                    $studentIdString = is_array($consolidatedStudentRequest->studentId) ? implode(",",$consolidatedStudentRequest->studentId) : $consolidatedStudentRequest->studentId;
                    $whereQuery .= " AND esmd.student_id IN ($studentIdString";
                }
                $query = "SELECT
                    esmd.groups_id AS groupsId,
                    esmd.student_id AS studentId,
                    esmd.academic_term_id AS termId,
                    esmd.mark_details AS markDetails,
                    esmd.mark_history AS markHistory,
                    esmd.total_supply_attempt_count AS supplyAttemptCount,
                    esmd.total_mark AS totalMarkObtained,
                    esmd.sgpa,
                    esmd.grade,
                    esmd.failed_status AS failedStatus,
                    atm.properties->>'$.orderNo' AS termOrder,
                    atm.name AS termName
                FROM
                    ec_semester_mark_details esmd
                INNER JOIN academic_term atm ON
                    atm.id = esmd.academic_term_id
                WHERE
                    1 = 1
                $whereQuery";
                $termMarkDetails = $this->executeQueryForList($query);
                array_walk($termMarkDetails,function($term,$key)use($subjectsMarkDetails){
                    $term->markDetails = json_decode($term->markDetails);
                    $term->markHistory = json_decode($term->markHistory);
                    $term->subjects = array_filter( $subjectsMarkDetails, function($subject)use($term){ 
                        return ($term->groupsId == $subject->groupsId && $term->termId == $subject->termId && $term->studentId == $subject->studentId) ? true : false;
                    });
                    // uasort($term->subjects, function($a, $b) {
                    //     return ($a->slot < $b->slot);
                    // });
                    $term->earnedCredit = array_sum(array_column($term->subjects,'earnedCredit'));
                });
                // this case to handle publish exam registration
                foreach($termMarkDetails as $termKey => $term){
                    $term->markHistory = array_filter($term->markHistory,function($value) use($notPublishedExamRegIds) {
                        return !(in_array($value->examRegistrationId,$notPublishedExamRegIds));
                    });
                    foreach($term->markHistory as $markHistory){
                        if( in_array($markHistory->revaluationId,$notPublishedExamRegIds)){
                            $markHistory->failedStatus = $markHistory->withoutRevaluationFailedStatus;
                            $markHistory->sgpa = $markHistory->withoutRevaluationsgpa;
                        }
                    }
                    $regularTermHistory = array_filter($term->markHistory,function($value){
                        return $value->historyType == "REGULAR";
                    });
                    if(empty($term->markHistory) || empty($regularTermHistory)){
                        unset($termMarkDetails[$termKey]);
                    }
                    usort($term->markHistory, function($a, $b) {
                        return ($a->examYear."-".date("m", mktime(0, 0, 0, (int)$a->examMonth, 10))."-".$a->historyType) < ($b->examYear."-".date("m", mktime(0, 0, 0, (int)$b->examMonth, 10))."-".$b->historyType);
                    });
                    $term->currentMarkHistory = reset($term->markHistory);
                    $term->markDetails = $term->currentMarkHistory;
                    $term->failedStatus = $term->markDetails->failedStatus;
                    $term->sgpa = $term->markDetails->sgpa;
                }
                $whereQuery = "";
                if(!empty($consolidatedStudentRequest->groupId)) {
                    $groupIdString = is_array($consolidatedStudentRequest->groupId) ? implode("','",$consolidatedStudentRequest->groupId) : $consolidatedStudentRequest->groupId;
                    $whereQuery .= " AND ecmd.groups_id IN ('$groupIdString') ";
                }
                if(!empty($consolidatedStudentRequest->studentId)) {
                    $studentIdString = is_array($consolidatedStudentRequest->studentId) ? implode(",",$consolidatedStudentRequest->studentId) : $consolidatedStudentRequest->studentId;
                    $whereQuery .= " AND ecmd.student_id IN ($studentIdString";
                }
                $query = "SELECT
                    ecmd.groups_id AS groupsId,
                    g.properties->>'$.programId' AS programId,
                    ecmd.student_id AS studentId,
                    ecmd.mark_details AS markDetails,
                    ecmd.no_of_arrears AS arrears,
                    ecmd.total_supply_attempt_count AS supplyAttemptCount,
                    ecmd.percentage AS cgpaPercentage,
                    ecmd.cgpa,
                    ecmd.grade,
                    ecmd.failed_status AS failedStatus,
                    sa.studentName AS name,
                    sa.myImage,
                    sa.studentGender AS gender,
                    sa.studentBirthday AS dob,
                    g.name AS batchName,
                    g.properties ->> '$.description' as batchDescription,
                    g.properties ->> '$.optionName' as batchOptionName,
                    g.properties ->> '$.startYear' as admissionYear,
                    spa.properties->>'$.registerNumber' AS regNo,
                    spa.properties->>'$.rollNumber' AS rollNo, 
                    gm.properties,
                    g.properties,
                    p.name AS programName,
                    d.name AS degreeName,
                    GROUP_CONCAT(str.name) AS streamName,
                    GROUP_CONCAT(str.properties->>'$.abbreviation') AS streamDesc,
                    ct.course_type AS courseType,
                    ct.courseTypeID AS courseTypeId
                FROM
                    ec_course_mark_details ecmd
                INNER JOIN `groups` g ON
                    g.id = ecmd.groups_id
                INNER JOIN studentaccount sa ON
                    sa.studentID = ecmd.student_id
                INNER JOIN student_program_account spa ON 
                        spa.student_id = sa.studentID 
                INNER JOIN group_members gm ON
                    gm.groups_id = g.id
                    AND CAST(gm.members->>'$.studentId' AS CHAR) = spa.id
                INNER JOIN program p ON
                    p.id = CAST(g.properties->>'$.programId' AS CHAR)
                INNER JOIN `degree` d ON
                    d.id = p.degree_id
                INNER JOIN course_type ct ON 
                    ct.courseTypeID = p.course_type_id
                LEFT JOIN stream str ON
                    JSON_SEARCH( p.stream_id, 'one', str.id) IS NOT NULL
                WHERE
                    1 = 1
                $whereQuery
                GROUP BY p.id, sa.studentID";
                $courseMarkDetails = $this->executeQueryForList($query);
                $programs = [];
                array_walk($courseMarkDetails,function($course,$key)use(&$programs,$termMarkDetails){
                    $course->markDetails = json_decode($course->markDetails);
                    $course->cgpaInWords = CommonUtil::convertNumberToWords($course->cgpa);
                    $course->academicTerms = array_filter( $termMarkDetails, function($term)use($course){ 
                        return ($term->studentId == $course->studentId) ? true : false;
                    });
                    $course->academicTerms = call_user_func_array('array_merge', array_map( 
                        function ($key, $value) {return array($key => $value);}, 
                        array_column($course->academicTerms, "termId"), 
                        $course->academicTerms)
                    );
                    uasort($course->academicTerms, function($a, $b) {
                        return ($a->termOrder > $b->termOrder);
                    });
                    $course->academicTerms = array_values($course->academicTerms);
                    $course->earnedCredit = array_sum(array_column($course->academicTerms,'earnedCredit'));
                    $course->examYear =$course->markDetails->latestExamYear;
                    $course->examMonth =$course->markDetails->latestExamMonth;
                    $programs[$course->programId]->students[$course->studentId] = $course;
                });
            } catch (\Exception $e) {
                throw new ProfessionalException(ProfessionalException::ERROR_FETCHING,"Cannot fetch details! Please try again.");
            }
            return $programs;
        } 
                 /**
     * get Student Details For Exam Registration 
     * @param $request
     * @return $students
     */
    public function getStudentExamRegistrationDetails($request){
            $request = $this->realEscapeObject($request);
            $whereQuery = "";
            $students = [];
            if(!empty($request->studentId)) {
                $whereQuery .= " AND sa.studentID = '$request->studentId";
            }
            if(!empty($request->groupId)){
                $groupIdString = is_array($request->groupId) ? "'" . implode("','",$request->groupId) . "'" : "'".$request->groupId."'";
                $whereQuery .= " AND g.id IN ( $groupIdString )";
            }
            $query = "SELECT DISTINCT
                sa.studentID as studentId,
                eer.id AS examRegistrationId,
                eer.name AS examRegistrationName,
                eer.type AS examType,
                eerb.properties AS examRegistrationProperties,
                eerb.properties AS batchProperties,
                eerb.properties AS batchProperties,
                eerb.properties ->>'$.isResultPublished' as isBatchResultPublished,
                esbrm.ec_block_student_reason_id as blockStudentReasonId,
                ebsr.name as blockReasonName,
                cpsa.staffName as contactPersonName,
                ebsr.properties ->> '$.description' AS contactPersonDecription,
                esad.properties->>'$.withHeldStatus' as withHeldStatus,
                eserd.properties ->>'$.isResultWithHeld' AS isResultWithHeld
            FROM
                `groups` g
            INNER JOIN ec_exam_registration_batch eerb ON
                eerb.groups_id = g.id
            INNER JOIN program p ON
                p.id = g.properties ->> '$.programId'
            INNER JOIN ec_exam_registration eer ON
                eer.id = eerb.ec_exam_registration_id
            INNER JOIN student_program_account spa ON spa.current_program_id  = p.id
                INNER JOIN studentaccount sa ON
                    sa.studentID = spa.student_id
            INNER JOIN ec_student_exam_registration_details eserd ON
                eserd.student_id = sa.studentID AND 
                eserd.ec_exam_registration_id = eer.id 
            LEFT JOIN ec_student_block_reason_mapping esbrm ON
                esbrm.student_id = sa.studentID AND esbrm.exam_registration_id = eer.id AND
                esbrm.blocking_type = 'RESULT_BLOCKING'
            LEFT JOIN ec_block_student_reason ebsr ON 
                ebsr.id = esbrm.ec_block_student_reason_id AND
                ebsr.type = 'SEMESTER_WISE'
            LEFT JOIN staffaccounts cpsa ON 
                cpsa.staffID = ebsr.contact_person_id 
            LEFT JOIN ec_student_additional_details esad ON 
                esad.student_id = sa.studentID AND
                esad.program_id = p.id AND
                esad.type = 'FINAL_MARK_CARD' 
            WHERE
                eer.trashed IS NULL ";
            try {
                $studentExamRegistrationDetails = $this->executeQueryForList($query.$whereQuery);
                foreach( $studentExamRegistrationDetails as $stdentExams){
                    $stdentExams->examRegistrationProperties = json_decode($stdentExams->examRegistrationProperties);
                    $stdentExams->batchProperties = json_decode($stdentExams->batchProperties);
                    $stdentExams->isResultBlocked = !empty($stdentExams->blockStudentReasonId) ? true : false;
                    $stdentExams->isResultWithHeld = $stdentExams->isResultWithHeld ? true : false;
                    $students[$stdentExams->studentId]->id = $stdentExams->studentId;
                    $students[$stdentExams->studentId]->withHeldStatus = $stdentExams->withHeldStatus ? true : false;
                    $students[$stdentExams->studentId]->exams[$stdentExams->examRegistrationId] = $stdentExams;
                }
            } catch (\Exception $e) {
                throw new ProfessionalException(ProfessionalException::ERROR_FETCHING_EXAM_REGISTRATION,"Cannot fetch Exam Registration of the student.");
            }
            return $students;
        }
        
        /**
         * Checks the student's semester-wise attendance criteria for exam registration.
         *
         * @param mixed $studentObj The student object containing student details.
         * @return mixed Returns the eligibility status for exam registration or the workflow ID for condonation approval.
         * @throws ProfessionalException If an error occurs during the process.
         */
        public function checkStudentSemesterWiseAttendanceCriteriaForExamRegistration($studentObj)
        {
            if(is_array($studentObj)){
                $studentObj = (object)$studentObj;
            }
            $studentObj = $this->realEscapeObject($studentObj);
            if (!$studentObj->studentId || !$studentObj->termId || (!$studentObj->workFlowId && !$studentObj->examRegistrationId)) {
                return false;
            }
            $studentId = $studentObj->studentId;
            $studentObj->studentGender = $studentObj->gender ?  $studentObj->gender : $studentObj->studentGender;
            $response = new \stdClass();
            $response->enableDayWiseAttendanceChecking = false;
            $response->eligibleToApplyByDayWiseAttendance = true;
            $condonationStatus = "";
            try {
                if($studentId){
                    $minAttendanceForExamRegRule = json_decode(CommonService::getInstance()->getSettings(SettingsConstents::EXAM_CONTROLLER, SettingsConstents::SEMESTER_EXAM_SETTINGS));
                    $minAttendanceForExamReg = $minAttendanceForExamRegRule->minAttendanceForExamReg;
                    $maxAttendanceAllowance = $minAttendanceForExamRegRule->maxAttendanceAllowance;
                    if($minAttendanceForExamReg->enableDayWiseAttendanceChecking){
                        $response->enableDayWiseAttendanceChecking = true;
                        $response->eligibleToApplyByDayWiseAttendance = false;
                        $response->isCondonationApproved = false;
                        
                        $studentObj->isSemesterWiseAttenanceRequest = true;
                        $examRegistration = $this->getExamRegistrationDetailsByRequest($studentObj);
                        $examProperties = $examRegistration->properties ? json_decode($examRegistration->properties) : '';
                        $batchProperties = $examRegistration->batchProperties ? json_decode($examRegistration->batchProperties) : '';
                        if( empty($examRegistration )){
                            return false;
                        }
                        // get day wise attendance
                        $studentObj->attendanceClosingDate = $batchProperties->attendanceClosingDate;
                        $dayWiseAttendance = V4AttendanceService::getInstance()->calculateDayWiseAttendancePercentage($studentObj);
                        $minimumAttendancePercentage = $examProperties->minimumAttendancePercentage ? $examProperties->minimumAttendancePercentage : 0;
                        $examProperties->genderWiseMinimumAttendance = (array) $examProperties->genderWiseMinimumAttendance;
                        if($studentObj->studentGender){
                            $minimumAttendancePercentage = $examProperties->genderWiseMinimumAttendance[strtoupper($studentObj->studentGender)] ? $examProperties->genderWiseMinimumAttendance[strtoupper($studentObj->studentGender)] : $minimumAttendancePercentage;
                            $response->minimumAttendancePercentage = $minimumAttendancePercentage;
                            $response->haveMinAttendance = $this->checkHaveMinimumAttendance($dayWiseAttendance->attendancePercentage,$minimumAttendancePercentage);
                            $response->eligibleToApplyByDayWiseAttendance = $response->haveMinAttendance ? true : $response->eligibleToApplyByDayWiseAttendance;
                            $dayWiseAttendance->maxAttendanceAllowance = $maxAttendanceAllowance;
                            $response->eligibleForCondonation = $response->eligibleToApplyByDayWiseAttendance ? false : $this->isEligibleForCondonation($dayWiseAttendance,$minimumAttendancePercentage);
                            if($response->eligibleForCondonation){
                                if($examProperties->workFlowId){
                                    $response->workflowId = $examProperties->workFlowId;
                                    $response->eligibleForCondonation = $examProperties->workFlowId ? $response->eligibleForCondonation : false;
                                    $condonationStatus = $examProperties->workFlowId ? $this->getStatusOfWorkflowRequest($studentObj->studentId, $studentObj->termId,$examProperties->workFlowId) : '';
                                }
                                $response->isCondonationApproved = ($condonationStatus == "APPROVED") ? true : false;
                                $response->eligibleToApplyByDayWiseAttendance = $response->isCondonationApproved ? true : $response->eligibleToApplyByDayWiseAttendance;
                            }
                        }
                    }
                }
                if ( $studentObj->fromWorkFlowModule ){
                    $isEligible = $response->eligibleForCondonation ? "1" : "0";
                    return $isEligible;
                }
                else{
                    return $response;
                }
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
        
        /**
         * Checks if the attendance percentage is greater than or equal to the minimum attendance required.
         *
         * @param float $attPercent The attendance percentage.
         * @param float $minAttend The minimum attendance required.
         * @return bool Returns true if the attendance percentage is greater than or equal to the minimum attendance required, false otherwise.
         */
        private function checkHaveMinimumAttendance($attPercent, $minAttend)
        {
            $haveMinAttendance = (float) $attPercent >= (float) $minAttend;
            return $haveMinAttendance;
        }
        /**
         * Checks if the student is eligible for condonation based on the attendance percentage.
         *
         * @param mixed $dayWiseAttendance The day-wise attendance details.
         * @param float $minAttend The minimum attendance required.
         * @return bool Returns true if the student is eligible for condonation, false otherwise.
         */
        private function isEligibleForCondonation($dayWiseAttendance,$minAttend){
            // value can be made dynamic later as of now it is 10
            $maxAttendanceAllowance = $dayWiseAttendance->maxAttendanceAllowance ? $dayWiseAttendance->maxAttendanceAllowance : 10;
            $totalAttandaceDays = $dayWiseAttendance->totalWorkingDates;
            $totalPresentDays = $dayWiseAttendance->totalDaysPresent + $maxAttendanceAllowance;
            $attendancePercentage = $totalAttandaceDays ? ($totalPresentDays / $totalAttandaceDays) * 100 : 0;
            $haveMinAttendance = $this->checkHaveMinimumAttendance($attendancePercentage,$minAttend);
            return $haveMinAttendance;
        }
        
        /**
         * get Header Details
         * @param $request
         * @return $header
         */
        
        public function getGroupedHeaderDetails($request){
            
            $whereQuery = "";
            $examRegWhereQuery = "";
            if(!empty($request->batchId)) {
                $whereQuery .= " AND g.id = '$request->batchId";
                $examRegWhereQuery .= " AND eerb.groups_id  = '$request->batchId";
            }
            if(!empty($request->termId)) {
                $whereQuery .= " AND csats.academic_term_id = '$request->termId";
                $examRegWhereQuery .= " AND eerb.properties->>'$.academicTermId' = '$request->termId";
            }
            if(!empty($request->examRegId)) {
                $examRegWhereQuery .= " AND ec.id = '$request->examRegId";
            }
            
            try {
            // for mormal curriculum
            $sql = "SELECT DISTINCT 
                        cc.properties->'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' as minorChoosed , 
                        cs.type,
                        UPPER(concat(IFNULL(vas.name,''),' ( ' ,IFNULL(vas.code,''),' )')) as subjectName,
                        vas.id as subjectId, 
                        cap.id, 
                        cap.properties->>'$.noOfSubjectThatAStudentCanChoose' as paperCount, 
                        cap.name as paperName,
                        count(DISTINCT caps.id) as paperSubjectCount,
                        GROUP_CONCAT(caps.id) as paperSubjectIds,
                        case when cap.properties->>'$.noOfSubjectThatAStudentCanChoose' <> count(caps.id) THEN 1
                        else 0
                        end as isgrouped
                        FROM `groups` g
                        INNER JOIN `cm_syllabus_academic_term_settings` csats ON csats.cm_syllabus_id IN (
                            SELECT csr.cm_syllabus_id FROM cm_curriculum_syllabus_relation csr
                            WHERE csr.cm_curriculum_id = g.cm_curriculum_id UNION
                            SELECT vesfbg.cm_syllabus_id FROM v4_extra_syllabus_for_batch_groups vesfbg
                            WHERE vesfbg.batch_groups_id = g.id
                        )
                        INNER JOIN `cm_academic_paper` cap ON cap.cm_syllabus_academic_term_settings_id = csats.id
                        INNER JOIN `cm_academic_paper_subjects` caps ON caps.cm_academic_paper_id = cap.id
                        INNER JOIN `v4_ams_subject` vas ON vas.id = caps.ams_subject_id
                        INNER JOIN cm_syllabus cs ON cs.id = csats.cm_syllabus_id
                        INNER JOIN cm_curriculum_syllabus_relation ccsr  ON ccsr.cm_syllabus_id = cs.id
                        INNER JOIN cm_curriculum cc ON cc.id = ccsr.cm_curriculum_id
                        WHERE 1= 1 AND cc.id = g.cm_curriculum_id
                        $whereQuery
                        AND (
                              cc.properties->'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' IS NULL OR
                              cc.properties->'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' = '' OR
                              cs.type <> 'MINOR'
                          )
                        GROUP BY cap.id  ORDER BY ccsr.orderNo ASC, cap.properties ->> '$.order' ASC, caps.properties ->> '$.order' ASC ";
                    
                    $acdemicPaperDetails = $this->executeQueryForList($sql);
                    
                // FETCHING THE SUBJECT DETAILS 
                $sqlData = "SELECT 
                  UPPER(concat(IFNULL(vas.name,''),' ( ' ,IFNULL(vas.code,''),' )')) as subjectName,
                  caps.id,
                  vas.id as subjectId
                  FROM `groups` g
                      INNER JOIN `cm_syllabus_academic_term_settings` csats ON csats.cm_syllabus_id IN (
                          SELECT csr.cm_syllabus_id FROM cm_curriculum_syllabus_relation csr
                          WHERE csr.cm_curriculum_id = g.cm_curriculum_id UNION
                          SELECT vesfbg.cm_syllabus_id FROM v4_extra_syllabus_for_batch_groups vesfbg
                          WHERE vesfbg.batch_groups_id = g.id
                      )
                      INNER JOIN `cm_academic_paper` cap ON cap.cm_syllabus_academic_term_settings_id = csats.id
                      INNER JOIN `cm_academic_paper_subjects` caps ON caps.cm_academic_paper_id = cap.id
                      INNER JOIN `v4_ams_subject` vas ON vas.id = caps.ams_subject_id";
                $sqlData .= " WHERE 1 = 1 $whereQuery  AND cap.properties->>'$.noOfSubjectThatAStudentCanChoose' > 1 ORDER BY cap.name, vas.name";
            
                $subjectDetails = $this->executeQueryForList($sqlData);
                
                $subjectMap = [];
                foreach ($subjectDetails as $subject) {
                    $subjectMap[$subject->id] = new \stdClass();
                    $subjectMap[$subject->id]->subjectId = $subject->subjectId;
                    $subjectMap[$subject->id]->subjectName = $subject->subjectName;
                }
                    
                    
                    //For MINOR SELECTED CURRICULUM
                    
                    $query = "SELECT     
                        cc.id as curriculumId,
                        cc.properties->>'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' as minorChoosed , 
                        cs.id as syllabusId,
                        cs.type,
                        UPPER(concat(IFNULL(vas.name,''),' ( ' ,IFNULL(vas.code,''),' )')) as subjectName,
                        vas.id as subjectId, 
                        cap.id as paperId, 
                        cap.properties->>'$.noOfSubjectThatAStudentCanChoose' as paperCount, 
                        cap.name as paperName,
                        -- count(caps.id) as paperSubjectCount,
                        -- GROUP_CONCAT(caps.id) as paperSubjectIds,
                        caps.id as paperSubjectIds
                        -- case when cap.properties->>'$.noOfSubjectThatAStudentCanChoose' <> count(caps.id) THEN 1
                        -- else 1
                        -- end as isgrouped
                        FROM `groups` g
                        INNER JOIN `cm_syllabus_academic_term_settings` csats ON csats.cm_syllabus_id IN (
                            SELECT csr.cm_syllabus_id FROM cm_curriculum_syllabus_relation csr
                            WHERE csr.cm_curriculum_id = g.cm_curriculum_id 
                            -- UNION
                            -- SELECT vesfbg.cm_syllabus_id FROM v4_extra_syllabus_for_batch_groups vesfbg
                            -- WHERE vesfbg.batch_groups_id = g.id
                        )
                        INNER JOIN `cm_academic_paper` cap ON cap.cm_syllabus_academic_term_settings_id = csats.id
                        INNER JOIN `cm_academic_paper_subjects` caps ON caps.cm_academic_paper_id = cap.id
                        INNER JOIN `v4_ams_subject` vas ON vas.id = caps.ams_subject_id
                        INNER JOIN cm_syllabus cs ON cs.id = csats.cm_syllabus_id
                        INNER JOIN cm_curriculum_syllabus_relation ccsr  ON ccsr.cm_syllabus_id = cs.id
                        INNER JOIN cm_curriculum cc ON cc.id = ccsr.cm_curriculum_id
                        WHERE 1= 1 AND cc.id = g.cm_curriculum_id
                        $whereQuery
                        AND 
                        (cc.properties->'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' IS NOT NULL OR
                              cc.properties->'$.noOfMaximumSyllabusOfTypeCanStudy.MINOR' <> '')
                          AND cs.type = 'MINOR'
                        -- AND cc.id = '3cc5026e350d11eeb'
                          -- AND g.id = 'aGiTFFjtWkgBbqfRz'
                          -- AND csats.academic_term_id  = 3
                        -- GROUP BY cap.id  
                        ORDER BY ccsr.orderNo ASC, cap.properties ->> '$.order' ASC, caps.properties ->> '$.order' ASC ";
                        
                        $examRegisteredPaperQuery = "   SELECT eers.cm_academic_paper_subjects_id as paperSubjectId from ec_exam_registration ec 
                        INNER JOIN ec_exam_registration_batch eerb ON eerb.ec_exam_registration_id = ec.id
                        INNER JOIN ec_exam_registration_subject eers ON eers.ec_exam_registration_batch_id = eerb.id
                        WHERE 1 =1 $examRegWhereQuery ";
                    // $academicPaperMinors = $this->executeQueryForList($query);
                    $result = $this->executeQueryForList($query, $this->mapper[ConsolidatedMarkReportServiceMapper::HEADER_LOOPING_MINOR]);
                    $selectedPapers = [];
                    $remainingSyllabus = $result[0]->syllabusDetails;
                    $limit = $result[0]->minorChoosed;
                            
                    while (count($selectedPapers) < $limit && !empty($remainingSyllabus)) {
                        $highestPaper = null;
                        $highestIndex = null;
                    
                        // Loop through syllabusDetails to find the highest paperCount
                        foreach ($remainingSyllabus as $index => $syllabus) {
                            if (!empty($syllabus->paperDetails)) {
                                // Sort the paperDetails array by paperCount in descending order
                                usort($syllabus->paperDetails, function ($a, $b) {
                                    return $b->paperCount <=> $a->paperCount;
                                });
                            
                                // Check the highest paper from this syllabus
                                $currentHighestPaper = $syllabus->paperDetails[0];
                                if ($highestPaper === null || $currentHighestPaper->paperCount > $highestPaper->paperCount) {
                                    $highestPaper = $currentHighestPaper;
                                    $highestIndex = $index;
                                }
                            }
                        }
                    
                        if ($highestPaper) {
                            $selectedPapers[] = $highestPaper;
                        
                            // Remove the selected paper from the syllabus's paperDetails
                            array_shift($remainingSyllabus[$highestIndex]->paperDetails);
                        
                            // If the syllabus has no more papers, remove it from consideration
                            if (empty($remainingSyllabus[$highestIndex]->paperDetails)) {
                                unset($remainingSyllabus[$highestIndex]);
                            }
                        }
                    }
                    
                    foreach ($result[0]->syllabusDetails as $syllabus) {
                        foreach ($syllabus->paperDetails as $paper) {
                            foreach ($paper->paperSubjectDetails as $subjectDetail) {
                                $paperSubjectIds[] = $subjectDetail->paperSubjectIds;
                            }
                        }
                    }
                    
                    foreach ($selectedPapers as $paper) {
                            foreach ($paper->paperSubjectDetails as $subjectDetail) {
                                $paperSubjectIds[] = $subjectDetail->paperSubjectIds;
                            }
                    }
                    
                    $paperSubjectIds = array_unique($paperSubjectIds);
                    
                    
                    $examRegisteredPaperDetails = array_column( $this->executeQueryForList($examRegisteredPaperQuery),'paperSubjectId');
                    
                    $paperSubjectIds = array_intersect($examRegisteredPaperDetails, $paperSubjectIds);
                    
                    $paperDetails = [];
                    foreach ($acdemicPaperDetails as $key => $paper) {
                    $count = ($paper->paperCount  === "" || $paper->paperCount  == 1) ? 1 : $paper->paperCount ;
                    
                    // Add copies of the object up to the specified count
                    for ($i = 0; $i < $count; $i++) {
                      $newItem = new \stdClass();
                      $newItem = clone $paper;
                      $newItem->paperSubjectIds = explode(",", $paper->paperSubjectIds);
                      $newItem->index = $i;
                      $newItem->id = $paper->id .'' . $i;
                      if($count > 1 && $paper->paperSubjectCount === $count){
                        $newItem->subjectName = isset($subjectMap[$newItem->paperSubjectIds[$i]]) ? $subjectMap[$newItem->paperSubjectIds[$i]]->subjectName : $newItem->subjectName;
                        $newItem->subjectId = isset($subjectMap[$newItem->paperSubjectIds[$i]]) ? $subjectMap[$newItem->paperSubjectIds[$i]]->subjectId : $newItem->subjectId;
                        $newItem->paperSubjectIds = isset($subjectMap[$newItem->paperSubjectIds[$i]]) ? [$newItem->paperSubjectIds[$i]] : $newItem->paperSubjectIds;
                      }
                      if(!empty(array_intersect($newItem->paperSubjectIds, $examRegisteredPaperDetails))){
                        $paperDetails[] = $newItem;
                      }
                    }
                    
                    }
                    
                    // FILTERING THE PAPERDETAILS WITH EXAM REGISTERD ONLY PAPER SUBJECTS 
                    // Filter the paperDetails array
                    $filteredPaperDetails = array_filter($paperDetails, function ($paper) use ($examRegisteredPaperDetails) {
                        if (isset($paper->paperSubjectIds)) {
                            // Filter the paperSubjectIds
                            $paper->paperSubjectIds = array_values(array_filter($paper->paperSubjectIds, function ($id) use ($examRegisteredPaperDetails) {
                                return in_array($id, $examRegisteredPaperDetails);
                            }));
                        
                            // If paperSubjectIds is empty, remove this object
                            if (empty($paper->paperSubjectIds)) {
                                return false;
                            }
                        }
                    
                        return true;
                    });
                    
                    // Reindex the filtered array
                    $paperDetails = array_values($filteredPaperDetails);
                    
                    $paperDetailsMinor = [];
                    // foreach ($academicPaperMinors as $key => $paper) {
                    //     $count = ($paper->minorChoosed  === "" || $paper->minorChoosed  == 1) ? 1 : $paper->minorChoosed ;
                        
                    //     // Add copies of the object up to the specified count
                    //     for ($i = 0; $i < $count; $i++) {
                    //       $newItem = new \stdClass();
                    //       $newItem = clone $paper;
                    //       $newItem->paperSubjectIds = explode(",", $paper->paperSubjectIds);
                    //       $newItem->index = $i;
                    //       $newItem->id = $paper->id .'' . $i;
                    //       if(!empty(array_intersect($newItem->paperSubjectIds, $examRegisteredPaperDetails))){
                    //         $paperDetailsMinor[] = $newItem;
                    //       }
                    //     }
                        
                    //     } 
                    
                    $idx = 1;
                    foreach ($selectedPapers as $key => $paper) {
                        $count = $paper->paperCount;
                        
                        // Add copies of the object up to the specified count
                        for ($i = 0; $i < $count; $i++) {
                          $newItem = new \stdClass();
                          $newItem = clone $paper;
                          $newItem->paperName = 'MINOR'.' '.($idx);
                          $newItem->isgrouped = 1;
                          $newItem->paperSubjectIds = $paperSubjectIds;
                          $newItem->index = $i;
                          $newItem->id = $paper->id .'' . $i . $idx;
                          if(!empty(array_intersect($newItem->paperSubjectIds, $examRegisteredPaperDetails))){
                            $paperDetailsMinor[] = $newItem;
                          }
                          $idx++;
                        }
                        
                        } 
                        
                    $paper = new \stdClass();
                    $mergedData = array_merge($paperDetails,$paperDetailsMinor);
                    if( $request->hasSubCourse ){
                        $request->distinctPaperIds = array_unique(array_reduce($mergedData, function ($carry, $item) {
                            return array_merge($carry, $item->paperSubjectIds);
                        }, []));
                        $distinctPapersWithClassType = $this->getSubCourseRelationByPaperIds($request);
                        foreach ($mergedData as $groupKey => $group) {
                            foreach($group->paperSubjectIds as $paperKey => $singlePaperId){
                                if (strpos($distinctPapersWithClassType[$singlePaperId]->classType, "THEORY") !== false && $distinctPapersWithClassType[$singlePaperId]->parentSubjectId) {
                                    $found = 0;
                                    foreach ($mergedData as $groupKey2 => $group2) {
                                        if($found == 1){
                                            break;
                                        } 
                                        foreach($group2->paperSubjectIds as $paperKey2 => $singlePaperId2){
                                            if ($singlePaperId2 != $singlePaperId && $distinctPapersWithClassType[$singlePaperId2]->parentSubjectId == $distinctPapersWithClassType[$singlePaperId]->parentSubjectId && strpos($distinctPapersWithClassType[$singlePaperId]->classType, "THEORY") == false) {
                                                unset($mergedData[$groupKey2]->paperSubjectIds[$paperKey2]);
                                                $mergedData[$groupKey]->paperSubjectIds[$paperKey] = $distinctPapersWithClassType[$singlePaperId]->parentSubjectId;
                                                $mergedData[$groupKey]->hasSubCourses = 1;
                                                $mergedData[$groupKey]->subCourses[$distinctPapersWithClassType[$singlePaperId]->parentSubjectId] = $distinctPapersWithClassType[$singlePaperId];
                                                $found = 1;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        $paper->subCourses = $distinctPapersWithClassType;
                        foreach ($mergedData as $groupKey => $group) {
                            if (empty($group->paperSubjectIds)) {
                                unset($mergedData[$groupKey]);
                            }
                        }
                    }
                    $paper->paperDetails = $mergedData;
                    // $paper->paperDetailsMinor = $paperDetailsMinor;
                    return $paper;
                    
                    } catch (\Exception $e) {
                        throw new ProfessionalException($e->getCode(), $e->getMessage());
                    }
            
        }
         /**
         * Fetch the details of students those who have registered for exam.
         *
         * @param string $batchId
         * @param string $currentTermId 
         * @return array Returns array of student program accountIds of students those who have registered for exam.
         */
        public function fetchExamRegisteredStudentsForTermPromotion($batchId,$currentTermId){
            $sql = "SELECT DISTINCT 
                        spa.id 
                    FROM
                        ec_exam_registration eer 
                    INNER JOIN ec_exam_registration_batch eerb ON 
                        eerb.ec_exam_registration_id = eer.id 
                    INNER JOIN ec_exam_registration_subject eers ON 
                        eers.ec_exam_registration_batch_id = eerb.id 
                    INNER JOIN ec_student_assessment_registration esar ON 
                        esar.am_assessment_id = eers.am_assessment_id  
                    INNER JOIN cm_academic_paper_subjects caps ON
                        caps.id = eers.cm_academic_paper_subjects_id 
                    INNER JOIN v4_ams_subject vas ON
                        vas.id = caps.ams_subject_id 
                    INNER JOIN studentaccount s ON
                        s.studentID = esar.student_id 
                    INNER JOIN student_program_account spa ON
                        spa.student_id = s.studentID
                    WHERE eerb.groups_id = '".$batchId."' AND esar.properties->>'$.registrationStatus' = 'REGISTERED' AND eerb.academicTermId = '$currentTermId'
                    GROUP BY spa.id";
            $studentList = $this->executeQueryForList($sql);
            return $studentList;
        }
        /**
         * Retrieves the sub-course relation by student based on the search request.
         *
         * @param object $searchRequest The search request object containing the criteria for the query.
         */
        public function getSubCourseRelationByPaperIds($searchRequest) {
            $searchRequest = $this->realEscapeObject($searchRequest);
            $whereQuery = "";
            $whereQuery = "";
            if(!empty($searchRequest->distinctPaperIds)){
                $paperSubjectIdString = is_array($searchRequest->distinctPaperIds) ? "'" . implode("','",$searchRequest->distinctPaperIds) . "'" : "'".$searchRequest->distinctPaperIds."'";
                $whereQuery .= " AND caps.id IN ( $paperSubjectIdString )";
            }
            try {
                $query = "SELECT
                    vsrm.parent_subject_id AS parentSubjectId,
                    vsrm.child_subject_id AS childSubjectId,
                    caps.properties->>'$.classType' as classType,
                    caps.id as paperSubjectId,
                    vas.code AS parentCode,
                    vas.name AS parentName
                FROM
                    cm_academic_paper_subjects caps 
                INNER JOIN v4_subject_relation_mapping vsrm ON
                    vsrm.child_subject_id = caps.ams_subject_id
                INNER JOIN v4_ams_subject vas ON
                    vas.id = vsrm.parent_subject_id
                WHERE
                    1=1 ";
                $subjectList = $this->executeQueryForList($query. $whereQuery);
    
                $subCourseSubjects = [];
                foreach ($subjectList as $subject) {
                    $subCourseSubjects[$subject->paperSubjectId]->childSubjectId = $subject->childSubjectId;
                    $subCourseSubjects[$subject->paperSubjectId]->parentSubjectId = $subject->parentSubjectId;
                    $subCourseSubjects[$subject->paperSubjectId]->parentCode = $subject->parentCode;
                    $subCourseSubjects[$subject->paperSubjectId]->parentName = $subject->parentName;
                    $subCourseSubjects[$subject->paperSubjectId]->classType = $subject->classType;
                }
    
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
            return $subCourseSubjects;
        }
         /**
         * Check if the student has any active mandatory online exams
         * 
         * This function verifies if a student has any currently active online exams that are mandatory
         * by checking if the current time falls within 15 minutes before the start time and the end time
         * of any registered exams for the student.
         * 
         * @param string|int $studentId The ID of the student to check
         * @return object|null Returns student ID if active exams exist, null otherwise
         * @throws ProfessionalException If an error occurs during the database query
         * @author CommonExamService
         */
        public function chekActiveMandatoryOnlineExam($studentId) {
            $studentId = $this->realEscapeString($studentId);
            
            try {
                $query = "SELECT esar.student_id as studentId
                    FROM oe_exams oe 
                    INNER JOIN ec_exam_registration_subject eers ON eers.am_assessment_id = oe.assessment_id
                    INNER JOIN ec_exam_registration_batch eerb On eerb.id = eers.ec_exam_registration_batch_id 
                    INNER JOIN ec_exam_registration eer ON eer.id = eerb.ec_exam_registration_id 
                    INNER JOIN ec_student_assessment_registration esar ON esar.am_assessment_id = eers.am_assessment_id AND esar.ec_exam_registration_type = eer.type 
                    WHERE NOW() BETWEEN DATE_SUB(oe.exam_start_time, INTERVAL 15 MINUTE) AND oe.exam_end_time 
                    AND esar.properties ->> '$.registrationStatus' = 'REGISTERED' 
                    AND oe.type = 'EXAM_CONTROLLER' 
                    AND oe.identifying_context->>'$.type' = 'ONLINE_EXAM' 
                    AND oe.is_archived = 0 
                    AND esar.student_id = '$studentId
                    AND eer.trashed IS NULL";
                    
                $onlineExamDetails = $this->executeQueryForObject($query);
                return $onlineExamDetails;
            } catch (\Exception $e) {
                throw new ProfessionalException($e->getCode(), $e->getMessage());
            }
        }
    }