This commit is contained in:
root
2026-05-26 23:14:55 -04:00
parent 5ed65a867c
commit c294d7bed7
38 changed files with 277 additions and 146 deletions
+161 -76
View File
@@ -1076,7 +1076,8 @@ class GradingController extends Controller
$requestedSemester = trim((string)($this->request->getGet('semester') ?? ''));
$requestedYear = trim((string)($this->request->getGet('school_year') ?? ''));
$semester = $requestedSemester !== '' ? $requestedSemester : ($configuredSemester !== '' ? $configuredSemester : 'Fall');
$isYearMode = (strtolower($requestedSemester) === 'year');
$semester = $isYearMode ? 'year' : ($requestedSemester !== '' ? $requestedSemester : ($configuredSemester !== '' ? $configuredSemester : 'Fall'));
$schoolYear = $requestedYear !== '' ? $requestedYear : $configuredYear;
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
@@ -1090,6 +1091,8 @@ class GradingController extends Controller
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'canViewGrading' => $canViewGrading,
'isYearMode' => $isYearMode,
'showAllSemesterOption' => true,
]);
}
@@ -1762,14 +1765,16 @@ class GradingController extends Controller
private function fetchBelowSixtyRows(string $schoolYear, string $semester): array
{
$semesterKey = strtolower(trim($semester));
$rows = $this->db->table('semester_scores ss')
$isYearMode = strtolower(trim($semester)) === 'year';
$semesterKey = $isYearMode ? '' : strtolower(trim($semester));
$builder = $this->db->table('semester_scores ss')
->select([
's.id AS student_id',
's.school_id',
's.firstname',
's.lastname',
'cs.class_section_name',
'ss.semester',
'ss.homework_avg',
'ss.project_avg',
'ss.participation_score',
@@ -1784,11 +1789,17 @@ class GradingController extends Controller
->join('classSection cs', 'cs.class_section_id = ss.class_section_id', 'left')
->where('s.is_active', 1)
->where('ss.school_year', $schoolYear)
->where("LOWER(TRIM(ss.semester))", $semesterKey)
->where('ss.semester_score IS NOT NULL', null, false)
->where('ss.semester_score <', 60)
->where('ss.semester_score <', 60);
if (!$isYearMode) {
$builder->where("LOWER(TRIM(ss.semester))", $semesterKey);
}
$rows = $builder
->orderBy('s.lastname', 'ASC')
->orderBy('s.firstname', 'ASC')
->orderBy('ss.semester', 'ASC')
->get()
->getResultArray();
@@ -1804,19 +1815,22 @@ class GradingController extends Controller
$commentMap = [];
if (!empty($studentIds)) {
$commentRows = $this->db->table('score_comments')
->select('student_id, comment, created_at')
$commentBuilder = $this->db->table('score_comments')
->select('student_id, semester, comment, created_at')
->where('score_type', 'general')
->where('school_year', $schoolYear)
->where("LOWER(TRIM(semester))", $semesterKey)
->whereIn('student_id', $studentIds)
->orderBy('created_at', 'DESC')
->get()
->getResultArray();
->orderBy('created_at', 'DESC');
if (!$isYearMode) {
$commentBuilder->where("LOWER(TRIM(semester))", $semesterKey);
}
$commentRows = $commentBuilder->get()->getResultArray();
foreach ($commentRows as $row) {
$sid = (int)($row['student_id'] ?? 0);
if ($sid > 0 && !isset($commentMap[$sid])) {
$commentMap[$sid] = (string)($row['comment'] ?? '');
$sem = strtolower(trim((string)($row['semester'] ?? '')));
$key = $sid . '_' . $sem;
if ($sid > 0 && !isset($commentMap[$key])) {
$commentMap[$key] = (string)($row['comment'] ?? '');
}
}
}
@@ -1824,21 +1838,24 @@ class GradingController extends Controller
$statusMap = [];
$noteMap = [];
if (!empty($studentIds)) {
$flagRows = $this->db->table('current_flag')
->select('student_id, flag_state, open_description, close_description')
$flagBuilder = $this->db->table('current_flag')
->select('student_id, semester, flag_state, open_description, close_description')
->where('flag', 'grade')
->where('school_year', $schoolYear)
->where("LOWER(TRIM(semester))", $semesterKey)
->whereIn('student_id', $studentIds)
->get()
->getResultArray();
->whereIn('student_id', $studentIds);
if (!$isYearMode) {
$flagBuilder->where("LOWER(TRIM(semester))", $semesterKey);
}
$flagRows = $flagBuilder->get()->getResultArray();
foreach ($flagRows as $row) {
$sid = (int)($row['student_id'] ?? 0);
if ($sid <= 0) continue;
$statusMap[$sid] = (string)($row['flag_state'] ?? '');
$sem = strtolower(trim((string)($row['semester'] ?? '')));
$key = $sid . '_' . $sem;
$statusMap[$key] = (string)($row['flag_state'] ?? '');
$openNote = trim((string)($row['open_description'] ?? ''));
$closeNote = trim((string)($row['close_description'] ?? ''));
$noteMap[$sid] = [
$noteMap[$key] = [
'open' => $openNote,
'closed' => $closeNote,
];
@@ -1847,10 +1864,12 @@ class GradingController extends Controller
foreach ($rows as &$row) {
$sid = (int)($row['student_id'] ?? 0);
$row['comment'] = $commentMap[$sid] ?? '';
$flagState = strtolower(trim((string)($statusMap[$sid] ?? '')));
$sem = strtolower(trim((string)($row['semester'] ?? '')));
$key = $sid . '_' . $sem;
$row['comment'] = $commentMap[$key] ?? '';
$flagState = strtolower(trim((string)($statusMap[$key] ?? '')));
$row['status'] = ($flagState === 'closed' || $flagState === 'canceled') ? 'Closed' : 'Open';
$noteBag = $noteMap[$sid] ?? ['open' => '', 'closed' => ''];
$noteBag = $noteMap[$key] ?? ['open' => '', 'closed' => ''];
$rawNote = $row['status'] === 'Closed' ? (string)$noteBag['closed'] : (string)$noteBag['open'];
if ($rawNote !== '') {
$lines = preg_split('/\R/', $rawNote);
@@ -2539,20 +2558,17 @@ class GradingController extends Controller
public function allDecisions()
{
$configuredSemester = (string)$this->semester;
$configuredYear = (string)$this->schoolYear;
$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;
$schoolYears = $this->getSchoolYearsForScores($schoolYear);
// Load saved decisions for this term
// Load saved year decisions (semester='year') for this school year
$decModel = new StudentDecisionModel();
$saved = $decModel
->where('semester', $semester)
->where('semester', 'year')
->where('school_year', $schoolYear)
->findAll();
$savedMap = [];
@@ -2560,70 +2576,109 @@ class GradingController extends Controller
$savedMap[(int)$s['student_id']] = $s;
}
// All semester_scores rows for this term (one per student)
$semKey = strtolower(trim($semester));
$scoreRows = $this->db->table('semester_scores ss')
// 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)
->where("LOWER(TRIM(ss.semester))", $semKey)
->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();
// Pull below-60 decisions
// 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('semester', $semester)
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$belowMap[(int)$b['student_id']] = $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 rows combining live scores with saved/computed decisions
// Build final rows with year_score = (fall + spring) / 2
$rows = [];
foreach ($scoreRows as $sr) {
$sid = (int)$sr['student_id'];
$score = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
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 ($score !== null && $score >= 60) {
} elseif ($yearScore !== null && $yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = '';
} elseif ($score !== null && isset($belowMap[$sid])) {
} elseif ($yearScore !== null && isset($belowMap[$sid])) {
$decision = (string)($belowMap[$sid]['decision'] ?? '');
$source = $decision !== '' ? 'manual' : 'pending';
$notes = (string)($belowMap[$sid]['notes'] ?? '');
} else {
$decision = $score !== null ? '' : '';
$decision = '';
$source = 'pending';
$notes = '';
}
$rows[] = [
'student_id' => $sid,
'school_id' => $sr['school_id'] ?? '',
'firstname' => $sr['firstname'] ?? '',
'lastname' => $sr['lastname'] ?? '',
'class_section_name' => $sr['class_section_name'] ?? '',
'semester_score' => $score,
'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,
@@ -2635,7 +2690,6 @@ class GradingController extends Controller
return view('grading/all_decisions', [
'rows' => $rows,
'semester' => $semester,
'schoolYear' => $schoolYear,
'schoolYears' => $schoolYears,
'generated' => $generated,
@@ -2644,49 +2698,73 @@ class GradingController extends Controller
public function generateAllDecisions()
{
$semester = trim((string)$this->request->getPost('semester'));
$schoolYear = trim((string)$this->request->getPost('school_year'));
if ($semester === '' || $schoolYear === '') {
return redirect()->back()->with('error', 'Missing semester or school year.');
if ($schoolYear === '') {
return redirect()->back()->with('error', 'Missing school year.');
}
$semKey = strtolower(trim($semester));
$scoreRows = $this->db->table('semester_scores ss')
// 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)
->where("LOWER(TRIM(ss.semester))", $semKey)
->whereIn('LOWER(TRIM(ss.semester))', ['fall', 'spring'])
->where('ss.semester_score IS NOT NULL', null, false)
->get()->getResultArray();
if (empty($scoreRows)) {
return redirect()->back()->with('error', 'No semester scores found for this selection.');
if (empty($allScoreRows)) {
return redirect()->back()->with('error', 'No semester scores found for this school year.');
}
// Pull all below-60 decisions for this term
// 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;
}
}
// Pull below-60 decisions for any semester of this year
$belowDecModel = new BelowSixtyDecisionModel();
$belowRows = $belowDecModel
->where('semester', $semester)
->where('school_year', $schoolYear)
->findAll();
$belowMap = [];
foreach ($belowRows as $b) {
$belowMap[(int)$b['student_id']] = $b;
$sid = (int)$b['student_id'];
if (!isset($belowMap[$sid]) || (string)($belowMap[$sid]['decision'] ?? '') === '') {
$belowMap[$sid] = $b;
}
}
// Load existing saved decisions to upsert
// Load existing year decisions to upsert
$decModel = new StudentDecisionModel();
$existing = $decModel
->where('semester', $semester)
->where('semester', 'year')
->where('school_year', $schoolYear)
->findAll();
$existingMap = [];
@@ -2695,16 +2773,23 @@ class GradingController extends Controller
}
$userId = (int)(session()->get('user_id') ?? 0) ?: null;
$now = utc_now();
$saved = 0;
$savedCount = 0;
foreach ($scoreRows as $sr) {
$sid = (int)$sr['student_id'];
$score = is_numeric($sr['semester_score']) ? (float)$sr['semester_score'] : null;
foreach ($studentMap as $sid => $info) {
$fall = $info['fall_score'];
$spring = $info['spring_score'];
if ($score === null) continue;
if ($fall !== null && $spring !== null) {
$yearScore = round(($fall + $spring) / 2, 2);
} elseif ($fall !== null) {
$yearScore = $fall;
} elseif ($spring !== null) {
$yearScore = $spring;
} else {
continue;
}
if ($score >= 60) {
if ($yearScore >= 60) {
$decision = 'Pass';
$source = 'auto';
$notes = null;
@@ -2720,10 +2805,10 @@ class GradingController extends Controller
$payload = [
'student_id' => $sid,
'semester' => $semester,
'semester' => 'year',
'school_year' => $schoolYear,
'class_section_name' => $sr['class_section_name'] ?? null,
'semester_score' => $score,
'class_section_name' => $info['class_section_name'] ?? null,
'semester_score' => $yearScore,
'decision' => $decision,
'source' => $source,
'notes' => $notes,
@@ -2735,12 +2820,12 @@ class GradingController extends Controller
} else {
$decModel->insert($payload);
}
$saved++;
$savedCount++;
}
$query = http_build_query(['semester' => $semester, 'school_year' => $schoolYear]);
$query = http_build_query(['school_year' => $schoolYear]);
return redirect()->to(base_url('grading/decisions') . '?' . $query)
->with('status', "Decisions generated for {$saved} students.");
->with('status', "Decisions generated for {$savedCount} students.");
}
public function getScoreComment()