diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java index 1dac4e36a0e..f53e041b101 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java @@ -31,6 +31,7 @@ import org.apache.commons.text.StringEscapeUtils; import org.codehaus.stax2.XMLOutputFactory2; import org.codehaus.stax2.io.EscapingWriterFactory; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -1863,10 +1864,19 @@ public class XmlUtil { } public static Document parseDocument(String theInput) throws IOException, SAXException { + StringReader reader = new StringReader(theInput); + return parseDocument(reader); + } + + public static Document parseDocument(Reader reader) throws SAXException, IOException { + return parseDocument(reader, true); + } + + public static Document parseDocument(Reader theReader, boolean theNamespaceAware) throws SAXException, IOException { DocumentBuilder builder; try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); - docBuilderFactory.setNamespaceAware(true); + docBuilderFactory.setNamespaceAware(theNamespaceAware); docBuilderFactory.setXIncludeAware(false); docBuilderFactory.setExpandEntityReferences(false); try { @@ -1885,10 +1895,23 @@ public class XmlUtil { throw new ConfigurationException(e); } - InputSource src = new InputSource(new StringReader(theInput)); + InputSource src = new InputSource(theReader); return builder.parse(src); } + + public static List getChildrenByTagName(Element theParent, String theName) { + List nodeList = new ArrayList(); + for (Node child = theParent.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() == Node.ELEMENT_NODE && theName.equals(child.getNodeName())) { + nodeList.add((Element) child); + } + } + + return nodeList; + } + + public static String encodeDocument(Node theElement) throws TransformerException { return encodeDocument(theElement, false); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java index c534cddf550..6ffcf4b7c7c 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java @@ -123,6 +123,10 @@ public class UploadTerminologyCommand extends BaseCommand { } private void invokeOperation(String theTermUrl, String[] theDatafile, IGenericClient theClient, IBaseParameters theInputParameters, String theOperationName) throws ParseException { + boolean isDeltaOperation = + theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD) || + theOperationName.equals(JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE); + ParametersUtil.addParameterToParametersUri(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_SYSTEM, theTermUrl); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); @@ -134,19 +138,37 @@ public class UploadTerminologyCommand extends BaseCommand { for (String nextDataFile : theDatafile) { try (FileInputStream fileInputStream = new FileInputStream(nextDataFile)) { - if (nextDataFile.endsWith(".csv") || nextDataFile.endsWith(".properties")) { + boolean isFhirType = nextDataFile.endsWith(".json") || nextDataFile.endsWith(".xml"); + if (nextDataFile.endsWith(".csv") || nextDataFile.endsWith(".properties") || isFhirType) { - ourLog.info("Compressing and adding file: {}", nextDataFile); - ZipEntry nextEntry = new ZipEntry(stripPath(nextDataFile)); - zipOutputStream.putNextEntry(nextEntry); + if (isDeltaOperation && isFhirType) { - CountingInputStream countingInputStream = new CountingInputStream(fileInputStream); - IOUtils.copy(countingInputStream, zipOutputStream); - haveCompressedContents = true; - compressedSourceBytesCount += countingInputStream.getCount(); + ourLog.info("Adding CodeSystem resource file: {}", nextDataFile); - zipOutputStream.flush(); - ourLog.info("Finished compressing {}", nextDataFile); + String contents = IOUtils.toString(fileInputStream, Charsets.UTF_8); + EncodingEnum encoding = EncodingEnum.detectEncodingNoDefault(contents); + if (encoding == null) { + throw new ParseException("Could not detect FHIR encoding for file: " + nextDataFile); + } + + CodeSystem resource = encoding.newParser(myFhirCtx).parseResource(CodeSystem.class, contents); + ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource); + + } else { + + ourLog.info("Compressing and adding file: {}", nextDataFile); + ZipEntry nextEntry = new ZipEntry(stripPath(nextDataFile)); + zipOutputStream.putNextEntry(nextEntry); + + CountingInputStream countingInputStream = new CountingInputStream(fileInputStream); + IOUtils.copy(countingInputStream, zipOutputStream); + haveCompressedContents = true; + compressedSourceBytesCount += countingInputStream.getCount(); + + zipOutputStream.flush(); + ourLog.info("Finished compressing {}", nextDataFile); + + } } else if (nextDataFile.endsWith(".zip")) { @@ -154,19 +176,6 @@ public class UploadTerminologyCommand extends BaseCommand { String fileName = "file:" + nextDataFile; addFileToRequestBundle(theInputParameters, fileName, IOUtils.toByteArray(fileInputStream)); - } else if (nextDataFile.endsWith(".json") || nextDataFile.endsWith(".xml")) { - - ourLog.info("Adding CodeSystem resource file: {}", nextDataFile); - - String contents = IOUtils.toString(fileInputStream, Charsets.UTF_8); - EncodingEnum encoding = EncodingEnum.detectEncodingNoDefault(contents); - if (encoding == null) { - throw new ParseException("Could not detect FHIR encoding for file: " + nextDataFile); - } - - CodeSystem resource = encoding.newParser(myFhirCtx).parseResource(CodeSystem.class, contents); - ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource); - } else { throw new ParseException("Don't know how to handle file: " + nextDataFile); diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 1f88c28779b..dec4f77b747 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -96,6 +96,21 @@ h2 + + + org.hibernate.search + hibernate-search-backend-lucene + + + org.apache.lucene + lucene-analyzers-phonetic + + + org.apache.lucene + lucene-backward-codecs + + + org.eclipse.jetty diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java index 9e3463aa160..d480f068b5c 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/CommonConfig.java @@ -92,14 +92,15 @@ public class CommonConfig { extraProperties.put("hibernate.cache.use_second_level_cache", "false"); extraProperties.put("hibernate.cache.use_structured_entries", "false"); extraProperties.put("hibernate.cache.use_minimal_puts", "false"); + extraProperties.put("hibernate.search.backend.type", "lucene"); - extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); - extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-filesystem"); - extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles"); - extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); - if (System.getProperty("lowmem") != null) { +// extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); +// extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-filesystem"); +// extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles"); +// extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); +// if (System.getProperty("lowmem") != null) { extraProperties.put(HibernateOrmMapperSettings.ENABLED, "false"); - } +// } return extraProperties; } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2702-add-icd10cm-support.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2702-add-icd10cm-support.yaml new file mode 100644 index 00000000000..163d0e9bfbe --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2702-add-icd10cm-support.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 2702 +title: "The JPA server terminology uploader now supports uploading ICD-10-CM (US Edition) using the + native format for that vocabulary." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/tools/hapi_fhir_cli.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/tools/hapi_fhir_cli.md index 778481d1dea..0222eab1701 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/tools/hapi_fhir_cli.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/tools/hapi_fhir_cli.md @@ -83,6 +83,12 @@ Note that the path and exact filename of the terminology files will likely need ./hapi-fhir-cli upload-terminology -d Downloads/LOINC_2.54_MULTI-AXIAL_HIERARCHY.zip -d Downloads/LOINC_2.54_Text.zip -f dstu3 -t http://localhost:8080/baseDstu3 -u http://loinc.org ``` +### ICD-10-CM + +``` +./hapi-fhir-cli upload-terminology -d Downloads/LOINC_2.54_MULTI-AXIAL_HIERARCHY.zip -d icd10cm_tabular_2021.xml -f dstu3 -t http://localhost:8080/baseDstu3 -u http://hl7.org/fhir/sid/icd-10-cm +``` + # Migrate Database The `migrate-database` command may be used to Migrate a database schema when upgrading a [HAPI FHIR JPA](/docs/server_jpa/introduction.html) project from one version of HAPI FHIR to another version. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index f2e3a0ae1b3..806ace404ac 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -127,14 +127,17 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { UploadStatistics stats; switch (codeSystemUrl) { - case ITermLoaderSvc.SCT_URI: - stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails); + case ITermLoaderSvc.ICD10CM_URI: + stats = myTerminologyLoaderSvc.loadIcd10cm(localFiles, theRequestDetails); + break; + case ITermLoaderSvc.IMGTHLA_URI: + stats = myTerminologyLoaderSvc.loadImgthla(localFiles, theRequestDetails); break; case ITermLoaderSvc.LOINC_URI: stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails); break; - case ITermLoaderSvc.IMGTHLA_URI: - stats = myTerminologyLoaderSvc.loadImgthla(localFiles, theRequestDetails); + case ITermLoaderSvc.SCT_URI: + stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails); break; default: stats = myTerminologyLoaderSvc.loadCustom(codeSystemUrl, localFiles, theRequestDetails); @@ -395,7 +398,7 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { return retVal; } - private static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor { + public static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor { private final File myNextFile; public FileBackedFileDescriptor(File theNextFile) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index d9864f8ed8e..538821f3d32 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; +import ca.uhn.fhir.jpa.term.icd10cm.Icd10CmLoader; import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListHandler; import ca.uhn.fhir.jpa.term.loinc.LoincAnswerListLinkHandler; import ca.uhn.fhir.jpa.term.loinc.LoincDocumentOntologyHandler; @@ -53,6 +54,7 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.xml.sax.SAXException; import javax.annotation.Nonnull; import javax.validation.constraints.NotNull; @@ -69,6 +71,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -260,6 +263,40 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc { } } + @Override + public UploadStatistics loadIcd10cm(List theFiles, RequestDetails theRequestDetails) { + ourLog.info("Beginning ICD-10-cm processing"); + + CodeSystem cs = new CodeSystem(); + cs.setUrl(ICD10CM_URI); + cs.setName("ICD-10-CM"); + cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); + cs.setStatus(Enumerations.PublicationStatus.ACTIVE); + + TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); + int count = 0; + + try (LoadedFileDescriptors compressedDescriptors = new LoadedFileDescriptors(theFiles)) { + for (FileDescriptor nextDescriptor : compressedDescriptors.getUncompressedFileDescriptors()) { + if (nextDescriptor.getFilename().toLowerCase(Locale.US).endsWith(".xml")) { + try (InputStream inputStream = nextDescriptor.getInputStream()) { + InputStreamReader reader = new InputStreamReader(inputStream, Charsets.UTF_8); + Icd10CmLoader loader = new Icd10CmLoader(codeSystemVersion); + loader.load(reader); + count += loader.getConceptCount(); + } + } + } + } catch (IOException | SAXException e) { + throw new InternalErrorException(e); + } + + cs.setVersion(codeSystemVersion.getCodeSystemVersionId()); + + IIdType target = storeCodeSystem(theRequestDetails, codeSystemVersion, cs, null, null); + return new UploadStatistics(count, target); + } + @Override public UploadStatistics loadCustom(String theSystem, List theFiles, RequestDetails theRequestDetails) { try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java index 09da4e85da2..87d2d060598 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermLoaderSvc.java @@ -36,6 +36,7 @@ public interface ITermLoaderSvc { String IMGTHLA_URI = "http://www.ebi.ac.uk/ipd/imgt/hla"; String LOINC_URI = "http://loinc.org"; String SCT_URI = "http://snomed.info/sct"; + String ICD10CM_URI = "http://hl7.org/fhir/sid/icd-10-cm"; String IEEE_11073_10101_URI = "urn:iso:std:iso:11073:10101"; UploadStatistics loadImgthla(List theFiles, RequestDetails theRequestDetails); @@ -44,6 +45,8 @@ public interface ITermLoaderSvc { UploadStatistics loadSnomedCt(List theFiles, RequestDetails theRequestDetails); + UploadStatistics loadIcd10cm(List theFiles, RequestDetails theRequestDetails); + UploadStatistics loadCustom(String theSystem, List theFiles, RequestDetails theRequestDetails); UploadStatistics loadDeltaAdd(String theSystem, List theFiles, RequestDetails theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoader.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoader.java new file mode 100644 index 00000000000..4aa04a3ff65 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoader.java @@ -0,0 +1,81 @@ +package ca.uhn.fhir.jpa.term.icd10cm; + +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; +import ca.uhn.fhir.util.XmlUtil; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.Reader; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class Icd10CmLoader { + + private final TermCodeSystemVersion myCodeSystemVersion; + private int myConceptCount; + + /** + * Constructor + */ + public Icd10CmLoader(TermCodeSystemVersion theCodeSystemVersion) { + myCodeSystemVersion = theCodeSystemVersion; + } + + + public void load(Reader theReader) throws IOException, SAXException { + myConceptCount = 0; + + Document document = XmlUtil.parseDocument(theReader, false); + Element documentElement = document.getDocumentElement(); + + // Extract version: Should only be 1 tag + for (Element nextVersion : XmlUtil.getChildrenByTagName(documentElement, "version")) { + String versionId = nextVersion.getTextContent(); + if (isNotBlank(versionId)) { + myCodeSystemVersion.setCodeSystemVersionId(versionId); + } + } + + // Extract Diags (codes) + for (Element nextChapter : XmlUtil.getChildrenByTagName(documentElement, "chapter")) { + for (Element nextSection : XmlUtil.getChildrenByTagName(nextChapter, "section")) { + for (Element nextDiag : XmlUtil.getChildrenByTagName(nextSection, "diag")) { + extractCode(nextDiag, null); + } + } + } + + } + + + private void extractCode(Element theDiagElement, TermConcept theParentConcept) { + String code = theDiagElement.getElementsByTagName("name").item(0).getTextContent(); + String display = theDiagElement.getElementsByTagName("desc").item(0).getTextContent(); + + TermConcept concept; + if (theParentConcept == null) { + concept = myCodeSystemVersion.addConcept(); + } else { + concept = theParentConcept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA); + } + + concept.setCode(code); + concept.setDisplay(display); + + for (Element nextChildDiag : XmlUtil.getChildrenByTagName(theDiagElement, "diag")) { + extractCode(nextChildDiag, concept); + } + + myConceptCount++; + } + + + public int getConceptCount() { + return myConceptCount; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index c6c3c85410a..6a4ec266f83 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.ClasspathUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.hl7.fhir.r4.model.Attachment; @@ -95,6 +96,25 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes } @Test + public void testUploadIcd10cm() { + byte[] packageBytes = ClasspathUtil.loadResourceAsByteArray("/icd/icd10cm_tabular_2021.xml"); + + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("upload-external-code-system") + .withParameter(Parameters.class, TerminologyUploaderProvider.PARAM_SYSTEM, new UriType(ITermLoaderSvc.ICD10CM_URI)) + .andParameter(TerminologyUploaderProvider.PARAM_FILE, new Attachment().setUrl("icd10cm_tabular_2021.xml").setData(packageBytes)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType) respParam.getParameter().get(1).getValue()).getValue(), greaterThan(1)); + assertThat(((Reference) respParam.getParameter().get(2).getValue()).getReference(), matchesPattern("CodeSystem\\/[a-zA-Z0-9\\.\\-]+")); + } + + @Test public void testUploadLoinc() throws Exception { byte[] packageBytes = createLoincZip(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIcd10cmJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIcd10cmJpaTest.java new file mode 100644 index 00000000000..fe5ed44f092 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIcd10cmJpaTest.java @@ -0,0 +1,61 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; +import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; +import ca.uhn.fhir.util.ClasspathUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +public class TerminologyLoaderSvcIcd10cmJpaTest extends BaseJpaR4Test { + private TermLoaderSvcImpl mySvc; + private ZipCollectionBuilder myFiles; + + @BeforeEach + public void before() { + mySvc = new TermLoaderSvcImpl(myTerminologyDeferredStorageSvc, myTermCodeSystemStorageSvc); + + myFiles = new ZipCollectionBuilder(); + } + + @Test + public void testLoadIcd10cm() throws IOException { + String filename = "icd/icd10cm_tabular_2021.xml"; + + String resource = ClasspathUtil.loadResource(filename); + List descriptors = new ArrayList<>(); + descriptors.add(new ITermLoaderSvc.ByteArrayFileDescriptor(filename, resource.getBytes(StandardCharsets.UTF_8))); + mySvc.loadIcd10cm(descriptors, new SystemRequestDetails()); + + myTerminologyDeferredStorageSvc.saveAllDeferred(); + + runInTransaction(() -> { + assertEquals(1, myTermCodeSystemDao.count()); + assertEquals(1, myTermCodeSystemVersionDao.count()); + assertEquals(0, myTermValueSetDao.count()); + assertEquals(0, myTermConceptMapDao.count()); + assertEquals(1, myResourceTableDao.count()); + assertEquals(17, myTermConceptDao.count()); + TermCodeSystem codeSystem = myTermCodeSystemDao.findByCodeSystemUri(ITermLoaderSvc.ICD10CM_URI); + + assertEquals("2021", codeSystem.getCurrentVersion().getCodeSystemVersionId()); + + TermCodeSystemVersion codeSystemVersion = myTermCodeSystemVersionDao.findByCodeSystemPidAndVersion(codeSystem.getPid(), "2021"); + assertEquals(codeSystem.getCurrentVersion().getPid(), codeSystemVersion.getPid()); + assertEquals(codeSystem.getResource().getId(), codeSystemVersion.getResource().getId()); + }); + + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoaderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoaderTest.java new file mode 100644 index 00000000000..8ebe9c40de1 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/icd10cm/Icd10CmLoaderTest.java @@ -0,0 +1,46 @@ +package ca.uhn.fhir.jpa.term.icd10cm; + +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.util.ClasspathUtil; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Icd10CmLoaderTest { + + @Test + public void testLoadIcd10Cm() throws IOException, SAXException { + StringReader reader = new StringReader(ClasspathUtil.loadResource("icd/icd10cm_tabular_2021.xml")); + TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); + Icd10CmLoader loader = new Icd10CmLoader(codeSystemVersion); + loader.load(reader); + + assertEquals("2021", codeSystemVersion.getCodeSystemVersionId()); + + List rootConcepts = new ArrayList<>(codeSystemVersion.getConcepts()); + assertEquals(2, rootConcepts.size()); + assertEquals("A00", rootConcepts.get(0).getCode()); + assertEquals("Cholera", rootConcepts.get(0).getDisplay()); + List conceptNames = rootConcepts.stream().map(t -> t.getCode()).collect(Collectors.toList()); + assertThat(conceptNames.toString(), conceptNames, Matchers.contains("A00", "A01")); + + assertEquals(3, rootConcepts.get(0).getChildCodes().size()); + TermConcept firstChildCode = rootConcepts.get(0).getChildCodes().get(0); + assertEquals("A00.0", firstChildCode.getCode()); + assertEquals("Cholera due to Vibrio cholerae 01, biovar cholerae", firstChildCode.getDisplay()); + conceptNames = rootConcepts.get(0).getChildCodes().stream().map(t -> t.getCode()).collect(Collectors.toList()); + assertThat(conceptNames.toString(), conceptNames, Matchers.contains("A00.0", "A00.1", "A00.9")); + + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/resources/icd/icd10cm_tabular_2021.xml b/hapi-fhir-jpaserver-base/src/test/resources/icd/icd10cm_tabular_2021.xml new file mode 100755 index 00000000000..105b0f2467f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/icd/icd10cm_tabular_2021.xml @@ -0,0 +1,105 @@ + + + 2021 + + + ICD-10-CM TABULAR LIST of DISEASES and INJURIES + + + + 1 + Certain infectious and parasitic diseases (A00-B99) + + + Intestinal infectious diseases + + +
+ Intestinal infectious diseases (A00-A09) + + A00 + Cholera + + A00.0 + Cholera due to Vibrio cholerae 01, biovar cholerae + + Classical cholera + + + + A00.1 + Cholera due to Vibrio cholerae 01, biovar eltor + + Cholera eltor + + + + A00.9 + Cholera, unspecified + + + + A01 + Typhoid and paratyphoid fevers + + A01.0 + Typhoid fever + + Infection due to Salmonella typhi + + + A01.00 + Typhoid fever, unspecified + + + A01.01 + Typhoid meningitis + + + A01.02 + Typhoid fever with heart involvement + + Typhoid endocarditis + Typhoid myocarditis + + + + A01.03 + Typhoid pneumonia + + + A01.04 + Typhoid arthritis + + + A01.05 + Typhoid osteomyelitis + + + A01.09 + Typhoid fever with other complications + + + + A01.1 + Paratyphoid fever A + + + A01.2 + Paratyphoid fever B + + + A01.3 + Paratyphoid fever C + + + A01.4 + Paratyphoid fever, unspecified + + Infection due to Salmonella paratyphi NOS + + + +
+
+