Add terminology uploader

This commit is contained in:
jamesagnew 2016-05-26 22:18:12 -04:00
parent 9ed1bb94f9
commit 69266dc7d0
7 changed files with 182 additions and 9 deletions

View File

@ -38,8 +38,11 @@ import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamExtractorDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu3;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvcDstu3;
import ca.uhn.fhir.jpa.term.TerminologyLoaderSvc;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.validation.IValidatorModule;
@ -51,6 +54,11 @@ public class BaseDstu3Config extends BaseConfig {
public IHapiTerminologySvcDstu3 terminologyService() {
return new HapiTerminologySvcDstu3();
}
@Bean(autowire = Autowire.BY_TYPE)
public IHapiTerminologyLoaderSvc terminologyLoaderService() {
return new TerminologyLoaderSvc();
}
@Bean
public HapiWorkerContext workerContext() {
@ -108,4 +116,12 @@ public class BaseDstu3Config extends BaseConfig {
return new SearchParamExtractorDstu3();
}
@Bean(autowire=Autowire.BY_TYPE)
public TerminologyUploaderProviderDstu3 terminologyUploaderProvider() {
TerminologyUploaderProviderDstu3 retVal = new TerminologyUploaderProviderDstu3();
retVal.setContext(defaultFhirContext());
return retVal;
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import static org.apache.commons.lang3.StringUtils.defaultString;
import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.dstu3.model.Attachment;
@ -10,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.provider.BaseJpaProvider;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.method.RequestDetails;
@ -34,17 +37,23 @@ public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider {
startRequest(theServletRequest);
try {
if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) {
throw new InvalidRequestException("Missing mandatory 'package' parameter, or package had no data");
}
byte[] data = thePackage.getData();
String url = theUrl.getValueAsString();
String url = theUrl != null ? theUrl.getValueAsString() : null;
url = defaultString(url);
UploadStatistics stats;
if (IHapiTerminologyLoaderSvc.SCT_URL.equals(url)) {
myTerminologyLoaderSvc.loadSnomedCt(data, theRequestDetails);
stats = myTerminologyLoaderSvc.loadSnomedCt(data, theRequestDetails);
} else {
throw new InvalidRequestException("Unknown URL: " + url);
}
Parameters retVal = new Parameters();
retVal.addParameter().setName("conceptCount").setValue(new IntegerType(0));
retVal.addParameter().setName("conceptCount").setValue(new IntegerType(stats.getConceptCount()));
return retVal;
} finally {
endRequest(theServletRequest);

View File

@ -5,7 +5,20 @@ import ca.uhn.fhir.rest.method.RequestDetails;
public interface IHapiTerminologyLoaderSvc {
String SCT_URL = "http://snomed.info/sct";
void loadSnomedCt(byte[] theZipBytes, RequestDetails theRequestDetails);
UploadStatistics loadSnomedCt(byte[] theZipBytes, RequestDetails theRequestDetails);
public static class UploadStatistics {
private int myConceptCount;
public int getConceptCount() {
return myConceptCount;
}
public UploadStatistics setConceptCount(int theConceptCount) {
myConceptCount = theConceptCount;
return this;
}
}
}

View File

@ -131,7 +131,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
}
@Override
public void loadSnomedCt(byte[] theZipBytes, RequestDetails theRequestDetails) {
public UploadStatistics loadSnomedCt(byte[] theZipBytes, RequestDetails theRequestDetails) {
List<String> allFilenames = Arrays.asList(SCT_FILE_DESCRIPTION, SCT_FILE_RELATIONSHIP, SCT_FILE_CONCEPT);
Map<String, File> filenameToFile = new HashMap<String, File>();
@ -174,7 +174,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
ourLog.info("Beginning SNOMED CT processing");
try {
processSnomedCtFiles(filenameToFile, theRequestDetails);
return processSnomedCtFiles(filenameToFile, theRequestDetails);
} finally {
ourLog.info("Finished SNOMED CT file import, cleaning up temporary files");
for (File nextFile : filenameToFile.values()) {
@ -183,7 +183,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
}
}
void processSnomedCtFiles(Map<String, File> filenameToFile, RequestDetails theRequestDetails) {
UploadStatistics processSnomedCtFiles(Map<String, File> filenameToFile, RequestDetails theRequestDetails) {
final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion();
final Map<String, TermConcept> id2concept = new HashMap<String, TermConcept>();
final Map<String, TermConcept> code2concept = new HashMap<String, TermConcept>();
@ -211,6 +211,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
codeSystemVersion.getConcepts().addAll(rootConcepts.values());
myTermSvc.storeNewCodeSystemVersion(SCT_URL, codeSystemVersion, theRequestDetails);
return new UploadStatistics().setConceptCount(code2concept.size());
}
@VisibleForTesting

View File

@ -15,6 +15,7 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum;

View File

@ -50,6 +50,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
private static Server ourServer;
protected static String ourServerBase;
private static GenericWebApplicationContext ourWebApplicationContext;
private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider;
public BaseResourceProviderDstu3Test() {
super();
@ -78,7 +79,9 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
ourRestServer.setPlainProviders(mySystemProvider);
myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class);
ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider);
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(ourRestServer, mySystemDao, myDaoConfig);
confProvider.setImplementationDescription("THIS IS THE DESC");

View File

@ -0,0 +1,129 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.TestUtil;
public class TerminologyProviderDstu3Test extends BaseResourceProviderDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyProviderDstu3Test.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Test
public void testUploadSct() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
Parameters respParam = ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1));
}
@Test
public void testUploadInvalidUrl() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL + "FOO"))
.andParameter("package", new Attachment().setData(packageBytes))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Unknown URL: http://snomed.info/sctFOO", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadMissingUrl() throws Exception {
byte[] packageBytes = createSctZip();
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "package", new Attachment().setData(packageBytes))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage());
}
//@formatter:on
}
@Test
public void testUploadMissingPackage() throws Exception {
//@formatter:off
try {
ourClient
.operation()
.onServer()
.named("upload-external-code-system")
.withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL))
.execute();
fail();
} catch (InvalidRequestException e) {
assertEquals("HTTP 400 Bad Request: Missing mandatory 'package' parameter, or package had no data", e.getMessage());
}
//@formatter:on
}
private byte[] createSctZip() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(bos);
List<String> inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt","sct2_Concept_Full-en_INT_20160131.txt","sct2_Description_Full-en_INT_20160131.txt","sct2_Identifier_Full_INT_20160131.txt","sct2_Relationship_Full_INT_20160131.txt","sct2_StatedRelationship_Full_INT_20160131.txt","sct2_TextDefinition_Full-en_INT_20160131.txt");
for (String nextName : inputNames) {
zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName));
zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName)));
}
zos.close();
byte[] packageBytes = bos.toByteArray();
return packageBytes;
}
}