devsofttech_hms_ci4/app/Commands/NormalizeOpdAutocompleteText.php

209 lines
7.1 KiB
PHP

<?php
namespace App\Commands;
use CodeIgniter\CLI\BaseCommand;
use CodeIgniter\CLI\CLI;
use Config\Database;
class NormalizeOpdAutocompleteText extends BaseCommand
{
protected $group = 'Maintenance';
protected $name = 'opd:normalize-autocomplete-text';
protected $description = 'One-time cleanup: normalize autocomplete text in OPD prescription and keyword master tables.';
protected $usage = 'opd:normalize-autocomplete-text [--apply] [--batch 500]';
protected $options = [
'--apply' => 'Apply updates. If omitted, command runs in dry-run mode only.',
'--batch' => 'Batch size for scan/update loop (default: 500).',
];
public function run(array $params)
{
$db = Database::connect();
$apply = CLI::getOption('apply') !== null;
$batchSize = max(100, (int) (CLI::getOption('batch') ?? 500));
CLI::write('OPD autocomplete text normalization', 'yellow');
CLI::write('Mode: ' . ($apply ? 'APPLY' : 'DRY-RUN'), $apply ? 'light_green' : 'light_yellow');
CLI::write('Batch size: ' . $batchSize, 'cyan');
$summary = [
'scanned' => 0,
'changed' => 0,
'updated' => 0,
'tables' => 0,
];
$summary = $this->processPrescriptionTable($db, $apply, $batchSize, $summary);
$summary = $this->processKeywordTable($db, $apply, $batchSize, $summary);
CLI::newLine();
CLI::write('Summary', 'yellow');
CLI::write('Tables processed: ' . $summary['tables']);
CLI::write('Rows scanned: ' . $summary['scanned']);
CLI::write('Rows with normalized changes: ' . $summary['changed']);
CLI::write('Rows updated: ' . $summary['updated'], $summary['updated'] > 0 ? 'light_green' : 'white');
if (! $apply) {
CLI::newLine();
CLI::write('No DB rows were modified in dry-run mode.', 'light_yellow');
CLI::write('Run again with --apply to persist changes.', 'light_yellow');
}
}
private function processPrescriptionTable($db, bool $apply, int $batchSize, array $summary): array
{
$table = 'opd_prescription';
if (! $db->tableExists($table)) {
CLI::write('Skipping ' . $table . ': table not found.', 'light_gray');
return $summary;
}
$fields = $db->getFieldNames($table) ?? [];
$idField = in_array('id', $fields, true) ? 'id' : null;
if ($idField === null) {
CLI::write('Skipping ' . $table . ': id column not found.', 'red');
return $summary;
}
$targets = array_values(array_intersect(
['Finding_Examinations', 'complaints', 'Complaint', 'provisional_diagnosis', 'Provisional_diagnosis'],
$fields
));
if ($targets === []) {
CLI::write('Skipping ' . $table . ': no target text columns found.', 'light_gray');
return $summary;
}
CLI::newLine();
CLI::write('Processing ' . $table . ' columns: ' . implode(', ', $targets), 'cyan');
$summary['tables']++;
$offset = 0;
while (true) {
$rows = $db->table($table)
->select(implode(', ', array_merge([$idField], $targets)))
->orderBy($idField, 'ASC')
->limit($batchSize, $offset)
->get()
->getResultArray();
if ($rows === []) {
break;
}
$offset += count($rows);
foreach ($rows as $row) {
$summary['scanned']++;
$update = [];
foreach ($targets as $column) {
$original = (string) ($row[$column] ?? '');
if ($original === '') {
continue;
}
$normalized = $this->normalizeAutocompleteText($original);
if ($normalized !== '' && $normalized !== $original) {
$update[$column] = $normalized;
}
}
if ($update === []) {
continue;
}
$summary['changed']++;
if ($apply) {
$db->table($table)->where($idField, $row[$idField])->update($update);
$summary['updated']++;
}
}
}
return $summary;
}
private function processKeywordTable($db, bool $apply, int $batchSize, array $summary): array
{
$table = 'opd_autotype_keyword_master';
if (! $db->tableExists($table)) {
CLI::write('Skipping ' . $table . ': table not found.', 'light_gray');
return $summary;
}
$fields = $db->getFieldNames($table) ?? [];
if (! in_array('id', $fields, true) || ! in_array('keyword', $fields, true)) {
CLI::write('Skipping ' . $table . ': required columns id/keyword not found.', 'red');
return $summary;
}
$hasSection = in_array('section_key', $fields, true);
$sections = ['complaints', 'provisional_diagnosis', 'finding_examinations'];
CLI::newLine();
CLI::write('Processing ' . $table . ' keyword column' . ($hasSection ? ' for selected sections only.' : '.'), 'cyan');
$summary['tables']++;
$offset = 0;
while (true) {
$builder = $db->table($table)->select($hasSection ? 'id, keyword, section_key' : 'id, keyword');
if ($hasSection) {
$builder->whereIn('section_key', $sections);
}
$rows = $builder
->orderBy('id', 'ASC')
->limit($batchSize, $offset)
->get()
->getResultArray();
if ($rows === []) {
break;
}
$offset += count($rows);
foreach ($rows as $row) {
$summary['scanned']++;
$original = (string) ($row['keyword'] ?? '');
if ($original === '') {
continue;
}
$normalized = $this->normalizeAutocompleteText($original);
if ($normalized === '' || $normalized === $original) {
continue;
}
$summary['changed']++;
if ($apply) {
$db->table($table)->where('id', $row['id'])->update(['keyword' => $normalized]);
$summary['updated']++;
}
}
}
return $summary;
}
private function normalizeAutocompleteText(string $text): string
{
$text = str_replace(["\r\n", "\r", "\n"], ' ', $text);
$text = preg_replace('/\s+/', ' ', $text) ?? $text;
$parts = preg_split('/\s*,\s*/', $text) ?: [];
$clean = [];
foreach ($parts as $part) {
$part = trim((string) $part);
if ($part === '') {
continue;
}
$clean[] = $part;
}
$normalized = implode(', ', $clean);
return rtrim(trim($normalized), ', ');
}
}