recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
+232
View File
@@ -0,0 +1,232 @@
<?php
namespace App\Commands;
use App\Models\ConfigurationModel;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use DateTime;
use DateTimeZone;
class ConfigUpdate extends BaseCommand
{
protected $group = 'Maintenance';
protected $name = 'config:update';
protected $description = 'Run a configuration update task (weekly cron).';
protected $arguments = [];
protected $usage = 'php spark config:update [task] [--task task|-t task] [--dry] [--force] [--tz=<timezone>]';
protected $options = [
'task' => 'Task name (or pass as first positional arg)',
't' => 'Short form of --task',
'dry' => 'Dry run (no DB writes)',
'force' => 'Ignore lock and run anyway',
'tz' => 'Timezone (default: configured school timezone)',
];
/** @var ConfigurationModel */
protected $configModel;
/** Map of available task names -> handler methods. */
protected array $tasks = [
'update_date_age_reference' => 'taskUpdateDateAgeReference',
'enable_attendance_on' => 'taskEnableAttendanceOn',
'enable_attendance_off' => 'taskEnableAttendanceOff',
'set_semester_spring' => 'taskSetSemesterSpring',
'set_semester_fall' => 'taskSetSemesterFall',
];
/**
* Set enable_attendance = 1
*/
protected function taskEnableAttendanceOn(bool $dry, DateTimeZone $tz): bool
{
return $this->setConfig('enable_attendance', '1', $dry);
}
/**
* Set enable_attendance = 0
*/
protected function taskEnableAttendanceOff(bool $dry, DateTimeZone $tz): bool
{
return $this->setConfig('enable_attendance', '0', $dry);
}
protected function taskUpdateDateAgeReference(bool $dry, DateTimeZone $tz): bool
{
$now = new DateTime('now', $tz);
$isJune1 = ($now->format('n') === '6' && $now->format('j') === '1');
$forced = (CLI::getOption('force') !== null);
if (!$isJune1 && !$forced) {
CLI::write(
"Today is {$now->format('Y-m-d')} (not June 1) — skipping. Use --force to override.",
'yellow'
);
return true; // no-op, not an error
}
$year = (int) $now->format('Y');
$value = sprintf('%04d-12-31', $year);
CLI::write("Set date_age_reference = {$value}" . ($dry ? ' [DRY]' : ''), 'light_gray');
if ($dry) return true;
// Use your model method
$ok = (bool) $this->configModel->setConfigValueByKey('date_age_reference', $value);
if ($ok) {
CLI::write("date_age_reference updated to {$value}", 'green');
} else {
CLI::error("Failed to update date_age_reference");
}
return $ok;
}
protected function taskSetSemesterSpring(bool $dry, DateTimeZone $tz): bool
{
$now = new DateTime('now', $tz);
$isFeb1 = ($now->format('n') === '2' && $now->format('j') === '1');
$forced = (CLI::getOption('force') !== null);
if (!$isFeb1 && !$forced) {
CLI::write(
"Today is {$now->format('Y-m-d')} (not Feb 1) — skipping. Use --force to override.",
'yellow'
);
return true; // no-op is success
}
CLI::write("Set semester = Spring" . ($dry ? ' [DRY]' : ''), 'light_gray');
if ($dry) return true;
return (bool) $this->configModel->setConfigValueByKey('semester', 'Spring');
}
protected function taskSetSemesterFall(bool $dry, DateTimeZone $tz): bool
{
$now = new DateTime('now', $tz);
$isJun1 = ($now->format('n') === '6' && $now->format('j') === '1');
$forced = (CLI::getOption('force') !== null);
if (!$isJun1 && !$forced) {
CLI::write(
"Today is {$now->format('Y-m-d')} (not Jun 1) — skipping. Use --force to override.",
'yellow'
);
return true;
}
CLI::write("Set semester = Fall" . ($dry ? ' [DRY]' : ''), 'light_gray');
if ($dry) return true;
return (bool) $this->configModel->setConfigValueByKey('semester', 'Fall');
}
public function run(array $params)
{
$this->configModel = model(ConfigurationModel::class);
$tz = $this->getOptionString('tz', $params) ?? ((string)(config('School')->attendance['timezone'] ?? 'UTC'));
$task = $this->getOptionString('task', $params, 't');
// Fallback: first positional arg (php spark config:update enable_attendance_on)
if (!$task && !empty($params) && strpos($params[0], '-') !== 0) {
$task = trim($params[0]);
}
$dry = $this->hasFlag('dry', $params);
$force = $this->hasFlag('force', $params);
$dtz = new DateTimeZone($tz);
if ($task === '' || !isset($this->tasks[$task])) {
CLI::error('Invalid or missing --task. Available: ' . implode(', ', array_keys($this->tasks)));
return;
}
// Per-task lock
$lockFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "ci4_config_update_{$task}.lock";
$fp = @fopen($lockFile, 'c+');
if (!$fp) {
CLI::error("Unable to open lock file: $lockFile");
return;
}
if (!$force && !flock($fp, LOCK_EX | LOCK_NB)) {
CLI::error("Task '$task' is already running (lock held). Use --force to override.");
fclose($fp);
return;
}
try {
$method = $this->tasks[$task];
CLI::write("Running task: {$task}" . ($dry ? ' [DRY RUN]' : ''), 'yellow');
$ok = $this->{$method}($dry, $dtz);
if ($ok === true) {
CLI::write("Task '{$task}' finished successfully.", 'green');
} else {
CLI::error("Task '{$task}' finished with warnings or no changes.");
}
} catch (\Throwable $e) {
CLI::error("Task '{$task}' failed: " . $e->getMessage());
} finally {
try {
flock($fp, LOCK_UN);
fclose($fp);
@unlink($lockFile);
} catch (\Throwable $e) {
}
}
}
/** Accepts --name=value, --name value, -s value, or scans $params. */
private function getOptionString(string $name, array $params, ?string $short = null): ?string
{
$v = CLI::getOption($name);
if (is_string($v) && $v !== '') return trim($v);
if ($short) {
$v = CLI::getOption($short);
if (is_string($v) && $v !== '') return trim($v);
}
foreach ($params as $i => $p) {
if (strpos($p, "--{$name}=") === 0) return trim(substr($p, strlen($name) + 3));
if ($short && strpos($p, "-{$short}=") === 0) return trim(substr($p, strlen($short) + 2));
if ($p === "--{$name}" || ($short && $p === "-{$short}")) {
return $params[$i + 1] ?? null;
}
}
return null;
}
/** True if flag present as --name or -s (no value needed). */
private function hasFlag(string $name, array $params, ?string $short = null): bool
{
if (CLI::getOption($name) !== null) return true;
if ($short && CLI::getOption($short) !== null) return true;
foreach ($params as $p) {
if ($p === "--{$name}" || ($short && $p === "-{$short}")) return true;
}
return false;
}
/* ------------------------- Helpers ------------------------- */
protected function setConfig(string $key, string $value, bool $dry): bool
{
// show current value
$current = $this->configModel->where('config_key', $key)->first()['config_value'] ?? '<NULL>';
CLI::write("{$key}: current={$current}", 'light_gray');
CLI::write("Set {$key} = {$value}" . ($dry ? ' [DRY]' : ''), 'light_gray');
if ($dry) return true;
// ✅ use your model function
$ok = (bool) $this->configModel->setConfigValueByKey($key, $value);
// read-back to verify whats persisted
$after = $this->configModel->where('config_key', $key)->first()['config_value'] ?? '<NULL>';
CLI::write("{$key}: after={$after}", $ok ? 'green' : 'red');
return $ok && ($after === $value);
}
}