Fetching additional referenced resources after processing.

fixing hospitalization key.
Tweaking the cerner client to use USCore resources, and then adding additional cerner specific resources.
This commit is contained in:
Jason Kulatunga 2022-11-06 18:42:34 -08:00
parent 4bc62135ea
commit aa479fd655
4 changed files with 106 additions and 21 deletions

View File

@ -9,6 +9,8 @@ import { v4 as uuidv4 } from 'uuid';
// @ts-ignore // @ts-ignore
import * as FHIR401Client_ProcessBundle from './fixtures/FHIR401Client_ProcessBundle.json'; import * as FHIR401Client_ProcessBundle from './fixtures/FHIR401Client_ProcessBundle.json';
// @ts-ignore
import * as FHIR401Client_ExtractResourceReference from './fixtures/FHIR401Client_ExtractResourceReference.json';
import {IDatabaseRepository} from '../../../database/interface'; import {IDatabaseRepository} from '../../../database/interface';
import {PouchdbCrypto} from '../../../database/plugins/crypto'; import {PouchdbCrypto} from '../../../database/plugins/crypto';
import {ClientConfig} from '../../../models/client/client-config'; import {ClientConfig} from '../../../models/client/client-config';
@ -101,4 +103,17 @@ describe('FHIR401Client', () => {
}, 10000); }, 10000);
}) })
describe('ExtractResourceReference', () => {
it('should correctly extract resource identifier from raw resources', async () => {
//setup
//test
const resp = await client.ExtractResourceReference("CarePlan", FHIR401Client_ExtractResourceReference)
//expect
expect(resp.length).toEqual(2);
expect(resp).toEqual(['Encounter/97961321', 'Practitioner/12763770']);
});
})
}) })

View File

