fix(fhir): fix ABHA PHR app parsing error in discharge FHIR bundle - correct Observation category codes for vitals vs admission/discharge conditions - add missing category to investigation Observations (NRCES profile requirement) - strip empty unit/code from valueQuantity to avoid PHR parser rejection - fix greedy regex in Composition narrative that could produce malformed XHTML - remove 0 from isPlaceholderValue to stop silent stripping of numeric FHIR values
This commit is contained in:
parent
cefa73dbae
commit
6525422af4
|
|
@ -310,14 +310,22 @@ class FhirDocumentBuilder
|
||||||
return $clean === [] ? null : $clean;
|
return $clean === [] ? null : $clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param mixed $value */
|
/**
|
||||||
|
* Returns true only for display-level placeholder strings that should never
|
||||||
|
* appear in a rendered FHIR bundle (pure "no data" sentinel values).
|
||||||
|
* NOTE: '0' is intentionally excluded — it is a valid FHIR quantity value,
|
||||||
|
* coded value, and identifier string. Its earlier inclusion caused silent
|
||||||
|
* data loss in observation valueQuantity and numeric identifiers.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
private function isPlaceholderValue($value): bool
|
private function isPlaceholderValue($value): bool
|
||||||
{
|
{
|
||||||
if (! is_string($value)) {
|
if (! is_string($value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return in_array(strtoupper(trim($value)), ['NA', 'N/A', 'NULL', 'NOT AVAILABLE'], true);
|
return in_array(strtoupper(trim($value)), ['NA', 'N/A', 'N / A', 'NULL', 'NOT AVAILABLE', 'UNDEFINED'], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function newId(string $prefix): string
|
private function newId(string $prefix): string
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,10 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
||||||
$isAdm = (stripos($catText, 'Admission') !== false || stripos($text, 'Admission') !== false);
|
$isAdm = (stripos($catText, 'Admission') !== false || stripos($text, 'Admission') !== false);
|
||||||
$isDis = (stripos($catText, 'Discharge') !== false || stripos($text, 'Discharge') !== false);
|
$isDis = (stripos($catText, 'Discharge') !== false || stripos($text, 'Discharge') !== false);
|
||||||
$obsCategoryDisplay = $isAdm ? 'Condition on Admission Time' : ($isDis ? 'Condition on Discharge Time' : 'Vital Signs');
|
$obsCategoryDisplay = $isAdm ? 'Condition on Admission Time' : ($isDis ? 'Condition on Discharge Time' : 'Vital Signs');
|
||||||
|
// ABDM PHR: use exam category for admission/discharge condition observations;
|
||||||
|
// vital-signs category only for true vital measurements.
|
||||||
|
$obsCategoryCode = ($isAdm || $isDis) ? 'exam' : 'vital-signs';
|
||||||
|
$obsCategorySystem = 'http://terminology.hl7.org/CodeSystem/observation-category';
|
||||||
|
|
||||||
$coding = [];
|
$coding = [];
|
||||||
$loincCode = trim((string) ($obs['loinc_code'] ?? ''));
|
$loincCode = trim((string) ($obs['loinc_code'] ?? ''));
|
||||||
|
|
@ -308,9 +312,9 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
||||||
'category' => [[
|
'category' => [[
|
||||||
'text' => $obsCategoryDisplay,
|
'text' => $obsCategoryDisplay,
|
||||||
'coding' => [[
|
'coding' => [[
|
||||||
'system' => 'http://terminology.hl7.org/CodeSystem/observation-category',
|
'system' => $obsCategorySystem,
|
||||||
'code' => 'vital-signs',
|
'code' => $obsCategoryCode,
|
||||||
'display' => 'Vital Signs',
|
'display' => $obsCategoryDisplay,
|
||||||
]],
|
]],
|
||||||
]],
|
]],
|
||||||
'code' => [
|
'code' => [
|
||||||
|
|
@ -397,12 +401,13 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
||||||
}
|
}
|
||||||
} elseif (is_numeric($value)) {
|
} elseif (is_numeric($value)) {
|
||||||
$unitInfo = $this->getVitalSignUnitInfo($text, trim((string) ($obs['unit'] ?? '')));
|
$unitInfo = $this->getVitalSignUnitInfo($text, trim((string) ($obs['unit'] ?? '')));
|
||||||
$obsResource['valueQuantity'] = [
|
$vq = ['value' => (float) $value];
|
||||||
'value' => (float) $value,
|
if ($unitInfo['unit'] !== '') {
|
||||||
'unit' => $unitInfo['unit'],
|
$vq['unit'] = $unitInfo['unit'];
|
||||||
'system' => 'http://unitsofmeasure.org',
|
$vq['system'] = 'http://unitsofmeasure.org';
|
||||||
'code' => $unitInfo['code'],
|
$vq['code'] = $unitInfo['code'] !== '' ? $unitInfo['code'] : $unitInfo['unit'];
|
||||||
];
|
}
|
||||||
|
$obsResource['valueQuantity'] = $vq;
|
||||||
} else {
|
} else {
|
||||||
$obsResource['valueString'] = $value;
|
$obsResource['valueString'] = $value;
|
||||||
}
|
}
|
||||||
|
|
@ -457,6 +462,15 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
||||||
'div' => '<div xmlns="http://www.w3.org/1999/xhtml"><p><b>Investigation:</b> ' . htmlspecialchars($text) . ($resultValue !== '' ? (': ' . htmlspecialchars($resultValue)) : '') . '</p></div>',
|
'div' => '<div xmlns="http://www.w3.org/1999/xhtml"><p><b>Investigation:</b> ' . htmlspecialchars($text) . ($resultValue !== '' ? (': ' . htmlspecialchars($resultValue)) : '') . '</p></div>',
|
||||||
],
|
],
|
||||||
'status' => 'final',
|
'status' => 'final',
|
||||||
|
// NRCES Observation profile requires at least one category.
|
||||||
|
'category' => [[
|
||||||
|
'coding' => [[
|
||||||
|
'system' => 'http://terminology.hl7.org/CodeSystem/observation-category',
|
||||||
|
'code' => 'laboratory',
|
||||||
|
'display' => 'Laboratory',
|
||||||
|
]],
|
||||||
|
'text' => 'Laboratory',
|
||||||
|
]],
|
||||||
'code' => [
|
'code' => [
|
||||||
'coding' => $coding,
|
'coding' => $coding,
|
||||||
'text' => $text,
|
'text' => $text,
|
||||||
|
|
@ -1391,7 +1405,8 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
||||||
if ($secTitle === '' || $secDiv === '') {
|
if ($secTitle === '' || $secDiv === '') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (preg_match('/<div[^>]*>(.*)<\/div>/is', $secDiv, $m)) {
|
// Non-greedy match to avoid swallowing sibling tags in multi-div strings.
|
||||||
|
if (preg_match('/<div[^>]*>(.*?)<\/div>/is', $secDiv, $m)) {
|
||||||
$inner = trim($m[1]);
|
$inner = trim($m[1]);
|
||||||
} else {
|
} else {
|
||||||
$inner = $secDiv;
|
$inner = $secDiv;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user