fix is_active student flag logic and make moving with enrollment status
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<?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 '';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user