Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 100 |
RelativeGradingService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 7 |
380.00 | |
0.00% |
0 / 100 |
calculateRelativeGrading | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 25 |
|||
removeZeroMarksStudents | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 4 |
|||
assignFailGrade | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 14 |
|||
calculateAndAssignGrades | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 30 |
|||
getStudentsInThisRange | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 14 |
|||
getGradePoint | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 3 |
|||
getGrade | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 10 |
<?php | |
namespace com\linways\core\ams\professional\service; | |
use com\linways\base\util\MakeSingletonTrait; | |
use com\linways\core\ams\professional\service\BaseService; | |
use com\linways\core\ams\professional\dto\GradeSchemaBasedOnType; | |
use com\linways\core\ams\professional\service\CurriculumManageService; | |
use com\linways\core\ams\professional\request\CalculateRelativeGradingRequest; | |
class RelativeGradingService extends BaseService | |
{ | |
use MakeSingletonTrait; | |
/** | |
* Calculates the relative grading for a given request. | |
* | |
* @param CalculateRelativeGradingRequest $request The request object containing the necessary data for relative grading calculation. | |
* @return Array $students. The array of students with their respective grades. | |
*/ | |
public function calculateRelativeGrading(CalculateRelativeGradingRequest $request) | |
{ | |
$request = $this->realEscapeObject($request); | |
try{ | |
//Step 1: Find the grade scheme | |
$params = new \stdClass; | |
$params->type = "ACADEMIC_PAPER_SUBJECT"; | |
$params->ids = [$request->paperSubjectId]; | |
$getGradeSchemeRequest = new GradeSchemaBasedOnType($params); | |
$getGradeSchemeRequest->ids = $params->ids; | |
$gradingDetails = CurriculumManageService::getInstance()->getPossibleSchemaDetails($getGradeSchemeRequest)[0]; | |
//Step2: Find the grade scheme rule | |
$rule = json_decode(CommonService::getInstance()->getSettings("ACADEMICS","RELATIVE_GRADING_RULE")); | |
//Step3: Sort the students based on marks | |
$students = $request->students; | |
// $students = [ | |
// (object) [ "studentId" => "1543", "marks" => 86 ], | |
// (object) [ "studentId" => "2987", "marks" => 83 ], | |
// (object) [ "studentId" => "7612", "marks" => 80 ], | |
// (object) [ "studentId" => "4390", "marks" => 80 ], | |
// (object) [ "studentId" => "5921", "marks" => 79 ], | |
// (object) [ "studentId" => "8345", "marks" => 78 ], | |
// (object) [ "studentId" => "1276", "marks" => 77 ], | |
// (object) [ "studentId" => "3908", "marks" => 76 ], | |
// (object) [ "studentId" => "2093", "marks" => 76 ], | |
// (object) [ "studentId" => "6581", "marks" => 75 ], | |
// (object) [ "studentId" => "9210", "marks" => 75 ], | |
// (object) [ "studentId" => "3429", "marks" => 74 ], | |
// (object) [ "studentId" => "8753", "marks" => 73 ], | |
// (object) [ "studentId" => "1028", "marks" => 72 ], | |
// (object) [ "studentId" => "4593", "marks" => 72 ], | |
// (object) [ "studentId" => "7485", "marks" => 72 ], | |
// (object) [ "studentId" => "2810", "marks" => 71 ], | |
// (object) [ "studentId" => "6234", "marks" => 71 ], | |
// (object) [ "studentId" => "9126", "marks" => 71 ], | |
// (object) [ "studentId" => "1057", "marks" => 68 ], | |
// (object) [ "studentId" => "3846", "marks" => 68 ], | |
// (object) [ "studentId" => "6731", "marks" => 67 ], | |
// (object) [ "studentId" => "8239", "marks" => 66 ], | |
// (object) [ "studentId" => "2903", "marks" => 66 ], | |
// (object) [ "studentId" => "5814", "marks" => 66 ], | |
// (object) [ "studentId" => "9342", "marks" => 65 ], | |
// (object) [ "studentId" => "1739", "marks" => 64 ], | |
// (object) [ "studentId" => "4287", "marks" => 64 ], | |
// (object) [ "studentId" => "7594", "marks" => 63 ], | |
// (object) [ "studentId" => "2183", "marks" => 63 ], | |
// (object) [ "studentId" => "6092", "marks" => 63 ], | |
// (object) [ "studentId" => "8921", "marks" => 63 ], | |
// (object) [ "studentId" => "1765", "marks" => 63 ], | |
// (object) [ "studentId" => "4728", "marks" => 62 ], | |
// (object) [ "studentId" => "7392", "marks" => 60 ], | |
// (object) [ "studentId" => "2916", "marks" => 60 ], | |
// (object) [ "studentId" => "5807", "marks" => 60 ], | |
// (object) [ "studentId" => "8240", "marks" => 59 ], | |
// (object) [ "studentId" => "2031", "marks" => 59 ], | |
// (object) [ "studentId" => "6739", "marks" => 59 ], | |
// (object) [ "studentId" => "8912", "marks" => 59 ], | |
// (object) [ "studentId" => "1358", "marks" => 58 ], | |
// (object) [ "studentId" => "4295", "marks" => 58 ], | |
// (object) [ "studentId" => "7640", "marks" => 57 ], | |
// (object) [ "studentId" => "2871", "marks" => 57 ], | |
// (object) [ "studentId" => "6132", "marks" => 56 ], | |
// (object) [ "studentId" => "9583", "marks" => 55 ], | |
// (object) [ "studentId" => "1467", "marks" => 55 ], | |
// (object) [ "studentId" => "4298", "marks" => 52 ], | |
// (object) [ "studentId" => "7032", "marks" => 52 ], | |
// (object) [ "studentId" => "2145", "marks" => 47 ], | |
// (object) [ "studentId" => "6573", "marks" => 47 ], | |
// (object) [ "studentId" => "8932", "marks" => 46 ], | |
// (object) [ "studentId" => "1329", "marks" => 46 ], | |
// (object) [ "studentId" => "4827", "marks" => 44 ], | |
// (object) [ "studentId" => "7305", "marks" => 44 ], | |
// (object) [ "studentId" => "2941", "marks" => 41 ], | |
// (object) [ "studentId" => "6718", "marks" => 40 ] | |
// ]; | |
usort($students, function($a, $b) { | |
return $b->marks - $a->marks; | |
}); | |
//Step4: Remove the students with 0 marks | |
$students = $this->removeZeroMarksStudents($students); | |
//Step5: Assign the students having fail marks the fail grade and remove from students array | |
$failedStudents = $this->assignFailGrade($students, $rule,$gradingDetails); | |
// Removes the failed students from the main students array | |
if(!empty($failedStudents)) { | |
array_splice($students,array_key_first($failedStudents)); | |
} | |
//Step6: Loop through each rule and assign corresponding grade | |
$students = $this->calculateAndAssignGrades($students, $rule,$gradingDetails); | |
//Step7: Merge the failed students with the students array | |
$students = array_merge($students, $failedStudents); | |
}catch(\Exception $e){ | |
throw new ProfessionalException($e->getCode(),$e->getMessage()); | |
} | |
return $students; | |
} | |
/** | |
* Removes students with zero marks from the given list. | |
*/ | |
private function removeZeroMarksStudents($students) | |
{ | |
return array_filter($students, function($student) { | |
return $student->marks > 0; | |
}); | |
} | |
/** | |
* Assigns a fail grade to students based on the given rule. | |
*/ | |
private function assignFailGrade($students, $rule,$gradingDetails) | |
{ | |
$failMark = $rule->failMark; | |
$failedStudents = []; | |
// Get the letter grade for the fail mark | |
$letterGrade = $this->getGrade($gradingDetails,$rule->failedGp,$rule->awardGpRules[0]->highestGp); | |
foreach ($students as $key => $student) { | |
if ($student->marks < $failMark) { | |
$student->relativeGradePoint = $rule->failedGp; | |
$student->relativeGrade = $letterGrade; | |
$student->isFailed = true; | |
$failedStudents[$key] = $student; | |
} | |
} | |
return $failedStudents; | |
} | |
/** | |
* Calculate and assign grades to students based on the provided grading rules and details. | |
* | |
* @param array $students An array of student objects to be graded. | |
* @param object $rule An object containing the grading rules, including awardGpRules. | |
* @param object $gradingDetails An object containing the details for grading. | |
* | |
* @return array An array of students who have been assigned grades. | |
*/ | |
private function calculateAndAssignGrades($students, $rule,$gradingDetails) | |
{ | |
$totalNoOfStudents = count($students); | |
// Initialize an array to keep track of students who have already been assigned grades | |
$alreadyAssignedStudents = []; | |
$alreadyAssignedStudentIds = []; | |
// Get the top grade point from the first grading rule | |
$topGp = $rule->awardGpRules[0]->highestGp; | |
foreach($rule->awardGpRules as $awardGpRule){ | |
// Extract the highest grade point, percentage of max top grade, percentage of min top grade, and starting grade point from the current rule | |
$highestGp = $awardGpRule->highestGp; | |
$percentOfMaxTopGrade = $awardGpRule->percentOfMaxTopGrade; | |
$percentOfMinTopGrade = $awardGpRule->percentOfMinTopGrade; | |
$startingGp = $awardGpRule->startingGp; | |
$highestTotalScore = 0; | |
// Get the students whose marks fall within the current grade range | |
$studentsInThisRange = $this->getStudentsInThisRange($students,$percentOfMinTopGrade, $percentOfMaxTopGrade); | |
// Remove students who have already been assigned grades from the current range | |
$studentsInThisRange = array_filter($studentsInThisRange, function($student) use ($alreadyAssignedStudentIds) { | |
return !in_array($student->studentId, $alreadyAssignedStudentIds); | |
}); | |
// Add the current range of students to the list of already assigned students | |
$alreadyAssignedStudents = array_merge($alreadyAssignedStudents, $studentsInThisRange); | |
$alreadyAssignedStudentIds = array_merge($alreadyAssignedStudentIds,array_column($studentsInThisRange,'studentId')); | |
$studentsInThisRange = array_values($studentsInThisRange); | |
$highestTotalScore = $studentsInThisRange[0]->marks; | |
$lowestTotalScore = $studentsInThisRange[count($studentsInThisRange) - 1]->marks; | |
// Loop through each student in the current range to assign grade points and grades | |
foreach($studentsInThisRange as $key => $student) { | |
// Assign the highest grade point to the first student in the range | |
if($key === 0) { | |
$student->relativeGradePoint = $highestGp; | |
} else { | |
// Calculate the grade point for the student based on their marks | |
$student->relativeGradePoint = $this->getGradePoint($startingGp, $highestGp, $highestTotalScore,$lowestTotalScore, $student->marks); | |
} | |
// Assign the grade to the student based on their grade point | |
$student->relativeGrade = $this->getGrade($gradingDetails,$student->relativeGradePoint,$topGp); | |
} | |
} | |
return $alreadyAssignedStudents; | |
} | |
/** | |
* Retrieves a list of students whose grades fall within a specified range. | |
* | |
* @param array $students An array of student objects, each containing a 'marks' property. | |
* @param float $percentOfMaxTopGrade The percentage of the maximum top grade to define the upper bound of the range. | |
* @return array An array of student objects whose grades fall within the specified range. | |
*/ | |
private function getStudentsInThisRange($students,$percentOfMinTopGrade, $percentOfMaxTopGrade) | |
{ | |
$indexOfStartStudent = ceil(count($students) * $percentOfMinTopGrade / 100); | |
$indexOfEndStudent = ceil(count($students) * $percentOfMaxTopGrade / 100); | |
// Initialize an array to hold the students in the current grade range | |
$studentsInThisRange = []; | |
$studentAtLastIndex = $students[$indexOfEndStudent-1]; | |
// Slice the students array to get the students in the current grade range | |
$studentsInThisRange = array_slice($students, $indexOfStartStudent, $indexOfEndStudent - $indexOfStartStudent); | |
// Loop through the students starting from the calculated start index | |
foreach (array_slice($students, $indexOfEndStudent) as $student) { | |
// If the student's marks are not equal to the marks of the student at the last index, break the loop | |
if ($student->marks !== $studentAtLastIndex->marks) { | |
break; | |
} | |
// Else add the student to the current grade range array | |
$studentsInThisRange[] = $student; | |
} | |
return $studentsInThisRange; | |
} | |
/** | |
* Calculate the grade point for a student based on their marks and the grading scale. | |
*/ | |
private function getGradePoint($startingGp, $highestGp, $highestTotalScore,$lowestTotalScore, $studentMarks) | |
{ | |
return $highestGp - (($highestTotalScore - $studentMarks) * (($highestGp - $startingGp)/($highestTotalScore-$lowestTotalScore))); | |
} | |
/** | |
* Calculates and returns the letter grade based on the given grade point and grading details. | |
*/ | |
private function getGrade($gradingDetails,$gradePoint,$highestGp) | |
{ | |
$normalisedGradePoint = ($gradePoint/$highestGp) * 100; | |
foreach($gradingDetails->grades as $gradingDetail){ | |
if($normalisedGradePoint >= $gradingDetail->rangeFrom && $normalisedGradePoint <= $gradingDetail->rangeTo){ | |
$grade = $gradingDetail->letterGrade; | |
break; | |
} | |
} | |
return $grade; | |
} | |
} |