reconstruction of the project
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AttendanceTracking;
|
||||
|
||||
use App\Models\AttendanceData;
|
||||
use App\Models\Student;
|
||||
use App\Models\StudentClass;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AttendanceViolationStudentResolverService
|
||||
{
|
||||
public function __construct(
|
||||
protected Student $studentModel,
|
||||
protected StudentClass $studentClassModel,
|
||||
protected AttendanceData $attendanceDataModel
|
||||
) {
|
||||
}
|
||||
|
||||
public function resolveForSchoolYear(string $schoolYear, ?string $semester = null): array
|
||||
{
|
||||
$debug = [
|
||||
'class_students' => 0,
|
||||
'student_ids' => 0,
|
||||
];
|
||||
|
||||
$classStudentsQuery = $this->studentClassModel->query();
|
||||
if (method_exists($this->studentClassModel, 'scopeActive')) {
|
||||
$classStudentsQuery->active();
|
||||
}
|
||||
|
||||
$classStudents = $classStudentsQuery
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$debug['class_students'] = count($classStudents);
|
||||
|
||||
if (empty($classStudents)) {
|
||||
$latestTerm = DB::table('student_class')
|
||||
->select('school_year', 'semester')
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
if ($latestTerm && !empty($latestTerm->school_year)) {
|
||||
$schoolYear = (string) $latestTerm->school_year;
|
||||
if (!empty($latestTerm->semester)) {
|
||||
$semester = (string) $latestTerm->semester;
|
||||
}
|
||||
|
||||
$classStudentsQuery = $this->studentClassModel->query();
|
||||
if (method_exists($this->studentClassModel, 'scopeActive')) {
|
||||
$classStudentsQuery->active();
|
||||
}
|
||||
|
||||
$classStudents = $classStudentsQuery
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$debug['class_students'] = count($classStudents);
|
||||
}
|
||||
}
|
||||
|
||||
[$studentIds, $studentCodes] = $this->extractStudentIdentifiers($classStudents);
|
||||
|
||||
if (empty($studentIds) && empty($studentCodes)) {
|
||||
[$studentIds, $studentCodes] = $this->deriveIdentifiersFromAttendanceData($schoolYear);
|
||||
}
|
||||
|
||||
[$studentIds, $studentCodes] = $this->filterInactiveIdentifiers($studentIds, $studentCodes);
|
||||
|
||||
$debug['student_ids'] = count($studentIds) + count($studentCodes);
|
||||
|
||||
if (empty($studentIds) && empty($studentCodes)) {
|
||||
return [
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
'student_ids' => [],
|
||||
'student_codes' => [],
|
||||
'students' => [],
|
||||
'student_code_to_id'=> [],
|
||||
'existing_ids' => [],
|
||||
'debug' => $debug,
|
||||
];
|
||||
}
|
||||
|
||||
$students = $this->loadActiveStudents($studentIds, $studentCodes);
|
||||
$students = $this->addPlaceholderStudentsForMissingCodes($students, $studentCodes);
|
||||
$students = $this->ensureEntriesForAllIdentifiers($students, $studentIds, $studentCodes);
|
||||
|
||||
[$studentCodeToId, $existingIds] = $this->buildLookupMaps($students);
|
||||
|
||||
return [
|
||||
'school_year' => $schoolYear,
|
||||
'semester' => $semester,
|
||||
'student_ids' => $studentIds,
|
||||
'student_codes' => $studentCodes,
|
||||
'students' => $students,
|
||||
'student_code_to_id' => $studentCodeToId,
|
||||
'existing_ids' => $existingIds,
|
||||
'debug' => $debug,
|
||||
];
|
||||
}
|
||||
|
||||
public function ensureAttendanceStudentExists(
|
||||
array &$students,
|
||||
array &$studentCodeToId,
|
||||
array &$existingIds,
|
||||
mixed $rawStudentId
|
||||
): ?int {
|
||||
if (is_numeric($rawStudentId)) {
|
||||
$sid = (int) $rawStudentId;
|
||||
if ($sid <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($existingIds[$sid])) {
|
||||
$students[] = [
|
||||
'id' => $sid,
|
||||
'school_id' => (string) $sid,
|
||||
'firstname' => (string) $sid,
|
||||
'lastname' => '',
|
||||
'parent_id' => 0,
|
||||
];
|
||||
$existingIds[$sid] = true;
|
||||
}
|
||||
|
||||
return $sid;
|
||||
}
|
||||
|
||||
if (is_string($rawStudentId)) {
|
||||
$code = trim($rawStudentId);
|
||||
if ($code === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sid = $studentCodeToId[$code] ?? null;
|
||||
if ($sid === null) {
|
||||
$sid = abs(crc32($code)) ?: random_int(3000000, 3999999);
|
||||
$studentCodeToId[$code] = $sid;
|
||||
|
||||
if (!isset($existingIds[$sid])) {
|
||||
$students[] = [
|
||||
'id' => $sid,
|
||||
'school_id' => $code,
|
||||
'firstname' => $code,
|
||||
'lastname' => '',
|
||||
'parent_id' => 0,
|
||||
];
|
||||
$existingIds[$sid] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $sid;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function extractStudentIdentifiers(array $classStudents): array
|
||||
{
|
||||
$studentIds = [];
|
||||
$studentCodes = [];
|
||||
|
||||
foreach ($classStudents as $row) {
|
||||
$raw = $row['student_id'] ?? null;
|
||||
|
||||
if (is_numeric($raw)) {
|
||||
$studentIds[] = (int) $raw;
|
||||
} elseif (is_string($raw) && $raw !== '') {
|
||||
$studentCodes[] = trim($raw);
|
||||
}
|
||||
}
|
||||
|
||||
$studentIds = array_values(array_unique(array_filter($studentIds, fn ($v) => $v > 0)));
|
||||
$studentCodes = array_values(array_unique(array_filter($studentCodes, fn ($v) => $v !== '')));
|
||||
|
||||
return [$studentIds, $studentCodes];
|
||||
}
|
||||
|
||||
protected function deriveIdentifiersFromAttendanceData(string $schoolYear): array
|
||||
{
|
||||
$studentIds = [];
|
||||
$studentCodes = [];
|
||||
|
||||
$attendanceStudents = DB::table($this->attendanceDataModel->getTable())
|
||||
->selectRaw('DISTINCT student_id')
|
||||
->when(!empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
|
||||
->orderBy('student_id')
|
||||
->get();
|
||||
|
||||
foreach ($attendanceStudents as $r) {
|
||||
$raw = $r->student_id ?? null;
|
||||
|
||||
if (is_numeric($raw)) {
|
||||
$studentIds[] = (int) $raw;
|
||||
} elseif (is_string($raw) && $raw !== '') {
|
||||
$studentCodes[] = trim($raw);
|
||||
}
|
||||
}
|
||||
|
||||
$studentIds = array_values(array_unique(array_filter($studentIds, fn ($v) => $v > 0)));
|
||||
$studentCodes = array_values(array_unique(array_filter($studentCodes, fn ($v) => $v !== '')));
|
||||
|
||||
return [$studentIds, $studentCodes];
|
||||
}
|
||||
|
||||
protected function filterInactiveIdentifiers(array $studentIds, array $studentCodes): array
|
||||
{
|
||||
if (!empty($studentIds)) {
|
||||
$inactiveRows = $this->studentModel->query()
|
||||
->select('id')
|
||||
->whereIn('id', $studentIds)
|
||||
->where('is_active', 0)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$inactiveIdSet = array_flip(array_map(
|
||||
static fn ($r) => (int) ($r['id'] ?? 0),
|
||||
$inactiveRows
|
||||
));
|
||||
|
||||
$studentIds = array_values(array_filter(
|
||||
$studentIds,
|
||||
static fn ($id) => !isset($inactiveIdSet[$id])
|
||||
));
|
||||
}
|
||||
|
||||
if (!empty($studentCodes)) {
|
||||
$inactiveCodeRows = $this->studentModel->query()
|
||||
->select('school_id')
|
||||
->whereIn('school_id', $studentCodes)
|
||||
->where('is_active', 0)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$inactiveCodeSet = array_flip(array_map(
|
||||
static fn ($r) => (string) ($r['school_id'] ?? ''),
|
||||
$inactiveCodeRows
|
||||
));
|
||||
|
||||
$studentCodes = array_values(array_filter(
|
||||
$studentCodes,
|
||||
static fn ($code) => $code !== '' && !isset($inactiveCodeSet[$code])
|
||||
));
|
||||
}
|
||||
|
||||
return [$studentIds, $studentCodes];
|
||||
}
|
||||
|
||||
protected function loadActiveStudents(array $studentIds, array $studentCodes): array
|
||||
{
|
||||
$students = [];
|
||||
|
||||
if (!empty($studentIds)) {
|
||||
$students = $this->studentModel->query()
|
||||
->whereIn('id', $studentIds)
|
||||
->where('is_active', 1)
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
if (!empty($studentCodes)) {
|
||||
$byCode = $this->studentModel->query()
|
||||
->whereIn('school_id', $studentCodes)
|
||||
->where('is_active', 1)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$seen = array_flip(array_map(fn ($s) => (int) ($s['id'] ?? 0), $students));
|
||||
|
||||
foreach ($byCode as $s) {
|
||||
$sid = (int) ($s['id'] ?? 0);
|
||||
if ($sid > 0 && !isset($seen[$sid])) {
|
||||
$students[] = $s;
|
||||
$seen[$sid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $students;
|
||||
}
|
||||
|
||||
protected function addPlaceholderStudentsForMissingCodes(array $students, array $studentCodes): array
|
||||
{
|
||||
$knownCodes = [];
|
||||
|
||||
foreach ($students as $s) {
|
||||
$code = trim((string) ($s['school_id'] ?? ''));
|
||||
if ($code !== '') {
|
||||
$knownCodes[$code] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($studentCodes as $code) {
|
||||
if (!isset($knownCodes[$code])) {
|
||||
$virtualId = abs(crc32($code)) ?: random_int(1000000, 1999999);
|
||||
$students[] = [
|
||||
'id' => $virtualId,
|
||||
'school_id' => $code,
|
||||
'firstname' => $code,
|
||||
'lastname' => '',
|
||||
'parent_id' => 0,
|
||||
'class_name' => '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $students;
|
||||
}
|
||||
|
||||
protected function ensureEntriesForAllIdentifiers(array $students, array $studentIds, array $studentCodes): array
|
||||
{
|
||||
[$studentCodeToId, $existingIds] = $this->buildLookupMaps($students);
|
||||
|
||||
foreach (array_merge($studentIds, $studentCodes) as $sidOrCode) {
|
||||
if (is_numeric($sidOrCode)) {
|
||||
$sid = (int) $sidOrCode;
|
||||
|
||||
if ($sid > 0 && !isset($existingIds[$sid])) {
|
||||
$students[] = [
|
||||
'id' => $sid,
|
||||
'school_id' => (string) $sid,
|
||||
'firstname' => (string) $sid,
|
||||
'lastname' => '',
|
||||
'parent_id' => 0,
|
||||
'class_name' => '',
|
||||
];
|
||||
$existingIds[$sid] = true;
|
||||
}
|
||||
} else {
|
||||
$code = (string) $sidOrCode;
|
||||
|
||||
if ($code !== '' && !isset($studentCodeToId[$code])) {
|
||||
$virtualId = abs(crc32($code)) ?: random_int(2000000, 2999999);
|
||||
$students[] = [
|
||||
'id' => $virtualId,
|
||||
'school_id' => $code,
|
||||
'firstname' => $code,
|
||||
'lastname' => '',
|
||||
'parent_id' => 0,
|
||||
'class_name' => '',
|
||||
];
|
||||
|
||||
$studentCodeToId[$code] = $virtualId;
|
||||
$existingIds[$virtualId] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $students;
|
||||
}
|
||||
|
||||
protected function buildLookupMaps(array $students): array
|
||||
{
|
||||
$studentCodeToId = [];
|
||||
foreach ($students as $stu) {
|
||||
$code = trim((string) ($stu['student_code'] ?? $stu['school_id'] ?? ''));
|
||||
if ($code !== '') {
|
||||
$studentCodeToId[$code] = (int) ($stu['id'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
$existingIds = array_flip(array_map(
|
||||
fn ($s) => (int) ($s['id'] ?? 0),
|
||||
$students
|
||||
));
|
||||
|
||||
return [$studentCodeToId, $existingIds];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user