fix enrollment logic, add financial aid, fix class distribution
Tests / PHPUnit (push) Failing after 1m6s
Tests / PHPUnit (push) Failing after 1m6s
This commit is contained in:
@@ -914,28 +914,37 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
$total = count($cands);
|
||||
if ($sectionCount * $minPerSection > $total) {
|
||||
$msg = 'Insufficient students: ' . $sectionCount . ' sections require at least ' . ($sectionCount * $minPerSection) . ' students, but only ' . $total . ' are available.';
|
||||
return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg);
|
||||
}
|
||||
if ($maxPerSection !== null && $total > $sectionCount * $maxPerSection) {
|
||||
$msg = 'Capacity exceeded: ' . $sectionCount . ' sections can hold at most ' . ($sectionCount * $maxPerSection) . ' students, but ' . $total . ' must be assigned.';
|
||||
return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
// Fetch lettered sections for this class
|
||||
$baseSection = $this->sectionForDistribution($classSectionId, $year);
|
||||
|
||||
// Fetch lettered sections for this class. The requested section count is a max:
|
||||
// if the class cannot split into 2+ sections, keep the assignment on the base grade.
|
||||
$letters = $this->letterSectionsForDistribution($classId, $year);
|
||||
if (empty($letters)) {
|
||||
$msg = 'No lettered sections found for the selected class.';
|
||||
$availableSectionCount = count($letters);
|
||||
if (!$baseSection || (int)($baseSection['class_id'] ?? 0) !== $classId) {
|
||||
$msg = 'No base grade found for the selected class.';
|
||||
return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg);
|
||||
}
|
||||
|
||||
if (count($letters) < $sectionCount) {
|
||||
$msg = 'Not enough sections available. Needed: ' . $sectionCount . ', available: ' . count($letters);
|
||||
return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg);
|
||||
$actualSectionCount = min($sectionCount, $availableSectionCount);
|
||||
if ($minPerSection > 0) {
|
||||
$actualSectionCount = min($actualSectionCount, max(1, intdiv($total, $minPerSection)));
|
||||
}
|
||||
if ($maxPerSection !== null) {
|
||||
$minimumNeededForCapacity = (int)ceil($total / $maxPerSection);
|
||||
$capacitySectionCount = max(1, $availableSectionCount);
|
||||
if ($minimumNeededForCapacity > $capacitySectionCount || $minimumNeededForCapacity > $sectionCount) {
|
||||
$msg = 'Capacity exceeded: available sections can hold at most ' . ($capacitySectionCount * $maxPerSection) . ' students, but ' . $total . ' must be assigned.';
|
||||
return $isAjax ? $json(['ok' => false, 'message' => $msg], 400) : redirect()->back()->with('error', $msg);
|
||||
}
|
||||
$actualSectionCount = max($actualSectionCount, $minimumNeededForCapacity);
|
||||
}
|
||||
|
||||
$letters = array_slice($letters, 0, $sectionCount);
|
||||
if ($actualSectionCount < 2) {
|
||||
$letters = [$baseSection];
|
||||
} else {
|
||||
$letters = array_slice($letters, 0, $actualSectionCount);
|
||||
}
|
||||
$buckets = $this->buildBalancedDistribution($cands, $letters, $minPerSection, $maxPerSection);
|
||||
|
||||
$draftModel = new StudentSectionDistributionDraftModel();
|
||||
@@ -946,21 +955,11 @@ class StudentController extends BaseController
|
||||
$draftIdByStudentId = [];
|
||||
|
||||
$this->db->transStart();
|
||||
$studentIdsToReplace = array_values(array_unique(array_map(
|
||||
static fn(array $student): int => (int)($student['student_id'] ?? 0),
|
||||
$cands
|
||||
)));
|
||||
if (!empty($studentIdsToReplace)) {
|
||||
$draftModel->where('school_year', $year)
|
||||
->whereIn('student_id', $studentIdsToReplace)
|
||||
->where('status', 'pending')
|
||||
->delete();
|
||||
}
|
||||
foreach ($buckets as $b) {
|
||||
$secId = (int)$b['class_section_id'];
|
||||
foreach ($b['assigned'] as $student) {
|
||||
$sid = (int)$student['student_id'];
|
||||
$draftId = (int)$draftModel->insert([
|
||||
$draftId = $this->upsertDistributionDraft($draftModel, [
|
||||
'student_id' => $sid,
|
||||
'class_id' => $classId,
|
||||
'class_section_id' => $secId,
|
||||
@@ -971,7 +970,6 @@ class StudentController extends BaseController
|
||||
'status' => 'pending',
|
||||
'batch_key' => $batchKey,
|
||||
'created_by' => $updatedBy,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
if ($draftId > 0) {
|
||||
@@ -1026,6 +1024,7 @@ class StudentController extends BaseController
|
||||
'age_at_reference' => $student['age_at_reference'] ?? null,
|
||||
'gender' => (string)($student['gender'] ?? ''),
|
||||
'last_year_class_section' => (string)($student['last_year_class_section'] ?? ''),
|
||||
'previous_final_score' => $student['previous_final_score'] ?? null,
|
||||
'class_id' => $classId,
|
||||
'class_section_id' => $secId,
|
||||
'class_section_name' => $nameById[$secId] ?? (string)$secId,
|
||||
@@ -1182,12 +1181,7 @@ class StudentController extends BaseController
|
||||
$now = utc_now();
|
||||
|
||||
$this->db->transStart();
|
||||
$draftModel->where('student_id', $studentId)
|
||||
->where('school_year', $year)
|
||||
->where('status', 'pending')
|
||||
->delete();
|
||||
|
||||
$draftId = (int)$draftModel->insert([
|
||||
$draftId = $this->upsertDistributionDraft($draftModel, [
|
||||
'student_id' => $studentId,
|
||||
'class_id' => $targetClassId,
|
||||
'class_section_id' => $targetSectionId,
|
||||
@@ -1198,7 +1192,6 @@ class StudentController extends BaseController
|
||||
'status' => 'pending',
|
||||
'batch_key' => sha1($year . ':' . $studentId . ':' . microtime(true)),
|
||||
'created_by' => $updatedBy,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
@@ -1286,7 +1279,8 @@ class StudentController extends BaseController
|
||||
return $this->mergeDistributionCandidates(
|
||||
$out,
|
||||
$this->decisionDistributionCandidates($classId, $year),
|
||||
$this->currentYearDistributionCandidates($classId, $year)
|
||||
$this->currentYearDistributionCandidates($classId, $year),
|
||||
$this->registeredKgDistributionCandidates($classId, $year)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1302,7 +1296,7 @@ class StudentController extends BaseController
|
||||
$builder = $this->db->table('student_class sc')
|
||||
->select('0 AS promotion_queue_id, sc.student_id, sc.school_year AS school_year_from, cs.class_id AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->join('students', 'students.id = sc.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id', 'left')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id AND cs.school_year = sc.school_year', 'left')
|
||||
->where('sc.school_year', $year)
|
||||
->where('sc.class_section_id IS NOT NULL', null, false);
|
||||
|
||||
@@ -1325,7 +1319,7 @@ class StudentController extends BaseController
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, cs.class_id AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->join('students', 'students.id = e.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = e.school_year', 'left')
|
||||
->where('e.school_year', $year)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
@@ -1413,6 +1407,40 @@ class StudentController extends BaseController
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function upsertDistributionDraft(StudentSectionDistributionDraftModel $draftModel, array $data): int
|
||||
{
|
||||
$studentId = (int)($data['student_id'] ?? 0);
|
||||
$year = (string)($data['school_year'] ?? '');
|
||||
if ($studentId <= 0 || $year === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$existing = $draftModel
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $year)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
$draftId = (int)($existing['id'] ?? 0);
|
||||
if ($draftId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unset($data['created_at']);
|
||||
$data['status'] = 'pending';
|
||||
$data['applied_at'] = null;
|
||||
$draftModel->update($draftId, $data);
|
||||
|
||||
return $draftId;
|
||||
}
|
||||
|
||||
if (empty($data['created_at'])) {
|
||||
$data['created_at'] = $data['updated_at'] ?? utc_now();
|
||||
}
|
||||
|
||||
return (int)$draftModel->insert($data);
|
||||
}
|
||||
|
||||
private function kgDistributionCandidates(int $classId, string $year): array
|
||||
{
|
||||
if ($classId <= 0 || $year === '' || ! $this->db->tableExists('enrollments') || ! $this->db->tableExists('students')) {
|
||||
@@ -1422,7 +1450,7 @@ class StudentController extends BaseController
|
||||
$builder = $this->db->table('enrollments e')
|
||||
->select('0 AS promotion_queue_id, e.student_id, e.school_year AS school_year_from, cs.class_id AS source_class_id, cs.class_section_name AS source_class_name, students.firstname, students.lastname, students.gender, students.age, students.dob, students.registration_grade', false)
|
||||
->join('students', 'students.id = e.student_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = e.school_year', 'left')
|
||||
->where('e.school_year', $year)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
@@ -1515,7 +1543,12 @@ class StudentController extends BaseController
|
||||
if ($this->db->fieldExists('school_year', 'students')) {
|
||||
$builder->where('school_year', $year);
|
||||
} elseif ($this->db->fieldExists('year_of_registration', 'students') && preg_match('/^(\d{4})/', $year, $matches)) {
|
||||
$builder->where('year_of_registration', (int)$matches[1]);
|
||||
$registrationYears = [(int)$matches[1]];
|
||||
$previousYear = $this->previousSchoolYearName($year);
|
||||
if ($previousYear !== null && preg_match('/^(\d{4})/', $previousYear, $previousMatches)) {
|
||||
$registrationYears[] = (int)$previousMatches[1];
|
||||
}
|
||||
$builder->whereIn('year_of_registration', array_values(array_unique($registrationYears)));
|
||||
}
|
||||
|
||||
if ($this->db->fieldExists('is_active', 'students')) {
|
||||
@@ -1537,6 +1570,10 @@ class StudentController extends BaseController
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->studentHasOnlyKgPriorPlacement($studentId, $year)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ageAtReference = $this->distributionAgeAtReference($row['dob'] ?? null, $year);
|
||||
$targetClassId = $this->distributionTargetClassIdForStudent(
|
||||
$classId,
|
||||
@@ -1565,6 +1602,72 @@ class StudentController extends BaseController
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function studentHasOnlyKgPriorPlacement(int $studentId, string $targetSchoolYear): bool
|
||||
{
|
||||
$previousYear = $this->previousSchoolYearName($targetSchoolYear);
|
||||
if ($studentId <= 0 || $previousYear === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$baseNames = [];
|
||||
if ($this->db->tableExists('student_class')) {
|
||||
$builder = $this->db->table('student_class sc')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id AND cs.school_year = sc.school_year', 'left')
|
||||
->where('sc.student_id', $studentId)
|
||||
->where('sc.school_year', $previousYear)
|
||||
->where('sc.class_section_id IS NOT NULL', null, false);
|
||||
|
||||
if ($this->db->fieldExists('is_event_only', 'student_class')) {
|
||||
$builder->groupStart()
|
||||
->where('sc.is_event_only', 0)
|
||||
->orWhere('sc.is_event_only', null)
|
||||
->groupEnd();
|
||||
}
|
||||
|
||||
foreach ($builder->get()->getResultArray() as $row) {
|
||||
$baseName = $this->baseClassNameForDistribution((string)($row['class_section_name'] ?? ''));
|
||||
if ($baseName !== '') {
|
||||
$baseNames[$baseName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->db->tableExists('enrollments')) {
|
||||
$rows = $this->db->table('enrollments e')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = e.school_year', 'left')
|
||||
->where('e.student_id', $studentId)
|
||||
->where('e.school_year', $previousYear)
|
||||
->where('e.class_section_id IS NOT NULL', null, false)
|
||||
->whereIn('e.enrollment_status', ['admission under review', 'payment pending', 'enrolled'])
|
||||
->groupStart()
|
||||
->where('e.is_withdrawn', 0)
|
||||
->orWhere('e.is_withdrawn', null)
|
||||
->groupEnd()
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$baseName = $this->baseClassNameForDistribution((string)($row['class_section_name'] ?? ''));
|
||||
if ($baseName !== '') {
|
||||
$baseNames[$baseName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($baseNames)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return count($baseNames) === 1 && isset($baseNames['KG']);
|
||||
}
|
||||
|
||||
private function baseClassNameForDistribution(string $classSectionName): string
|
||||
{
|
||||
return strtoupper(trim(preg_replace('/-.+$/', '', $classSectionName) ?? ''));
|
||||
}
|
||||
|
||||
private function isDistributionKgClass(int $classId, string $year): bool
|
||||
{
|
||||
if ($classId <= 0) {
|
||||
@@ -1722,7 +1825,8 @@ class StudentController extends BaseController
|
||||
|
||||
$targetClassId = $this->targetClassIdFromDecision(
|
||||
(string)($row['class_section_name'] ?? ''),
|
||||
(string)($row['decision'] ?? '')
|
||||
(string)($row['decision'] ?? ''),
|
||||
$targetSchoolYear
|
||||
);
|
||||
$ageAtReference = $this->distributionAgeAtReference($row['dob'] ?? null, $targetSchoolYear);
|
||||
$targetClassId = $this->distributionTargetClassIdForStudent(
|
||||
@@ -1754,7 +1858,7 @@ class StudentController extends BaseController
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function targetClassIdFromDecision(string $classSectionName, string $decision): ?int
|
||||
private function targetClassIdFromDecision(string $classSectionName, string $decision, string $targetSchoolYear = ''): ?int
|
||||
{
|
||||
$baseName = strtoupper(trim(preg_replace('/-.+$/', '', $classSectionName) ?? ''));
|
||||
if ($baseName === '') {
|
||||
@@ -1767,17 +1871,30 @@ class StudentController extends BaseController
|
||||
$targetBaseName = '1';
|
||||
} elseif (ctype_digit($baseName)) {
|
||||
$level = (int)$baseName;
|
||||
$targetBaseName = $level >= 9 ? 'YOUTH' : (string)($level + 1);
|
||||
$targetBaseName = $level >= 10 ? 'YOUTH' : (string)($level + 1);
|
||||
} elseif ($baseName === 'YOUTH') {
|
||||
$targetBaseName = 'YOUTH';
|
||||
}
|
||||
}
|
||||
|
||||
$row = $this->classSectionModel
|
||||
$query = $this->classSectionModel
|
||||
->select('class_id')
|
||||
->where('UPPER(class_section_name)', $targetBaseName)
|
||||
->where("class_section_name NOT LIKE '%-%'", null, false)
|
||||
->first();
|
||||
->orderBy('id', 'DESC');
|
||||
if ($targetSchoolYear !== '' && $this->db->fieldExists('school_year', 'classSection')) {
|
||||
$query->where('school_year', $targetSchoolYear);
|
||||
}
|
||||
|
||||
$row = $query->first();
|
||||
|
||||
if (!$row && $targetSchoolYear !== '' && $this->db->fieldExists('school_year', 'classSection')) {
|
||||
$row = $this->classSectionModel
|
||||
->select('class_id')
|
||||
->where('UPPER(class_section_name)', $targetBaseName)
|
||||
->where("class_section_name NOT LIKE '%-%'", null, false)
|
||||
->first();
|
||||
}
|
||||
|
||||
return $row ? (int)$row['class_id'] : null;
|
||||
}
|
||||
@@ -1884,7 +2001,7 @@ class StudentController extends BaseController
|
||||
if ($this->db->tableExists('student_class')) {
|
||||
$builder = $this->db->table('student_class sc')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id', 'left')
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id AND cs.school_year = sc.school_year', 'left')
|
||||
->where('sc.student_id', $studentId)
|
||||
->where('sc.school_year', $previousYear)
|
||||
->where('sc.class_section_id IS NOT NULL', null, false);
|
||||
@@ -1911,7 +2028,7 @@ class StudentController extends BaseController
|
||||
if (empty($names) && $this->db->tableExists('enrollments')) {
|
||||
$rows = $this->db->table('enrollments e')
|
||||
->select('cs.class_section_name')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id', 'left')
|
||||
->join('classSection cs', 'cs.class_section_id = e.class_section_id AND cs.school_year = e.school_year', 'left')
|
||||
->where('e.student_id', $studentId)
|
||||
->where('e.school_year', $previousYear)
|
||||
->where('e.class_section_id IS NOT NULL', null, false)
|
||||
@@ -2236,6 +2353,7 @@ class StudentController extends BaseController
|
||||
'age_at_reference' => $student['age_at_reference'] ?? null,
|
||||
'gender' => (string)($student['gender'] ?? ''),
|
||||
'last_year_class_section' => (string)($student['last_year_class_section'] ?? ''),
|
||||
'previous_final_score' => $student['previous_final_score'] ?? null,
|
||||
'class_id' => $classId,
|
||||
'class_section_id' => 0,
|
||||
'class_section_name' => $className,
|
||||
@@ -2277,7 +2395,26 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
$timezone = new \DateTimeZone((string)(config('School')->attendance['timezone'] ?? user_timezone()));
|
||||
$reference = new \DateTimeImmutable($matches[1] . '-09-01', $timezone);
|
||||
$reference = null;
|
||||
$configuredReference = '';
|
||||
try {
|
||||
$configuredReference = trim((string)($this->configModel ? $this->configModel->getConfig('date_age_reference') : ''));
|
||||
} catch (\Throwable $e) {
|
||||
$configuredReference = '';
|
||||
}
|
||||
|
||||
if ($configuredReference !== '') {
|
||||
$candidate = \DateTimeImmutable::createFromFormat('!Y-m-d', $configuredReference, $timezone);
|
||||
$errors = \DateTimeImmutable::getLastErrors();
|
||||
$hasErrors = is_array($errors) && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0);
|
||||
if ($candidate !== false && !$hasErrors && $candidate->format('Y') === $matches[1]) {
|
||||
$reference = $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($reference === null) {
|
||||
$reference = new \DateTimeImmutable($matches[1] . '-09-01', $timezone);
|
||||
}
|
||||
|
||||
return $reference->setTime(0, 0, 0);
|
||||
}
|
||||
@@ -2312,6 +2449,10 @@ class StudentController extends BaseController
|
||||
return $this->distributionBaseClassIdByName('KG', $schoolYear);
|
||||
}
|
||||
|
||||
if ($defaultClassId === null && $ageAtReference === 6) {
|
||||
return $this->distributionBaseClassIdByName('1', $schoolYear);
|
||||
}
|
||||
|
||||
if ($this->isDistributionKgSource($defaultClassId, $sourceClassName, $schoolYear)) {
|
||||
if ($ageAtReference !== null && $ageAtReference < 6) {
|
||||
return $this->distributionBaseClassIdByName('KG', $schoolYear) ?? $defaultClassId;
|
||||
@@ -2474,8 +2615,8 @@ class StudentController extends BaseController
|
||||
}
|
||||
|
||||
$builder = $this->db->table('student_section_distribution_drafts d')
|
||||
->select('d.id AS draft_id, d.class_id, d.class_section_id, d.previous_school_year, cs.class_section_name, students.firstname, students.lastname, students.gender, students.dob, d.student_id')
|
||||
->join('classSection cs', 'cs.class_section_id = d.class_section_id', 'left')
|
||||
->select('d.id AS draft_id, d.class_id, d.class_section_id, d.previous_school_year, d.previous_final_score, cs.class_section_name, students.firstname, students.lastname, students.gender, students.dob, d.student_id')
|
||||
->join('classSection cs', 'cs.class_section_id = d.class_section_id AND cs.school_year = d.school_year', 'left')
|
||||
->join('students', 'students.id = d.student_id', 'left')
|
||||
->where('d.class_id', $classId)
|
||||
->where('d.school_year', $year)
|
||||
@@ -2511,6 +2652,10 @@ class StudentController extends BaseController
|
||||
'class_section_id' => $sectionId,
|
||||
'class_section_name' => (string)($row['class_section_name'] ?? $sectionId),
|
||||
'total' => 0,
|
||||
'male' => 0,
|
||||
'female' => 0,
|
||||
'score_total' => 0.0,
|
||||
'score_count' => 0,
|
||||
'student_names' => [],
|
||||
'student_assignments' => [],
|
||||
];
|
||||
@@ -2520,6 +2665,16 @@ class StudentController extends BaseController
|
||||
if ($name === '') {
|
||||
$name = 'Student #' . $studentId;
|
||||
}
|
||||
$gender = strtolower((string)($row['gender'] ?? ''));
|
||||
if ($gender === 'female') {
|
||||
$sections[$sectionId]['female']++;
|
||||
} else {
|
||||
$sections[$sectionId]['male']++;
|
||||
}
|
||||
if (is_numeric($row['previous_final_score'] ?? null)) {
|
||||
$sections[$sectionId]['score_total'] += (float)$row['previous_final_score'];
|
||||
$sections[$sectionId]['score_count']++;
|
||||
}
|
||||
$sections[$sectionId]['student_names'][] = $name;
|
||||
$sections[$sectionId]['student_assignments'][] = [
|
||||
'draft_id' => (int)($row['draft_id'] ?? 0),
|
||||
@@ -2527,6 +2682,7 @@ class StudentController extends BaseController
|
||||
'student_name' => $name,
|
||||
'age_at_reference' => $this->distributionAgeAtReference($row['dob'] ?? null, $year),
|
||||
'gender' => (string)($row['gender'] ?? ''),
|
||||
'previous_final_score' => is_numeric($row['previous_final_score'] ?? null) ? (float)$row['previous_final_score'] : null,
|
||||
'last_year_class_section' => $this->distributionPreviousClassSectionName(
|
||||
$studentId,
|
||||
$year,
|
||||
@@ -2538,6 +2694,15 @@ class StudentController extends BaseController
|
||||
$sections[$sectionId]['total']++;
|
||||
}
|
||||
|
||||
foreach ($sections as &$section) {
|
||||
$scoreCount = (int)($section['score_count'] ?? 0);
|
||||
$section['average_score'] = $scoreCount > 0
|
||||
? round((float)$section['score_total'] / $scoreCount, 2)
|
||||
: null;
|
||||
unset($section['score_total'], $section['score_count']);
|
||||
}
|
||||
unset($section);
|
||||
|
||||
return array_values($sections);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user