add projet

This commit is contained in:
root
2026-03-05 12:29:37 -05:00
parent 8d1eef8ba8
commit 23b7db1107
9109 changed files with 1106501 additions and 73 deletions
+516
View File
@@ -0,0 +1,516 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\Configuration;
use App\Models\Homework;
use App\Models\StudentClass;
use App\Models\Student;
use App\Models\TeacherClass;
use App\Models\User;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response;
class HomeworkController extends BaseApiController
{
protected Homework $homework;
protected Configuration $config;
protected StudentClass $studentClass;
protected Student $student;
protected TeacherClass $teacherClass;
protected User $user;
protected string $schoolYear;
protected string $semester;
protected $semesterScoreService;
public function __construct()
{
parent::__construct();
$this->homework = model(Homework::class);
$this->config = model(Configuration::class);
$this->studentClass = model(StudentClass::class);
$this->student = model(Student::class);
$this->teacherClass = model(TeacherClass::class);
$this->user = model(User::class);
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
$this->semesterScoreService = \Config\Services::semesterScoreService();
}
/**
* Get paginated homework list
*/
public function index()
{
$page = max(1, (int) ($this->request->getGet('page') ?? 1));
$perPage = min(100, max(1, (int) ($this->request->getGet('per_page') ?? 20)));
$classSectionId = $this->request->getGet('class_section_id');
$query = $this->homework->newQuery()
->when($this->schoolYear !== '', fn($q) => $q->where('school_year', $this->schoolYear))
->when($this->semester !== '', fn($q) => $q->where('semester', $this->semester))
->when($classSectionId, fn($q) => $q->where('class_section_id', $classSectionId))
->orderBy('created_at', 'DESC');
$result = $this->paginate($query, $page, $perPage);
return $this->success($result, 'Homework retrieved successfully');
}
/**
* Get homework by student ID
*/
public function getByStudent($id = null)
{
$query = $this->homework->newQuery()
->when($id, fn($q) => $q->where('student_id', $id))
->when($this->schoolYear !== '', fn($q) => $q->where('school_year', $this->schoolYear))
->when($this->semester !== '', fn($q) => $q->where('semester', $this->semester));
$homework = $query->get()->toArray();
return $this->success($homework, 'Homework retrieved successfully');
}
/**
* Create a single homework entry
*/
public function create()
{
$user = $this->getCurrentUser();
$data = $this->payloadData();
if (empty($data)) {
return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST);
}
$errors = $this->validateRequest($data, [
'student_id' => 'required|integer',
'class_section_id' => 'required|integer',
'homework_index' => 'required|integer',
]);
if (!empty($errors)) {
return $this->respondValidationError($errors);
}
$payload = [
'student_id' => $data['student_id'],
'class_section_id' => $data['class_section_id'],
'homework_index' => $data['homework_index'],
'score' => $data['score'] ?? null,
'comment' => $data['comment'] ?? null,
'school_year' => $this->schoolYear,
'semester' => $this->semester,
'updated_by' => $user->id ?? null,
];
$homework = $this->homework->create($payload);
return $this->success($homework, 'Homework created successfully', Response::HTTP_CREATED);
}
/**
* Update a single homework entry
*/
public function update($id = null)
{
$homework = $this->homework->find($id);
if (!$homework) {
return $this->respondError('Homework not found', Response::HTTP_NOT_FOUND);
}
$user = $this->getCurrentUser();
$data = $this->payloadData();
if (empty($data)) {
return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST);
}
$allowed = ['score', 'comment'];
$update = [];
foreach ($allowed as $field) {
if (array_key_exists($field, $data)) {
$update[$field] = $data[$field];
}
}
if (empty($update)) {
return $this->respondError('No valid fields to update', Response::HTTP_BAD_REQUEST);
}
$update['updated_by'] = $user->id ?? null;
$update['updated_at'] = utc_now();
$this->homework->update($id, $update);
return $this->success($this->homework->find($id), 'Homework updated successfully');
}
/**
* Bulk update homework scores for multiple students
*/
public function updateScores()
{
$data = $this->payloadData();
$scores = $data['scores'] ?? null;
$classSectionId = (int) ($data['class_section_id'] ?? 0);
$updatedBy = $this->getCurrentUserId();
if (!$scores || !is_array($scores)) {
return $this->respondError('Scores data is required', Response::HTTP_BAD_REQUEST);
}
if ($classSectionId <= 0) {
return $this->respondError('Class section ID is required', Response::HTTP_BAD_REQUEST);
}
try {
foreach ($scores as $studentId => $hwData) {
if (!is_numeric($studentId)) continue;
$student = $this->student->find($studentId);
if (!$student) continue;
foreach ($hwData as $index => $score) {
// STEP 1: Always fetch the existing record
$existing = $this->homework->where([
'student_id' => $studentId,
'homework_index' => $index,
'class_section_id' => $classSectionId,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
])->first();
// STEP 2: If blank, delete if exists
if ($score === '' || $score === null) {
if ($existing) {
$this->homework->delete($existing['id']);
log_message('debug', "Deleted blank score for student $studentId index $index");
}
continue;
}
// STEP 3: Save numeric score
if (is_numeric($score)) {
$updateData = [
'student_id' => $studentId,
'school_id' => $student['school_id'] ?? '',
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'homework_index' => $index,
'score' => (float)$score,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
'updated_at' => utc_now(),
];
if ($existing) {
$this->homework->update($existing['id'], $updateData);
log_message('debug', "Updated homework ID {$existing['id']} with score $score");
} else {
$updateData['created_at'] = utc_now();
$this->homework->insert($updateData);
log_message('debug', "Inserted score for student $studentId index $index");
}
}
}
}
// Update semester scores for students in the class section
$studentUserInfo = $this->student->getStudentInfoByClassSectionId(
$classSectionId,
$this->semester,
$this->schoolYear
);
try {
foreach ($studentUserInfo as $studentInfo) {
$this->semesterScoreService->updateStudentScores([
'student_id' => $studentInfo['student_id'],
'school_id' => $studentInfo['school_id'] ?? null,
'class_section_id' => $studentInfo['class_section_id'],
'updated_by' => $studentInfo['updated_by'] ?? $updatedBy,
], $this->semester, $this->schoolYear);
}
} catch (RuntimeException $e) {
log_message('error', 'Failed to update semester scores: ' . $e->getMessage());
}
return $this->success(null, 'Homework scores updated successfully');
} catch (\Throwable $e) {
log_message('error', 'Update homework scores error: ' . $e->getMessage());
return $this->respondError('Failed to update homework scores: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* Add a new homework column/index for all students in a class section
*/
public function addNextColumn()
{
$data = $this->payloadData();
$classSectionId = (int) ($data['class_section_id'] ?? 0);
$updatedBy = $this->getCurrentUserId();
if ($classSectionId <= 0) {
return $this->respondError('Class section ID is required', Response::HTTP_BAD_REQUEST);
}
try {
// Step 1: Get the highest existing homework_index
$existingIndexes = $this->homework
->select('homework_index')
->where('class_section_id', $classSectionId)
->where('semester', $this->semester)
->where('school_year', $this->schoolYear)
->groupBy('homework_index')
->orderBy('homework_index', 'DESC')
->findAll();
$maxIndex = 0;
foreach ($existingIndexes as $row) {
if (isset($row['homework_index']) && is_numeric($row['homework_index'])) {
$maxIndex = max($maxIndex, (int)$row['homework_index']);
}
}
$nextIndex = $maxIndex + 1;
// Step 2: Get all students in the class
$students = $this->studentClass
->where('class_section_id', $classSectionId)
->where('school_year', $this->schoolYear)
->findAll();
// Step 3: Insert a new homework row for each student if not already exists
$firstInsertedId = null;
$insertedCount = 0;
foreach ($students as $i => $student) {
$studentId = $student['student_id'];
// Check if record already exists
$existing = $this->homework
->where('student_id', $studentId)
->where('homework_index', $nextIndex)
->where('class_section_id', $classSectionId)
->where('semester', $this->semester)
->where('school_year', $this->schoolYear)
->first();
if ($existing) continue;
$studentRecord = $this->student->find($studentId);
$insertData = [
'student_id' => $studentId,
'school_id' => $studentRecord['school_id'] ?? '',
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'homework_index' => $nextIndex,
'score' => null,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
'created_at' => utc_now(),
'updated_at' => utc_now()
];
$id = $this->homework->insert($insertData);
if ($i === 0) {
$firstInsertedId = $id;
}
$insertedCount++;
}
return $this->success([
'homework_index' => $nextIndex,
'new_homework_id' => $firstInsertedId,
'students_processed' => $insertedCount
], 'Homework column added successfully');
} catch (\Throwable $e) {
log_message('error', 'Add homework column error: ' . $e->getMessage());
return $this->respondError('Failed to add homework column: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* Get homework data for a class section (for management view)
*/
public function getByClassSection()
{
$classSectionId = (int) ($this->request->getGet('class_section_id') ?? 0);
if ($classSectionId <= 0) {
return $this->respondError('Class section ID is required', Response::HTTP_BAD_REQUEST);
}
try {
$homeworkScores = $this->getHomeworkScoresByStudent($classSectionId, $this->semester, $this->schoolYear);
$students = $this->getStudentsByClassSectionAndYear($classSectionId, $this->schoolYear);
$homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $this->semester, $this->schoolYear);
return $this->success([
'homework_scores' => $homeworkScores,
'students' => $students,
'homework_headers' => $homeworkHeaders,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
'class_section_id' => $classSectionId,
'has_homework_cols' => !empty($homeworkHeaders),
], 'Homework data retrieved successfully');
} catch (\Throwable $e) {
log_message('error', 'Get homework by class section error: ' . $e->getMessage());
return $this->respondError('Failed to retrieve homework data: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
/**
* Get students with their homework scores
*/
public function getStudentsWithScores()
{
$classSectionId = (int) ($this->request->getGet('class_section_id') ?? 0);
if ($classSectionId <= 0) {
return $this->respondError('Class section ID is required', Response::HTTP_BAD_REQUEST);
}
try {
$homeworkHeaders = $this->getHomeworkHeaders($classSectionId, $this->semester, $this->schoolYear);
$students = $this->getStudentsWithHomeworkScores($classSectionId, $homeworkHeaders);
return $this->success([
'students' => $students,
'homework_headers' => $homeworkHeaders,
'semester' => $this->semester,
'school_year' => $this->schoolYear,
'class_section_id' => $classSectionId,
], 'Students with homework scores retrieved successfully');
} catch (\Throwable $e) {
log_message('error', 'Get students with scores error: ' . $e->getMessage());
return $this->respondError('Failed to retrieve students with scores: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
// ──────────────── Helper Methods ────────────────
/**
* Get students by class section and year
*/
private function getStudentsByClassSectionAndYear($classSectionId, $schoolYear)
{
// Step 1: Get student IDs from student_class table
$studentClassRows = $this->studentClass
->select('student_id')
->where('class_section_id', $classSectionId)
->where('school_year', $schoolYear)
->findAll();
$studentIds = array_column($studentClassRows, 'student_id');
if (empty($studentIds)) {
return [];
}
// Step 2: Get student data from students table
$students = $this->student
->whereIn('id', $studentIds)
->orderBy('lastname', 'ASC')
->orderBy('firstname', 'ASC')
->findAll();
return $students;
}
/**
* Get homework scores grouped by student
*/
private function getHomeworkScoresByStudent($classSectionId, $semester, $schoolYear)
{
$rows = $this->homework
->select('student_id, homework_index, score')
->where('class_section_id', $classSectionId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->findAll();
$studentScores = [];
foreach ($rows as $row) {
$studentId = $row['student_id'];
$index = $row['homework_index'];
$score = $row['score'];
if (!isset($studentScores[$studentId])) {
$studentScores[$studentId] = ['scores' => []];
}
$studentScores[$studentId]['scores'][$index] = $score;
}
return $studentScores;
}
/**
* Get students with their homework scores
*/
private function getStudentsWithHomeworkScores($classSectionId, $homeworkHeaders)
{
$studentClasses = $this->studentClass
->where('class_section_id', $classSectionId)
->where('school_year', $this->schoolYear)
->findAll();
$students = [];
foreach ($studentClasses as $sc) {
$student = $this->student->find($sc['student_id']);
if (!$student) continue;
$scores = [];
foreach ($homeworkHeaders as $index) {
$entry = $this->homework
->where('homework_index', $index)
->where('student_id', $sc['student_id'])
->where('class_section_id', $classSectionId)
->where('semester', $this->semester)
->where('school_year', $this->schoolYear)
->first();
$scores[$index] = $entry['score'] ?? '';
}
$students[] = [
'student_id' => $sc['student_id'],
'firstname' => $student['firstname'],
'lastname' => $student['lastname'],
'scores' => $scores
];
}
usort($students, fn($a, $b) => strcmp($a['lastname'], $b['lastname']) ?: strcmp($a['firstname'], $b['firstname']));
return $students;
}
/**
* Get homework headers (unique homework_index values)
*/
private function getHomeworkHeaders($classSectionId, $semester, $schoolYear)
{
$rows = $this->homework
->select('homework_index')
->where('class_section_id', $classSectionId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->orderBy('homework_index', 'ASC')
->findAll();
$headers = [];
foreach ($rows as $row) {
$index = $row['homework_index'];
if (!is_array($index)) {
$headers[$index] = true;
}
}
ksort($headers);
return array_keys($headers);
}
}