Skip to main content
Glama
coding.ts7.29 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { WithId } from '@medplum/core'; import { append } from '@medplum/core'; import type { CodeSystem, CodeSystemConcept, CodeSystemConceptProperty, Coding, Resource, ResourceType, } from '@medplum/fhirtypes'; import type { Pool, PoolClient } from 'pg'; import type { Designation, ImportedProperty } from '../operations/codesystemimport'; import { importCodeSystem } from '../operations/codesystemimport'; import { parentProperty } from '../operations/utils/terminology'; import { Column, Condition, Conjunction } from '../sql'; import { LookupTable } from './lookuptable'; /** * The CodingTable class is used to index and search Coding values associated with a CodeSystem. * Each system/code/display triple is represented as a separate row in the "Coding" table. */ export class CodingTable extends LookupTable { getTableName(): string { return 'Coding'; } getColumnName(code: string): string { return code; } /** * Returns false, because the Coding table is never used for normal SearchParameter search. * @returns Always false. */ isIndexed(): boolean { return false; } extractValues(): object[] { throw new Error('CodingTable.extractValues not implemented'); } async batchIndexResources<T extends Resource>( client: PoolClient, resources: WithId<T>[], create: boolean ): Promise<void> { for (const resource of resources) { if ( resource.resourceType === 'CodeSystem' && (resource.content === 'complete' || resource.content === 'example') ) { if (!create) { await this.deleteValuesForResource(client, resource); } const elements = this.getCodeSystemElements(resource); await importCodeSystem(client, resource, elements.concepts, elements.properties, elements.designations); } } } /** * Deletes the resource from the lookup table. * @param client - The database client. * @param resource - The resource to delete. */ async deleteValuesForResource(client: Pool | PoolClient, resource: Resource): Promise<void> { const resourceType = resource.resourceType; if (resourceType !== 'CodeSystem') { return; } await LookupTable.purge(client, 'CodeSystem_Property', { tableName: resourceType, joinCondition: new Conjunction([ new Condition(new Column(resourceType, 'id'), '=', new Column('CodeSystem_Property', 'system')), new Condition(new Column(resourceType, 'id'), '=', resource.id), ]), }); await LookupTable.purge(client, 'Coding_Property', { tableName: 'Coding', joinCondition: new Conjunction([ new Condition(new Column('Coding_Property', 'coding'), '=', new Column('Coding', 'id')), new Condition(new Column('Coding', 'system'), '=', resource.id), ]), }); await LookupTable.purge(client, 'Coding', { tableName: resourceType, joinCondition: new Conjunction([ new Condition(new Column(resourceType, 'id'), '=', new Column('Coding', 'system')), new Condition(new Column(resourceType, 'id'), '=', resource.id), ]), }); } /** * Purges resources of the specified type that were last updated before the specified date. * This is only available to the system and super admin accounts. * @param client - The database client. * @param resourceType - The FHIR resource type. * @param before - The date before which resources should be purged. */ async purgeValuesBefore(client: Pool | PoolClient, resourceType: ResourceType, before: string): Promise<void> { if (resourceType !== 'CodeSystem') { return; } const resourceOlderThanCutoff = new Condition(new Column(resourceType, 'lastUpdated'), '<', before); await LookupTable.purge(client, 'CodeSystem_Property', { tableName: resourceType, joinCondition: new Conjunction([ new Condition(new Column(resourceType, 'id'), '=', new Column('CodeSystem_Property', 'system')), resourceOlderThanCutoff, ]), }); await LookupTable.purge( client, 'Coding_Property', { tableName: 'Coding', joinCondition: new Condition(new Column('Coding_Property', 'coding'), '=', new Column('Coding', 'id')), }, { tableName: resourceType, joinCondition: new Conjunction([ new Condition(new Column('Coding', 'system'), '=', new Column(resourceType, 'id')), resourceOlderThanCutoff, ]), } ); await LookupTable.purge(client, 'Coding', { tableName: 'CodeSystem', joinCondition: new Conjunction([ new Condition(new Column(resourceType, 'id'), '=', new Column('Coding', 'system')), resourceOlderThanCutoff, ]), }); } private getCodeSystemElements(codeSystem: CodeSystem): { concepts: Coding[]; properties: ImportedProperty[]; designations: Designation[]; } { const result = Object.create(null); if (codeSystem.concept) { for (const concept of codeSystem.concept) { this.addCodeSystemConcepts(codeSystem, concept, result); } } return result; } /** * Recursively adds CodeSystem concepts. * See: https://www.hl7.org/fhir/codesystem-definitions.html#CodeSystem.concept * @param codeSystem - The CodeSystem. * @param concept - The CodeSystem concept. * @param result - The results. * @param result.concepts - Concepts defined by the CodeSystem. * @param result.properties - Coding properties specified by the CodeSystem. * @param result.designations - Coding synonyms specified by the CodeSystem. */ private addCodeSystemConcepts( codeSystem: CodeSystem, concept: CodeSystemConcept, result: { concepts: Coding[]; properties: ImportedProperty[]; designations: Designation[] } ): void { const { code, display } = concept; result.concepts = append(result.concepts, { code, display }); if (concept.property) { for (const prop of concept.property) { result.properties = append(result.properties, { code, property: prop.code, value: getPropertyValue(prop) }); } } if (concept.concept) { for (const child of concept.concept) { this.addCodeSystemConcepts(codeSystem, child, result); result.properties = append(result.properties, { code: child.code, property: codeSystem.property?.find((p) => p.uri === parentProperty)?.code ?? codeSystem.hierarchyMeaning ?? 'parent', value: code, }); } } if (concept.designation) { for (const designation of concept.designation) { result.designations = append(result.designations, { code: concept.code, language: designation.language, value: designation.value, }); } } } } function getPropertyValue(prop: CodeSystemConceptProperty): string { if (prop.valueBoolean !== undefined) { return prop.valueBoolean ? 'true' : 'false'; } else if (prop.valueCode) { return prop.valueCode; } else if (prop.valueString) { return prop.valueString; } return ''; }

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