add projet
This commit is contained in:
+482
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\AttendanceRecord;
|
||||
use App\Models\ClassSection;
|
||||
use App\Models\Configuration;
|
||||
use App\Models\FinalExam;
|
||||
use App\Models\Homework;
|
||||
use App\Models\MidtermExam;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quiz;
|
||||
use App\Models\ScoreComment;
|
||||
use App\Models\SemesterScore;
|
||||
use App\Models\StudentClass;
|
||||
use App\Models\Student;
|
||||
use App\Models\TeacherClass;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class GradingController extends BaseApiController
|
||||
{
|
||||
protected Configuration $config;
|
||||
protected Homework $homework;
|
||||
protected User $user;
|
||||
protected StudentClass $studentClass;
|
||||
protected Student $student;
|
||||
protected TeacherClass $teacherClass;
|
||||
protected ClassSection $classSection;
|
||||
protected string $schoolYear;
|
||||
protected string $semester;
|
||||
protected $semesterScoreService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->config = model(Configuration::class);
|
||||
$this->homework = model(Homework::class);
|
||||
$this->user = model(User::class);
|
||||
$this->studentClass = model(StudentClass::class);
|
||||
$this->student = model(Student::class);
|
||||
$this->teacherClass = model(TeacherClass::class);
|
||||
$this->classSection = model(ClassSection::class);
|
||||
$this->schoolYear = (string) ($this->config->getConfig('school_year') ?? '');
|
||||
$this->semester = (string) ($this->config->getConfig('semester') ?? '');
|
||||
$this->semesterScoreService = \Config\Services::semesterScoreService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show scores for a specific type, class section, and student
|
||||
*/
|
||||
public function show($type, $classSectionId = null, $studentId = null)
|
||||
{
|
||||
$model = $this->getModelByType($type);
|
||||
if (!$model) {
|
||||
return $this->respondError('Invalid type', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$student = model(Student::class);
|
||||
$student = null;
|
||||
if ($studentId) {
|
||||
$student = $student->find($studentId);
|
||||
if (!$student) {
|
||||
return $this->respondError('Student not found', Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$scores = $model->where([
|
||||
'student_id' => $studentId,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear
|
||||
])->findAll();
|
||||
|
||||
return $this->success([
|
||||
'student' => $student,
|
||||
'scores' => $scores,
|
||||
'type' => $type,
|
||||
'class_section_id' => $classSectionId,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear,
|
||||
], 'Scores retrieved successfully');
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Grading show error: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to retrieve scores', Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update scores for various types
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$data = $this->payloadData();
|
||||
$type = $data['type'] ?? null;
|
||||
$studentId = (int) ($data['student_id'] ?? 0);
|
||||
$classSectionId = (int) ($data['class_section_id'] ?? 0);
|
||||
|
||||
if (!$type) {
|
||||
return $this->respondError('Type is required', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$model = $this->getModelByType($type);
|
||||
if (!$model) {
|
||||
return $this->respondError('Invalid type', Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
if (in_array($type, ['homework', 'quiz', 'project'])) {
|
||||
$scoreIds = $data['score_ids'] ?? [];
|
||||
$scores = $data['scores'] ?? [];
|
||||
$comments = $data['comments'] ?? [];
|
||||
|
||||
foreach ($scoreIds as $i => $id) {
|
||||
$updateData = [
|
||||
'score' => $scores[$i] ?? null,
|
||||
'updated_at' => utc_now()
|
||||
];
|
||||
if (isset($comments[$i])) {
|
||||
$updateData['comment'] = $comments[$i];
|
||||
}
|
||||
$model->update($id, $updateData);
|
||||
}
|
||||
} elseif (in_array($type, ['midterm', 'final', 'test'])) {
|
||||
$score = $data['score'] ?? null;
|
||||
$updateData = [
|
||||
'score' => $score,
|
||||
'updated_at' => utc_now()
|
||||
];
|
||||
|
||||
$existing = $model->where([
|
||||
'student_id' => $studentId,
|
||||
'class_section_id' => $classSectionId,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear
|
||||
])->first();
|
||||
|
||||
if ($existing) {
|
||||
$model->update($existing['id'], $updateData);
|
||||
} else {
|
||||
$insertData = array_merge($updateData, [
|
||||
'student_id' => $studentId,
|
||||
'class_section_id' => $classSectionId,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear,
|
||||
'created_at' => utc_now()
|
||||
]);
|
||||
$model->insert($insertData);
|
||||
}
|
||||
} elseif ($type === 'comments') {
|
||||
$comment = $data['comment'] ?? null;
|
||||
|
||||
// Remove existing comments for this student/semester/year
|
||||
$model->where([
|
||||
'student_id' => $studentId,
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear
|
||||
])->delete();
|
||||
|
||||
// Insert new comment
|
||||
$model->insert([
|
||||
'student_id' => $studentId,
|
||||
'score_type' => 'general',
|
||||
'semester' => $this->semester,
|
||||
'school_year' => $this->schoolYear,
|
||||
'comment' => $comment,
|
||||
'commented_by' => $this->getCurrentUserId(),
|
||||
'created_at' => utc_now()
|
||||
]);
|
||||
}
|
||||
|
||||
// Update semester scores for students in the class section
|
||||
if ($classSectionId > 0) {
|
||||
$studentTeacherInfo = $this->student->getStudentInfoByClassSectionId(
|
||||
$classSectionId,
|
||||
$this->semester,
|
||||
$this->schoolYear
|
||||
);
|
||||
|
||||
try {
|
||||
foreach ($studentTeacherInfo 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'] ?? $this->getCurrentUserId(),
|
||||
], $this->semester, $this->schoolYear);
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
log_message('error', 'Failed to update semester scores: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(null, 'Scores updated successfully');
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Grading update error: ' . $e->getMessage());
|
||||
return $this->respondError('Failed to update scores: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grading main view data - all students grouped by class sections
|
||||
*/
|
||||
public function grading()
|
||||
{
|
||||
$schoolYear = (string) $this->schoolYear;
|
||||
$semester = (string) $this->semester;
|
||||
|
||||
// Roster + dual-join to semester_scores:
|
||||
// ss_b: match when semester_scores.class_section_id = BUSINESS ID (student_class.class_section_id)
|
||||
// ss_p: match when semester_scores.class_section_id = PK (classSection.id)
|
||||
$rows = DB::table('student_class as sc')
|
||||
->select([
|
||||
'cs.id as section_pk',
|
||||
'cs.class_section_id as section_id',
|
||||
'cs.class_id',
|
||||
'cs.class_section_name',
|
||||
's.id as student_id',
|
||||
's.school_id',
|
||||
's.firstname',
|
||||
's.lastname',
|
||||
DB::raw('COALESCE(ss_b.ptap_score, ss_p.ptap_score) as ss_ptap_score'),
|
||||
DB::raw('COALESCE(ss_b.semester_score, ss_p.semester_score) as ss_semester_score'),
|
||||
'ss_b.class_section_id as matched_biz_csid',
|
||||
'ss_p.class_section_id as matched_pk_csid'
|
||||
])
|
||||
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id')
|
||||
->join('students as s', 's.id', '=', 'sc.student_id')
|
||||
->leftJoin('semester_scores as ss_b', function($join) use ($semester, $schoolYear) {
|
||||
$join->on('ss_b.student_id', '=', 's.id')
|
||||
->on('ss_b.class_section_id', '=', 'sc.class_section_id')
|
||||
->whereRaw('LOWER(ss_b.semester) = LOWER(?)', [$semester])
|
||||
->where('ss_b.school_year', '=', $schoolYear);
|
||||
})
|
||||
->leftJoin('semester_scores as ss_p', function($join) use ($semester, $schoolYear) {
|
||||
$join->on('ss_p.student_id', '=', 's.id')
|
||||
->on('ss_p.class_section_id', '=', 'cs.id')
|
||||
->whereRaw('LOWER(ss_p.semester) = LOWER(?)', [$semester])
|
||||
->where('ss_p.school_year', '=', $schoolYear);
|
||||
})
|
||||
->where('sc.school_year', $schoolYear)
|
||||
->orderBy('cs.class_id', 'ASC')
|
||||
->orderBy('cs.class_section_name', 'ASC')
|
||||
->orderBy('s.lastname', 'ASC')
|
||||
->orderBy('s.firstname', 'ASC')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
// Build structures keyed by BUSINESS section id
|
||||
$grades = []; // class_id => [ ['class_section_id','class_section_name'], ... ]
|
||||
$studentsBySection = []; // section_id => [ students... ]
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$sectionId = (int) ($r->section_id ?? 0);
|
||||
$classId = (int) ($r->class_id ?? 0);
|
||||
$sectionName = (string) ($r->class_section_name ?? '');
|
||||
|
||||
if ($sectionId <= 0 || $classId <= 0) continue;
|
||||
|
||||
if (!isset($grades[$classId])) {
|
||||
$grades[$classId] = [];
|
||||
}
|
||||
|
||||
$exists = false;
|
||||
foreach ($grades[$classId] as $s) {
|
||||
if ((int)$s['class_section_id'] === $sectionId) {
|
||||
$exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exists) {
|
||||
$grades[$classId][] = [
|
||||
'class_section_id' => $sectionId,
|
||||
'class_section_name' => $sectionName,
|
||||
];
|
||||
}
|
||||
|
||||
$sid = (int) ($r->student_id ?? 0);
|
||||
if ($sid <= 0) continue;
|
||||
|
||||
$ptapScore = $r->ss_ptap_score ?? null;
|
||||
$semesterScore = $r->ss_semester_score ?? null;
|
||||
|
||||
if (!isset($studentsBySection[$sectionId])) {
|
||||
$studentsBySection[$sectionId] = [];
|
||||
}
|
||||
|
||||
$studentsBySection[$sectionId][] = [
|
||||
'id' => $sid,
|
||||
'school_id' => $r->school_id ?? null,
|
||||
'firstname' => $r->firstname ?? null,
|
||||
'lastname' => $r->lastname ?? null,
|
||||
'ptap' => is_null($ptapScore) ? null : (float) $ptapScore,
|
||||
'semester_score' => is_null($semesterScore) ? null : (float) $semesterScore,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'grades' => $grades,
|
||||
'students_by_section' => $studentsBySection,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
], 'Grading data retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all scores and comments for students
|
||||
*/
|
||||
public function getScoreComment()
|
||||
{
|
||||
// Get all students for the current semester and school year
|
||||
$studentClassEntries = $this->studentClass
|
||||
->where('semester', $this->semester)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->findAll();
|
||||
|
||||
// Group student IDs
|
||||
$studentIds = array_map(fn($entry) => $entry['student_id'], $studentClassEntries);
|
||||
|
||||
if (empty($studentIds)) {
|
||||
return $this->success([], 'No students found');
|
||||
}
|
||||
|
||||
// Fetch all scores and comments for the students
|
||||
$scoresAndComments = $this->getAllScoresAndComments($studentIds, $this->semester, $this->schoolYear);
|
||||
|
||||
return $this->success($scoresAndComments, 'Scores and comments retrieved successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all scores and comments for a list of students based on semester and school year.
|
||||
*
|
||||
* @param array $studentIds List of student IDs.
|
||||
* @param string $semester Current semester (e.g., 'fall', 'spring').
|
||||
* @param string $schoolYear Current school year (e.g., '2025-2026').
|
||||
* @return array
|
||||
*/
|
||||
private function getAllScoresAndComments($studentIds, $semester, $schoolYear)
|
||||
{
|
||||
// Validate input parameters
|
||||
if (empty($studentIds) || !is_array($studentIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (empty($semester) || empty($schoolYear)) {
|
||||
throw new \InvalidArgumentException('Semester and school year must be provided');
|
||||
}
|
||||
|
||||
// Initialize models
|
||||
$models = [
|
||||
'final_exam' => model(FinalExam::class),
|
||||
'homework' => model(Homework::class),
|
||||
'midterm' => model(MidtermExam::class),
|
||||
'project' => model(Project::class),
|
||||
'quiz' => model(Quiz::class),
|
||||
'comments' => model(ScoreComment::class),
|
||||
'semester_scores' => model(SemesterScore::class),
|
||||
'student' => model(Student::class),
|
||||
'student_class' => model(StudentClass::class),
|
||||
'teacher_class' => model(TeacherClass::class),
|
||||
'config' => model(Configuration::class),
|
||||
'attendance' => model(AttendanceRecord::class)
|
||||
];
|
||||
|
||||
// Get semester days configuration
|
||||
$semesterKey = strtolower($semester) === 'fall' ? 'total_semester1_days' : 'total_semester2_days';
|
||||
$totalSemesterDays = $models['config']->getConfig($semesterKey) ?? 0;
|
||||
|
||||
// Common query conditions
|
||||
$conditions = [
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear
|
||||
];
|
||||
|
||||
// Fetch all student data first
|
||||
$students = $models['student']->whereIn('id', $studentIds)
|
||||
->where('school_year', $schoolYear)
|
||||
->findAll();
|
||||
|
||||
if (empty($students)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Initialize result array with student data
|
||||
$allScores = [];
|
||||
|
||||
foreach ($students as $student) {
|
||||
$className = $models['student_class']->getClassSectionsByStudentId($student['id'], $schoolYear);
|
||||
$updatedBy = $models['teacher_class']->getTeacherIdByClassSection($className, $semester, $schoolYear);
|
||||
|
||||
$allScores[$student['id']] = [
|
||||
'school_id' => $student['school_id'],
|
||||
'firstname' => $student['firstname'],
|
||||
'lastname' => $student['lastname'],
|
||||
'class_name' => $className,
|
||||
'comments' => []
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch and process attendance data
|
||||
foreach ($studentIds as $studentId) {
|
||||
if (!isset($allScores[$studentId])) continue;
|
||||
|
||||
$absences = $models['attendance']->getTotalAbsences($studentId, $semester, $schoolYear);
|
||||
$attendance = min((($totalSemesterDays - $absences + 1) / $totalSemesterDays) * 100, 100);
|
||||
|
||||
$allScores[$studentId]['attendance'] = [
|
||||
'score' => round($attendance, 2),
|
||||
'absences' => $absences,
|
||||
'total_days' => $totalSemesterDays
|
||||
];
|
||||
}
|
||||
|
||||
// Fetch and process all score types
|
||||
$scoreTypes = [
|
||||
'final_exam' => $models['final_exam'],
|
||||
'homework' => $models['homework'],
|
||||
'midterm' => $models['midterm'],
|
||||
'project' => $models['project'],
|
||||
'quiz' => $models['quiz'],
|
||||
'semester_score' => $models['semester_scores']
|
||||
];
|
||||
|
||||
foreach ($scoreTypes as $type => $model) {
|
||||
$scores = $model->whereIn('student_id', $studentIds)
|
||||
->where($conditions)
|
||||
->findAll();
|
||||
|
||||
foreach ($scores as $score) {
|
||||
if ($type === 'semester_score') {
|
||||
$allScores[$score['student_id']][$type] = [
|
||||
'homework_avg' => $score['homework_avg'],
|
||||
'quiz_avg' => $score['quiz_avg'],
|
||||
'project_avg' => $score['project_avg'],
|
||||
'midterm_exam_score' => $score['midterm_exam_score'],
|
||||
'final_exam_score' => $score['final_exam_score'],
|
||||
'attendance_score' => $score['attendance_score'],
|
||||
'participation_score' => $score['participation_score'],
|
||||
'ptap_score' => $score['ptap_score'],
|
||||
'test_avg' => $score['test_avg'],
|
||||
'semester_score' => $score['semester_score'],
|
||||
'semester' => $score['semester'],
|
||||
'school_year' => $score['school_year']
|
||||
];
|
||||
} else {
|
||||
$allScores[$score['student_id']][$type] = $score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch and process comments
|
||||
$comments = $models['comments']->whereIn('student_id', $studentIds)
|
||||
->where($conditions)
|
||||
->findAll();
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
$allScores[$comment['student_id']]['comments'][] = $comment;
|
||||
}
|
||||
|
||||
return $allScores;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model instance by type
|
||||
*/
|
||||
private function getModelByType(?string $type)
|
||||
{
|
||||
return match ($type) {
|
||||
'homework' => model(Homework::class),
|
||||
'quiz' => model(Quiz::class),
|
||||
'project' => model(Project::class),
|
||||
'midterm' => model(MidtermExam::class),
|
||||
'final' => model(FinalExam::class),
|
||||
'test' => model(SemesterScore::class),
|
||||
'comments' => model(ScoreComment::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user