Skip to main content
Glama
bmi-calculation-bot.ts6.63 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { createReference, getReferenceString, LOINC, UCUM } from '@medplum/core'; import type { BotEvent, MedplumClient } from '@medplum/core'; import type { CodeableConcept, Observation, Patient } from '@medplum/fhirtypes'; /** * BMI Calculation Bot * * This bot processes a Patient resource and: * 1. Searches for height and weight observations * 2. Finds the latest observation of each type * 3. Calculates BMI from the latest height and weight * 4. Creates a BMI Observation */ // LOINC codes for observations const LOINC_HEIGHT = '8302-2'; // Body height const LOINC_WEIGHT = '29463-7'; // Body weight const LOINC_BMI = '39156-5'; // Body mass index (BMI) [Ratio] // Observation category for vital signs const VITAL_SIGNS_CATEGORY: CodeableConcept = { coding: [ { system: 'http://terminology.hl7.org/CodeSystem/observation-category', code: 'vital-signs', display: 'Vital Signs', }, ], }; /** * Calculates BMI from weight (kg) and height (m) * BMI = weight (kg) / (height (m))^2 * @param weightKg - Weight in kilograms * @param heightM - Height in meters * @returns The calculated BMI */ function calculateBMI(weightKg: number, heightM: number): number { if (heightM <= 0) { throw new Error('Height must be greater than 0'); } return weightKg / (heightM * heightM); } /** * Gets the latest observation for a given code * @param medplum - The Medplum client * @param patient - The patient resource * @param code - The LOINC code to search for * @returns The latest observation, or undefined if none found */ async function getLatestObservation( medplum: MedplumClient, patient: Patient, code: string ): Promise<Observation | undefined> { const observations = await medplum.searchResources('Observation', { patient: getReferenceString(patient), code: `${LOINC}|${code}`, _sort: '-date', _count: 1, }); return observations[0]; } /** * Extracts numeric value from an observation * @param observation - The observation to extract value from * @returns The numeric value, or undefined if not available */ function getObservationValue(observation: Observation): number | undefined { const quantity = observation.valueQuantity; if (quantity?.value !== undefined) { return quantity.value; } return undefined; } /** * Main bot handler * @param medplum - The Medplum client * @param event - The bot event containing the Patient resource */ export async function handler(medplum: MedplumClient, event: BotEvent<Patient>): Promise<void> { const patient = event.input; // Get the latest height observation const heightObservation = await getLatestObservation(medplum, patient, LOINC_HEIGHT); if (!heightObservation) { console.log(`No height observation found for patient ${getReferenceString(patient)}`); return; } // Get the latest weight observation const weightObservation = await getLatestObservation(medplum, patient, LOINC_WEIGHT); if (!weightObservation) { console.log(`No weight observation found for patient ${getReferenceString(patient)}`); return; } // Extract values from observations const heightValue = getObservationValue(heightObservation); const weightValue = getObservationValue(weightObservation); if (heightValue === undefined || weightValue === undefined) { console.log( `Missing values: height=${heightValue}, weight=${weightValue} for patient ${getReferenceString(patient)}` ); return; } // Get units from observations const heightUnit = heightObservation.valueQuantity?.unit; const weightUnit = weightObservation.valueQuantity?.unit; // Convert height to meters // Common units: m (meters), cm (centimeters), in (inches) let heightM = heightValue; if (heightUnit?.toLowerCase().includes('cm') || heightUnit?.toLowerCase().includes('centimeter')) { heightM = heightValue / 100; // cm to m } else if (heightUnit?.toLowerCase().includes('in') || heightUnit?.toLowerCase().includes('inch')) { heightM = heightValue * 0.0254; // inches to m } else if (!heightUnit?.toLowerCase().includes('m') && !heightUnit?.toLowerCase().includes('meter')) { // If unit is not recognized and not already meters, assume it might be in a different format // Log a warning but try to proceed console.warn(`Unrecognized height unit: ${heightUnit}. Assuming meters.`); } // Convert weight to kilograms // Common units: kg (kilograms), g (grams), lb (pounds), oz (ounces) let weightKg = weightValue; if (weightUnit?.toLowerCase().includes('kg') && !weightUnit?.toLowerCase().includes('kg')) { weightKg = weightValue / 1000; // grams to kg } else if (weightUnit?.toLowerCase() === 'g' || weightUnit?.toLowerCase() === 'grams') { weightKg = weightValue / 1000; // grams to kg } else if (weightUnit?.toLowerCase() === 'mg' || weightUnit?.toLowerCase() === 'milligrams') { weightKg = weightValue / 1000000; // mg to kg } else if (weightUnit?.toLowerCase().includes('lb') || weightUnit?.toLowerCase().includes('pound')) { weightKg = weightValue * 0.453592; // pounds to kg } else if (weightUnit?.toLowerCase().includes('oz') || weightUnit?.toLowerCase().includes('ounce')) { weightKg = weightValue * 0.0283495; // ounces to kg } else if (!weightUnit?.toLowerCase().includes('kg') && !weightUnit?.toLowerCase().includes('kilogram')) { // If unit is not recognized and not already kg, assume it might be in a different format // Log a warning but try to proceed console.warn(`Unrecognized weight unit: ${weightUnit}. Assuming kilograms.`); } // Calculate BMI const bmi = calculateBMI(weightKg, heightM); // Create BMI observation const bmiObservation: Observation = { resourceType: 'Observation', status: 'final', subject: createReference(patient), code: { coding: [ { system: LOINC, code: LOINC_BMI, display: 'Body mass index (BMI) [Ratio]', }, ], }, category: [VITAL_SIGNS_CATEGORY], valueQuantity: { value: bmi, unit: 'kg/m2', system: UCUM, code: 'kg/m2', }, effectiveDateTime: new Date().toISOString(), derivedFrom: [createReference(heightObservation), createReference(weightObservation)], }; // Create a new BMI observation (allows tracking BMI over time) await medplum.createResource(bmiObservation); console.log(`Created BMI observation: ${bmi.toFixed(2)} kg/m2 for patient ${getReferenceString(patient)}`); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server