db = \Config\Database::connect(); $this->teacherClassModel = new TeacherClassModel(); $this->configModel = new ConfigurationModel(); $this->classSectionModel = new ClassSectionModel(); $this->classWinnerModel = new CompetitionClassWinnerModel(); if ($this->db->tableExists('class_student')) { $this->classStudentTable = 'class_student'; } elseif ($this->db->tableExists('student_class')) { $this->classStudentTable = 'student_class'; } else { $this->classStudentTable = ''; } $this->hasClassWinnerTable = $this->db->tableExists('competition_class_winners'); $this->hasQuestionCount = $this->hasClassWinnerTable && $this->db->fieldExists('question_count', 'competition_class_winners'); } public function index() { [$assignments, $schoolYear, $semester] = $this->getTeacherContext(); $activeClassId = $this->resolveActiveClassId($assignments); $activeClassName = $this->getActiveClassName($assignments, $activeClassId); if (empty($assignments) || $activeClassId <= 0) { return view('teacher/competition_scores/index', [ 'competitions' => [], 'activeClassId' => $activeClassId, 'activeClassName' => $activeClassName, 'questionCounts' => [], 'scoreCounts' => [], 'studentTotal' => 0, 'sectionMap' => $this->getClassSectionMap(), 'schoolYear' => $schoolYear, 'semester' => $semester, 'hasClasses' => false, ]); } $competitions = $this->getCompetitionsForClass($activeClassId, $schoolYear, $semester); $competitionIds = array_values(array_filter(array_map(static function ($row) { return (int) ($row['id'] ?? 0); }, $competitions))); $questionCounts = $this->getQuestionCounts($competitionIds, $activeClassId); $scoreCounts = $this->getScoreCounts($competitionIds, $activeClassId); $studentTotal = $this->getClassStudentCount($activeClassId, $schoolYear); return view('teacher/competition_scores/index', [ 'competitions' => $competitions, 'activeClassId' => $activeClassId, 'activeClassName' => $activeClassName, 'questionCounts' => $questionCounts, 'scoreCounts' => $scoreCounts, 'studentTotal' => $studentTotal, 'sectionMap' => $this->getClassSectionMap(), 'schoolYear' => $schoolYear, 'semester' => $semester, 'hasClasses' => true, ]); } public function edit($id) { [$assignments, $schoolYear, $semester] = $this->getTeacherContext(); $allowedClassIds = $this->getAllowedClassIds($assignments); if (empty($allowedClassIds)) { return redirect()->to('/teacher/competition-scores') ->with('error', 'No class assignments found for your account.'); } $competitionModel = new CompetitionModel(); $scoreModel = new CompetitionScoreModel(); $competition = $competitionModel->find($id); if (!$competition) { return redirect()->to('/teacher/competition-scores') ->with('error', 'Competition not found.'); } $isLocked = !empty($competition['is_locked']); $activeClassId = $this->resolveActiveClassId($assignments); $lockedClassId = (int) ($competition['class_section_id'] ?? 0); $classSectionId = $lockedClassId > 0 ? $lockedClassId : $activeClassId; if ($classSectionId <= 0) { return redirect()->to('/teacher/competition-scores') ->with('error', 'Select a class section before entering scores.'); } if (!in_array($classSectionId, $allowedClassIds, true)) { return redirect()->to('/teacher/competition-scores') ->with('error', 'You are not assigned to that class.'); } $students = $this->getStudentsForCompetition($competition, $classSectionId); $existingScores = $scoreModel ->where('competition_id', $id) ->where('class_section_id', $classSectionId) ->findAll(); $scoreMap = []; foreach ($existingScores as $row) { $scoreMap[$row['student_id']] = $row['score']; } $questionCount = null; if ($this->hasQuestionCount && $this->hasClassWinnerTable) { $row = $this->classWinnerModel ->select('question_count') ->where('competition_id', $id) ->where('class_section_id', $classSectionId) ->first(); if ($row && $row['question_count'] !== null) { $questionCount = (int) $row['question_count']; } } $classStudentCount = $this->getClassStudentCount( $classSectionId, $competition['school_year'] ?? $schoolYear ); $activeClassName = $this->getActiveClassName($assignments, $classSectionId); return view('teacher/competition_scores/scores', [ 'competition' => $competition, 'students' => $students, 'scoreMap' => $scoreMap, 'classSectionId' => $classSectionId, 'classSectionName' => $activeClassName, 'classStudentCount' => $classStudentCount, 'questionCount' => $questionCount, 'classSelectionLocked' => $lockedClassId > 0, 'isLocked' => $isLocked, ]); } public function save($id) { [$assignments] = $this->getTeacherContext(); $allowedClassIds = $this->getAllowedClassIds($assignments); if (empty($allowedClassIds)) { return redirect()->to('/teacher/competition-scores') ->with('error', 'No class assignments found for your account.'); } $competitionModel = new CompetitionModel(); $scoreModel = new CompetitionScoreModel(); $competition = $competitionModel->find($id); if (!$competition) { return redirect()->to('/teacher/competition-scores') ->with('error', 'Competition not found.'); } if (!empty($competition['is_locked'])) { return redirect()->to("/teacher/competition-scores/{$id}") ->with('error', 'Competition is locked. You cannot update scores.'); } $lockedClassId = (int) ($competition['class_section_id'] ?? 0); $classSectionId = $lockedClassId > 0 ? $lockedClassId : (int) $this->request->getPost('class_section_id'); if ($classSectionId <= 0) { return redirect()->to('/teacher/competition-scores') ->with('error', 'Select a class section before saving scores.'); } if (!in_array($classSectionId, $allowedClassIds, true)) { return redirect()->to('/teacher/competition-scores') ->with('error', 'You are not assigned to that class.'); } $scores = (array) $this->request->getPost('scores'); $cleanScores = []; $invalidScores = []; foreach ($scores as $studentId => $scoreValue) { $scoreValue = trim((string) $scoreValue); if ($scoreValue === '') { continue; } if (!preg_match('/^\d+$/', $scoreValue)) { $invalidScores[] = $studentId; continue; } $cleanScores[(int) $studentId] = (int) $scoreValue; } if (!empty($invalidScores)) { return redirect()->to("/teacher/competition-scores/{$id}") ->with('error', 'Scores must be whole numbers (no decimals).'); } foreach ($cleanScores as $studentId => $scoreValue) { $existing = $scoreModel ->where('competition_id', $id) ->where('student_id', (int) $studentId) ->where('class_section_id', $classSectionId) ->first(); if ($existing) { $scoreModel->update($existing['id'], [ 'score' => $scoreValue, 'class_section_id' => $classSectionId, ]); } else { $scoreModel->insert([ 'competition_id' => (int) $id, 'student_id' => (int) $studentId, 'class_section_id' => $classSectionId, 'score' => $scoreValue, ]); } } return redirect()->to("/teacher/competition-scores/{$id}") ->with('success', 'Scores saved.'); } private function getTeacherContext(): array { $userId = (int) (session()->get('user_id') ?? 0); $schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); $semester = (string) ($this->configModel->getConfig('semester') ?? ''); $assignments = $this->teacherClassModel->getClassAssignmentsByUserId( $userId, $schoolYear, $semester ); return [$assignments, $schoolYear, $semester]; } private function getAllowedClassIds(array $assignments): array { $ids = array_map(static function ($row) { return (int) ($row['class_section_id'] ?? 0); }, $assignments); $ids = array_values(array_filter(array_unique($ids))); return $ids; } private function resolveActiveClassId(array $assignments): int { $allowed = $this->getAllowedClassIds($assignments); $active = (int) (session()->get('class_section_id') ?? 0); if ($active > 0 && in_array($active, $allowed, true)) { return $active; } $active = $allowed[0] ?? 0; if ($active > 0) { session()->set('class_section_id', $active); } return (int) $active; } private function getActiveClassName(array $assignments, int $classSectionId): ?string { if ($classSectionId <= 0) { return null; } foreach ($assignments as $row) { if ((int) ($row['class_section_id'] ?? 0) === $classSectionId) { return $row['class_section_name'] ?? null; } } $row = $this->classSectionModel ->select('class_section_name') ->where('class_section_id', $classSectionId) ->first(); return $row['class_section_name'] ?? null; } private function getCompetitionsForClass(int $classSectionId, string $schoolYear, string $semester): array { $competitionModel = new CompetitionModel(); $builder = $competitionModel->orderBy('id', 'DESC'); if ($classSectionId > 0) { $builder->groupStart() ->where('class_section_id', $classSectionId) ->orWhere('class_section_id', 0) ->orWhere('class_section_id IS NULL', null, false) ->groupEnd(); } if ($schoolYear !== '') { $builder->groupStart() ->where('school_year', $schoolYear) ->orWhere('school_year', '') ->orWhere('school_year IS NULL', null, false) ->groupEnd(); } if ($semester !== '') { $builder->groupStart() ->where('semester', $semester) ->orWhere('semester', '') ->orWhere('semester IS NULL', null, false) ->groupEnd(); } return $builder->findAll(); } private function getQuestionCounts(array $competitionIds, int $classSectionId): array { if (!$this->hasQuestionCount || !$this->hasClassWinnerTable || empty($competitionIds)) { return []; } $rows = $this->classWinnerModel ->select('competition_id, question_count') ->where('class_section_id', $classSectionId) ->whereIn('competition_id', $competitionIds) ->findAll(); $out = []; foreach ($rows as $row) { $compId = (int) ($row['competition_id'] ?? 0); if ($compId > 0 && $row['question_count'] !== null) { $out[$compId] = (int) $row['question_count']; } } return $out; } private function getScoreCounts(array $competitionIds, int $classSectionId): array { if (empty($competitionIds)) { return []; } $rows = $this->db->table('competition_scores') ->select('competition_id, COUNT(*) AS total') ->where('class_section_id', $classSectionId) ->whereIn('competition_id', $competitionIds) ->groupBy('competition_id') ->get() ->getResultArray(); $out = []; foreach ($rows as $row) { $compId = (int) ($row['competition_id'] ?? 0); if ($compId > 0) { $out[$compId] = (int) ($row['total'] ?? 0); } } return $out; } private function getClassStudentCount(int $classSectionId, ?string $schoolYear): int { if ($classSectionId <= 0 || $this->classStudentTable === '') { return 0; } $builder = $this->db->table($this->classStudentTable) ->select('COUNT(*) AS total') ->where('class_section_id', $classSectionId); if ($schoolYear && $this->db->fieldExists('school_year', $this->classStudentTable)) { $builder->where('school_year', $schoolYear); } $row = $builder->get()->getRowArray(); return (int) ($row['total'] ?? 0); } private function getStudentsForCompetition(array $competition, int $classSectionId): array { if ($classSectionId <= 0 || $this->classStudentTable === '') { return []; } $builder = $this->db->table('students s') ->select('s.id, s.school_id, s.firstname, s.lastname') ->join($this->classStudentTable . ' cs', 'cs.student_id = s.id', 'inner') ->where('cs.class_section_id', $classSectionId); $hasSchoolYear = $this->db->fieldExists('school_year', $this->classStudentTable); if ($hasSchoolYear && !empty($competition['school_year'])) { $builder->where('cs.school_year', $competition['school_year']); } $builder->distinct(); $builder->orderBy('s.lastname', 'ASC') ->orderBy('s.firstname', 'ASC'); return $builder->get()->getResultArray(); } private function getClassSectionMap(): array { $sections = $this->classSectionModel ->select('class_section_id, class_section_name') ->orderBy('class_section_name', 'ASC') ->findAll(); $map = []; foreach ($sections as $section) { $id = (int) ($section['class_section_id'] ?? 0); if ($id > 0) { $map[$id] = $section['class_section_name'] ?? (string) $id; } } return $map; } }