Files
alrahma_sunday_school/app/Controllers/View/CompetitionScoresController.php
T
2026-02-10 22:11:06 -05:00

442 lines
16 KiB
PHP

<?php
namespace App\Controllers\View;
use App\Controllers\BaseController;
use App\Models\ClassSectionModel;
use App\Models\CompetitionClassWinnerModel;
use App\Models\CompetitionModel;
use App\Models\CompetitionScoreModel;
use App\Models\ConfigurationModel;
use App\Models\TeacherClassModel;
class CompetitionScoresController extends BaseController
{
protected $db;
protected TeacherClassModel $teacherClassModel;
protected ConfigurationModel $configModel;
protected ClassSectionModel $classSectionModel;
protected CompetitionClassWinnerModel $classWinnerModel;
protected string $classStudentTable;
protected bool $hasQuestionCount = false;
protected bool $hasClassWinnerTable = false;
public function __construct()
{
$this->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;
}
}