ac-fhir-models
Advanced tools
Comparing version 7.6.4 to 7.7.0-rc.0
{ | ||
"name": "ac-fhir-models", | ||
"version": "7.6.4", | ||
"version": "7.7.0-rc.0", | ||
"author": "Henrik Joreteg <henrik@anesthesiacharting.com>", | ||
@@ -42,5 +42,5 @@ "dependencies": { | ||
"static": "fixpack && npm run format && npm run lint && npm run tsc", | ||
"test": "tape -r esm src/**/*.spec.js | tap-spec", | ||
"test": "tape -r esm -r ./test-helpers.js src/**/*.spec.js | tap-spec", | ||
"tsc": "tsc -p jsconfig.json" | ||
} | ||
} |
@@ -14,2 +14,3 @@ import dlv from 'dlv' | ||
* @returns {{ | ||
* resourceType: string | ||
* create: (staringData: any) => any | ||
@@ -16,0 +17,0 @@ * set: (data: any, nameOrObject: string | any, value?: any) => any |
@@ -20,3 +20,3 @@ // @ts-check | ||
note: 'note.[0].text', | ||
dateTime: getTimeProp('recordedDate'), | ||
date: getTimeProp('recordedDate'), | ||
}, | ||
@@ -23,0 +23,0 @@ definition: buildDefinition( |
@@ -34,3 +34,3 @@ // @ts-check | ||
test('allergyIntolerance', t => { | ||
const dateTime = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
@@ -42,3 +42,3 @@ const startProps = { | ||
note: 'Allergic to peanuts', | ||
dateTime, | ||
date, | ||
} | ||
@@ -64,3 +64,3 @@ | ||
}, | ||
recordedDate: dateTime, | ||
recordedDate: date, | ||
}) | ||
@@ -67,0 +67,0 @@ |
@@ -114,3 +114,3 @@ // @ts-check | ||
test('patientCommentDefinition', t => { | ||
const dateTime = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
@@ -120,3 +120,3 @@ const startProps = { | ||
author: { type: 'Practitioner', id: '456' }, | ||
date: dateTime, | ||
date, | ||
text: 'Hello', | ||
@@ -132,3 +132,3 @@ appointmentId: 'abc', | ||
sender: { reference: 'Practitioner/456' }, | ||
sent: dateTime, | ||
sent: date, | ||
payload: [{ contentString: 'Hello' }], | ||
@@ -155,3 +155,3 @@ category: [ | ||
test('patientCommentDefinition with editStatus', t => { | ||
const dateTime = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
@@ -161,3 +161,3 @@ const startProps = { | ||
author: { type: 'Practitioner', id: '456' }, | ||
date: dateTime, | ||
date, | ||
text: 'Hello', | ||
@@ -164,0 +164,0 @@ editStatus: 'edited', |
// @ts-check | ||
import { buildDefinition } from 'sinks' | ||
import { buildDefinition, setValue } from 'sinks' | ||
import { createFHIRModelDefinition } from '../helpers/fhir-model.js' | ||
@@ -9,2 +9,3 @@ import { fhirFieldTypes } from '../helpers/fhir-types.js' | ||
getReferenceProp, | ||
toFHIRInstant, | ||
} from '../helpers/prop-definitions.js' | ||
@@ -35,2 +36,299 @@ | ||
const compositionFields = { | ||
resourceType: 'str', | ||
id: 'str', | ||
orgId: 'str', | ||
title: 'str', | ||
'subject.reference': 'str', | ||
'author.[].reference': 'str', | ||
'type.coding.[].system': 'str', | ||
'type.coding.[].code': 'str', | ||
'type.coding.[].display': 'str', | ||
date: 'str', | ||
'contained.[].resourceType': 'str', | ||
'contained.[].id': 'str', | ||
'contained.[].contentType': 'str', | ||
'contained.[].data': 'str', | ||
'meta.lastUpdated': 'str', | ||
'meta.versionId': 'str', | ||
status: 'compositionStatus', | ||
'section.[].title': 'str', | ||
'section.[].entry.[].reference': 'str', | ||
'section.[].entry.[].extension.[].url': 'str', | ||
'section.[].entry.[].extension.[].valueString': 'str', | ||
'section.[].code.coding.[].system': 'str', | ||
'section.[].code.coding.[].code': 'str', | ||
'section.[].code.coding.[].display': 'str', | ||
'attester.[].party.reference': 'str', | ||
'attester.[].mode': 'str', | ||
'attester.[].time': 'str', | ||
} | ||
// a section which just serializes the input json into an extension on the section entry | ||
const getCompositionJsonSectionProp = ({ | ||
title, | ||
coding, | ||
extensionUrl, | ||
definition = null, | ||
}) => { | ||
const predicate = section => | ||
section.code && | ||
section.code.coding && | ||
section.code.coding.some( | ||
c => c.system === coding.system && c.code === coding.code | ||
) | ||
return { | ||
get(data) { | ||
const sections = data.section || [] | ||
const existingSection = sections.find(predicate) | ||
if (!existingSection) return null | ||
const entries = existingSection.entry || [] | ||
if (entries.length === 0) return null | ||
for (const entry of entries) { | ||
const ext = entry.extension.find(ext => ext.url === extensionUrl) | ||
if (ext) { | ||
return JSON.parse(ext.valueString) | ||
} | ||
} | ||
return null | ||
}, | ||
set(data, value) { | ||
const sections = data.section || [] | ||
const length = sections.length | ||
const foundIndex = sections.findIndex(predicate) | ||
const index = foundIndex !== -1 ? foundIndex : length | ||
if (value === null) { | ||
return setValue(data, `section.[${index}]`, null) | ||
} | ||
if (definition) { | ||
definition.validate(value) | ||
} | ||
const newEntryValue = { | ||
extension: [ | ||
{ | ||
url: extensionUrl, | ||
valueString: JSON.stringify(value), | ||
}, | ||
], | ||
} | ||
if (foundIndex !== -1) { | ||
return setValue(data, `section.[${index}].entry`, [newEntryValue]) | ||
} else { | ||
return setValue(data, `section.[${index}]`, { | ||
title, | ||
code: { coding: [coding] }, | ||
entry: [newEntryValue], | ||
}) | ||
} | ||
}, | ||
} | ||
} | ||
const getCompositionSectionProp = ({ | ||
title, | ||
coding, | ||
singularEntry = false, | ||
}) => { | ||
const predicate = section => | ||
section.code && | ||
section.code.coding && | ||
section.code.coding.some( | ||
c => c.system === coding.system && c.code === coding.code | ||
) | ||
return { | ||
get(obj) { | ||
const sections = obj.section || [] | ||
const existing = sections.find(predicate) | ||
if (!existing) return null | ||
const entries = existing.entry || [] | ||
if (entries.length === 0) return null | ||
if (singularEntry) { | ||
return entries[0].reference | ||
} else { | ||
return entries.map(e => e.reference) | ||
} | ||
}, | ||
set(obj, entryReferences) { | ||
const sections = obj.section || [] | ||
const length = sections.length | ||
const foundIndex = sections.findIndex(predicate) | ||
const index = foundIndex !== -1 ? foundIndex : length | ||
if (entryReferences === null) { | ||
return setValue(obj, `section.[${index}]`, null) | ||
} | ||
const newEntryValue = singularEntry | ||
? [{ reference: entryReferences }] | ||
: entryReferences.map(ref => ({ reference: ref })) | ||
if (foundIndex !== -1) { | ||
return setValue(obj, `section.[${index}].entry`, newEntryValue) | ||
} else { | ||
return setValue(obj, `section.[${index}]`, { | ||
title, | ||
code: { coding: [coding] }, | ||
entry: newEntryValue, | ||
}) | ||
} | ||
}, | ||
} | ||
} | ||
export const getAttestersProp = () => ({ | ||
get: data => { | ||
const attester = data.attester || [] | ||
return attester.map(att => ({ | ||
memberId: att.party.reference.replace('Practitioner/', ''), | ||
mode: att.mode, | ||
date: att.time, | ||
})) | ||
}, | ||
set: (data, value) => { | ||
const attester = value.map(att => ({ | ||
party: { reference: `Practitioner/${att.memberId}` }, | ||
mode: att.mode || 'professional', | ||
time: att.date || toFHIRInstant(new Date()), | ||
})) | ||
return setValue(data, 'attester', attester) | ||
}, | ||
}) | ||
export const surgicalHistoryJSONDefinition = buildDefinition({ | ||
anesthesiaHistorySummary: 'str', | ||
familyAnesthesiaHistorySummary: 'str', | ||
surgicalHistorySummary: 'str', | ||
healthHistorySummary: 'str', | ||
hospitalizationSummary: 'str', | ||
}) | ||
export const systemsReviewJSONDefinition = buildDefinition({ | ||
'{}.hasEntries': 'bool', | ||
'{}.entries': 'arr', | ||
'{}.entries.[].editing': 'bool', | ||
'{}.entries.[].details.{}': 'str', | ||
'{}.entries.[].details.{}.name': 'str', | ||
'{}.entries.[].details.{}.label': 'str', | ||
'{}.entries.[].details.{}.path': 'str', | ||
'{}.entries.[].details.{}.value': 'str', | ||
}) | ||
export const healthHistoryDefinition = createFHIRModelDefinition({ | ||
resourceType: 'Composition', | ||
props: { | ||
id: 'id', | ||
title: 'title', | ||
status: 'status', | ||
authorId: getReferenceProp('author', 'Practitioner'), | ||
patientId: getDirectReferenceProp('subject', 'Patient'), | ||
date: 'date', | ||
attesters: getAttestersProp(), | ||
physicalAttributes: getCompositionSectionProp({ | ||
title: 'Physical Attributes', | ||
coding: { | ||
system: 'http://loinc.org', | ||
code: '8716-3', | ||
display: 'Vital Signs', | ||
}, | ||
singularEntry: true, | ||
}), | ||
surgicalHistory: getCompositionJsonSectionProp({ | ||
title: 'Surgical History', | ||
coding: { | ||
system: 'http://loinc.org', | ||
code: '10167-5', | ||
display: 'History of Surgical Procedures', | ||
}, | ||
extensionUrl: | ||
'https://ns.anesthesiacharting.com/ext/json-surgical-history', | ||
definition: surgicalHistoryJSONDefinition, | ||
}), | ||
systemsReview: getCompositionJsonSectionProp({ | ||
title: 'Systems Review', | ||
coding: { | ||
system: 'http://loinc.org', | ||
code: '10187-3', | ||
display: 'Review of Systems', | ||
}, | ||
extensionUrl: 'https://ns.anesthesiacharting.com/ext/json-systems-review', | ||
definition: systemsReviewJSONDefinition, | ||
}), | ||
allergies: getCompositionSectionProp({ | ||
title: 'Allergies', | ||
coding: { | ||
system: 'http://loinc.org', | ||
code: '8658-7', | ||
display: 'History of Allergies', | ||
}, | ||
}), | ||
medications: getCompositionSectionProp({ | ||
title: 'Medications', | ||
coding: { | ||
system: 'http://loinc.org', | ||
code: '8677-7', | ||
display: 'History of Medication use', | ||
}, | ||
}), | ||
signature: getCompositionJsonSectionProp({ | ||
title: 'Signature', | ||
coding: { | ||
system: 'https://ns.anesthesiacharting.com/composition-sections', | ||
code: 'signature', | ||
display: 'Signature', | ||
}, | ||
extensionUrl: 'https://ns.anesthesiacharting.com/ext/json-signature', | ||
}), | ||
lastUpdated: 'meta.lastUpdated', | ||
}, | ||
definition: buildDefinition(compositionFields, fhirFieldTypes), | ||
baseProps: { | ||
title: 'Health History', | ||
type: { | ||
coding: [ | ||
{ | ||
system: 'http://loinc.org', | ||
code: '35089-2', | ||
display: 'History and Physical Panel', | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
export const healthHistoryPatientAnswersDefinition = createFHIRModelDefinition({ | ||
resourceType: 'Composition', | ||
props: { | ||
id: 'id', | ||
title: 'title', | ||
status: 'status', | ||
authorId: getReferenceProp('author', 'Patient'), | ||
patientId: getDirectReferenceProp('subject', 'Patient'), | ||
date: 'date', | ||
healthHistory: getContainedObjectProp('healthHistory'), | ||
}, | ||
definition: buildDefinition(compositionFields, fhirFieldTypes), | ||
baseProps: { | ||
title: 'Health History Patient Answers', | ||
type: { | ||
coding: [ | ||
{ | ||
system: 'https://loinc.org', | ||
code: '11329-0', | ||
display: 'History general Narrative - Reported', | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
export const compositionDefinition = createFHIRModelDefinition({ | ||
@@ -47,24 +345,3 @@ resourceType: 'Composition', | ||
}, | ||
definition: buildDefinition( | ||
{ | ||
resourceType: 'str', | ||
id: 'str', | ||
orgId: 'str', | ||
title: 'str', | ||
'subject.reference': 'str', | ||
'author.[].reference': 'str', | ||
'type.coding.[].system': 'str', | ||
'type.coding.[].code': 'str', | ||
'type.coding.[].display': 'str', | ||
date: 'str', | ||
'contained.[].resourceType': 'str', | ||
'contained.[].id': 'str', | ||
'contained.[].contentType': 'str', | ||
'contained.[].data': 'str', | ||
'meta.lastUpdated': 'str', | ||
'meta.versionId': 'str', | ||
status: 'compositionStatus', | ||
}, | ||
fhirFieldTypes | ||
), | ||
definition: buildDefinition(compositionFields, fhirFieldTypes), | ||
baseProps: { | ||
@@ -71,0 +348,0 @@ type: { |
@@ -42,3 +42,3 @@ // @ts-check | ||
status: 'status', | ||
dateTime: getTimeProp('dateAsserted'), | ||
date: getTimeProp('dateAsserted'), | ||
}, | ||
@@ -45,0 +45,0 @@ definition: buildDefinition( |
@@ -50,7 +50,7 @@ // @ts-check | ||
test('medicationStatement', t => { | ||
const dateTime = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
const startProps = { | ||
medication: { id: '1234', name: 'Ibuprofen' }, | ||
patientId: '123', | ||
dateTime, | ||
date, | ||
} | ||
@@ -75,3 +75,3 @@ | ||
}, | ||
dateAsserted: dateTime, | ||
dateAsserted: date, | ||
}, | ||
@@ -78,0 +78,0 @@ 'generated FHIR should match' |
@@ -14,6 +14,3 @@ // @ts-check | ||
'https://ns.anesthesiacharting.com/observation-types' | ||
const nsHealthHistoryTypes = 'https://ns.anesthesiacharting.com/health-history' | ||
const nsSystemReview = 'https://ns.anesthesiacharting.com/system-review' | ||
const nsSystemReviewAnswer = | ||
'https://ns.anesthesiacharting.com/system-review-answer' | ||
const rawValueExtensionUrl = 'https://ns.anesthesiacharting.com/ext/raw-value' | ||
@@ -24,3 +21,4 @@ const getObservationComponentValueQuantityProp = ( | ||
display, | ||
unit | ||
unit, | ||
{ supportRaw = false } = {} | ||
) => { | ||
@@ -35,3 +33,17 @@ const predicate = item => | ||
if (!component) return null | ||
return dlv(component, 'valueQuantity.value') | ||
const value = dlv(component, 'valueQuantity.value') | ||
const unit = dlv(component, 'valueQuantity.unit') | ||
const result = { value, unit } | ||
const ext = (component.extension || []).find( | ||
ext => ext.url === rawValueExtensionUrl | ||
) | ||
if (ext) { | ||
result.raw = ext.valueString | ||
} | ||
return result | ||
}, | ||
@@ -46,2 +58,25 @@ set: (data, value) => { | ||
if (value) { | ||
if (supportRaw && !(typeof value === 'object' && 'value' in value)) { | ||
throw new Error( | ||
'Value must have value and optional raw properties: ' + | ||
JSON.stringify(value) | ||
) | ||
} | ||
const newProps = { | ||
valueQuantity: { | ||
unit, | ||
}, | ||
} | ||
if (supportRaw) { | ||
newProps.valueQuantity.value = value.value | ||
newProps.extension = | ||
'raw' in value | ||
? [{ url: rawValueExtensionUrl, valueString: value.raw }] | ||
: null | ||
} else { | ||
newProps.valueQuantity.value = value | ||
} | ||
return setValue(data, `component.[${index}]`, { | ||
@@ -57,6 +92,3 @@ code: { | ||
}, | ||
valueQuantity: { | ||
unit, | ||
value, | ||
}, | ||
...newProps, | ||
}) | ||
@@ -90,4 +122,12 @@ } else { | ||
'Body Weight', | ||
'kg' | ||
'kg', | ||
{ supportRaw: true } | ||
) | ||
const rawHeightInCmProp = getObservationComponentValueQuantityProp( | ||
ncLoinc, | ||
'8302-2', | ||
'Body Height', | ||
'cm', | ||
{ supportRaw: true } | ||
) | ||
@@ -101,9 +141,2 @@ const rawBmiRatioProp = getObservationComponentValueQuantityProp( | ||
const rawHeightInCmProp = getObservationComponentValueQuantityProp( | ||
ncLoinc, | ||
'8302-2', | ||
'Body Height', | ||
'cm' | ||
) | ||
const calcBmiRatio = (weight, height) => { | ||
@@ -118,3 +151,3 @@ if (!height || !weight) { | ||
const weightInKgProp = { | ||
const weightWithRawProp = { | ||
get: rawWeightInKgProp.get, | ||
@@ -124,7 +157,10 @@ set: (data, weight) => { | ||
data = rawWeightInKgProp.set(data, weight) | ||
return rawBmiRatioProp.set(data, calcBmiRatio(weight, height)) | ||
return rawBmiRatioProp.set( | ||
data, | ||
calcBmiRatio(weight && weight.value, height && height.value) | ||
) | ||
}, | ||
} | ||
const heightInCmProp = { | ||
const heightWithRawProp = { | ||
get: rawHeightInCmProp.get, | ||
@@ -134,3 +170,6 @@ set: (data, height) => { | ||
data = rawHeightInCmProp.set(data, height) | ||
return rawBmiRatioProp.set(data, calcBmiRatio(weight, height)) | ||
return rawBmiRatioProp.set( | ||
data, | ||
calcBmiRatio(weight && weight.value, height && height.value) | ||
) | ||
}, | ||
@@ -152,2 +191,4 @@ } | ||
'component.[].code.coding.[].display': 'str', | ||
'component.[].extension.[].url': 'str', | ||
'component.[].extension.[].valueString': 'str', | ||
'component.[].valueQuantity.value': 'num', | ||
@@ -163,3 +204,3 @@ 'component.[].valueQuantity.unit': 'str', | ||
patientId: getDirectReferenceProp('subject', 'Patient'), | ||
dateTime: getTimeProp('effectiveDateTime'), | ||
date: getTimeProp('effectiveDateTime'), | ||
status: 'status', | ||
@@ -179,7 +220,8 @@ } | ||
...baseObservationProps, | ||
weightInKg: weightInKgProp, | ||
heightInCm: heightInCmProp, | ||
weight: weightWithRawProp, | ||
height: heightWithRawProp, | ||
bmiRatio: { | ||
get: rawBmiRatioProp.get, | ||
set: data => { | ||
// cannot be set directly, is set when weight or height is set | ||
return data | ||
@@ -205,148 +247,2 @@ }, | ||
const getObservationComponentValueTextProp = (system, code) => { | ||
const predicate = item => | ||
dlv(item, 'code.coding.0.system') === system && | ||
dlv(item, 'code.coding.0.code') === code | ||
return { | ||
get: data => { | ||
const component = (data.component || []).find(predicate) | ||
if (!component) return null | ||
return dlv(component, 'valueString') | ||
}, | ||
set: (data, value) => { | ||
const array = data.component || [] | ||
const length = (array && array.length) || 0 | ||
const foundIndex = array.findIndex(predicate) | ||
const index = foundIndex > -1 ? foundIndex : length | ||
if (value) { | ||
return setValue(data, `component.[${index}]`, { | ||
code: { | ||
coding: [ | ||
{ | ||
code, | ||
system, | ||
}, | ||
], | ||
}, | ||
valueString: value, | ||
}) | ||
} else { | ||
return setValue(data, `component.[${index}]`, null) | ||
} | ||
}, | ||
} | ||
} | ||
export const getSystemReviewComponentProp = (system, code) => { | ||
const predicate = item => | ||
dlv(item, 'code.coding.0.system') === system && | ||
dlv(item, 'code.coding.0.code') === code | ||
return { | ||
get: data => { | ||
const component = (data.component || []).find(predicate) | ||
if (!component) return null | ||
const code = dlv(component, 'valueCodeableConcept.coding.0.code') | ||
const text = dlv(component, 'valueCodeableConcept.text') | ||
const hasEntries = code === 'YES' | ||
return text ? { hasEntries, text } : { hasEntries } | ||
}, | ||
set: (data, value) => { | ||
const array = data.component || [] | ||
const length = (array && array.length) || 0 | ||
const foundIndex = array.findIndex(predicate) | ||
const index = foundIndex > -1 ? foundIndex : length | ||
const toSet = { | ||
code: { | ||
coding: [ | ||
{ | ||
code, | ||
system, | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
code: value.hasEntries ? 'YES' : 'NO', | ||
system: nsSystemReviewAnswer, | ||
}, | ||
], | ||
}, | ||
} | ||
if (value.text) { | ||
toSet.valueCodeableConcept.text = value.text | ||
} | ||
if (value) { | ||
return setValue(data, `component.[${index}]`, toSet) | ||
} else { | ||
return setValue(data, `component.[${index}]`, null) | ||
} | ||
}, | ||
} | ||
} | ||
const textQuestions = [ | ||
'hospitalizationSummary', | ||
'surgicalHistorySummary', | ||
'anesthesiaHistorySummary', | ||
'familyAnesthesiaHistorySummary', | ||
] | ||
const systemQuestions = [ | ||
'general', | ||
'cardiac', | ||
'pulmonary', | ||
'hepatic', | ||
'renal', | ||
'hematologic', | ||
'endocrine', | ||
'oncology', | ||
'cns', | ||
'muscular', | ||
'gastro', | ||
'psychiatric', | ||
'gyn', | ||
'developmental', | ||
'habits', | ||
] | ||
export const healthHistoryObservationDefinition = createFHIRModelDefinition({ | ||
resourceType: 'Observation', | ||
props: { | ||
...baseObservationProps, | ||
...textQuestions.reduce((acc, question) => { | ||
acc[question] = getObservationComponentValueTextProp( | ||
nsHealthHistoryTypes, | ||
question | ||
) | ||
return acc | ||
}, {}), | ||
...systemQuestions.reduce((acc, question) => { | ||
acc[question] = getSystemReviewComponentProp(nsSystemReview, question) | ||
return acc | ||
}, {}), | ||
}, | ||
definition: baseObservationSinkDefinition, | ||
baseProps: { | ||
status: 'final', | ||
code: { | ||
coding: [ | ||
{ | ||
system: nsAcObservationTypes, | ||
code: 'health-history', | ||
display: 'Health History', | ||
}, | ||
], | ||
}, | ||
}, | ||
}) | ||
// Since we can have different observation types with different properties, | ||
@@ -359,6 +255,3 @@ // we need to determine which definition to use based on the observation type. | ||
} | ||
if (baseValues.observationType === 'health-history') { | ||
return healthHistoryObservationDefinition.getValues(data) | ||
} | ||
return baseValues | ||
} |
@@ -5,3 +5,2 @@ // @ts-check | ||
getObservationValues, | ||
healthHistoryObservationDefinition, | ||
physicalAttributesObservationDefinition, | ||
@@ -11,16 +10,10 @@ } from './observation' | ||
test('getObservationValues', t => { | ||
const dateTimeString = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
const physical = physicalAttributesObservationDefinition.create({ | ||
patientId: '123', | ||
dateTime: dateTimeString, | ||
weightInKg: 72, | ||
heightInCm: 180, | ||
date, | ||
weight: { value: 72, raw: '72 kg' }, | ||
height: { value: 180, raw: '6 ft' }, | ||
}) | ||
const healthHistory = healthHistoryObservationDefinition.create({ | ||
patientId: '123', | ||
dateTime: dateTimeString, | ||
hospitalizationSummary: 'Summary of hospitalization', | ||
}) | ||
const someOtherObservation = { | ||
@@ -43,6 +36,6 @@ // give it some attributes so we can be sure it's not parsed | ||
patientId: '123', | ||
dateTime: dateTimeString, | ||
weightInKg: 72, | ||
heightInCm: 180, | ||
bmiRatio: 22.2, | ||
date, | ||
weight: { value: 72, unit: 'kg', raw: '72 kg' }, | ||
height: { value: 180, unit: 'cm', raw: '6 ft' }, | ||
bmiRatio: { value: 22.2, unit: 'kg/m2' }, | ||
status: 'final', | ||
@@ -52,15 +45,6 @@ observationType: 'physical-attributes', | ||
const healthHistoryValues = getObservationValues(healthHistory) | ||
t.deepEqual(healthHistoryValues, { | ||
patientId: '123', | ||
dateTime: dateTimeString, | ||
hospitalizationSummary: 'Summary of hospitalization', | ||
status: 'final', | ||
observationType: 'health-history', | ||
}) | ||
const otherValues = getObservationValues(someOtherObservation) | ||
t.deepEqual(otherValues, { | ||
patientId: '123', | ||
dateTime: dateTimeString, | ||
date, | ||
status: 'final', | ||
@@ -80,5 +64,5 @@ observationType: 'some-other-observation', | ||
patientId: '123', | ||
dateTime: new Date().toISOString(), | ||
weightInKg: 72, | ||
heightInCm: 180, | ||
date: new Date().toISOString(), | ||
weight: { value: 72 }, | ||
height: { value: 180 }, | ||
}) | ||
@@ -88,6 +72,6 @@ | ||
const data2 = def.set(data, 'weightInKg', 80) | ||
const data2 = def.set(data, 'weight', { value: 80 }) | ||
t.equal(bmiComponent(data2).valueQuantity.value, 24.7, 'Updates from weight') | ||
const data3 = def.set(data2, 'heightInCm', 150) | ||
const data3 = def.set(data2, 'height', { value: 150 }) | ||
t.equal(bmiComponent(data3).valueQuantity.value, 35.6, 'Updates from height') | ||
@@ -98,3 +82,3 @@ | ||
const data5 = def.set(data4, 'heightInCm', null) | ||
const data5 = def.set(data4, 'height', null) | ||
t.notOk(bmiComponent(data5), 'Clears BMI on clearing height') | ||
@@ -106,8 +90,8 @@ | ||
test('physicalAttrsObservationDefinition', t => { | ||
const dateTime = new Date().toISOString() | ||
const date = new Date().toISOString() | ||
const startProps = { | ||
patientId: '123', | ||
dateTime, | ||
weightInKg: 72, | ||
heightInCm: 180, | ||
date, | ||
weight: { value: 72, raw: '72 kg' }, | ||
height: { value: 180, raw: '6 ft' }, | ||
} | ||
@@ -121,3 +105,3 @@ | ||
status: 'final', | ||
effectiveDateTime: dateTime, | ||
effectiveDateTime: date, | ||
code: { | ||
@@ -144,2 +128,8 @@ coding: [ | ||
valueQuantity: { value: 72, unit: 'kg' }, | ||
extension: [ | ||
{ | ||
url: 'https://ns.anesthesiacharting.com/ext/raw-value', | ||
valueString: '72 kg', | ||
}, | ||
], | ||
}, | ||
@@ -157,2 +147,8 @@ { | ||
valueQuantity: { value: 180, unit: 'cm' }, | ||
extension: [ | ||
{ | ||
url: 'https://ns.anesthesiacharting.com/ext/raw-value', | ||
valueString: '6 ft', | ||
}, | ||
], | ||
}, | ||
@@ -176,3 +172,5 @@ { | ||
...startProps, | ||
bmiRatio: 22.2, | ||
weight: { value: 72, unit: 'kg', raw: '72 kg' }, | ||
height: { value: 180, unit: 'cm', raw: '6 ft' }, | ||
bmiRatio: { value: 22.2, unit: 'kg/m2' }, | ||
status: 'final', | ||
@@ -184,415 +182,1 @@ observationType: 'physical-attributes', | ||
}) | ||
test('healthHistory', t => { | ||
const dateTime = new Date().toISOString() | ||
const startProps = { | ||
dateTime, | ||
patientId: '123', | ||
hospitalizationSummary: 'Summary of hospitalization', | ||
surgicalHistorySummary: 'Summary of surgical history', | ||
anesthesiaHistorySummary: 'Summary of anesthesia history', | ||
familyAnesthesiaHistorySummary: 'Summary of family anesthesia history', | ||
general: { hasEntries: true, text: 'GENERAL TEXT' }, | ||
cardiac: { hasEntries: true, text: 'CARDIAC TEXT' }, | ||
pulmonary: { hasEntries: true, text: 'PULMONARY TEXT' }, | ||
hepatic: { hasEntries: true, text: 'HEPATIC TEXT' }, | ||
renal: { hasEntries: true, text: 'RENAL TEXT' }, | ||
hematologic: { hasEntries: true, text: 'HEMATOLOGIC TEXT' }, | ||
endocrine: { hasEntries: true, text: 'ENDOCRINE TEXT' }, | ||
oncology: { hasEntries: true, text: 'ONCOLOGY TEXT' }, | ||
cns: { hasEntries: true, text: 'CNS TEXT' }, | ||
muscular: { hasEntries: true, text: 'MUSCULAR TEXT' }, | ||
gastro: { hasEntries: true, text: 'GASTRO TEXT' }, | ||
psychiatric: { hasEntries: true, text: 'PSYCHIATRIC TEXT' }, | ||
gyn: { hasEntries: true, text: 'GYN TEXT' }, | ||
developmental: { hasEntries: true, text: 'DEVELOPMENTAL TEXT' }, | ||
habits: { hasEntries: true, text: 'HABITS TEXT' }, | ||
} | ||
const resource = healthHistoryObservationDefinition.create(startProps) | ||
t.deepEqual(resource, { | ||
resourceType: 'Observation', | ||
status: 'final', | ||
subject: { reference: 'Patient/123' }, | ||
effectiveDateTime: dateTime, | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/observation-types', | ||
code: 'health-history', | ||
display: 'Health History', | ||
}, | ||
], | ||
}, | ||
component: [ | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/health-history', | ||
code: 'hospitalizationSummary', | ||
}, | ||
], | ||
}, | ||
valueString: 'Summary of hospitalization', | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/health-history', | ||
code: 'surgicalHistorySummary', | ||
}, | ||
], | ||
}, | ||
valueString: 'Summary of surgical history', | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/health-history', | ||
code: 'anesthesiaHistorySummary', | ||
}, | ||
], | ||
}, | ||
valueString: 'Summary of anesthesia history', | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/health-history', | ||
code: 'familyAnesthesiaHistorySummary', | ||
}, | ||
], | ||
}, | ||
valueString: 'Summary of family anesthesia history', | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'general', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'GENERAL TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'cardiac', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'CARDIAC TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'pulmonary', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'PULMONARY TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'hepatic', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'HEPATIC TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'renal', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'RENAL TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'hematologic', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'HEMATOLOGIC TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'endocrine', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'ENDOCRINE TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'oncology', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'ONCOLOGY TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'cns', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'CNS TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'muscular', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'MUSCULAR TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'gastro', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'GASTRO TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'psychiatric', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'PSYCHIATRIC TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'gyn', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'GYN TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'developmental', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'DEVELOPMENTAL TEXT', | ||
}, | ||
}, | ||
{ | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'habits', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'YES', | ||
}, | ||
], | ||
text: 'HABITS TEXT', | ||
}, | ||
}, | ||
], | ||
}) | ||
t.deepEqual(healthHistoryObservationDefinition.getValues(resource), { | ||
...startProps, | ||
status: 'final', | ||
observationType: 'health-history', | ||
}) | ||
const propsSystemReviewNo = { | ||
general: { hasEntries: false }, | ||
} | ||
const resource2 = healthHistoryObservationDefinition.create( | ||
propsSystemReviewNo | ||
) | ||
t.deepEqual(resource2.component[0], { | ||
code: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review', | ||
code: 'general', | ||
}, | ||
], | ||
}, | ||
valueCodeableConcept: { | ||
coding: [ | ||
{ | ||
system: 'https://ns.anesthesiacharting.com/system-review-answer', | ||
code: 'NO', | ||
}, | ||
], | ||
}, | ||
}) | ||
const values2 = healthHistoryObservationDefinition.getValues(resource2) | ||
t.deepEqual(values2.general, { hasEntries: false }) | ||
t.end() | ||
}) |
Sorry, the diff of this file is too big to display
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
210199
38
7250
2