fix(abdm): split blood pressure into systolic and diastolic components for LOINC 85354-9 in IPD discharge FHIR

This commit is contained in:
DevSoftTech Server 2026-09-04 19:14:55 +05:30
parent 4e748dbaa8
commit 1f26c3e4ef
2 changed files with 166 additions and 4 deletions

View File

@ -114,7 +114,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
];
foreach ($conditionGroups as $group) {
foreach ($group['items'] as $idx => $cond) {
$text = trim((string) ($cond['text'] ?? ''));
$text = trim((string) ($cond['text'] ?? $cond['name'] ?? ''));
if ($text === '') {
continue;
}
@ -156,7 +156,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
}
foreach ((array) ($source['procedures'] ?? []) as $idx => $proc) {
$text = trim((string) ($proc['text'] ?? ''));
$text = trim((string) ($proc['text'] ?? $proc['name'] ?? ''));
if ($text === '') {
continue;
}
@ -306,7 +306,80 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
'effectiveDateTime' => (string) ($obs['effective_at'] ?? $timestamp),
];
if (is_numeric($value)) {
if ($loincCode === '85354-9' || str_contains(strtoupper($text), 'BP') || str_contains(strtoupper($text), 'BLOOD PRESSURE')) {
$loincCode = '85354-9';
$loincDisplay = 'Blood pressure panel with all children optional';
$coding = [[
'system' => 'http://loinc.org',
'code' => '85354-9',
'display' => 'Blood pressure panel with all children optional',
]];
$obsResource['code'] = [
'coding' => $coding,
'text' => $text,
];
if (preg_match('/(\d+(?:\.\d+)?)\s*[\/\-]\s*(\d+(?:\.\d+)?)/', $value, $bpMatches)) {
$systolic = (float) $bpMatches[1];
$diastolic = (float) $bpMatches[2];
$obsResource['component'] = [
[
'code' => [
'coding' => [[
'system' => 'http://loinc.org',
'code' => '8480-6',
'display' => 'Systolic blood pressure',
]],
'text' => 'Systolic blood pressure',
],
'valueQuantity' => [
'value' => $systolic,
'unit' => 'mmHg',
'system' => 'http://unitsofmeasure.org',
'code' => 'mm[Hg]',
],
],
[
'code' => [
'coding' => [[
'system' => 'http://loinc.org',
'code' => '8462-4',
'display' => 'Diastolic blood pressure',
]],
'text' => 'Diastolic blood pressure',
],
'valueQuantity' => [
'value' => $diastolic,
'unit' => 'mmHg',
'system' => 'http://unitsofmeasure.org',
'code' => 'mm[Hg]',
],
],
];
} elseif (is_numeric($value)) {
$systolic = (float) $value;
$obsResource['component'] = [
[
'code' => [
'coding' => [[
'system' => 'http://loinc.org',
'code' => '8480-6',
'display' => 'Systolic blood pressure',
]],
'text' => 'Systolic blood pressure',
],
'valueQuantity' => [
'value' => $systolic,
'unit' => 'mmHg',
'system' => 'http://unitsofmeasure.org',
'code' => 'mm[Hg]',
],
],
];
} else {
$obsResource['valueString'] = $value;
}
} elseif (is_numeric($value)) {
$unitInfo = $this->getVitalSignUnitInfo($text, trim((string) ($obs['unit'] ?? '')));
$obsResource['valueQuantity'] = [
'value' => (float) $value,
@ -805,7 +878,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
$text = trim((string) $item['text']);
$val = trim((string) $item['value']);
$unit = trim((string) ($item['unit'] ?? ''));
$unitInfo = is_numeric($val) ? $this->getVitalSignUnitInfo($text, $unit) : ['unit' => ''];
$unitInfo = (is_numeric($val) || preg_match('/^\d+\s*[\/\-]\s*\d+/', $val)) ? $this->getVitalSignUnitInfo($text, $unit) : ['unit' => ''];
$displayUnit = $unit !== '' ? $unit : ($unitInfo['unit'] ?? '');
$valStr = $val . ($displayUnit !== '' && stripos($val, $displayUnit) === false ? ' ' . $displayUnit : '');
return trim($text . ': ' . $valStr);

View File

@ -734,4 +734,93 @@ final class FhirGeneratorsTest extends CIUnitTestCase
$this->assertGreaterThanOrEqual(1, count($composition['section']));
$this->assertNotEmpty($composition['section'][0]['entry']);
}
public function testDischargeSummaryGeneratorCreatesFullSectionsAndComponents(): void
{
$generator = $this->factory->discharge();
$output = $generator->generate([
'record_id' => '3209',
'encounter' => [
'id' => 'A26090003209',
'start' => '2026-09-01T18:57:00+05:30',
'end' => '2026-09-04T19:00:00+05:30',
'ward' => 'General Ward',
'bed' => 'GW-05',
],
'patient' => [
'id' => '15355',
'name' => 'JANVI BISHT',
'gender' => 'female',
'dob' => '2013-06-25',
'abha_id' => '91407564383062',
],
'doctor_name' => 'Dr. Sanjay Kumar',
'chief_complaints' => [
['name' => 'Fever'],
['name' => 'Cough'],
['name' => 'Breathlessness'],
['name' => 'Vomiting'],
],
'diagnoses' => [
['name' => 'VIRAL FEVER'],
],
'medications' => [
['name' => 'DOZPANTA DSR', 'dosage' => 'BF | OD | for 5 Days'],
['name' => 'WANAPOL', 'dosage' => 'AF | BD | for 2 Days'],
],
'observations' => [
['text' => 'Pulse', 'value' => '40', 'unit' => '/min', 'category' => 'Condition on Admission Time'],
['text' => 'BP', 'value' => '120/80', 'unit' => 'mmHg', 'category' => 'Condition on Admission Time'],
['text' => 'Pulse', 'value' => '25', 'unit' => '/min', 'category' => 'Condition on Discharge Time'],
['text' => 'BP', 'value' => '110/70', 'unit' => 'mmHg', 'category' => 'Condition on Discharge Time'],
],
'investigations' => [
['text' => 'HB', 'value' => '11', 'unit' => 'g/dL', 'loinc_code' => '718-7'],
],
'care_plans' => [
['title' => 'Dietary Advice', 'description' => 'Hydration: 8-10 glasses of water daily.'],
],
]);
$this->assertSame('DischargeSummaryRecord', $output['hi_type']);
$bundle = $output['fhir_bundle'];
$this->assertSame('Bundle', $bundle['resourceType']);
$resourceTypes = array_column(array_column($bundle['entry'], 'resource'), 'resourceType');
$this->assertContains('Composition', $resourceTypes);
$this->assertContains('Patient', $resourceTypes);
$this->assertContains('Encounter', $resourceTypes);
$this->assertContains('Condition', $resourceTypes);
$this->assertContains('MedicationRequest', $resourceTypes);
$this->assertContains('Observation', $resourceTypes);
$this->assertContains('DiagnosticReport', $resourceTypes);
$this->assertContains('CarePlan', $resourceTypes);
$this->assertContains('DocumentReference', $resourceTypes);
// Verify BP observations have components
$bpObservations = [];
foreach ($bundle['entry'] as $entry) {
$res = $entry['resource'];
if (($res['resourceType'] ?? '') === 'Observation' && ($res['code']['coding'][0]['code'] ?? '') === '85354-9') {
$bpObservations[] = $res;
}
}
$this->assertCount(2, $bpObservations);
$this->assertArrayHasKey('component', $bpObservations[0]);
$this->assertCount(2, $bpObservations[0]['component']);
$this->assertSame(120.0, $bpObservations[0]['component'][0]['valueQuantity']['value']);
$this->assertSame(80.0, $bpObservations[0]['component'][1]['valueQuantity']['value']);
$this->assertSame('8480-6', $bpObservations[0]['component'][0]['code']['coding'][0]['code']);
$this->assertSame('8462-4', $bpObservations[0]['component'][1]['code']['coding'][0]['code']);
// Verify composition sections
$composition = $bundle['entry'][0]['resource'];
$sectionTitles = array_column($composition['section'], 'title');
$this->assertContains('Chief complaints', $sectionTitles);
$this->assertContains('Problems and Diagnoses', $sectionTitles);
$this->assertContains('Condition on Admission Time', $sectionTitles);
$this->assertContains('Condition on Discharge Time', $sectionTitles);
$this->assertContains('Medications', $sectionTitles);
$this->assertContains('Care plan', $sectionTitles);
}
}