Files
alrahma_sunday_school/app/Commands/SyncStudentActivity.php
T
root 0a31aa1393
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 47s
Tests / PHPUnit (push) Successful in 1m16s
fix is_active student flag logic and make moving with enrollment status
2026-08-20 03:11:37 -04:00

172 lines
6.1 KiB
PHP

<?php
namespace App\Commands;
use App\Services\EnrollmentStatusService;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use CodeIgniter\Database\BaseConnection;
class SyncStudentActivity extends BaseCommand
{
protected $group = 'Enrollment';
protected $name = 'students:sync-activity';
protected $description = 'Synchronize students.is_active from current-year enrollment_status.';
protected $usage = 'php spark students:sync-activity --school-year=YYYY-YYYY [--dry-run]';
protected $options = [
'--school-year' => 'School year to use as the controlling enrollment year.',
'--dry-run' => 'Report changes without updating students.',
];
private BaseConnection $db;
private EnrollmentStatusService $statusService;
public function run(array $params)
{
$this->db = \Config\Database::connect();
$this->statusService = new EnrollmentStatusService($this->db);
$schoolYear = $this->optionValue('school-year');
if ($schoolYear === '') {
$schoolYear = $this->statusService->currentSchoolYear();
}
if ($schoolYear === '') {
CLI::error('Missing --school-year and no configured current school year was found.');
return EXIT_ERROR;
}
$dryRun = CLI::getOption('dry-run') !== null;
$duplicates = $this->duplicates($schoolYear);
$unknownStatuses = [];
$withoutEnrollment = [];
$mismatches = [];
$updated = 0;
$students = $this->db->table('students')
->select('id, school_id, firstname, lastname, is_active')
->orderBy('id', 'ASC')
->get()
->getResultArray();
foreach ($students as $student) {
$studentId = (int) $student['id'];
$enrollment = $this->statusService->controllingEnrollment($studentId, $schoolYear);
if ($enrollment === null) {
$expected = 0;
$withoutEnrollment[] = $this->studentLabel($student);
} else {
try {
$status = $this->statusService->normalizeStatus((string) ($enrollment['enrollment_status'] ?? ''));
$expected = $this->statusService->activeFlagForStatus($status);
} catch (\InvalidArgumentException $e) {
$unknownStatuses[] = [
'student' => $this->studentLabel($student),
'enrollment_id' => (int) ($enrollment['id'] ?? 0),
'status' => (string) ($enrollment['enrollment_status'] ?? ''),
];
continue;
}
}
$actual = (int) ($student['is_active'] ?? 1);
if ($actual !== $expected) {
$mismatches[] = [
'student' => $this->studentLabel($student),
'current' => $actual,
'expected' => $expected,
'status' => (string) ($enrollment['enrollment_status'] ?? 'no enrollment'),
];
if (! $dryRun) {
$this->db->table('students')->where('id', $studentId)->update(['is_active' => $expected]);
$updated++;
}
}
}
CLI::write('School year: ' . $schoolYear);
CLI::write('Dry run: ' . ($dryRun ? 'yes' : 'no'));
CLI::write('Duplicate enrollment groups: ' . count($duplicates));
foreach ($duplicates as $row) {
CLI::write(sprintf(
' student_id=%d school_year=%s semester=%s count=%d',
(int) $row['student_id'],
(string) $row['school_year'],
(string) ($row['semester'] ?? ''),
(int) $row['row_count']
));
}
CLI::write('Unknown statuses: ' . count($unknownStatuses));
foreach ($unknownStatuses as $row) {
CLI::write(sprintf(' %s enrollment_id=%d status=%s', $row['student'], $row['enrollment_id'], $row['status']));
}
CLI::write('Students without current enrollment: ' . count($withoutEnrollment));
foreach (array_slice($withoutEnrollment, 0, 50) as $label) {
CLI::write(' ' . $label);
}
if (count($withoutEnrollment) > 50) {
CLI::write(' ... ' . (count($withoutEnrollment) - 50) . ' more');
}
CLI::write('Mismatches: ' . count($mismatches));
foreach ($mismatches as $row) {
CLI::write(sprintf(
' %s current=%d expected=%d status=%s',
$row['student'],
$row['current'],
$row['expected'],
$row['status']
));
}
if (! $dryRun) {
CLI::write('Updated students: ' . $updated);
}
return EXIT_SUCCESS;
}
private function duplicates(string $schoolYear): array
{
return $this->db->table('enrollments')
->select('student_id, school_year, semester, COUNT(*) AS row_count')
->where('school_year', $schoolYear)
->groupBy('student_id, school_year, semester')
->having('COUNT(*) >', 1)
->orderBy('student_id', 'ASC')
->get()
->getResultArray();
}
private function studentLabel(array $student): string
{
$name = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? ''));
return '#' . (int) ($student['id'] ?? 0) . ($name !== '' ? ' ' . $name : '');
}
private function optionValue(string $name): string
{
$value = CLI::getOption($name);
if (is_string($value) && trim($value) !== '') {
return trim($value);
}
$argv = $_SERVER['argv'] ?? [];
$prefix = '--' . $name . '=';
foreach ($argv as $index => $arg) {
if (is_string($arg) && str_starts_with($arg, $prefix)) {
return trim(substr($arg, strlen($prefix)));
}
if ($arg === '--' . $name && isset($argv[$index + 1])) {
return trim((string) $argv[$index + 1]);
}
}
return '';
}
}