update policy and fix other issues
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 49s
Tests / PHPUnit (push) Successful in 1m19s

This commit is contained in:
root
2026-08-23 18:19:08 -04:00
parent 103f970110
commit a6e0ce1432
13 changed files with 433 additions and 317 deletions
@@ -0,0 +1,140 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class BackfillReturningStudentYearStatus extends Migration
{
public function up(): void
{
if (! $this->db->tableExists('student_year_status') || ! $this->db->tableExists('school_years')) {
return;
}
$years = $this->db->table('school_years')
->select('id, name, previous_school_year_id')
->orderBy('id', 'ASC')
->get()
->getResultArray();
$yearsById = [];
$yearsByName = [];
foreach ($years as $year) {
$id = (int) ($year['id'] ?? 0);
$name = trim((string) ($year['name'] ?? ''));
if ($id > 0 && preg_match('/^\d{4}-\d{4}$/', $name)) {
$yearsById[$id] = $year;
$yearsByName[$name] = $year;
}
}
foreach ($yearsById as $targetYear) {
$targetName = trim((string) ($targetYear['name'] ?? ''));
$sourceName = $this->sourceYearName($targetYear, $yearsById, $yearsByName);
if ($sourceName === null || $sourceName === $targetName) {
continue;
}
foreach ($this->returningStudentIds($sourceName) as $studentId) {
$this->upsertReturningStatus($studentId, $targetName);
}
}
}
public function down(): void
{
// No rollback: this migration repairs source-of-truth status rows.
}
/**
* @param array<string, mixed> $targetYear
* @param array<int, array<string, mixed>> $yearsById
* @param array<string, array<string, mixed>> $yearsByName
*/
private function sourceYearName(array $targetYear, array $yearsById, array $yearsByName): ?string
{
$previousId = (int) ($targetYear['previous_school_year_id'] ?? 0);
if ($previousId > 0) {
$name = trim((string) ($yearsById[$previousId]['name'] ?? ''));
if (preg_match('/^\d{4}-\d{4}$/', $name)) {
return $name;
}
}
$targetName = trim((string) ($targetYear['name'] ?? ''));
if (! preg_match('/^(\d{4})-(\d{4})$/', $targetName, $matches)) {
return null;
}
$sourceName = ((int) $matches[1] - 1) . '-' . ((int) $matches[2] - 1);
return isset($yearsByName[$sourceName]) ? $sourceName : null;
}
/**
* @return list<int>
*/
private function returningStudentIds(string $sourceSchoolYear): array
{
$ids = [];
foreach (['student_year_status', 'student_class', 'enrollments'] as $table) {
if (! $this->db->tableExists($table) || ! $this->db->fieldExists('school_year', $table)) {
continue;
}
$rows = $this->db->table($table)
->select('student_id')
->where('school_year', $sourceSchoolYear)
->where('student_id IS NOT NULL', null, false)
->groupBy('student_id')
->get()
->getResultArray();
foreach ($rows as $row) {
$studentId = (int) ($row['student_id'] ?? 0);
if ($studentId > 0) {
$ids[$studentId] = true;
}
}
}
return array_keys($ids);
}
private function upsertReturningStatus(int $studentId, string $targetSchoolYear): void
{
if ($studentId <= 0 || ! preg_match('/^\d{4}-\d{4}$/', $targetSchoolYear)) {
return;
}
$now = date('Y-m-d H:i:s');
$existing = $this->db->table('student_year_status')
->select('id, is_new')
->where('student_id', $studentId)
->where('school_year', $targetSchoolYear)
->get(1)
->getRowArray();
if ($existing !== null) {
if ((int) ($existing['is_new'] ?? 1) !== 0) {
$this->db->table('student_year_status')
->where('id', (int) $existing['id'])
->update([
'is_new' => 0,
'updated_at' => $now,
]);
}
return;
}
$this->db->table('student_year_status')->insert([
'student_id' => $studentId,
'school_year' => $targetSchoolYear,
'is_new' => 0,
'created_at' => $now,
'updated_at' => $now,
]);
}
}