@ -1,4 +1,4 @@
import {IClient, IResourceBundleRaw, IResourceRaw} from '../../interface'; import {IClient, IResourceBundleEntryRaw, IResourceBundleRaw, IResourceRaw} from '../../interface';
import {BaseClient} from './base_client'; import {BaseClient} from './base_client';
import {Source} from '../../../models/database/source'; import {Source} from '../../../models/database/source';
import {IDatabaseRepository} from '../../../database/interface'; import {IDatabaseRepository} from '../../../database/interface';
@ -88,8 +88,29 @@ export class FHIR401Client extends BaseClient implements IClient {
let bundle = await this.GetResourceBundlePaginated(`${resourceType}?patient=${this.source.patient}`) let bundle = await this.GetResourceBundlePaginated(`${resourceType}?patient=${this.source.patient}`)
let wrappedResourceModels = await this.ProcessBundle(bundle) let wrappedResourceModels = await this.ProcessBundle(bundle)
let resourceUpsertSummary = await db.UpsertResources(wrappedResourceModels) let resourceUpsertSummary = await db.UpsertResources(wrappedResourceModels)
upsertSummary.updatedResources = upsertSummary.updatedResources.concat(resourceUpsertSummary.updatedResources) upsertSummary.updatedResources = upsertSummary.updatedResources.concat(resourceUpsertSummary.updatedResources)
upsertSummary.totalResources += resourceUpsertSummary.totalResources upsertSummary.totalResources += resourceUpsertSummary.totalResources
// check if theres any "extracted" resource references that we should sync as well
let extractedResourceReferences = []
extractedResourceReferences = wrappedResourceModels.reduce((previousVal, wrappedResource) => {
return previousVal.concat(this.ExtractResourceReference(wrappedResource.source_resource_type, wrappedResource.resource_raw))
}, extractedResourceReferences)
if(extractedResourceReferences.length > 0 ){
console.log("Extracted Resource References", extractedResourceReferences)
let extractedResourceBundle = await this.GenerateResourceBundleFromResourceIds(extractedResourceReferences)
let wrappedExtractedResourceBundle = await this.ProcessBundle(extractedResourceBundle)
let extractedResourceUpsertSummary = await db.UpsertResources(wrappedExtractedResourceBundle)
upsertSummary.updatedResources = upsertSummary.updatedResources.concat(extractedResourceUpsertSummary.updatedResources)
upsertSummary.totalResources += extractedResourceUpsertSummary.totalResources
}
} }
catch (e) { catch (e) {
console.error(`An error occurred while processing ${resourceType} bundle ${this.source.patient}`) console.error(`An error occurred while processing ${resourceType} bundle ${this.source.patient}`)
@ -109,7 +130,7 @@ export class FHIR401Client extends BaseClient implements IClient {
* @param resourceRaw * @param resourceRaw
* @constructor * @constructor
*/ */
public async ExtractResourceReference(sourceResourceType: string, resourceRaw): Promise<string[]> { public ExtractResourceReference(sourceResourceType: string, resourceRaw): string[] {
let resourceRefs = [] let resourceRefs = []
switch (sourceResourceType) { switch (sourceResourceType) {
@ -258,16 +279,15 @@ export class FHIR401Client extends BaseClient implements IClient {
resourceRefs.push(reasonReference.reference) resourceRefs.push(reasonReference.reference)
}) })
//hospitalization[x].origin can contain //hospitalization.origin can contain
//- Location //- Location
//- Organization //- Organization
//hospitalization[x].destination can contain resourceRefs.push(resourceRaw.hospitalization?.origin?.reference)
//hospitalization.destination can contain
//- Location //- Location
//- Organization //- Organization
resourceRaw.hospitalization?.map((hospitalization) => { resourceRefs.push(resourceRaw.hospitalization?.destination?.reference)
resourceRefs.push(hospitalization.origin?.reference)
resourceRefs.push(hospitalization.destination?.reference)
})
//location[x].location can contain //location[x].location can contain
//- Location //- Location
@ -440,7 +460,6 @@ export class FHIR401Client extends BaseClient implements IClient {
// remove all null values, remove all duplicates // remove all null values, remove all duplicates
let cleanResourceRefs = resourceRefs.filter(i => !(typeof i === 'undefined' || i === null)); let cleanResourceRefs = resourceRefs.filter(i => !(typeof i === 'undefined' || i === null));
cleanResourceRefs = [...new Set(cleanResourceRefs)] cleanResourceRefs = [...new Set(cleanResourceRefs)]
return cleanResourceRefs return cleanResourceRefs
} }
@ -504,6 +523,24 @@ export class FHIR401Client extends BaseClient implements IClient {
}) })
} }
/**
* Given a list of resource ids (Patient/xxx, Observation/yyyy), request these resources and generate a pseudo-bundle file
* @param resourceIds
* @constructor
* @protected
*/
protected async GenerateResourceBundleFromResourceIds(resourceIds: string[]): Promise<IResourceBundleRaw>{
resourceIds = [...new Set(resourceIds)] //make sure they are unique references.
let rawResourceRefs = await Promise.all(resourceIds.map(async (extractedResourceRef) => {
return {
resource: await this.GetRequest(extractedResourceRef) as IResourceRaw
} as IResourceBundleEntryRaw
}))
return {resourceType: "Bundle", entry: rawResourceRefs}
}
/** /**
* Retrieve a resource bundle. While "next" link is present in response, continue to request urls and append BundleEntries * Retrieve a resource bundle. While "next" link is present in response, continue to request urls and append BundleEntries
* @param relativeResourcePath * @param relativeResourcePath

View File

@ -0,0 +1,41 @@
{
"resourceType": "CarePlan",
"id": "197543976",
"meta": {
"versionId": "1",
"lastUpdated": "2021-10-04T14:46:33.000Z"
},
"text": {
"status": "additional",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><p><b>Care Plan</b></p><p><b>Patient</b>: SMART, NANCYU NHA EEEEEE</p><p><b>Title</b>: Comorbidities found via Retrieve Dx</p><p><b>Description</b>: Malnutrition:Severe --- Inputs [Height: 165.1, Weight: 25, BMI (Calculated): 9.171615976368047]\n\nAnoxic Brain Injury\n\n</p><p><b>Status</b>: Active</p><p><b>Intent</b>: Plan</p><p><b>Category</b>: Assessment and Plan of Treatment</p><p><b>Author</b>: P., MD, Cardio</p><p><b>Effective Period</b>: Oct 4, 2021 2:46 P.M. UTC</p></div>"
},
"status": "active",
"intent": "plan",
"category": [
{
"coding": [
{
"system": "http://hl7.org/fhir/us/core/CodeSystem/careplan-category",
"code": "assess-plan",
"display": "Assessment and Plan of Treatment"
}
],
"text": "Assessment and Plan of Treatment"
}
],
"title": "Comorbidities found via Retrieve Dx",
"subject": {
"reference": "Patient/12724066",
"display": "SMART, NANCYU NHA EEEEEE"
},
"encounter": {
"reference": "Encounter/97961321"
},
"period": {
"start": "2021-10-04T14:46:33.000Z"
},
"author": {
"reference": "Practitioner/12763770",
"display": "P., MD, Cardio"
}
}

View File

@ -18,23 +18,15 @@ export class CernerClient extends FHIR401Client implements IClient {
* @constructor * @constructor
*/ */
async SyncAll(db: IDatabaseRepository): Promise<UpsertSummary> { async SyncAll(db: IDatabaseRepository): Promise<UpsertSummary> {
const supportedResources: string[] = [ const supportedResources: string[] = this.usCoreResources.concat([
"AllergyIntolerance", "Account",
"CarePlan", "Appointment",
"CareTeam",
"Condition",
"Consent", "Consent",
"Device",
"Encounter",
"FamilyMemberHistory", "FamilyMemberHistory",
"Goal",
"Immunization",
"InsurancePlan", "InsurancePlan",
"MedicationRequest", "MedicationRequest",
"NutritionOrder", "NutritionOrder",
"Observation",
"Person", "Person",
"Procedure",
"Provenance", "Provenance",
"Questionnaire", "Questionnaire",
"QuestionnaireResponse", "QuestionnaireResponse",
@ -42,7 +34,7 @@ export class CernerClient extends FHIR401Client implements IClient {
"Schedule", "Schedule",
"ServiceRequest", "ServiceRequest",
"Slot", "Slot",
] ])
return this.SyncAllByResourceName(db, supportedResources) return this.SyncAllByResourceName(db, supportedResources)
} }