recreate project
This commit is contained in:
@@ -0,0 +1,441 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user