Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
1ff50cb0db
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
import ca.uhn.fhir.context.support.IContextValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
|
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
|
||||||
|
import ca.uhn.fhir.util.ValidateUtil;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
@ -10,6 +11,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.hibernate.search.annotations.*;
|
import org.hibernate.search.annotations.*;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.springframework.validation.ValidationUtils;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
@ -185,6 +187,7 @@ public class TermConcept implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCode(String theCode) {
|
public void setCode(String theCode) {
|
||||||
|
ValidateUtil.isNotBlankOrThrowInvalidRequest(theCode, "Code must not be null or empty");
|
||||||
myCode = theCode;
|
myCode = theCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -432,7 +432,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
|
|
||||||
fetchParents(concept, retVal);
|
fetchParents(concept, retVal);
|
||||||
|
|
||||||
ourLog.info("Fetched {} codes above code {} in {}ms", new Object[] {retVal.size(), theCode, stopwatch.getMillis()});
|
ourLog.info("Fetched {} codes above code {} in {}ms", retVal.size(), theCode, stopwatch.getMillis());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,7 +463,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
|
|
||||||
fetchChildren(concept, retVal);
|
fetchChildren(concept, retVal);
|
||||||
|
|
||||||
ourLog.info("Fetched {} codes below code {} in {}ms", new Object[] {retVal.size(), theCode, stopwatch.elapsed(TimeUnit.MILLISECONDS)});
|
ourLog.info("Fetched {} codes below code {} in {}ms", retVal.size(), theCode, stopwatch.elapsed(TimeUnit.MILLISECONDS));
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +537,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processDeferredConceptMaps() {
|
private void processDeferredConceptMaps() {
|
||||||
int count = Math.min(myDeferredConceptMaps.size(), 5);
|
int count = Math.min(myDeferredConceptMaps.size(), 20);
|
||||||
for (ConceptMap nextConceptMap : new ArrayList<>(myDeferredConceptMaps.subList(0, count))) {
|
for (ConceptMap nextConceptMap : new ArrayList<>(myDeferredConceptMaps.subList(0, count))) {
|
||||||
ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId());
|
ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId());
|
||||||
createOrUpdateConceptMap(nextConceptMap);
|
createOrUpdateConceptMap(nextConceptMap);
|
||||||
|
@ -559,7 +559,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
|
|
||||||
if (codeCount > 0) {
|
if (codeCount > 0) {
|
||||||
ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)",
|
ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)",
|
||||||
new Object[] {codeCount, myDeferredConcepts.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
|
codeCount, myDeferredConcepts.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codeCount == 0) {
|
if (codeCount == 0) {
|
||||||
|
@ -580,7 +580,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
|
|
||||||
if (relCount > 0) {
|
if (relCount > 0) {
|
||||||
ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)",
|
ourLog.info("Saved {} deferred relationships ({} remain) in {}ms ({}ms / code)",
|
||||||
new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
|
relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((myDeferredConcepts.size() + myConceptLinksToSaveLater.size()) == 0) {
|
if ((myDeferredConcepts.size() + myConceptLinksToSaveLater.size()) == 0) {
|
||||||
|
@ -589,13 +589,13 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processDeferredValueSets() {
|
private void processDeferredValueSets() {
|
||||||
int count = Math.min(myDeferredValueSets.size(), 5);
|
int count = Math.min(myDeferredValueSets.size(), 20);
|
||||||
for (ValueSet nextValueSet : new ArrayList<>(myDeferredValueSets.subList(0, count))) {
|
for (ValueSet nextValueSet : new ArrayList<>(myDeferredValueSets.subList(0, count))) {
|
||||||
ourLog.info("Creating ValueSet: {}", nextValueSet.getId());
|
ourLog.info("Creating ValueSet: {}", nextValueSet.getId());
|
||||||
createOrUpdateValueSet(nextValueSet);
|
createOrUpdateValueSet(nextValueSet);
|
||||||
myDeferredValueSets.remove(nextValueSet);
|
myDeferredValueSets.remove(nextValueSet);
|
||||||
}
|
}
|
||||||
ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredConceptMaps.size());
|
ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredValueSets.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processReindexing() {
|
private void processReindexing() {
|
||||||
|
@ -668,7 +668,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] {count, concepts.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count)});
|
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", count, concepts.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -758,6 +758,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
// Grab the existing versions so we can delete them later
|
// Grab the existing versions so we can delete them later
|
||||||
List<TermCodeSystemVersion> existing = myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystemResourcePid);
|
List<TermCodeSystemVersion> existing = myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystemResourcePid);
|
||||||
|
|
||||||
|
// verifyNoDuplicates(theCodeSystemVersion.getConcepts(), new HashSet<String>());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions
|
* For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions
|
||||||
*/
|
*/
|
||||||
|
@ -909,6 +911,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyNoDuplicates(Collection<TermConcept> theConcepts, Set<String> theCodes) {
|
||||||
|
for (TermConcept next : theConcepts) {
|
||||||
|
if (!theCodes.add(next.getCode())) {
|
||||||
|
throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes");
|
||||||
|
}
|
||||||
|
verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is present only for unit tests, do not call from client code
|
* This method is present only for unit tests, do not call from client code
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -240,7 +240,7 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc {
|
||||||
iterateOverZipFile(theDescriptors, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
iterateOverZipFile(theDescriptors, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||||
|
|
||||||
// Answer lists (ValueSets of potential answers/values for loinc "questions")
|
// Answer lists (ValueSets of potential answers/values for loinc "questions")
|
||||||
handler = new LoincAnswerListHandler(codeSystemVersion, code2concept, propertyNames, valueSets);
|
handler = new LoincAnswerListHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps);
|
||||||
iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||||
|
|
||||||
// Answer list links (connects loinc observation codes to answerlist codes)
|
// Answer list links (connects loinc observation codes to answerlist codes)
|
||||||
|
|
|
@ -23,9 +23,8 @@ package ca.uhn.fhir.jpa.term.loinc;
|
||||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
||||||
import ca.uhn.fhir.jpa.term.IRecordHandler;
|
|
||||||
import org.apache.commons.csv.CSVRecord;
|
import org.apache.commons.csv.CSVRecord;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -33,10 +32,9 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import static org.apache.commons.lang3.StringUtils.trim;
|
|
||||||
|
|
||||||
public class LoincAnswerListHandler implements IRecordHandler {
|
public class LoincAnswerListHandler extends BaseHandler {
|
||||||
|
|
||||||
private final Map<String, TermConcept> myCode2Concept;
|
private final Map<String, TermConcept> myCode2Concept;
|
||||||
private final TermCodeSystemVersion myCodeSystemVersion;
|
private final TermCodeSystemVersion myCodeSystemVersion;
|
||||||
|
@ -44,7 +42,8 @@ public class LoincAnswerListHandler implements IRecordHandler {
|
||||||
private final List<ValueSet> myValueSets;
|
private final List<ValueSet> myValueSets;
|
||||||
private final Map<String, ValueSet> myIdToValueSet = new HashMap<>();
|
private final Map<String, ValueSet> myIdToValueSet = new HashMap<>();
|
||||||
|
|
||||||
public LoincAnswerListHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Set<String> thePropertyNames, List<ValueSet> theValueSets) {
|
public LoincAnswerListHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Set<String> thePropertyNames, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) {
|
||||||
|
super(theCode2concept, theValueSets, theConceptMaps);
|
||||||
myCodeSystemVersion = theCodeSystemVersion;
|
myCodeSystemVersion = theCodeSystemVersion;
|
||||||
myCode2Concept = theCode2concept;
|
myCode2Concept = theCode2concept;
|
||||||
myPropertyNames = thePropertyNames;
|
myPropertyNames = thePropertyNames;
|
||||||
|
@ -70,6 +69,10 @@ public class LoincAnswerListHandler implements IRecordHandler {
|
||||||
String extCodeSystem = trim(theRecord.get("ExtCodeSystem"));
|
String extCodeSystem = trim(theRecord.get("ExtCodeSystem"));
|
||||||
String extCodeSystemVersion = trim(theRecord.get("ExtCodeSystemVersion"));
|
String extCodeSystemVersion = trim(theRecord.get("ExtCodeSystemVersion"));
|
||||||
|
|
||||||
|
if (isBlank(answerString)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Answer list code
|
// Answer list code
|
||||||
if (!myCode2Concept.containsKey(answerListId)) {
|
if (!myCode2Concept.containsKey(answerListId)) {
|
||||||
TermConcept concept = new TermConcept(myCodeSystemVersion, answerListId);
|
TermConcept concept = new TermConcept(myCodeSystemVersion, answerListId);
|
||||||
|
@ -88,21 +91,13 @@ public class LoincAnswerListHandler implements IRecordHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answer list ValueSet
|
// Answer list ValueSet
|
||||||
ValueSet vs;
|
ValueSet vs = getValueSet(answerListId, "urn:oid:" + answerListOid, answerListName);
|
||||||
if (!myIdToValueSet.containsKey(answerListId)) {
|
if (vs.getIdentifier().isEmpty()) {
|
||||||
vs = new ValueSet();
|
|
||||||
vs.setUrl("urn:oid:" + answerListOid);
|
|
||||||
vs.addIdentifier()
|
vs.addIdentifier()
|
||||||
.setSystem(IHapiTerminologyLoaderSvc.LOINC_URI)
|
.setSystem(IHapiTerminologyLoaderSvc.LOINC_URI)
|
||||||
.setValue(answerListId);
|
.setValue(answerListId);
|
||||||
vs.setId(answerListId);
|
|
||||||
vs.setName(answerListName);
|
|
||||||
vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
|
||||||
myIdToValueSet.put(answerListId, vs);
|
|
||||||
myValueSets.add(vs);
|
|
||||||
} else {
|
|
||||||
vs = myIdToValueSet.get(answerListId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vs
|
vs
|
||||||
.getCompose()
|
.getCompose()
|
||||||
.getIncludeFirstRep()
|
.getIncludeFirstRep()
|
||||||
|
|
|
@ -58,8 +58,12 @@ public class LoincPartHandler implements IRecordHandler {
|
||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
TermConcept concept = new TermConcept(myCodeSystemVersion, partNumber);
|
TermConcept concept = myCode2Concept.get(partNumber);
|
||||||
|
if (concept == null) {
|
||||||
|
concept = new TermConcept(myCodeSystemVersion, partNumber);
|
||||||
concept.setDisplay(partName);
|
concept.setDisplay(partName);
|
||||||
|
myCode2Concept.put(partNumber, concept);
|
||||||
|
}
|
||||||
|
|
||||||
if (isNotBlank(partDisplayName)) {
|
if (isNotBlank(partDisplayName)) {
|
||||||
concept.addDesignation()
|
concept.addDesignation()
|
||||||
|
@ -68,7 +72,6 @@ public class LoincPartHandler implements IRecordHandler {
|
||||||
.setValue(partDisplayName);
|
.setValue(partDisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
myCode2Concept.put(partDisplayName, concept);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ public DaoConfig daoConfig() {
|
||||||
</p>
|
</p>
|
||||||
<div class="source">
|
<div class="source">
|
||||||
<pre>
|
<pre>
|
||||||
Cache-Control: nocache
|
Cache-Control: no-cache
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
<p><b>Disable Paging at the Request Level</b></p>
|
<p><b>Disable Paging at the Request Level</b></p>
|
||||||
|
|
Loading…
Reference in New Issue