UNPKG

cql-execution

Version:

An execution framework for the Clinical Quality Language (CQL)

312 lines 12.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CalculateAgeAt = exports.CalculateAge = exports.Concept = exports.ConceptRef = exports.ConceptDef = exports.Code = exports.CodeRef = exports.CodeDef = exports.CodeSystemRef = exports.CodeSystemDef = exports.ExpandValueSet = exports.InValueSet = exports.AnyInValueSet = exports.ValueSetRef = exports.ValueSetDef = void 0; const expression_1 = require("./expression"); const dt = __importStar(require("../datatypes/datatypes")); const builder_1 = require("./builder"); const util_1 = require("../util/util"); class ValueSetDef extends expression_1.Expression { constructor(json) { var _a; super(json); this.name = json.name; this.id = json.id; this.version = json.version; this.codesystems = (_a = json.codeSystem) === null || _a === void 0 ? void 0 : _a.map((cs) => new CodeSystemRef(cs)); } async exec(ctx) { let codeSystems; if (this.codesystems) { codeSystems = (await Promise.all(this.codesystems.map(async (csRef) => csRef.exec(ctx)))); } const valueset = new dt.CQLValueSet(this.id, this.version, this.name, codeSystems); // ctx.rootContext().set(this.name, valueset); Note (2025): this seems to be unneccesary, remove completely in future if not needed return valueset; } } exports.ValueSetDef = ValueSetDef; class ValueSetRef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.libraryName = json.libraryName; } async exec(ctx) { let valueset = ctx.getValueSet(this.name, this.libraryName); if (valueset instanceof expression_1.Expression) { valueset = await valueset.execute(ctx); } return valueset; } } exports.ValueSetRef = ValueSetRef; class AnyInValueSet extends expression_1.Expression { constructor(json) { super(json); this.codes = (0, builder_1.build)(json.codes); this.valueset = json.valueset ? new ValueSetRef(json.valueset) : (0, builder_1.build)(json.valuesetExpression); } async exec(ctx) { const codes = await this.codes.execute(ctx); // spec indicates to return false if code is null, throw error if value set cannot be resolved if (codes == null) { return false; } const valueset = await this.valueset.execute(ctx); if (valueset == null || !valueset.isValueSet) { throw new Error('ValueSet must be provided to AnyInValueSet expression'); } const vsExpansion = await (0, util_1.resolveValueSet)(valueset, ctx); return codes.some((code) => vsExpansion.hasMatch(code)); } } exports.AnyInValueSet = AnyInValueSet; class InValueSet extends expression_1.Expression { constructor(json) { super(json); this.code = (0, builder_1.build)(json.code); this.valueset = json.valueset ? new ValueSetRef(json.valueset) : (0, builder_1.build)(json.valuesetExpression); } async exec(ctx) { const code = await this.code.execute(ctx); // spec indicates to return false if code is null, throw error if value set cannot be resolved if (code == null) { return false; } const valueset = await this.valueset.execute(ctx); if (valueset == null || !valueset.isValueSet) { throw new Error('ValueSet must be provided to InValueSet expression'); } // If there is a code and valueset return whether or not the valueset has the code const vsExpansion = await (0, util_1.resolveValueSet)(valueset, ctx); return vsExpansion.hasMatch(code); } } exports.InValueSet = InValueSet; class ExpandValueSet extends expression_1.Expression { constructor(json) { super(json); this.valueset = (0, builder_1.build)(json.operand); } async exec(ctx) { const valueset = await this.valueset.execute(ctx); if (valueset == null) { return null; } else if (!valueset.isValueSet) { throw new Error('ExpandValueSet function invoked on object that is not a ValueSet'); } const vsExpansion = await (0, util_1.resolveValueSet)(valueset, ctx); return vsExpansion.expand(); } } exports.ExpandValueSet = ExpandValueSet; class CodeSystemDef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.id = json.id; this.version = json.version; } async exec(_ctx) { return new dt.CodeSystem(this.id, this.version, this.name); } } exports.CodeSystemDef = CodeSystemDef; class CodeSystemRef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.libraryName = json.libraryName; } async exec(ctx) { const codeSystemDef = ctx.getCodeSystem(this.name, this.libraryName); return codeSystemDef.execute(ctx); } } exports.CodeSystemRef = CodeSystemRef; class CodeDef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.id = json.id; this.systemName = json.codeSystem.name; this.display = json.display; } async exec(ctx) { const system = await ctx.getCodeSystem(this.systemName).execute(ctx); return new dt.Code(this.id, system.id, system.version, this.display); } } exports.CodeDef = CodeDef; class CodeRef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.library = json.libraryName; } async exec(ctx) { ctx = this.library ? ctx.getLibraryContext(this.library) : ctx; const codeDef = ctx.getCode(this.name); return codeDef ? codeDef.execute(ctx) : undefined; } } exports.CodeRef = CodeRef; class Code extends expression_1.Expression { constructor(json) { super(json); this.code = json.code; this.systemName = json.system.name; this.version = json.version; this.display = json.display; } // Define a simple getter to allow type-checking of this class without instanceof // and in a way that survives minification (as opposed to checking constructor.name) get isCode() { return true; } async exec(ctx) { const system = ctx.getCodeSystem(this.systemName) || {}; return new dt.Code(this.code, system.id, this.version, this.display); } } exports.Code = Code; class ConceptDef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; this.display = json.display; this.codes = json.code; } async exec(ctx) { const codes = await Promise.all(this.codes.map(async (code) => { const codeDef = ctx.getCode(code.name); return codeDef ? codeDef.execute(ctx) : undefined; })); return new dt.Concept(codes, this.display); } } exports.ConceptDef = ConceptDef; class ConceptRef extends expression_1.Expression { constructor(json) { super(json); this.name = json.name; } async exec(ctx) { const conceptDef = ctx.getConcept(this.name); return conceptDef ? conceptDef.execute(ctx) : undefined; } } exports.ConceptRef = ConceptRef; class Concept extends expression_1.Expression { constructor(json) { super(json); this.codes = json.code; this.display = json.display; } // Define a simple getter to allow type-checking of this class without instanceof // and in a way that survives minification (as opposed to checking constructor.name) get isConcept() { return true; } toCode(ctx, code) { const system = ctx.getCodeSystem(code.system.name) || {}; return new dt.Code(code.code, system.id, code.version, code.display); } async exec(ctx) { const codes = this.codes.map((code) => this.toCode(ctx, code)); return new dt.Concept(codes, this.display); } } exports.Concept = Concept; class CalculateAge extends expression_1.Expression { constructor(json) { super(json); this.precision = json.precision; } async exec(ctx) { const birthDate = await this.execArgs(ctx); // From the spec: "Note that for AgeInYears and AgeInMonths, the birthDate is specified as a // Date and Today() is used to obtain the current date; whereas with the other precisions, // birthDate is specified as a DateTime, and Now() is used to obtain the current DateTime." // See: https://cql.hl7.org/09-b-cqlreference.html#age let asOf; if (this.precision.toLowerCase() === dt.DateTime.Unit.YEAR || this.precision.toLowerCase() === dt.DateTime.Unit.MONTH) { asOf = dt.DateTime.fromJSDate(ctx.getExecutionDateTime()).getDate(); } else { asOf = dt.DateTime.fromJSDate(ctx.getExecutionDateTime()); } return calculateAge(this.precision, birthDate, asOf); } } exports.CalculateAge = CalculateAge; class CalculateAgeAt extends expression_1.Expression { constructor(json) { super(json); this.precision = json.precision; } async exec(ctx) { const [birthDate, asOf] = await this.execArgs(ctx); const timeZoneOffset = ctx.getExecutionDateTime().timezoneOffset; return calculateAge(this.precision, birthDate, asOf, timeZoneOffset); } } exports.CalculateAgeAt = CalculateAgeAt; /** * Calculates the age as of a certain date based on the passed in birth date. If the asOf date is * a Date, then birth date will be converted to a Date (if necessary) before calculation is * performed. If the asOf is a DateTime, then the birth date will be converted to a DateTime (if * necessary) before calculation is performed. The result is an integer or uncertainty specifying * the age in the requested precision units. * @param precision - the precision as specified in the ELM (e.g., Year, Month, Week, etc.) * @param birthDate - the birth date to use for age calculations (may be Date or DateTime) * @param asOf - the date on which the age should be calculated (may be Date or DateTime) * @param timeZoneOffset - the passed in timeZoneOffset (if it exists) to be used when * converting birthDate from a Date to a DateTime * @returns the age as an integer or uncertainty in the requested precision units */ function calculateAge(precision, birthDate, asOf, timeZoneOffset) { if (birthDate != null && asOf != null) { // Ensure we use like types (Date or DateTime) based on asOf type if (asOf.isDate && birthDate.isDateTime) { birthDate = birthDate.getDate(); } else if (asOf.isDateTime && birthDate.isDate) { birthDate = birthDate.getDateTime(timeZoneOffset); } const result = birthDate.durationBetween(asOf, precision.toLowerCase()); if (result === null || result === void 0 ? void 0 : result.isPoint()) { return result.low; } else { return result; } } return null; } //# sourceMappingURL=clinical.js.map