Split glass report into three tables
This commit is contained in:
No files matched your search
@@ -549,6 +549,298 @@ class ReportController extends Controller
|
|||||||
->pluck('rekapantibiotik.antibiotic')
|
->pluck('rekapantibiotik.antibiotic')
|
||||||
->toArray();
|
->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) {
|
public function genGlassReport(Request $request) {
|
||||||
$bulan = $request->input('bulan');
|
$bulan = $request->input('bulan');
|
||||||
$tahun = $request->input('tahun');
|
$tahun = $request->input('tahun');
|
||||||
@@ -574,11 +866,16 @@ class ReportController extends Controller
|
|||||||
// 2. Ambil Data Antibiotik HANYA untuk 50 pasien tersebut
|
// 2. Ambil Data Antibiotik HANYA untuk 50 pasien tersebut
|
||||||
$pageIds = $orderbydate->pluck('id')->toArray();
|
$pageIds = $orderbydate->pluck('id')->toArray();
|
||||||
$antibiotikLookup = $this->mapAntibiotikData($pageIds);
|
$antibiotikLookup = $this->mapAntibiotikData($pageIds);
|
||||||
|
$glassLookups = $this->getGlassReportLookups($orderbydate->getCollection());
|
||||||
|
$glassTables = $this->splitGlassReportRows($orderbydate->getCollection(), $glassLookups);
|
||||||
|
$glassHeaders = $this->getGlassReportHeaders();
|
||||||
|
|
||||||
return view('admin.glassreport', [
|
return view('admin.glassreport', [
|
||||||
'orderbydate' => $orderbydate,
|
'orderbydate' => $orderbydate,
|
||||||
'antibiotikLookup' => $antibiotikLookup, // Kirim array hasil mapping
|
'antibiotikLookup' => $antibiotikLookup, // Kirim array hasil mapping
|
||||||
'jsonantibiotik' => $this->listAntibiotik,
|
'jsonantibiotik' => $this->listAntibiotik,
|
||||||
|
'glassTables' => $glassTables,
|
||||||
|
'glassHeaders' => $glassHeaders,
|
||||||
'bulan' => $bulan,
|
'bulan' => $bulan,
|
||||||
'tahun' => $tahun
|
'tahun' => $tahun
|
||||||
]);
|
]);
|
||||||
@@ -600,10 +897,12 @@ class ReportController extends Controller
|
|||||||
|
|
||||||
$response = new StreamedResponse(function() use ($bulan, $tahun) {
|
$response = new StreamedResponse(function() use ($bulan, $tahun) {
|
||||||
$handle = fopen('php://output', 'w');
|
$handle = fopen('php://output', 'w');
|
||||||
|
$headers = $this->getGlassReportHeaders();
|
||||||
// Header CSV
|
$tables = [
|
||||||
$staticHeader = ['ID RS', 'Nama Pasien', 'No RM', 'JK', 'Tgl Lahir', 'Usia', 'Ruang', 'Tgl Masuk', 'Tgl Sample', 'Origin', 'Jenis Spesimen', 'Spesies Bakteri', 'ESBL', 'MRSA'];
|
'A' => [],
|
||||||
fputcsv($handle, array_merge($staticHeader, $this->listAntibiotik));
|
'B' => [],
|
||||||
|
'C' => [],
|
||||||
|
];
|
||||||
|
|
||||||
// Query Tanpa Get() tapi Chunk()
|
// Query Tanpa Get() tapi Chunk()
|
||||||
$query = Periksa::query()->whereYear('daftar', $tahun);
|
$query = Periksa::query()->whereYear('daftar', $tahun);
|
||||||
@@ -612,39 +911,28 @@ class ReportController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Proses per 500 data agar RAM stabil
|
// Proses per 500 data agar RAM stabil
|
||||||
$query->chunk(500, function($rows) use ($handle) {
|
$query->orderBy('id')->chunk(500, function($rows) use (&$tables) {
|
||||||
|
$lookups = $this->getGlassReportLookups($rows);
|
||||||
// Mapping Antibiotik untuk batch 500 ini saja
|
$chunkTables = $this->splitGlassReportRows($rows, $lookups);
|
||||||
$chunkIds = $rows->pluck('id')->toArray();
|
|
||||||
$antibiotikLookup = $this->mapAntibiotikData($chunkIds);
|
|
||||||
|
|
||||||
foreach ($rows as $row) {
|
foreach (['A', 'B', 'C'] as $tableKey) {
|
||||||
$csvRow = [
|
foreach ($chunkTables[$tableKey] as $tableRow) {
|
||||||
$row->id_rs ?? '', // Sesuaikan nama kolom
|
$tables[$tableKey][] = $tableRow;
|
||||||
$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) {
|
||||||
|
fputcsv($handle, ['TABEL '.$tableKey]);
|
||||||
|
fputcsv($handle, $headers[$tableKey]);
|
||||||
|
|
||||||
|
foreach ($tables[$tableKey] as $tableRow) {
|
||||||
|
fputcsv($handle, $tableRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
fputcsv($handle, []);
|
||||||
|
}
|
||||||
|
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,52 +9,41 @@
|
|||||||
<div class="ribbon ribbon-danger">Rekapitulasi Data Bulan {{$bulan}} Tahun {{$tahun}}</div>
|
<div class="ribbon ribbon-danger">Rekapitulasi Data Bulan {{$bulan}} Tahun {{$tahun}}</div>
|
||||||
<p></p>
|
<p></p>
|
||||||
<div class="mt-3 mb-3">
|
<div class="mt-3 mb-3">
|
||||||
<a href="{{ route('exportGlassReport', ['bulan' => $bulan, 'tahun' => $tahun]) }}" class="btn btn-success">
|
<a href="{{ route('exportGlassReport', ['bulan' => $bulan, 'tahun' => $tahun]) }}" class="btn btn-success">
|
||||||
<i class="fa fa-file-excel-o"></i> Download Full Excel (CSV)
|
<i class="fa fa-file-excel-o"></i> Download Full Excel (CSV)
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive">
|
@foreach (['A', 'B', 'C'] as $tableKey)
|
||||||
<table class="table table-bordered" id="datatable">
|
<div class="table-responsive" style="margin-bottom: 24px;">
|
||||||
<thead>
|
<h4>TABEL {{ $tableKey }}</h4>
|
||||||
<tr>
|
<table class="table table-bordered table-sm">
|
||||||
<th>Nama Pasien</th>
|
<thead>
|
||||||
<th>No RM</th>
|
|
||||||
<th>Bakteri</th>
|
|
||||||
@foreach($jsonantibiotik as $antibiotic)
|
|
||||||
<th title="{{ $antibiotic }}">{{ $antibiotic }}</th>
|
|
||||||
@endforeach
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@forelse($orderbydate as $data)
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ $data->nmpasien }}</td>
|
@foreach (($glassHeaders[$tableKey] ?? []) as $header)
|
||||||
<td>{{ $data->noregister }}</td>
|
<th>{{ $header }}</th>
|
||||||
<td>{{ $data->nm_spesimen }}</td> @foreach($jsonantibiotik as $headerAntibiotik)
|
|
||||||
@php
|
|
||||||
// LOGIC UTAMA:
|
|
||||||
// Cek di array lookup: [id_pasien][nama_antibiotik]
|
|
||||||
// Jika ada, ambil nilainya. Jika tidak, kosong.
|
|
||||||
$interpretasi = $antibiotikLookup[$data->id][$headerAntibiotik] ?? '';
|
|
||||||
@endphp
|
|
||||||
<td class="text-center" style="{{ $interpretasi == 'R' ? 'background-color:#ffcccc' : '' }}">
|
|
||||||
{{ $interpretasi }}
|
|
||||||
</td>
|
|
||||||
@endforeach
|
@endforeach
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
</thead>
|
||||||
<tr>
|
<tbody>
|
||||||
<td colspan="{{ count($jsonantibiotik) + 3 }}">Data tidak ditemukan</td>
|
@forelse (($glassTables[$tableKey] ?? []) as $row)
|
||||||
</tr>
|
<tr>
|
||||||
@endforelse
|
@foreach ($row as $cell)
|
||||||
</tbody>
|
<td>{{ $cell }}</td>
|
||||||
</table>
|
@endforeach
|
||||||
</div>
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="{{ count($glassHeaders[$tableKey] ?? []) }}">Data tidak ditemukan</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -629,7 +629,7 @@ $(document).ready(function () {
|
|||||||
type : 'error',
|
type : 'error',
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var url = '{{url('/')}}/glassreport?bulan='+bulan+'?tahun='+tahun;
|
var url = '{{url('/')}}/glassreport?bulan='+bulan+'&tahun='+tahun;
|
||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+105
-7
@@ -402,6 +402,27 @@ def create_genexpert_ack_r01_response(incoming_hl7):
|
|||||||
msa = f"MSA|CA|{incoming_control_id}"
|
msa = f"MSA|CA|{incoming_control_id}"
|
||||||
return f"{msh}\r{msa}\r"
|
return f"{msh}\r{msa}\r"
|
||||||
|
|
||||||
|
def format_hl7_date(value):
|
||||||
|
if not value:
|
||||||
|
return ""
|
||||||
|
if isinstance(value, datetime.datetime):
|
||||||
|
return value.strftime("%Y%m%d")
|
||||||
|
if isinstance(value, datetime.date):
|
||||||
|
return value.strftime("%Y%m%d")
|
||||||
|
text = str(value).strip()
|
||||||
|
digits = re.sub(r"[^0-9]", "", text)
|
||||||
|
return digits[:8] if len(digits) >= 8 else ""
|
||||||
|
|
||||||
|
def map_hl7_sex(value):
|
||||||
|
text = str(value or "").strip().upper()
|
||||||
|
if not text:
|
||||||
|
return ""
|
||||||
|
if text.startswith("L") or "LAKI" in text or text == "M":
|
||||||
|
return "M"
|
||||||
|
if text.startswith("P") or "PEREM" in text or text == "F":
|
||||||
|
return "F"
|
||||||
|
return ""
|
||||||
|
|
||||||
def create_genexpert_rsp_z02_response(orders, incoming_hl7, ip_addr=None):
|
def create_genexpert_rsp_z02_response(orders, incoming_hl7, ip_addr=None):
|
||||||
qpd_segment = extract_segment(incoming_hl7, "QPD")
|
qpd_segment = extract_segment(incoming_hl7, "QPD")
|
||||||
qpd = parse_genexpert_qpd(qpd_segment)
|
qpd = parse_genexpert_qpd(qpd_segment)
|
||||||
@@ -417,25 +438,45 @@ def create_genexpert_rsp_z02_response(orders, incoming_hl7, ip_addr=None):
|
|||||||
segments.append(qpd_segment)
|
segments.append(qpd_segment)
|
||||||
|
|
||||||
for patient_idx, order in enumerate(orders, start=1):
|
for patient_idx, order in enumerate(orders, start=1):
|
||||||
patient_id = str(order.norm or order.rnoreg or "").strip()
|
patient_id = sanitize_astm_field(order.norm or order.rnoreg, max_len=50)
|
||||||
sample_id = str(order.rnoreg or "").strip()
|
sample_id = sanitize_astm_field(order.rnoreg, max_len=50)
|
||||||
order_ts = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
order_ts = (
|
||||||
|
getattr(order, "rtglast", None).strftime('%Y%m%d%H%M%S')
|
||||||
|
if getattr(order, "rtglast", None)
|
||||||
|
else datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||||
|
)
|
||||||
assay_code, assay_source, capability_match = resolve_genexpert_assay(order, ip_addr)
|
assay_code, assay_source, capability_match = resolve_genexpert_assay(order, ip_addr)
|
||||||
if not assay_code:
|
if not assay_code:
|
||||||
print(f"[GENEXPERT] Payload order dilewati rnoreg={sample_id} karena assay kosong.")
|
print(f"[GENEXPERT] Payload order dilewati rnoreg={sample_id} karena assay kosong.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
first_name, last_name = split_patient_name(order.nama)
|
||||||
|
patient_name = sanitize_astm_field(f"{last_name}^{first_name}", uppercase=True, max_len=80, allow_component_sep=True)
|
||||||
|
dob = format_hl7_date(getattr(order, "tgllahir", None))
|
||||||
|
sex = map_hl7_sex(getattr(order, "rjenis", ""))
|
||||||
|
address = sanitize_astm_field(getattr(order, "alamat", ""), max_len=120)
|
||||||
|
room = sanitize_astm_field(getattr(order, "ruangan", ""), uppercase=True, max_len=80)
|
||||||
|
doctor = sanitize_astm_field(getattr(order, "namadok", ""), max_len=80)
|
||||||
|
requested_test_name = sanitize_astm_field(getattr(order, "tes", ""), max_len=120)
|
||||||
|
specimen_type = sanitize_astm_field(
|
||||||
|
getattr(order, "nm_spesimen", "") or getattr(order, "kd_spesimen", "") or "ORH",
|
||||||
|
uppercase=True,
|
||||||
|
max_len=40
|
||||||
|
)
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"[GENEXPERT-DEBUG] Build RSP rnoreg={sample_id}, ip={ip_addr}, "
|
f"[GENEXPERT-DEBUG] Build RSP rnoreg={sample_id}, ip={ip_addr}, "
|
||||||
f"patient_id={patient_id}, assay_code={assay_code}, assay_source={assay_source}, "
|
f"patient_id={patient_id}, assay_code={assay_code}, assay_source={assay_source}, "
|
||||||
f"capability_match={capability_match}, query_name='{query_name}', query_tag='{query_tag}'"
|
f"capability_match={capability_match}, patient_name='{patient_name}', dob='{dob}', "
|
||||||
|
f"sex='{sex}', room='{room}', doctor='{doctor}', specimen_type='{specimen_type}', "
|
||||||
|
f"requested_test_name='{requested_test_name}', query_name='{query_name}', query_tag='{query_tag}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
segments.append(f"PID|{patient_idx}||{patient_id}")
|
segments.append(f"PID|{patient_idx}||{patient_id}||{patient_name}||{dob}|{sex}|||{address}")
|
||||||
segments.append(f"ORC|NW|1|||||||{order_ts}")
|
segments.append(f"ORC|NW|1|||||||{order_ts}")
|
||||||
segments.append(f"OBR|1|||{assay_code}|||||||A")
|
segments.append(f"OBR|1|||{assay_code}^{requested_test_name}^L|||||||A")
|
||||||
segments.append("TQ1|||||||||R")
|
segments.append("TQ1|||||||||R")
|
||||||
segments.append(f"SPM|1|{sample_id}^||ORH|||||||P")
|
segments.append(f"SPM|1|{sample_id}^{sample_id}||{specimen_type}|||||||P")
|
||||||
|
|
||||||
return "\r".join(segments) + "\r"
|
return "\r".join(segments) + "\r"
|
||||||
|
|
||||||
@@ -456,6 +497,7 @@ def send_all_orders(conn, ip_addr, hl7_msg, msg_id, response_framing="mllp"):
|
|||||||
print(f"[GENEXPERT] Mengirim {len(orders)} order ke {ip_addr}")
|
print(f"[GENEXPERT] Mengirim {len(orders)} order ke {ip_addr}")
|
||||||
rsp = create_genexpert_rsp_z02_response(orders, hl7_msg, ip_addr=ip_addr)
|
rsp = create_genexpert_rsp_z02_response(orders, hl7_msg, ip_addr=ip_addr)
|
||||||
first_accnumber = str(orders[0].rnoreg or "").strip() if orders else ""
|
first_accnumber = str(orders[0].rnoreg or "").strip() if orders else ""
|
||||||
|
debug_genexpert_order_message(rsp, ip_addr=ip_addr)
|
||||||
send_genexpert_response(conn, ip_addr, rsp, response_framing, label=f"qbp-order:{first_accnumber}")
|
send_genexpert_response(conn, ip_addr, rsp, response_framing, label=f"qbp-order:{first_accnumber}")
|
||||||
|
|
||||||
for order in orders:
|
for order in orders:
|
||||||
@@ -553,6 +595,62 @@ def send_genexpert_response(conn, ip_addr, hl7_message, framing, label=""):
|
|||||||
)
|
)
|
||||||
conn.sendall(payload)
|
conn.sendall(payload)
|
||||||
|
|
||||||
|
def debug_genexpert_order_message(hl7_message, ip_addr=None):
|
||||||
|
segments = parse_hl7_segments(hl7_message)
|
||||||
|
current_pid = ""
|
||||||
|
current_patient_name = ""
|
||||||
|
order_index = 0
|
||||||
|
|
||||||
|
for segment in segments:
|
||||||
|
fields = segment.split("|")
|
||||||
|
seg_type = fields[0] if fields else ""
|
||||||
|
|
||||||
|
if seg_type == "PID":
|
||||||
|
current_pid = fields[3] if len(fields) > 3 else ""
|
||||||
|
current_patient_name = fields[5] if len(fields) > 5 else ""
|
||||||
|
dob = fields[7] if len(fields) > 7 else ""
|
||||||
|
sex = fields[8] if len(fields) > 8 else ""
|
||||||
|
address = fields[11] if len(fields) > 11 else ""
|
||||||
|
print(
|
||||||
|
f"[GENEXPERT-ORDER-DEBUG] ip={ip_addr}, seg=PID, patient_id='{current_pid}', "
|
||||||
|
f"patient_name='{current_patient_name}', dob='{dob}', sex='{sex}', "
|
||||||
|
f"address='{address}', raw='{segment}'"
|
||||||
|
)
|
||||||
|
elif seg_type == "ORC":
|
||||||
|
order_index = fields[2] if len(fields) > 2 else ""
|
||||||
|
order_time = fields[9] if len(fields) > 9 else ""
|
||||||
|
print(
|
||||||
|
f"[GENEXPERT-ORDER-DEBUG] ip={ip_addr}, seg=ORC, placer_order='{order_index}', "
|
||||||
|
f"order_time='{order_time}', patient_id='{current_pid}', "
|
||||||
|
f"patient_name='{current_patient_name}', raw='{segment}'"
|
||||||
|
)
|
||||||
|
elif seg_type == "OBR":
|
||||||
|
assay_code = fields[4] if len(fields) > 4 else ""
|
||||||
|
result_status = fields[11] if len(fields) > 11 else ""
|
||||||
|
assay_parts = assay_code.split("^")
|
||||||
|
assay_id = assay_parts[0] if len(assay_parts) > 0 else ""
|
||||||
|
assay_name = assay_parts[1] if len(assay_parts) > 1 else ""
|
||||||
|
print(
|
||||||
|
f"[GENEXPERT-ORDER-DEBUG] ip={ip_addr}, seg=OBR, placer_order='{order_index}', "
|
||||||
|
f"assay_code='{assay_id}', assay_name='{assay_name}', result_status='{result_status}', "
|
||||||
|
f"patient_id='{current_pid}', patient_name='{current_patient_name}', raw='{segment}'"
|
||||||
|
)
|
||||||
|
elif seg_type == "TQ1":
|
||||||
|
priority = fields[9] if len(fields) > 9 else ""
|
||||||
|
print(
|
||||||
|
f"[GENEXPERT-ORDER-DEBUG] ip={ip_addr}, seg=TQ1, placer_order='{order_index}', "
|
||||||
|
f"priority='{priority}', patient_id='{current_pid}', "
|
||||||
|
f"patient_name='{current_patient_name}', raw='{segment}'"
|
||||||
|
)
|
||||||
|
elif seg_type == "SPM":
|
||||||
|
specimen_id = fields[2] if len(fields) > 2 else ""
|
||||||
|
specimen_type = fields[4] if len(fields) > 4 else ""
|
||||||
|
print(
|
||||||
|
f"[GENEXPERT-ORDER-DEBUG] ip={ip_addr}, seg=SPM, placer_order='{order_index}', "
|
||||||
|
f"specimen_id='{specimen_id}', specimen_type='{specimen_type}', patient_id='{current_pid}', "
|
||||||
|
f"patient_name='{current_patient_name}', raw='{segment}'"
|
||||||
|
)
|
||||||
|
|
||||||
def build_genexpert_result_query(accnumber, msg_control_id):
|
def build_genexpert_result_query(accnumber, msg_control_id):
|
||||||
ts = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
ts = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||||
# Query hasil berbasis accession number di QRD-8.
|
# Query hasil berbasis accession number di QRD-8.
|
||||||
|
|||||||
Reference in New Issue
Block a user