This commit is contained in:
Dwi Swandhana
2026-05-18 12:24:18 +07:00
parent c70d7ebb93
commit c78d15e67a
7 changed files with 343 additions and 84 deletions

No files matched your search

+76 -47
View File
@@ -605,13 +605,14 @@ class AstmMessageService
{
$normalizedData = str_replace(["\r\n", "\r"], "\n", $response);
$segments = explode("\n", $normalizedData);
$patient_id = '';
$patient_name = '';
$accession_number = '';
$waktu_periksa = date('d-m-Y H:i:s'); // Default jika tidak ada di ASTM
// Array untuk menyimpan data terstruktur
$parsedResults = [];
$hasil_list = []; // Tetap dipertahankan untuk kebutuhan legacy/summary
$hasil_list = [];
foreach ($segments as $rsegmen) {
$cekdata = explode('|', $rsegmen);
@@ -619,32 +620,37 @@ class AstmMessageService
$recordType = preg_replace("/[^a-zA-Z]/", "", $cekdata[0]);
// 1. Parsing Pasien
if ($recordType === 'P') {
$patient_id = $cekdata[3] ?? ($cekdata[4] ?? '');
if (isset($cekdata[5])) {
$patient_name = trim(str_replace('^', ' ', $cekdata[5]));
$patient_id = !empty($cekdata[3]) ? $cekdata[3] : (!empty($cekdata[4]) ? $cekdata[4] : '');
$raw_name = isset($cekdata[5]) ? trim(str_replace('^', ' ', $cekdata[5])) : '';
$patient_name = !empty(trim($raw_name)) ? $raw_name : $patient_id;
}
// 2. Parsing Accession Number
if ($recordType === 'O') {
$accession_number = !empty($cekdata[2]) ? $cekdata[2] : $accession_number;
// Jika ada waktu di record O (index 6), kita bisa format (opsional)
if (!empty($cekdata[6])) {
$rawTime = preg_replace('/[^0-9]/', '', $cekdata[6]);
if (strlen($rawTime) >= 14) {
$waktu_periksa = date('d-m-Y H:i:s', strtotime(substr($rawTime, 0, 14)));
}
}
}
if ($recordType === 'O') {
$accession_number = $cekdata[2] ?? '';
}
// 3. Parsing Data Result, Ct, dan EndPt
if ($recordType === 'R') {
if (isset($cekdata[2]) && isset($cekdata[3])) {
$test_info_raw = $cekdata[2];
$result_val = trim(str_replace('^', '', $cekdata[3]));
if ($result_val !== '') {
// Pecah test info berdasarkan '^'
$test_info = explode('^', $test_info_raw);
// Pada format GeneXpert: index ke-6 biasanya nama target (FII, FII 20210G, dll)
// index ke-7 biasanya penanda tipe data (Ct, EndPt)
$target_name = $test_info[6] ?? 'Unknown Target';
$metric_type = $test_info[7] ?? 'Result'; // Jika tidak ada, berarti ini Result (POS/NEG)
$target_name = !empty($test_info[6]) ? $test_info[6] : 'Unknown Target';
$metric_type = (!empty($test_info[7])) ? $test_info[7] : 'Result';
// Inisialisasi array untuk target ini jika belum ada
if (!isset($parsedResults[$target_name])) {
$parsedResults[$target_name] = [
'Result' => '-',
@@ -653,12 +659,11 @@ class AstmMessageService
];
}
// Masukkan nilai ke dalam struktur data
$parsedResults[$target_name][$metric_type] = $result_val;
// Untuk summary legacy (kolom organism/additional_result)
if ($metric_type === 'Result') {
$hasil_list[] = $target_name . ": " . $result_val;
if ($metric_type === 'Result' && $result_val !== '') {
// Simpan semua result ke dalam list untuk kesimpulan
$hasil_list[$target_name] = "<strong>{$target_name}:</strong> {$result_val}";
}
}
}
@@ -670,19 +675,50 @@ class AstmMessageService
return false;
}
// Buat string kesimpulan DULUAN sebelum membuat HTML
$kesimpulan_html = implode(" | ", array_values($hasil_list));
$kesimpulan_db = strip_tags($kesimpulan_html); // Versi tanpa tag HTML untuk kolom DB
$kesimpulan_safe = substr($kesimpulan_db, 0, 100);
// ====================================================================
// MEMBUAT HTML REPORT & GRAFIK (CHART.JS)
// ====================================================================
// 1. Buat Tabel HTML
$reporthtml = '<div class="genexpert-report" style="font-family: Arial, sans-serif;">';
$reporthtml .= '<h4 style="margin-bottom:10px;">GeneXpert Assay Result</h4>';
$reporthtml .= '<table border="1" cellpadding="5" style="width: 100%; border-collapse: collapse; text-align: left; margin-bottom: 20px;">';
$reporthtml .= '<tr style="background-color: #f4f4f4;">
// Wrapper Utama
$reporthtml = '<div class="genexpert-report" style="font-family: Arial, sans-serif; max-width: 900px; margin: auto; border: 1px solid #ddd; padding: 20px; border-radius: 8px;">';
$reporthtml .= '<h3 style="text-align: center; margin-top: 0; color: #2c3e50;">Hasil Pemeriksaan GeneXpert</h3>';
$reporthtml .= '<hr style="border: 0; border-top: 2px solid #3498db; margin-bottom: 20px;">';
// Bagian 1: Identitas Pasien (Grid Table)
$reporthtml .= '<table style="width: 100%; margin-bottom: 20px; font-size: 14px; border: none;">';
$reporthtml .= '<tr>';
$reporthtml .= '<td style="width: 15%; padding: 4px 0;"><strong>No. Reg LIS</strong></td>';
$reporthtml .= '<td style="width: 35%; padding: 4px 0;">: ' . htmlspecialchars($accession_number) . '</td>';
$reporthtml .= '<td style="width: 15%; padding: 4px 0;"><strong>ID Pasien</strong></td>';
$reporthtml .= '<td style="width: 35%; padding: 4px 0;">: ' . htmlspecialchars($patient_id) . '</td>';
$reporthtml .= '</tr>';
$reporthtml .= '<tr>';
$reporthtml .= '<td style="padding: 4px 0;"><strong>Nama Pasien</strong></td>';
$reporthtml .= '<td style="padding: 4px 0;">: ' . htmlspecialchars($patient_name) . '</td>';
$reporthtml .= '<td style="padding: 4px 0;"><strong>Waktu Tes</strong></td>';
$reporthtml .= '<td style="padding: 4px 0;">: ' . htmlspecialchars($waktu_periksa) . '</td>';
$reporthtml .= '</tr>';
$reporthtml .= '</table>';
// Bagian 2: Kesimpulan / Hasil Utama
$reporthtml .= '<div style="background-color: #f8f9fa; padding: 15px; border-left: 5px solid #2ecc71; margin-bottom: 25px; border-radius: 4px;">';
$reporthtml .= '<h4 style="margin: 0 0 10px 0; color: #2c3e50;">Kesimpulan Hasil:</h4>';
$reporthtml .= '<p style="margin: 0; font-size: 14px; line-height: 1.6;">' . $kesimpulan_html . '</p>';
$reporthtml .= '</div>';
// Bagian 3: Tabel Detail Analit
$reporthtml .= '<h4 style="margin-bottom:10px; color: #2c3e50;">Detail Target / Analit</h4>';
$reporthtml .= '<table border="1" cellpadding="8" style="width: 100%; border-collapse: collapse; text-align: left; margin-bottom: 25px; font-size: 13px;">';
$reporthtml .= '<tr style="background-color: #ecf0f1; color: #2c3e50;">
<th>Target/Analyte</th>
<th>Result</th>
<th>Ct Value</th>
<th>EndPt</th>
<th>End Point</th>
</tr>';
$chartLabels = [];
@@ -697,7 +733,6 @@ class AstmMessageService
<td>{$data['EndPt']}</td>
</tr>";
// Siapkan data untuk grafik (hanya ambil target yang memiliki nilai numerik Ct/EndPt)
if (is_numeric($data['Ct']) || is_numeric($data['EndPt'])) {
$chartLabels[] = $target;
$chartCtData[] = is_numeric($data['Ct']) ? (float)$data['Ct'] : 0;
@@ -706,27 +741,25 @@ class AstmMessageService
}
$reporthtml .= '</table>';
// 2. Buat Grafik menggunakan Chart.js (Inject Script)
// Bagian 4: Grafik Chart.js
if (!empty($chartLabels)) {
$chartId = 'gxChart_' . preg_replace('/[^a-zA-Z0-9]/', '', $accession_number);
$labelsJson = json_encode($chartLabels);
$ctJson = json_encode($chartCtData);
$endPtJson = json_encode($chartEndPtData);
// Tambahkan Canvas untuk grafik
$reporthtml .= '<h4 style="margin-bottom:10px; color: #2c3e50; text-align: center;">Grafik Amplifikasi</h4>';
$reporthtml .= '<div style="width: 100%; max-width: 800px; margin: auto;">';
$reporthtml .= '<canvas id="'.$chartId.'"></canvas>';
$reporthtml .= '</div>';
// Tambahkan script Chart.js (asumsi CDN) dan inisialisasi
$reporthtml .= "
<script src=\"https://cdn.jsdelivr.net/npm/chart.js\"></script>
<script>
// Gunakan DOMContentLoaded agar script jalan setelah HTML di-render di frontend
document.addEventListener('DOMContentLoaded', function() {
var ctx = document.getElementById('{$chartId}').getContext('2d');
new Chart(ctx, {
type: 'bar', // Menggunakan kombinasi Bar & Line chart
type: 'bar',
data: {
labels: {$labelsJson},
datasets: [
@@ -745,6 +778,7 @@ class AstmMessageService
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderWidth: 2,
tension: 0.1,
yAxisID: 'y1'
}
]
@@ -762,8 +796,8 @@ class AstmMessageService
type: 'linear',
display: true,
position: 'right',
title: { display: true, text: 'EndPt' },
grid: { drawOnChartArea: false } // Agar garis grid tidak bertumpuk
title: { display: true, text: 'End Point' },
grid: { drawOnChartArea: false }
}
}
}
@@ -772,15 +806,12 @@ class AstmMessageService
</script>
";
}
$reporthtml .= '</div>';
$reporthtml .= '</div>'; // End of Wrapper Utama
// ====================================================================
// SIMPAN KE DATABASE
// ====================================================================
$kesimpulan = implode(" | ", $hasil_list);
$kesimpulan_safe = substr($kesimpulan, 0, 100);
try {
Periksa::where('nofoto', $accession_number)
->whereNotIn('status', ['Selesai', 'Arsip', 'Dibatalkan (Arsip)', 'Batal'])
@@ -788,7 +819,6 @@ class AstmMessageService
'status' => 'Data GeneXpert di Terima'
]);
Riwayat::create([
'nofoto' => $accession_number,
'jawaban' => $reporthtml,
@@ -797,15 +827,14 @@ class AstmMessageService
'verifikasi' => '',
]);
$resultSample = ResultSample::firstOrNew(['accession_number' => $accession_number]);
$resultSample->sender_name = $alat;
$resultSample->patient_id = substr($patient_id, 0, 50);
$resultSample->patient_name_first = substr($patient_name, 0, 100);
$resultSample->organism = $kesimpulan_safe; // Tetap menyimpan text summary
$resultSample->additional_result = json_encode($parsedResults); // Menyimpan JSON utuh agar lebih informatif
$resultSample->message_datetime = date('Y-m-d H:i:s');
$resultSample = ResultSample::firstOrNew(['accession_number' => $accession_number]);
$resultSample->sender_name = $alat;
$resultSample->patient_id = substr($patient_id, 0, 50);
$resultSample->patient_name_first = substr($patient_name, 0, 100);
$resultSample->organism = $kesimpulan_safe; // Disimpan versi plain text
$resultSample->additional_result = json_encode($parsedResults);
$resultSample->message_datetime = date('Y-m-d H:i:s');
$resultSample->save();
Log::info("GeneXpert Result Disimpan", ["NoReg" => $accession_number, "Hasil" => $kesimpulan_safe]);
return true;
} catch (\Exception $e) {
Log::error("GeneXpert DB Save Error: " . $e->getMessage());