fix(abdm): align IPD discharge FHIR with NRCES profile
Use mandatory SNOMED discharge composition and section codings, model completed investigations as DiagnosticReportLab resources, and restore local M3 attachment/value-string rendering.
This commit is contained in:
parent
bd557ef709
commit
16be448b6f
|
|
@ -1643,7 +1643,10 @@ class Patient extends BaseController
|
|||
]);
|
||||
}
|
||||
|
||||
$row['summary'] = json_decode((string) ($row['summary_json'] ?? ''), true) ?: [];
|
||||
$document = (new \App\Libraries\Abdm\M3HiuDocumentRepository())->getDocument($docId);
|
||||
$row['summary'] = is_array($document['summary'] ?? null)
|
||||
? $document['summary']
|
||||
: (json_decode((string) ($row['summary_json'] ?? ''), true) ?: []);
|
||||
|
||||
return $this->response->setJSON([
|
||||
'ok' => 1,
|
||||
|
|
|
|||
|
|
@ -29,10 +29,17 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
->updateBundleMeta(['meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle',
|
||||
]]])
|
||||
->buildComposition($this->buildBaseComposition($source, 'IPD Discharge Summary', '18842-5', 'Discharge summary'))
|
||||
->updateComposition(['meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/DischargeSummaryRecord',
|
||||
]]])
|
||||
->buildComposition($this->buildBaseComposition($source, 'IPD Discharge Summary', '373942005', 'Discharge summary'))
|
||||
->updateComposition([
|
||||
'meta' => ['profile' => [
|
||||
'https://nrces.in/ndhm/fhir/r4/StructureDefinition/DischargeSummaryRecord',
|
||||
]],
|
||||
'type' => ['coding' => [[
|
||||
'system' => 'http://snomed.info/sct',
|
||||
'code' => '373942005',
|
||||
'display' => 'Discharge summary',
|
||||
]], 'text' => 'Discharge Summary'],
|
||||
])
|
||||
->addPatient($this->buildBasePatient($source));
|
||||
|
||||
$encounter = $this->buildEncounter($source);
|
||||
|
|
@ -96,6 +103,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addCondition([
|
||||
'resourceType' => 'Condition',
|
||||
'id' => 'discharge-condition-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Condition']],
|
||||
'clinicalStatus' => [
|
||||
'coding' => [[
|
||||
'system' => 'http://terminology.hl7.org/CodeSystem/condition-clinical',
|
||||
|
|
@ -138,6 +146,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addProcedure([
|
||||
'resourceType' => 'Procedure',
|
||||
'id' => 'discharge-procedure-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Procedure']],
|
||||
'status' => 'completed',
|
||||
'code' => [
|
||||
'coding' => $coding,
|
||||
|
|
@ -158,6 +167,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addMedicationRequest([
|
||||
'resourceType' => 'MedicationRequest',
|
||||
'id' => 'discharge-medication-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/MedicationRequest']],
|
||||
'status' => 'active',
|
||||
'intent' => 'order',
|
||||
'subject' => ['reference' => $patientRef],
|
||||
|
|
@ -194,6 +204,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => 'discharge-observation-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'status' => 'final',
|
||||
'category' => [[
|
||||
'text' => trim((string) ($obs['category'] ?? 'vital-signs')),
|
||||
|
|
@ -229,18 +240,45 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
];
|
||||
}
|
||||
|
||||
$builder->addServiceRequest([
|
||||
'resourceType' => 'ServiceRequest',
|
||||
'id' => 'discharge-investigation-' . $recordId . '-' . $idx,
|
||||
'status' => 'active',
|
||||
'intent' => 'order',
|
||||
$observationId = 'discharge-investigation-observation-' . $recordId . '-' . $idx;
|
||||
$resultValue = trim((string) ($inv['value'] ?? ''));
|
||||
if ($resultValue === '' && preg_match('/^(.+?):\s*(.+)$/', $text, $parts) === 1) {
|
||||
$text = trim($parts[1]);
|
||||
$resultValue = trim($parts[2]);
|
||||
}
|
||||
|
||||
$builder->addObservation([
|
||||
'resourceType' => 'Observation',
|
||||
'id' => $observationId,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/Observation']],
|
||||
'status' => 'final',
|
||||
'code' => [
|
||||
'coding' => $coding,
|
||||
'text' => $text,
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'authoredOn' => (string) ($inv['authored_on'] ?? $timestamp),
|
||||
'effectiveDateTime' => (string) ($inv['reported_at'] ?? $inv['authored_on'] ?? $timestamp),
|
||||
'valueString' => $resultValue !== '' ? $resultValue : null,
|
||||
]);
|
||||
$builder->addDiagnosticReport([
|
||||
'resourceType' => 'DiagnosticReport',
|
||||
'id' => 'discharge-investigation-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab']],
|
||||
'status' => 'final',
|
||||
'category' => [['coding' => [[
|
||||
'system' => 'http://snomed.info/sct',
|
||||
'code' => '108252007',
|
||||
'display' => 'Laboratory procedure',
|
||||
]]]],
|
||||
'code' => [
|
||||
'coding' => $coding,
|
||||
'text' => $text,
|
||||
],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'encounter' => $encounterRef ? ['reference' => $encounterRef] : null,
|
||||
'issued' => (string) ($inv['reported_at'] ?? $inv['authored_on'] ?? $timestamp),
|
||||
'result' => [['reference' => 'urn:uuid:' . $observationId]],
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -253,6 +291,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addAllergyIntolerance([
|
||||
'resourceType' => 'AllergyIntolerance',
|
||||
'id' => 'discharge-allergy-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/AllergyIntolerance']],
|
||||
'clinicalStatus' => [
|
||||
'coding' => [[
|
||||
'system' => 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical',
|
||||
|
|
@ -289,6 +328,7 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addCarePlan([
|
||||
'resourceType' => 'CarePlan',
|
||||
'id' => 'discharge-careplan-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/CarePlan']],
|
||||
'status' => 'active',
|
||||
'intent' => 'plan',
|
||||
'title' => $title,
|
||||
|
|
@ -308,12 +348,14 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$builder->addDocumentReference([
|
||||
'resourceType' => 'DocumentReference',
|
||||
'id' => 'discharge-document-' . $recordId . '-' . $idx,
|
||||
'meta' => ['profile' => ['https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentReference']],
|
||||
'status' => 'current',
|
||||
'docStatus' => 'final',
|
||||
'type' => ['coding' => [[
|
||||
'system' => 'http://loinc.org',
|
||||
'code' => (string) ($document['loinc_code'] ?? '55107-7'),
|
||||
'display' => (string) ($document['title'] ?? 'Clinical document'),
|
||||
]]],
|
||||
'system' => 'http://snomed.info/sct',
|
||||
'code' => '373942005',
|
||||
'display' => 'Discharge summary',
|
||||
]], 'text' => 'Discharge Summary'],
|
||||
'subject' => ['reference' => $patientRef],
|
||||
'date' => (string) ($document['created_at'] ?? $timestamp),
|
||||
'author' => is_array($practitioner) ? [[
|
||||
|
|
@ -336,14 +378,14 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
}
|
||||
|
||||
$sectionDefinitions = [
|
||||
['title' => 'Diagnosis', 'code' => '11450-4', 'prefix' => 'discharge-condition-', 'items' => (array) ($source['conditions'] ?? [])],
|
||||
['title' => 'Procedures', 'code' => '47519-4', 'prefix' => 'discharge-procedure-', 'items' => (array) ($source['procedures'] ?? [])],
|
||||
['title' => 'Discharge medications', 'code' => '10183-2', 'prefix' => 'discharge-medication-', 'items' => (array) ($source['medications'] ?? [])],
|
||||
['title' => 'Observations', 'code' => '8716-3', 'prefix' => 'discharge-observation-', 'items' => (array) ($source['observations'] ?? [])],
|
||||
['title' => 'Investigations', 'code' => '18776-5', 'prefix' => 'discharge-investigation-', 'items' => (array) ($source['investigations'] ?? [])],
|
||||
['title' => 'Allergies', 'code' => '48765-2', 'prefix' => 'discharge-allergy-', 'items' => (array) ($source['allergies'] ?? [])],
|
||||
['title' => 'Discharge advice', 'code' => '8653-8', 'prefix' => 'discharge-careplan-', 'items' => (array) ($source['care_plans'] ?? [])],
|
||||
['title' => 'Attached documents', 'code' => '55107-7', 'prefix' => 'discharge-document-', 'items' => (array) ($source['documents'] ?? [])],
|
||||
['title' => 'Medical history', 'code' => '1003642006', 'display' => 'Past medical history section', 'prefix' => 'discharge-condition-', 'items' => (array) ($source['conditions'] ?? [])],
|
||||
['title' => 'Procedures', 'code' => '1003640003', 'display' => 'History of past procedure section', 'prefix' => 'discharge-procedure-', 'items' => (array) ($source['procedures'] ?? [])],
|
||||
['title' => 'Medications', 'code' => '1003606003', 'display' => 'Medication history section', 'prefix' => 'discharge-medication-', 'items' => (array) ($source['medications'] ?? [])],
|
||||
['title' => 'Physical examination', 'code' => '425044008', 'display' => 'Physical exam section', 'prefix' => 'discharge-observation-', 'items' => (array) ($source['observations'] ?? [])],
|
||||
['title' => 'Investigations', 'code' => '721981007', 'display' => 'Diagnostic studies report', 'prefix' => 'discharge-investigation-', 'items' => (array) ($source['investigations'] ?? [])],
|
||||
['title' => 'Allergies', 'code' => '722446000', 'display' => 'Allergy record', 'prefix' => 'discharge-allergy-', 'items' => (array) ($source['allergies'] ?? [])],
|
||||
['title' => 'Care plan', 'code' => '734163000', 'display' => 'Care plan', 'prefix' => 'discharge-careplan-', 'items' => (array) ($source['care_plans'] ?? [])],
|
||||
['title' => 'Document reference', 'code' => '373942005', 'display' => 'Discharge summary', 'prefix' => 'discharge-document-', 'items' => (array) ($source['documents'] ?? [])],
|
||||
];
|
||||
$sections = [];
|
||||
foreach ($sectionDefinitions as $definition) {
|
||||
|
|
@ -362,8 +404,9 @@ class DischargeFhirGenerator extends \App\Libraries\Abdm\Fhir\Generators\Abstrac
|
|||
$sections[] = [
|
||||
'title' => $definition['title'],
|
||||
'code' => ['coding' => [[
|
||||
'system' => 'http://loinc.org',
|
||||
'system' => 'http://snomed.info/sct',
|
||||
'code' => $definition['code'],
|
||||
'display' => $definition['display'],
|
||||
]]],
|
||||
'entry' => $references,
|
||||
'text' => $narrativeParts !== [] ? [
|
||||
|
|
|
|||
|
|
@ -308,6 +308,8 @@ class M3HiuDocumentRepository
|
|||
$val = '';
|
||||
if (isset($resource['valueQuantity']) && is_array($resource['valueQuantity'])) {
|
||||
$val = trim((string) (($resource['valueQuantity']['value'] ?? '') . ' ' . ($resource['valueQuantity']['unit'] ?? '')));
|
||||
} elseif (isset($resource['valueString'])) {
|
||||
$val = trim((string) $resource['valueString']);
|
||||
}
|
||||
if ($codeText !== '' || $val !== '') {
|
||||
$vitals[] = ['name' => $codeText, 'value' => $val];
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ final class FhirGeneratorsTest extends CIUnitTestCase
|
|||
'conditions' => [['text' => 'Viral fever']],
|
||||
'procedures' => [['text' => 'Supportive care']],
|
||||
'medications' => [['name' => 'Paracetamol', 'dosage' => 'As directed']],
|
||||
'investigations' => [['text' => 'Haemoglobin: 11', 'loinc_code' => '718-7']],
|
||||
'care_plans' => [['title' => 'Advice', 'description' => 'Review after three days']],
|
||||
'documents' => [[
|
||||
'title' => 'Discharge Summary PDF',
|
||||
|
|
@ -135,6 +136,8 @@ final class FhirGeneratorsTest extends CIUnitTestCase
|
|||
$this->assertSame('IPD Discharge Summary', $composition['title'] ?? null);
|
||||
$this->assertContains('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DocumentBundle', $bundle['meta']['profile'] ?? []);
|
||||
$this->assertContains('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DischargeSummaryRecord', $composition['meta']['profile'] ?? []);
|
||||
$this->assertSame('http://snomed.info/sct', $composition['type']['coding'][0]['system'] ?? null);
|
||||
$this->assertSame('373942005', $composition['type']['coding'][0]['code'] ?? null);
|
||||
$this->assertNotEmpty($composition['author'] ?? []);
|
||||
$this->assertNotEmpty($composition['custodian'] ?? []);
|
||||
$this->assertNotEmpty($composition['section'] ?? []);
|
||||
|
|
@ -153,6 +156,14 @@ final class FhirGeneratorsTest extends CIUnitTestCase
|
|||
$documents = array_values(array_filter($bundle['entry'], static fn (array $entry): bool => ($entry['resource']['resourceType'] ?? '') === 'DocumentReference'));
|
||||
$this->assertCount(1, $documents);
|
||||
$this->assertSame('%PDF-test', base64_decode((string) ($documents[0]['resource']['content'][0]['attachment']['data'] ?? ''), true));
|
||||
$this->assertSame('final', $documents[0]['resource']['docStatus'] ?? null);
|
||||
$this->assertSame('373942005', $documents[0]['resource']['type']['coding'][0]['code'] ?? null);
|
||||
|
||||
$sectionCodes = array_column(array_map(static fn (array $section): array => (array) ($section['code']['coding'][0] ?? []), $composition['section']), 'code');
|
||||
$this->assertSame(['1003642006', '1003640003', '1003606003', '721981007', '734163000', '373942005'], $sectionCodes);
|
||||
$reports = array_values(array_filter($bundle['entry'], static fn (array $entry): bool => ($entry['resource']['resourceType'] ?? '') === 'DiagnosticReport'));
|
||||
$this->assertCount(1, $reports);
|
||||
$this->assertContains('https://nrces.in/ndhm/fhir/r4/StructureDefinition/DiagnosticReportLab', $reports[0]['resource']['meta']['profile'] ?? []);
|
||||
|
||||
$encoded = json_encode($bundle, JSON_UNESCAPED_SLASHES);
|
||||
preg_match_all('/"reference":"(urn:uuid:[^"]+)"/', (string) $encoded, $matches);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user