Split glass report into three tables

This commit is contained in:
Dwi Swandhana
2026-04-20 05:15:17 +07:00
parent f301de8cb5
commit 5c5bf0ce19
4 changed files with 454 additions and 79 deletions
+322 -34
View File
@@ -549,6 +549,298 @@ class ReportController extends Controller
->pluck('rekapantibiotik.antibiotic')
->toArray();
}
private function getGlassReportHeaders() {
$tableC = [
'ID Rumah Sakit',
'Nama Pasien',
'No Rekam Medis',
'Jenis Kelamin',
'Tanggal Lahir',
'Usia',
'Ruang Rawat',
'Tanggal Pasien Masuk',
'Tanggal Pengambilan Spesimen',
'Specimen Origin',
'Jenis Spesimen',
];
$tableAAntibiotics = [
'Cefoxitin',
'Oxacillin',
'Penicillin',
'Ampicillin',
'Amoxicillin',
'Cefazolin',
'Cefuroxime',
'Cefixime',
'Cefotaxime',
'Ceftriaxone',
'Ceftazidime',
'Cefepime',
'Ceftaroline',
'Ertapenem',
'Imipenem',
'Meropenem',
'Aztreonam',
'Amoxicillin-clavulanate',
'Ampicillin-sulbactam',
'Piperacillin-tazobactam',
'Ceftazidime-avibactam',
'Gentamicin',
'Amikacin',
'Ciprofloxacin',
'Levofloxacin',
'Moxifloxacin',
'Trimethoprim-sulfamethoxazole',
'Azithromycin',
'Clarithromycin',
'Erythromycin',
'Clindamycin',
'Tetracycline',
'Minocycline',
'Doxycycline',
'Colistin',
'Vancomycin',
'Linezolid',
'Tigecycline',
'Daptomycin',
'Nitrofurantoin',
'Fosfomycin',
];
$tableBAntibiotics = [
'Fluconazole',
'Voriconazole',
'Amphotericin B',
'Micafungin',
'Caspofungin',
'Flucytosine',
];
return [
'A' => array_merge($tableC, ['Nama Spesies Bakteri', 'ESBL', 'MRSA'], $tableAAntibiotics),
'B' => array_merge($tableC, ['Nama Spesies Jamur'], $tableBAntibiotics),
'C' => $tableC,
'A_antibiotics' => $tableAAntibiotics,
'B_antibiotics' => $tableBAntibiotics,
];
}
private function getGlassReportAntibioticAliases() {
return [
'Cefoxitin' => ['cefoxitin', 'cefoxitinscreen'],
'Oxacillin' => ['oxacillin'],
'Penicillin' => ['penicillin', 'benzylpenicillin'],
'Ampicillin' => ['ampicillin'],
'Amoxicillin' => ['amoxicillin'],
'Cefazolin' => ['cefazolin', 'cefazolinurine', 'cefazolinother'],
'Cefuroxime' => ['cefuroxime', 'cefuroximeiv', 'cefuroximeoral'],
'Cefixime' => ['cefixime'],
'Cefotaxime' => ['cefotaxime'],
'Ceftriaxone' => ['ceftriaxone'],
'Ceftazidime' => ['ceftazidime'],
'Cefepime' => ['cefepime'],
'Ceftaroline' => ['ceftaroline'],
'Ertapenem' => ['ertapenem'],
'Imipenem' => ['imipenem'],
'Meropenem' => ['meropenem'],
'Aztreonam' => ['aztreonam'],
'Amoxicillin-clavulanate' => ['amoxicillinclavulanate', 'amoxicillinclavulanicacid'],
'Ampicillin-sulbactam' => ['ampicillinsulbactam'],
'Piperacillin-tazobactam' => ['piperacillintazobactam'],
'Ceftazidime-avibactam' => ['ceftazidimeavibactam'],
'Gentamicin' => ['gentamicin'],
'Amikacin' => ['amikacin'],
'Ciprofloxacin' => ['ciprofloxacin'],
'Levofloxacin' => ['levofloxacin'],
'Moxifloxacin' => ['moxifloxacin'],
'Trimethoprim-sulfamethoxazole' => ['trimethoprimsulfamethoxazole', 'trimetrophimesulfamethoxazole'],
'Azithromycin' => ['azithromycin'],
'Clarithromycin' => ['clarithromycin'],
'Erythromycin' => ['erythromycin'],
'Clindamycin' => ['clindamycin'],
'Tetracycline' => ['tetracycline', 'tetracyclinescreenonly', 'tetracyclineu'],
'Minocycline' => ['minocycline'],
'Doxycycline' => ['doxycycline'],
'Colistin' => ['colistin'],
'Vancomycin' => ['vancomycin'],
'Linezolid' => ['linezolid'],
'Tigecycline' => ['tigecycline'],
'Daptomycin' => ['daptomycin'],
'Nitrofurantoin' => ['nitrofurantoin'],
'Fosfomycin' => ['fosfomycin'],
'Fluconazole' => ['fluconazole'],
'Voriconazole' => ['voriconazole'],
'Amphotericin B' => ['amphotericinb'],
'Micafungin' => ['micafungin'],
'Caspofungin' => ['caspofungin'],
'Flucytosine' => ['flucytosine'],
];
}
private function normalizeGlassReportValue($value) {
return preg_replace('/[^a-z0-9]+/', '', strtolower(strip_tags((string) $value)));
}
private function pickGlassReportResistanceValue($row, $header) {
$rawResistance = $row->resistence ?? $row->resistance ?? null;
$rawInterpretation = $row->interpretation ?? null;
$resistance = trim((string) $rawResistance);
$interpretation = trim((string) $rawInterpretation);
if ($resistance === '') {
return $interpretation;
}
$normalizedResistance = $this->normalizeGlassReportValue($resistance);
$normalizedAntibiotic = $this->normalizeGlassReportValue($row->antibiotic ?? '');
$normalizedHeader = $this->normalizeGlassReportValue($header);
if ($normalizedResistance === $normalizedAntibiotic || $normalizedResistance === $normalizedHeader) {
return $interpretation;
}
return $resistance;
}
private function getGlassReportOrigin($row) {
if (empty($row->daftar) || empty($row->mulai)) {
return '';
}
try {
$selisihHari = Carbon::parse($row->daftar)->diffInDays(Carbon::parse($row->mulai));
return $selisihHari >= 3 ? 'Hospital Origin' : 'Community Origin';
} catch (\Throwable $e) {
return '';
}
}
private function buildGlassReportBaseRow($row) {
$jenisSpesimen = trim(implode(' ', array_filter([
$row->kd_spesimen ?? '',
$row->nm_spesimen ?? '',
])));
return [
$row->nmrs ?? '',
$row->nmpasien ?? '',
$row->noregister ?? '',
$row->jkpasien ?? '',
$row->tgllahirpasien ?? '',
$row->usia ?? '',
$row->asalpasien ?? '',
$row->daftar ?? '',
$row->mulai ?? '',
$this->getGlassReportOrigin($row),
$jenisSpesimen,
];
}
private function getGlassReportLookups($rows) {
$orderIds = $rows->pluck('id')->filter()->values()->all();
$accnumbers = $rows->pluck('nofoto')->filter()->values()->all();
$komponen = DB::table('db_komponenjawaban')
->select('accnumber', 'komponen', 'isidata')
->whereIn('accnumber', $accnumbers)
->whereIn('komponen', ['bakteri', 'id_bakteri01', 'id_bakteri02', 'id_mikroorganisme'])
->whereNotNull('isidata')
->where('isidata', '!=', '')
->get()
->groupBy('accnumber');
$antibiotik = RekapAntibiotik::query()
->whereIn('orderid', $orderIds)
->get(['orderid', 'antibiotic', 'resistance', 'interpretation'])
->groupBy('orderid');
return [
'komponen' => $komponen,
'antibiotik' => $antibiotik,
];
}
private function splitGlassReportRows($rows, $lookups) {
$headers = $this->getGlassReportHeaders();
$aliases = $this->getGlassReportAntibioticAliases();
$tables = ['A' => [], 'B' => [], 'C' => []];
foreach ($rows as $row) {
$baseRow = $this->buildGlassReportBaseRow($row);
$komponenRows = $lookups['komponen'][$row->nofoto] ?? collect([]);
$antibioticRows = $lookups['antibiotik'][$row->id] ?? collect([]);
$bacterialName = '';
$fungalName = '';
$foundMorphology = false;
foreach ($komponenRows as $komponenRow) {
$value = trim(strip_tags((string) $komponenRow->isidata));
if ($value === '') {
continue;
}
if (stripos($value, 'Ditemukan morfologi') !== false) {
$foundMorphology = true;
}
if (in_array($komponenRow->komponen, ['bakteri', 'id_bakteri01', 'id_bakteri02'], true) && $bacterialName === '' && stripos($value, 'Ditemukan morfologi') === false) {
$bacterialName = $value;
}
if ($komponenRow->komponen === 'id_mikroorganisme' && $fungalName === '') {
$fungalName = $value;
}
}
$antibioticValues = [];
foreach ($antibioticRows as $antibioticRow) {
$matchedHeader = null;
$normalizedAntibiotic = $this->normalizeGlassReportValue($antibioticRow->antibiotic ?? '');
$normalizedResistance = $this->normalizeGlassReportValue($antibioticRow->resistance ?? '');
foreach ($aliases as $header => $variants) {
if (in_array($normalizedAntibiotic, $variants, true) || in_array($normalizedResistance, $variants, true)) {
$matchedHeader = $header;
break;
}
}
if ($matchedHeader === null || isset($antibioticValues[$matchedHeader])) {
continue;
}
$antibioticValues[$matchedHeader] = $this->pickGlassReportResistanceValue($antibioticRow, $matchedHeader);
}
$hasJenisSpesimen = trim((string) ($baseRow[10] ?? '')) !== '';
if ($bacterialName !== '' && $hasJenisSpesimen) {
$tableRow = array_merge($baseRow, [
$bacterialName,
$row->id_esbl ?? '',
$row->id_mrsa ?? '',
]);
foreach ($headers['A_antibiotics'] as $antibioticHeader) {
$tableRow[] = $antibioticValues[$antibioticHeader] ?? '';
}
$tables['A'][] = $tableRow;
continue;
}
if ($foundMorphology || $fungalName !== '') {
$tableRow = array_merge($baseRow, [
$fungalName !== '' ? $fungalName : 'Ditemukan morfologi',
]);
foreach ($headers['B_antibiotics'] as $antibioticHeader) {
$tableRow[] = $antibioticValues[$antibioticHeader] ?? '';
}
$tables['B'][] = $tableRow;
continue;
}
$tables['C'][] = $baseRow;
}
return $tables;
}
public function genGlassReport(Request $request) {
$bulan = $request->input('bulan');
$tahun = $request->input('tahun');
@@ -574,11 +866,16 @@ class ReportController extends Controller
// 2. Ambil Data Antibiotik HANYA untuk 50 pasien tersebut
$pageIds = $orderbydate->pluck('id')->toArray();
$antibiotikLookup = $this->mapAntibiotikData($pageIds);
$glassLookups = $this->getGlassReportLookups($orderbydate->getCollection());
$glassTables = $this->splitGlassReportRows($orderbydate->getCollection(), $glassLookups);
$glassHeaders = $this->getGlassReportHeaders();
return view('admin.glassreport', [
'orderbydate' => $orderbydate,
'antibiotikLookup' => $antibiotikLookup, // Kirim array hasil mapping
'jsonantibiotik' => $this->listAntibiotik,
'glassTables' => $glassTables,
'glassHeaders' => $glassHeaders,
'bulan' => $bulan,
'tahun' => $tahun
]);
@@ -600,10 +897,12 @@ class ReportController extends Controller
$response = new StreamedResponse(function() use ($bulan, $tahun) {
$handle = fopen('php://output', 'w');
// Header CSV
$staticHeader = ['ID RS', 'Nama Pasien', 'No RM', 'JK', 'Tgl Lahir', 'Usia', 'Ruang', 'Tgl Masuk', 'Tgl Sample', 'Origin', 'Jenis Spesimen', 'Spesies Bakteri', 'ESBL', 'MRSA'];
fputcsv($handle, array_merge($staticHeader, $this->listAntibiotik));
$headers = $this->getGlassReportHeaders();
$tables = [
'A' => [],
'B' => [],
'C' => [],
];
// Query Tanpa Get() tapi Chunk()
$query = Periksa::query()->whereYear('daftar', $tahun);
@@ -612,39 +911,28 @@ class ReportController extends Controller
}
// Proses per 500 data agar RAM stabil
$query->chunk(500, function($rows) use ($handle) {
// Mapping Antibiotik untuk batch 500 ini saja
$chunkIds = $rows->pluck('id')->toArray();
$antibiotikLookup = $this->mapAntibiotikData($chunkIds);
$query->orderBy('id')->chunk(500, function($rows) use (&$tables) {
$lookups = $this->getGlassReportLookups($rows);
$chunkTables = $this->splitGlassReportRows($rows, $lookups);
foreach ($rows as $row) {
$csvRow = [
$row->id_rs ?? '', // Sesuaikan nama kolom
$row->nmpasien,
$row->noregister,
$row->jkpasien,
$row->tgllahirpasien,
$row->usia,
$row->asalpasien,
$row->mulai,
$row->daftar,
'', // Origin
$row->kd_spesimen,
$row->nm_spesimen, // Asumsi nama bakteri disini atau kolom lain
$row->id_esbl,
$row->id_mrsa
];
foreach ($this->listAntibiotik as $headerAntibiotik) {
$nilai = $antibiotikLookup[$row->id][$headerAntibiotik] ?? '';
$csvRow[] = $nilai;
}
fputcsv($handle, $csvRow);
}
foreach (['A', 'B', 'C'] as $tableKey) {
foreach ($chunkTables[$tableKey] as $tableRow) {
$tables[$tableKey][] = $tableRow;
}
}
});
foreach (['A', 'B', 'C'] as $tableKey) {
fputcsv($handle, ['TABEL '.$tableKey]);
fputcsv($handle, $headers[$tableKey]);
foreach ($tables[$tableKey] as $tableRow) {
fputcsv($handle, $tableRow);
}
fputcsv($handle, []);
}
fclose($handle);
});