fix decisions and rank

This commit is contained in:
root
2026-05-30 03:19:10 -04:00
parent fcfa56b3f5
commit 9ee75fe4cc
8 changed files with 1552 additions and 901 deletions
+413 -319
View File
@@ -2298,95 +2298,124 @@ class GradingController extends Controller
return 50000;
}
public function belowSixtyDecisions()
{
$configuredSemester = (string) $this->semester;
$configuredYear = (string) $this->schoolYear;
{
$configuredSemester = (string) $this->semester;
$configuredYear = (string) $this->schoolYear;
$semester = trim((string)($this->request->getGet('semester') ?? ''));
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
if ($semester === '') $semester = $configuredSemester !== '' ? $configuredSemester : 'Fall';
if ($schoolYear === '') $schoolYear = $configuredYear;
$semester = trim((string)($this->request->getGet('semester') ?? ''));
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
$rows = $this->fetchBelowSixtyRows($schoolYear, $semester);
if ($semester === '') {
$semester = $configuredSemester !== '' ? $configuredSemester : 'Fall';
}
$decisionModel = new BelowSixtyDecisionModel();
$studentIds = array_values(array_unique(array_filter(
array_map(static fn($r) => (int)($r['student_id'] ?? 0), $rows),
static fn($id) => $id > 0
)));
if ($schoolYear === '') {
$schoolYear = $configuredYear;
}
$decisionMap = [];
if (!empty($studentIds)) {
$dRows = $decisionModel
->whereIn('student_id', $studentIds)
->where('semester', $semester)
->where('school_year', $schoolYear)
->findAll();
foreach ($dRows as $d) {
$decisionMap[(int)$d['student_id']] = $d;
}
}
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
$rows = $this->fetchBelowSixtyRows($schoolYear, $semester);
foreach ($rows as &$row) {
$sid = (int)($row['student_id'] ?? 0);
$row['decision'] = $decisionMap[$sid]['decision'] ?? '';
$row['decision_notes'] = $decisionMap[$sid]['notes'] ?? '';
}
unset($row);
$studentIds = array_values(array_unique(array_filter(
array_map(static fn($r) => (int)($r['student_id'] ?? 0), $rows),
static fn($id) => $id > 0
)));
// Load consolidated decisions from student_decisions for this term
$sdModel = new StudentDecisionModel();
$sdRows = $sdModel
// ── Load manual below-60 semester decisions ─────────────────────────────
//
// This table still uses semester, because below-60 decisions are tied to
// the selected below-60 screen/term.
$decisionModel = new BelowSixtyDecisionModel();
$decisionMap = [];
if (!empty($studentIds)) {
$dRows = $decisionModel
->whereIn('student_id', $studentIds)
->where('semester', $semester)
->where('school_year', $schoolYear)
->findAll();
$sdMap = [];
foreach ($sdRows as $sd) {
$sdMap[(int)$sd['student_id']] = $sd;
}
// Load the most recent certificate per student for this school_year
$studentIds = array_values(array_unique(array_filter(
array_map(static fn($r) => (int)($r['student_id'] ?? 0), $rows),
static fn($id) => $id > 0
)));
$certMap = [];
if (!empty($studentIds)) {
$certRows = $this->db->table('certificate_records')
->select('student_id, certificate_number, issued_at')
->where('school_year', $schoolYear)
->whereIn('student_id', $studentIds)
->orderBy('issued_at', 'DESC')
->get()->getResultArray();
foreach ($certRows as $cr) {
$sid = (int)($cr['student_id'] ?? 0);
if ($sid > 0 && !isset($certMap[$sid])) {
$certMap[$sid] = (string)($cr['certificate_number'] ?? '');
}
foreach ($dRows as $d) {
$decisionMap[(int)$d['student_id']] = $d;
}
}
foreach ($rows as &$row) {
$sid = (int)($row['student_id'] ?? 0);
$row['decision'] = $decisionMap[$sid]['decision'] ?? '';
$row['decision_notes'] = $decisionMap[$sid]['notes'] ?? '';
}
unset($row);
// ── Load consolidated YEAR decisions from student_decisions ─────────────
//
// IMPORTANT:
// student_decisions no longer has semester or semester_score.
// It is now one row per student per school_year using year_score.
$sdMap = [];
if (!empty($studentIds)) {
$sdRows = $this->db->table('student_decisions')
->whereIn('student_id', $studentIds)
->where('school_year', $schoolYear)
->get()
->getResultArray();
foreach ($sdRows as $sd) {
$sid = (int)($sd['student_id'] ?? 0);
if ($sid > 0) {
$sdMap[$sid] = $sd;
}
}
foreach ($rows as &$row) {
$sid = (int)($row['student_id'] ?? 0);
$row['consolidated_decision'] = $sdMap[$sid]['decision'] ?? null;
$row['certificate_number'] = $certMap[$sid] ?? '';
}
unset($row);
$canViewGrading = $this->userHasMenuUrl('grading');
return view('grading/below_sixty_decisions', [
'rows' => $rows,
'semester' => $semester,
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'canViewGrading' => $canViewGrading,
]);
}
// ── Load the most recent certificate per student for this school year ───
$certMap = [];
if (!empty($studentIds)) {
$certRows = $this->db->table('certificate_records')
->select('student_id, certificate_number, issued_at')
->where('school_year', $schoolYear)
->whereIn('student_id', $studentIds)
->orderBy('issued_at', 'DESC')
->get()
->getResultArray();
foreach ($certRows as $cr) {
$sid = (int)($cr['student_id'] ?? 0);
if ($sid > 0 && !isset($certMap[$sid])) {
$certMap[$sid] = (string)($cr['certificate_number'] ?? '');
}
}
}
foreach ($rows as &$row) {
$sid = (int)($row['student_id'] ?? 0);
$row['consolidated_decision'] = $sdMap[$sid]['decision'] ?? null;
$row['year_score'] = $sdMap[$sid]['year_score'] ?? null;
$row['certificate_number'] = $certMap[$sid] ?? '';
}
unset($row);
$canViewGrading = $this->userHasMenuUrl('grading');
return view('grading/below_sixty_decisions', [
'rows' => $rows,
'semester' => $semester,
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'canViewGrading' => $canViewGrading,
]);
}
public function saveBelowSixtyDecision()
{
$studentId = (int)$this->request->getPost('student_id');
@@ -2643,278 +2672,343 @@ class GradingController extends Controller
->with('status', 'Decision email sent to parent(s).');
}
public function allDecisions()
{
$configuredYear = (string)$this->schoolYear;
public function allDecisions()
{
$configuredYear = (string)$this->schoolYear;
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
if ($schoolYear === '') $schoolYear = $configuredYear;
$schoolYear = trim((string)($this->request->getGet('school_year') ?? ''));
if ($schoolYear === '') {
$schoolYear = $configuredYear;
}
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
// Load saved year decisions (semester='year') for this school year
$decModel = new StudentDecisionModel();
$saved = $decModel
->where('semester', 'year')
->where('school_year', $schoolYear)
->findAll();
$savedMap = [];
foreach ($saved as $s) {
$savedMap[(int)$s['student_id']] = $s;
// Load saved YEAR decisions for this school year.
// New structure:
// one row per student per school_year
// uses year_score, not semester_score
// does not use semester = 'year'
$decModel = new StudentDecisionModel();
$saved = $decModel
->where('school_year', $schoolYear)
->findAll();
$savedMap = [];
foreach ($saved as $s) {
$sid = (int)($s['student_id'] ?? 0);
if ($sid > 0) {
$savedMap[$sid] = $s;
}
}
// Fetch Fall and Spring semester scores per student.
// These raw semester scores are only used to calculate the final year_score.
$allScoreRows = $this->db->table('semester_scores ss')
->select([
's.id AS student_id',
's.school_id',
's.firstname',
's.lastname',
'cs.class_section_name',
'LOWER(TRIM(ss.semester)) AS sem_key',
'ss.semester_score',
])
->join('students s', 's.id = ss.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
->where('ss.semester_score IS NOT NULL', null, false)
->orderBy('cs.class_section_name', 'ASC')
->orderBy('s.lastname', 'ASC')
->orderBy('s.firstname', 'ASC')
->get()
->getResultArray();
// Group Fall/Spring scores by student.
$studentMap = [];
foreach ($allScoreRows as $sr) {
$sid = (int)($sr['student_id'] ?? 0);
if ($sid <= 0) {
continue;
}
// Fetch Fall and Spring semester_scores per student for this school year
$allScoreRows = $this->db->table('semester_scores ss')
->select([
's.id AS student_id',
's.school_id',
's.firstname',
's.lastname',
'cs.class_section_name',
'LOWER(TRIM(ss.semester)) AS sem_key',
'ss.semester_score',
])
->join('students s', 's.id = ss.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
->where('ss.semester_score IS NOT NULL', null, false)
->orderBy('cs.class_section_name', 'ASC')
->orderBy('s.lastname', 'ASC')
->orderBy('s.firstname', 'ASC')
->get()->getResultArray();
// Group by student: keep one base info row + fall/spring scores
$studentMap = [];
foreach ($allScoreRows as $sr) {
$sid = (int)$sr['student_id'];
if (!isset($studentMap[$sid])) {
$studentMap[$sid] = [
'student_id' => $sid,
'school_id' => $sr['school_id'] ?? '',
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'fall_score' => null,
'spring_score' => null,
];
}
$semKey = strtolower(trim((string)($sr['sem_key'] ?? '')));
$val = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
if ($semKey === 'fall') {
$studentMap[$sid]['fall_score'] = $val;
} elseif ($semKey === 'spring') {
$studentMap[$sid]['spring_score'] = $val;
}
}
// Pull below-60 decisions for either semester (use worst available)
$belowDecModel = new BelowSixtyDecisionModel();
$belowRows = $belowDecModel
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$sid = (int)$b['student_id'];
// prefer a non-empty decision over empty
if (!isset($belowMap[$sid]) || (string)($belowMap[$sid]['decision'] ?? '') === '') {
$belowMap[$sid] = $b;
}
}
// Build final rows with year_score = (fall + spring) / 2
$rows = [];
foreach ($studentMap as $sid => $info) {
$fall = $info['fall_score'];
$spring = $info['spring_score'];
if ($fall !== null && $spring !== null) {
$yearScore = round(($fall + $spring) / 2, 2);
} elseif ($fall !== null) {
$yearScore = $fall;
} elseif ($spring !== null) {
$yearScore = $spring;
} else {
$yearScore = null;
}
if (isset($savedMap[$sid])) {
$decision = (string)($savedMap[$sid]['decision'] ?? '');
$source = (string)($savedMap[$sid]['source'] ?? 'auto');
$notes = (string)($savedMap[$sid]['notes'] ?? '');
} elseif ($yearScore !== null && $yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = '';
} elseif ($yearScore !== null && isset($belowMap[$sid])) {
$decision = (string)($belowMap[$sid]['decision'] ?? '');
$source = $decision !== '' ? 'manual' : 'pending';
$notes = (string)($belowMap[$sid]['notes'] ?? '');
} else {
$decision = '';
$source = 'pending';
$notes = '';
}
$rows[] = [
'student_id' => $sid,
'school_id' => $info['school_id'],
'firstname' => $info['firstname'],
'lastname' => $info['lastname'],
'class_section_name' => $info['class_section_name'],
'fall_score' => $fall,
'spring_score' => $spring,
'year_score' => $yearScore,
'decision' => $decision,
'source' => $source,
'notes' => $notes,
'saved' => isset($savedMap[$sid]),
if (!isset($studentMap[$sid])) {
$studentMap[$sid] = [
'school_id' => $sr['school_id'] ?? '',
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'fall_score' => null,
'spring_score' => null,
];
}
$generated = !empty($saved);
$semKey = strtolower(trim((string)($sr['sem_key'] ?? '')));
$val = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
return view('grading/all_decisions', [
'rows' => $rows,
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'generated' => $generated,
]);
if ($semKey === 'fall') {
$studentMap[$sid]['fall_score'] = $val;
} elseif ($semKey === 'spring') {
$studentMap[$sid]['spring_score'] = $val;
}
}
public function generateAllDecisions()
{
$schoolYear = trim((string)$this->request->getPost('school_year'));
// Pull below-60 manual decisions for this school year.
// Used only when the calculated year_score is below 60.
$belowDecModel = new BelowSixtyDecisionModel();
if ($schoolYear === '') {
return redirect()->back()->with('error', 'Missing school year.');
$belowRows = $belowDecModel
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$sid = (int)($b['student_id'] ?? 0);
if ($sid <= 0) {
continue;
}
// Fetch Fall and Spring scores per student
$allScoreRows = $this->db->table('semester_scores ss')
->select([
's.id AS student_id',
's.firstname',
's.lastname',
'cs.class_section_name',
'LOWER(TRIM(ss.semester)) AS sem_key',
'ss.semester_score',
])
->join('students s', 's.id = ss.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
->where('ss.semester_score IS NOT NULL', null, false)
->get()->getResultArray();
// Keep the first non-empty decision found for this student.
if (!isset($belowMap[$sid]) || trim((string)($belowMap[$sid]['decision'] ?? '')) === '') {
$belowMap[$sid] = $b;
}
}
if (empty($allScoreRows)) {
return redirect()->back()->with('error', 'No semester scores found for this school year.');
// Build final display rows.
$rows = [];
foreach ($studentMap as $sid => $info) {
$fall = $info['fall_score'];
$spring = $info['spring_score'];
if ($fall !== null && $spring !== null) {
$yearScore = round(($fall + $spring) / 2, 2);
} elseif ($fall !== null) {
$yearScore = round((float)$fall, 2);
} elseif ($spring !== null) {
$yearScore = round((float)$spring, 2);
} else {
$yearScore = null;
}
// Group by student
$studentMap = [];
foreach ($allScoreRows as $sr) {
$sid = (int)$sr['student_id'];
if (!isset($studentMap[$sid])) {
$studentMap[$sid] = [
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'fall_score' => null,
'spring_score' => null,
];
}
$semKey = strtolower(trim((string)($sr['sem_key'] ?? '')));
$val = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
if ($semKey === 'fall') {
$studentMap[$sid]['fall_score'] = $val;
} elseif ($semKey === 'spring') {
$studentMap[$sid]['spring_score'] = $val;
if (isset($savedMap[$sid])) {
$savedRow = $savedMap[$sid];
$decision = trim((string)($savedRow['decision'] ?? ''));
$source = trim((string)($savedRow['source'] ?? 'pending'));
$notes = (string)($savedRow['notes'] ?? '');
if (isset($savedRow['year_score']) && $savedRow['year_score'] !== '' && is_numeric($savedRow['year_score'])) {
$yearScore = round((float)$savedRow['year_score'], 2);
}
} elseif ($yearScore !== null && $yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = '';
} elseif ($yearScore !== null && isset($belowMap[$sid])) {
$decision = trim((string)($belowMap[$sid]['decision'] ?? ''));
$source = $decision !== '' ? 'manual' : 'pending';
$notes = (string)($belowMap[$sid]['notes'] ?? '');
} else {
$decision = '';
$source = 'pending';
$notes = '';
}
// Pull below-60 decisions for any semester of this year
$belowDecModel = new BelowSixtyDecisionModel();
$belowRows = $belowDecModel
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$sid = (int)$b['student_id'];
if (!isset($belowMap[$sid]) || (string)($belowMap[$sid]['decision'] ?? '') === '') {
$belowMap[$sid] = $b;
}
$rows[] = [
'student_id' => $sid,
'school_id' => $info['school_id'],
'firstname' => $info['firstname'],
'lastname' => $info['lastname'],
'class_section_name' => $info['class_section_name'],
'fall_score' => $fall,
'spring_score' => $spring,
'year_score' => $yearScore,
'decision' => $decision,
'source' => $source,
'notes' => $notes,
'saved' => isset($savedMap[$sid]),
];
}
$generated = !empty($saved);
return view('grading/all_decisions', [
'rows' => $rows,
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'generated' => $generated,
]);
}
public function generateAllDecisions()
{
$schoolYear = trim((string)$this->request->getPost('school_year'));
if ($schoolYear === '') {
return redirect()->back()->with('error', 'Missing school year.');
}
// Fetch Fall and Spring scores per student.
$allScoreRows = $this->db->table('semester_scores ss')
->select([
's.id AS student_id',
's.firstname',
's.lastname',
'cs.class_section_name',
'LOWER(TRIM(ss.semester)) AS sem_key',
'ss.semester_score',
])
->join('students s', 's.id = ss.student_id', 'inner')
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
->where('ss.semester_score IS NOT NULL', null, false)
->get()
->getResultArray();
if (empty($allScoreRows)) {
return redirect()->back()->with('error', 'No semester scores found for this school year.');
}
// Group Fall/Spring scores by student.
$studentMap = [];
foreach ($allScoreRows as $sr) {
$sid = (int)($sr['student_id'] ?? 0);
if ($sid <= 0) {
continue;
}
// Load existing year decisions to upsert
$decModel = new StudentDecisionModel();
$existing = $decModel
->where('semester', 'year')
->where('school_year', $schoolYear)
->findAll();
$existingMap = [];
foreach ($existing as $e) {
$existingMap[(int)$e['student_id']] = $e;
}
$userId = (int)(session()->get('user_id') ?? 0) ?: null;
$savedCount = 0;
foreach ($studentMap as $sid => $info) {
$fall = $info['fall_score'];
$spring = $info['spring_score'];
if ($fall !== null && $spring !== null) {
$yearScore = round(($fall + $spring) / 2, 2);
} elseif ($fall !== null) {
$yearScore = $fall;
} elseif ($spring !== null) {
$yearScore = $spring;
} else {
continue;
}
if ($yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = null;
} elseif (isset($belowMap[$sid]) && (string)($belowMap[$sid]['decision'] ?? '') !== '') {
$decision = (string)$belowMap[$sid]['decision'];
$source = 'manual';
$notes = ($belowMap[$sid]['notes'] ?? '') !== '' ? (string)$belowMap[$sid]['notes'] : null;
} else {
$decision = null;
$source = 'pending';
$notes = null;
}
$payload = [
'student_id' => $sid,
'semester' => 'year',
'school_year' => $schoolYear,
'class_section_name' => $info['class_section_name'] ?? null,
'semester_score' => $yearScore,
'decision' => $decision,
'source' => $source,
'notes' => $notes,
'generated_by' => $userId,
if (!isset($studentMap[$sid])) {
$studentMap[$sid] = [
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'fall_score' => null,
'spring_score' => null,
];
if (isset($existingMap[$sid])) {
$decModel->update($existingMap[$sid]['id'], $payload);
} else {
$decModel->insert($payload);
}
$savedCount++;
}
$query = http_build_query(['school_year' => $schoolYear]);
return redirect()->to(base_url('grading/decisions') . '?' . $query)
->with('status', "Decisions generated for {$savedCount} students.");
$semKey = strtolower(trim((string)($sr['sem_key'] ?? '')));
$val = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
if ($semKey === 'fall') {
$studentMap[$sid]['fall_score'] = $val;
} elseif ($semKey === 'spring') {
$studentMap[$sid]['spring_score'] = $val;
}
}
// Pull below-60 manual decisions for this school year.
$belowDecModel = new BelowSixtyDecisionModel();
$belowRows = $belowDecModel
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$sid = (int)($b['student_id'] ?? 0);
if ($sid <= 0) {
continue;
}
if (!isset($belowMap[$sid]) || trim((string)($belowMap[$sid]['decision'] ?? '')) === '') {
$belowMap[$sid] = $b;
}
}
// Load existing year decisions so we update instead of duplicating.
// New structure: one row per student per school_year.
$decModel = new StudentDecisionModel();
$existing = $decModel
->where('school_year', $schoolYear)
->findAll();
$existingMap = [];
foreach ($existing as $e) {
$sid = (int)($e['student_id'] ?? 0);
if ($sid > 0) {
$existingMap[$sid] = $e;
}
}
$userId = (int)(session()->get('user_id') ?? 0) ?: null;
$savedCount = 0;
foreach ($studentMap as $sid => $info) {
$fall = $info['fall_score'];
$spring = $info['spring_score'];
if ($fall !== null && $spring !== null) {
$yearScore = round(($fall + $spring) / 2, 2);
} elseif ($fall !== null) {
$yearScore = round((float)$fall, 2);
} elseif ($spring !== null) {
$yearScore = round((float)$spring, 2);
} else {
continue;
}
if ($yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = null;
} elseif (isset($belowMap[$sid]) && trim((string)($belowMap[$sid]['decision'] ?? '')) !== '') {
$decision = trim((string)$belowMap[$sid]['decision']);
$source = 'manual';
$notes = trim((string)($belowMap[$sid]['notes'] ?? ''));
$notes = $notes !== '' ? $notes : null;
} else {
$decision = null;
$source = 'pending';
$notes = null;
}
// Important fix:
// use year_score, not semester_score.
// do not save semester = 'year'.
$payload = [
'student_id' => $sid,
'school_year' => $schoolYear,
'class_section_name' => $info['class_section_name'] ?? null,
'year_score' => $yearScore,
'decision' => $decision,
'source' => $source,
'notes' => $notes,
'generated_by' => $userId,
];
if (isset($existingMap[$sid])) {
$decModel->update((int)$existingMap[$sid]['id'], $payload);
} else {
$decModel->insert($payload);
}
$savedCount++;
}
$query = http_build_query(['school_year' => $schoolYear]);
return redirect()->to(base_url('grading/decisions') . '?' . $query)
->with('status', "Decisions generated for {$savedCount} students.");
}
public function getScoreComment()
{
// Get all students for the current semester and school year