Basic Retrieve View Source
import { z } from 'zod';
import dayjs from 'dayjs';
import { TernaryEnum } from '@cqlab/cqflow-core';
import {
  Library,
  Define,
  Documentation,
  ReturnType,
  MockData,
  FhirLibrary,
  Params,
} from '@cqlab/cqdefine';
import { MockPatientIdEnum } from '../mock-patients';
import { ExampleCodings, ExampleCodingsEnum } from '../vocabulary/codings';
import { sortObservations } from './utils/utils';

const makeMockDataItem = (id: MockPatientIdEnum) => ({ id: id, label: id });

// Declare a schema using zod to document and validate a return type
const cholesterolThreshold = z.number().int().positive().min(0);
type CholesterolThreshold = z.infer<typeof cholesterolThreshold>;

// Return reading
const quantitySchema = z.object({
  unit: z.string().nullable(),
  value: z.number(),
});
type Quantity = z.infer<typeof quantitySchema>;

@Library('ParameterizedRetrieveLibrary')
@MockData([
  makeMockDataItem(MockPatientIdEnum.empty_data),
  makeMockDataItem(MockPatientIdEnum.high_cholesterol),
])
export class ParameterizedRetrieveLibrary extends FhirLibrary {
  static readonly className: string = 'Test';

  @Define('Get cholesterol reading')
  @Documentation('Gets the most recent cholesterol reading in mg/dL')
  @ReturnType(quantitySchema)
  async getCholesterolReading(): Promise<Quantity | null> {
    const cholesterolCode =
      ExampleCodings[ExampleCodingsEnum.cholesterol_ratio];

    const observations = await this.retriever.getObservationsByCode(
      cholesterolCode
    );

    if (observations.length === 0) {
      return null;
    }

    const mostRecentObservation =
      sortObservations(observations)[observations.length - 1];

    if (!mostRecentObservation.valueQuantity?.value) {
      return null;
    }

    // Assume the unit is mg/dL, but may need to do more complex work here
    // if there is a chance different units could be used
    return {
      value: mostRecentObservation.valueQuantity.value,
      unit: 'mg/dL',
    };
  }

  // The @Params decorator allows us to parameterize a function so that
  // it can be re-used in flexible ways. Either it can be used by other
  // functions in a library, or exposed directly to calling code / or APIs
  // We use Zod to validate and auto-document the API with JSON Schema
  @Define('Is cholesterol reading above threshold')
  @Params(cholesterolThreshold)
  @Documentation(
    'Provide a threshold to see if patient has cholesterol above that. Allows a "high threshold" to be configurable.'
  )
  async isCholesterolAboveThreshold(
    threshold: CholesterolThreshold
  ): Promise<TernaryEnum> {
    const cholesterolReading = await this.getCholesterolReading();

    if (!cholesterolReading) {
      return TernaryEnum.UNKNOWN;
    }

    // We have full TypeScript support for dealing with the input params
    return cholesterolReading.value > threshold
      ? TernaryEnum.TRUE
      : TernaryEnum.FALSE;
  }

  @Define('Is cholesterol reading above 200 mg/dL')
  @Documentation('Checks to see if the cholesterol reading is above 200 mg/dL')
  async isCholesterolReadingAbove200(): Promise<TernaryEnum> {
    return this.isCholesterolAboveThreshold(200);
  }
}
CHOOSE PATIENT:
Get cholesterol reading [getCholesterolReading][retrieve]
Gets the most recent cholesterol reading in mg/dL
Is cholesterol reading above threshold [isCholesterolAboveThreshold][evaluate]
Provide a threshold to see if patient has cholesterol above that. Allows a "high threshold" to be configurable.
Is cholesterol reading above 200 mg/dL [isCholesterolReadingAbove200][evaluate]
Checks to see if the cholesterol reading is above 200 mg/dL