import type { ServiceRequest } from "fhir/r4"; /** * Several tests ordered in one act, read back off the orders themselves. * * The server writes these exact literals and the plugin reads them back, so they have to match byte * for byte -- the same contract every other Akashic extension the client reads carries. * * NOTHING EXTRA IS FETCHED TO GROUP A CHART. Each component names its own group, so the list a screen * already holds is enough; reading the parent would be a request per panel for a name the components * are already carrying. */ export const PANEL_EXTENSION_URL = "https://akashichealthrecords.com/fhir/StructureDefinition/diagnostic-panel"; export const PANEL_IDENTIFIER_SYSTEM = "https://akashichealthrecords.com/fhir/identifiers/diagnostic-panel"; export interface DiagnosticPanel { id: string; name?: string; } /** The group an order belongs to, or undefined when it was ordered on its own. */ export const readDiagnosticPanel = (order: ServiceRequest): DiagnosticPanel | undefined => { const found = (order.extension ?? []).find((e) => e.url === PANEL_EXTENSION_URL); // `requisition` is where the standard says a group identifier goes, so an order grouped by anything // but this system's own marker still reads as grouped. const fromStandard = order.requisition?.system === PANEL_IDENTIFIER_SYSTEM ? order.requisition.value : undefined; if (!found) return fromStandard ? { id: fromStandard } : undefined; const part = (url: string) => (found.extension ?? []).find((e) => e.url === url); const id = part("id")?.valueString ?? fromStandard; return id ? { id, name: part("name")?.valueString } : undefined; }; export interface OrderGroup { /** The panel these were ordered in, absent when the test was ordered on its own. */ panel?: DiagnosticPanel; orders: ServiceRequest[]; } /** * The orders as a chart shows them: a panel once, everything else on its own. * * IN THE ORDER THEY ARRIVED, and a panel takes the position of its FIRST component. The list is sorted * by when things were ordered, and moving a panel to where its last member happens to sit would put a * group somewhere its own date does not explain. */ export const groupByPanel = (orders: ServiceRequest[]): OrderGroup[] => { const groups: OrderGroup[] = []; const byPanel = new Map(); for (const order of orders) { const panel = readDiagnosticPanel(order); if (!panel) { groups.push({ orders: [order] }); continue; } const held = byPanel.get(panel.id); if (held) { held.orders.push(order); // A name written on any component names the panel: the first one may be the one that went // without it, and a group that reads "2 tests" beside components that know better is worse // than one that takes the name from wherever it appears. if (!held.panel?.name && panel.name) held.panel = { ...panel }; continue; } const fresh: OrderGroup = { panel: { ...panel }, orders: [order] }; byPanel.set(panel.id, fresh); groups.push(fresh); } return groups; };