DONE: add data injection, update ui on login

This commit is contained in:
ISMAIL MASSERAN
2026-05-13 16:41:20 +08:00
parent 6730a3c40e
commit 65045d6935
9 changed files with 1613 additions and 247 deletions
@@ -0,0 +1,158 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class UpdateVoterPelaburanFromJson extends Command
{
protected $signature = 'voter:update-pelaburan-from-json
{--file=database/seeds/inject/list-pelabur.json : Relative path from project root}
{--election_id= : Override election_id (default: latest in voter table)}
{--dry-run : Show what would change without updating}';
protected $description = 'Update voter pelaburan (dividen) from list-pelabur.json for latest election only';
public function handle()
{
$fileOpt = (string) $this->option('file');
$filePath = $this->resolvePath($fileOpt);
if (!is_file($filePath)) {
$this->error("❌ JSON file not found: {$filePath}");
return 1;
}
$electionId = $this->option('election_id');
$electionId = $electionId !== null && $electionId !== '' ? (int) $electionId : (int) DB::table('voter')->max('election_id');
if ($electionId <= 0) {
$this->error("❌ Cannot determine latest election_id (got {$electionId}).");
return 1;
}
$json = file_get_contents($filePath);
$rows = json_decode($json, true);
if (!is_array($rows)) {
$this->error("❌ Invalid JSON: expected array of rows.");
return 1;
}
$map = [];
foreach ($rows as $row) {
if (!is_array($row)) {
continue;
}
$noAnggota = isset($row['no_anggota']) ? trim((string) $row['no_anggota']) : '';
if ($noAnggota === '') {
continue;
}
$pelaburanRaw = $row['pelaburan'] ?? 0;
$map[$noAnggota] = $this->cleanNumber($pelaburanRaw);
}
if (count($map) === 0) {
$this->error("❌ No usable rows found in JSON (missing no_anggota?).");
return 1;
}
$dryRun = (bool) $this->option('dry-run');
$this->info("Target election_id: {$electionId}");
$this->info("JSON members loaded: " . count($map));
$this->info($dryRun ? "Mode: DRY RUN (no DB updates)" : "Mode: UPDATE");
$totalInElection = (int) DB::table('voter')->where('election_id', $electionId)->count();
$this->info("Voters in election: {$totalInElection}");
$updated = 0;
$matched = 0;
$missingInJson = 0;
$unchanged = 0;
$bar = $this->output->createProgressBar(max(1, $totalInElection));
$bar->start();
DB::table('voter')
->select(['id', 'no_anggota', 'pelaburan'])
->where('election_id', $electionId)
->orderBy('id')
->chunkById(500, function ($chunk) use ($map, $dryRun, &$updated, &$matched, &$missingInJson, &$unchanged, $bar) {
foreach ($chunk as $voter) {
$noAnggota = $voter->no_anggota !== null ? trim((string) $voter->no_anggota) : '';
if ($noAnggota === '' || !array_key_exists($noAnggota, $map)) {
$missingInJson++;
$bar->advance();
continue;
}
$matched++;
$newPelaburan = $map[$noAnggota];
$oldPelaburan = $voter->pelaburan !== null ? (float) $voter->pelaburan : 0.0;
if (abs($oldPelaburan - $newPelaburan) < 0.00001) {
$unchanged++;
$bar->advance();
continue;
}
if (!$dryRun) {
DB::table('voter')
->where('id', $voter->id)
->update([
'pelaburan' => $newPelaburan,
'updated_at' => now(),
]);
}
$updated++;
$bar->advance();
}
}, 'id');
$bar->finish();
$this->line('');
$this->info("✅ Matched in JSON: {$matched}");
$this->info("✅ Updated: {$updated}" . ($dryRun ? " (would update)" : ""));
$this->info("✅ Unchanged: {$unchanged}");
$this->info("⚠️ Missing in JSON: {$missingInJson}");
return 0;
}
private function resolvePath(string $path): string
{
// absolute path
if (strpos($path, DIRECTORY_SEPARATOR) === 0) {
return $path;
}
return base_path($path);
}
private function cleanNumber($value): float
{
if ($value === null) {
return 0.0;
}
$value = (string) $value;
$value = str_replace(['RM', ',', ' '], '', $value);
if (preg_match('/^\((.*)\)$/', $value, $matches)) {
$value = $matches[1];
}
$value = preg_replace('/[^0-9\.\-]/', '', $value);
return is_numeric($value) ? (float) $value : 0.0;
}
}
+1
View File
@@ -15,6 +15,7 @@ class Kernel extends ConsoleKernel
protected $commands = [
//
Commands\ImportCSV::class,
Commands\UpdateVoterPelaburanFromJson::class,
];
/**