fix(abdm): enforce NRCES Wellness observation profiles, LOINC code retention, and practitioner performer
This commit is contained in:
parent
bd02f895c5
commit
e992c3be37
|
|
@ -66,6 +66,9 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
}
|
||||
$builder->addPractitioner($practitioner);
|
||||
|
||||
$practitionerRef = 'urn:uuid:' . (string) ($practitioner['id'] ?? 'practitioner-1');
|
||||
$practitionerDisplay = (string) ($practitioner['name'][0]['text'] ?? 'Practitioner');
|
||||
|
||||
$organization = $this->buildOrganization($source);
|
||||
if (! is_array($organization)) {
|
||||
$orgId = (string) ($source['organization']['id'] ?? $source['hfr_id'] ?? 'IN0510000871');
|
||||
|
|
@ -115,7 +118,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
|
||||
if ($sysBp !== null || $diaBp !== null) {
|
||||
$obsId = 'obs-bp-' . $recordId;
|
||||
$vitalObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$vitalObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationVitalSigns',
|
||||
];
|
||||
|
||||
$bpComponents = [];
|
||||
if ($sysBp !== null && is_numeric($sysBp['value'] ?? null)) {
|
||||
|
|
@ -156,7 +162,12 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
$bpObs = [
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationVitalSigns']],
|
||||
'meta' => [
|
||||
'profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationVitalSigns',
|
||||
],
|
||||
],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -174,6 +185,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => 'Blood Pressure',
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'effectiveDateTime' => $timestamp,
|
||||
];
|
||||
|
||||
|
|
@ -223,33 +238,57 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
}
|
||||
}
|
||||
|
||||
// Determine target composition section & profile
|
||||
// Determine target composition section & profile adhering to NRCES ABDM FHIR R4
|
||||
if (in_array($loincCode, $bodyMeasLoincCodes, true)) {
|
||||
$categoryCode = 'vital-signs';
|
||||
$categoryDisplay = 'Vital Signs';
|
||||
$profile = 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation';
|
||||
$bodyMeasObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$profiles = [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationBodyMeasurement',
|
||||
];
|
||||
$bodyMeasObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationBodyMeasurements',
|
||||
];
|
||||
} elseif (in_array($loincCode, $generalAssessLoincCodes, true)) {
|
||||
$categoryCode = 'laboratory';
|
||||
$categoryDisplay = 'Laboratory';
|
||||
$profile = 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation';
|
||||
$generalAssessObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$profiles = [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationGeneralAssessment',
|
||||
];
|
||||
$generalAssessObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationGeneralAssessment',
|
||||
];
|
||||
} elseif (in_array($loincCode, $activityLoincCodes, true)) {
|
||||
$categoryCode = 'activity';
|
||||
$categoryDisplay = 'Activity';
|
||||
$profile = 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation';
|
||||
$activityObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$profiles = [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationPhysicalActivity',
|
||||
];
|
||||
$activityObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationPhysicalActivity',
|
||||
];
|
||||
} else {
|
||||
$categoryCode = 'vital-signs';
|
||||
$categoryDisplay = 'Vital Signs';
|
||||
$profile = 'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationVitalSigns';
|
||||
$vitalObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$profiles = [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationVitalSigns',
|
||||
];
|
||||
$vitalObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationVitalSigns',
|
||||
];
|
||||
}
|
||||
|
||||
$builder->addObservation([
|
||||
$obsPayload = [
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => [$profile]],
|
||||
'meta' => ['profile' => $profiles],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -267,7 +306,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => $display,
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'effectiveDateTime' => $timestamp,
|
||||
'valueQuantity' => [
|
||||
'value' => $numericVal !== null ? $numericVal : (string) $val,
|
||||
|
|
@ -275,7 +317,12 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'system' => 'http://unitsofmeasure.org',
|
||||
'code' => (string) ($ucum['code'] ?? $ucumCode ?: $unit),
|
||||
],
|
||||
]);
|
||||
];
|
||||
if ($encounterRef) {
|
||||
$obsPayload['encounter'] = ['reference' => $encounterRef];
|
||||
}
|
||||
|
||||
$builder->addObservation($obsPayload);
|
||||
}
|
||||
|
||||
// 2. Process Physical Examination / General Assessments Findings
|
||||
|
|
@ -287,12 +334,18 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
}
|
||||
|
||||
$obsId = 'exam-' . $recordId . '-' . $idx;
|
||||
$generalAssessObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$generalAssessObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationGeneralAssessment',
|
||||
];
|
||||
|
||||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationGeneralAssessment',
|
||||
]],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -310,6 +363,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => 'Physical Examination',
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'effectiveDateTime' => $timestamp,
|
||||
'valueString' => $text,
|
||||
|
|
@ -321,12 +378,18 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
$lmpDate = trim((string) ($womenWellness['lmp'] ?? $source['lmp'] ?? ''));
|
||||
if ($lmpDate !== '') {
|
||||
$obsId = 'women-lmp-' . $recordId;
|
||||
$womenObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$womenObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationWomenHealth',
|
||||
];
|
||||
|
||||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationWomenHealth',
|
||||
]],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -344,6 +407,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => 'Last Menstrual Period (LMP)',
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'effectiveDateTime' => $timestamp,
|
||||
'valueDateTime' => date(DATE_ATOM, strtotime($lmpDate) ?: time()),
|
||||
|
|
@ -356,12 +423,18 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
}
|
||||
|
||||
$obsId = 'women-obs-' . $recordId . '-' . $key;
|
||||
$womenObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$womenObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationWomenHealth',
|
||||
];
|
||||
|
||||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationWomenHealth',
|
||||
]],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -374,6 +447,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => ucwords(str_replace('_', ' ', $key)),
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'effectiveDateTime' => $timestamp,
|
||||
'valueString' => trim($valStr),
|
||||
|
|
@ -394,17 +471,28 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
$obsId = 'wellness-social-' . $recordId . '-' . $idx;
|
||||
|
||||
if ($codeStr === 'general-nursing' || str_contains(strtolower($displayStr), 'nursing')) {
|
||||
$otherObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$otherObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'Observation',
|
||||
];
|
||||
$obsTitle = 'General Nursing Observations';
|
||||
$obsProfile = ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation'];
|
||||
} else {
|
||||
$lifestyleObsRefs[] = ['reference' => 'urn:uuid:' . $obsId];
|
||||
$lifestyleObsRefs[] = [
|
||||
'reference' => 'urn:uuid:' . $obsId,
|
||||
'display' => 'ObservationLifestyle',
|
||||
];
|
||||
$obsTitle = ! empty($displayStr) ? $displayStr : 'Lifestyle & General Wellness Advice';
|
||||
$obsProfile = [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation',
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/ObservationLifestyle',
|
||||
];
|
||||
}
|
||||
|
||||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $obsId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'meta' => ['profile' => $obsProfile],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'coding' => [[
|
||||
|
|
@ -422,6 +510,10 @@ class WellnessFhirGenerator extends AbstractModuleFhirGenerator
|
|||
'text' => $obsTitle,
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'performer' => [[
|
||||
'reference' => $practitionerRef,
|
||||
'display' => $practitionerDisplay,
|
||||
]],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'effectiveDateTime' => $timestamp,
|
||||
'valueString' => $text,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,21 @@ class CodingResolver
|
|||
/** @return array<string,mixed> */
|
||||
public function resolveLoincForLabTest(string $localCode, string $displayName, string $department = '', string $specimenType = ''): array
|
||||
{
|
||||
$trimmedCode = trim($localCode);
|
||||
if (preg_match('/^\d{3,5}-\d$/', $trimmedCode)) {
|
||||
return [
|
||||
'coding' => [[
|
||||
'system' => 'http://loinc.org',
|
||||
'code' => $trimmedCode,
|
||||
'display' => trim($displayName) !== '' ? trim($displayName) : $trimmedCode,
|
||||
]],
|
||||
'coding_source' => 'loinc_direct',
|
||||
'confidence' => 1.0,
|
||||
'warnings' => [],
|
||||
'unresolved' => false,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->resolveByHeuristic($localCode, $displayName, $this->labLoincMap, 'http://loinc.org');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user