Work on terminology services
This commit is contained in:
parent
00ced6a652
commit
96d6fa1b8a
|
@ -9,6 +9,8 @@ public interface ITerminologySvc {
|
|||
|
||||
void storeNewCodeSystemVersion(String theSystemUri, TermCodeSystemVersion theCodeSytem);
|
||||
|
||||
Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersion, String theCode);
|
||||
Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode);
|
||||
|
||||
Set<TermConcept> findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemResourceVersionPid, String theCode);
|
||||
|
||||
}
|
||||
|
|
|
@ -3,11 +3,13 @@ package ca.uhn.fhir.jpa.term;
|
|||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.ValidationUtils;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
|
||||
|
@ -28,28 +30,99 @@ public class TerminologySvcImpl implements ITerminologySvc {
|
|||
|
||||
@Autowired
|
||||
private ITermCodeSystemVersionDao myCodeSystemVersionDao;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ITermConceptParentChildLinkDao myConceptParentChildLinkDao;
|
||||
|
||||
|
||||
@Autowired
|
||||
private ITermConceptDao myConceptDao;
|
||||
|
||||
@Autowired
|
||||
private ITermCodeSystemDao myCodeSystemDao;
|
||||
|
||||
|
||||
@Autowired
|
||||
private FhirContext myContext;
|
||||
|
||||
|
||||
|
||||
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
|
||||
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
||||
TermConcept nextChild = nextChildLink.getChild();
|
||||
if (theSetToPopulate.add(nextChild)) {
|
||||
fetchChildren(nextChild, theSetToPopulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TermConcept fetchLoadedCode(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
|
||||
TermCodeSystemVersion codeSystem = myCodeSystemVersionDao.findByCodeSystemResourceAndVersion(theCodeSystemResourcePid, theCodeSystemVersionPid);
|
||||
TermConcept concept = myConceptDao.findByCodeSystemAndCode(codeSystem, theCode);
|
||||
return concept;
|
||||
}
|
||||
|
||||
private void fetchParents(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
|
||||
for (TermConceptParentChildLink nextChildLink : theConcept.getParents()) {
|
||||
TermConcept nextChild = nextChildLink.getParent();
|
||||
if (theSetToPopulate.add(nextChild)) {
|
||||
fetchParents(nextChild, theSetToPopulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
@Transactional(propagation=Propagation.REQUIRED)
|
||||
public Set<TermConcept> findCodesAbove(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
TermConcept concept = fetchLoadedCode(theCodeSystemResourcePid, theCodeSystemVersionPid, theCode);
|
||||
|
||||
Set<TermConcept> retVal = new HashSet<TermConcept>();
|
||||
retVal.add(concept);
|
||||
|
||||
fetchParents(concept, retVal);
|
||||
|
||||
ourLog.info("Fetched {} codes above code {} in {}ms", retVal.size(), theCode, stopwatch.elapsed(TimeUnit.MILLISECONDS));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
TermConcept concept = fetchLoadedCode(theCodeSystemResourcePid, theCodeSystemVersionPid, theCode);
|
||||
|
||||
Set<TermConcept> retVal = new HashSet<TermConcept>();
|
||||
retVal.add(concept);
|
||||
|
||||
fetchChildren(concept, retVal);
|
||||
|
||||
ourLog.info("Fetched {} codes below code {} in {}ms", retVal.size(), theCode, stopwatch.elapsed(TimeUnit.MILLISECONDS));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack) {
|
||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
persistChildren(next.getChild(), theCodeSystem, theConceptsStack);
|
||||
}
|
||||
|
||||
myConceptDao.save(theConcept);
|
||||
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
myConceptParentChildLinkDao.save(next);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void storeNewCodeSystemVersion(String theSystemUri, TermCodeSystemVersion theCodeSystem) {
|
||||
ourLog.info("Storing code system");
|
||||
|
||||
|
||||
ValidateUtil.isNotNullOrThrowInvalidRequest(theCodeSystem.getResource() != null, "No resource supplied");
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theSystemUri, "No system URI supplied");
|
||||
|
||||
|
||||
TermCodeSystem codeSystem = myCodeSystemDao.findByCodeSystemUri(theSystemUri);
|
||||
if (codeSystem == null) {
|
||||
TermCodeSystem newCodeSystem = new TermCodeSystem();
|
||||
|
@ -61,41 +134,25 @@ public class TerminologySvcImpl implements ITerminologySvc {
|
|||
throw new InvalidRequestException(myContext.getLocalizer().getMessage(TerminologySvcImpl.class, "cannotCreateDuplicateCodeSystemUri", theSystemUri, codeSystem.getResource().getIdDt().getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Validate the code system
|
||||
IdentityHashMap<TermConcept, Object> conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||
for (TermConcept next : theCodeSystem.getConcepts()) {
|
||||
validateConceptForStorage(next, theCodeSystem, conceptsStack);
|
||||
}
|
||||
|
||||
|
||||
myCodeSystemVersionDao.save(theCodeSystem);
|
||||
|
||||
|
||||
conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||
for (TermConcept next : theCodeSystem.getConcepts()) {
|
||||
persistChildren(next, theCodeSystem, conceptsStack);
|
||||
}
|
||||
}
|
||||
|
||||
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack) {
|
||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
persistChildren(next.getChild(), theCodeSystem, theConceptsStack);
|
||||
}
|
||||
|
||||
myConceptDao.save(theConcept);
|
||||
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
myConceptParentChildLinkDao.save(next);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack) {
|
||||
ValidateUtil.isNotNullOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "Codesystem contains a code which does not reference the codesystem");
|
||||
ValidateUtil.isNotNullOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "Codesystem contains a code which does not reference the codesystem");
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code which does not reference the codesystem");
|
||||
|
||||
|
||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||
throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
|
||||
}
|
||||
|
@ -103,34 +160,8 @@ public class TerminologySvcImpl implements ITerminologySvc {
|
|||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack);
|
||||
}
|
||||
|
||||
|
||||
theConceptsStack.remove(theConcept);
|
||||
}
|
||||
|
||||
@Transactional(propagation=Propagation.REQUIRED)
|
||||
@Override
|
||||
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
|
||||
TermCodeSystemVersion codeSystem = myCodeSystemVersionDao.findByCodeSystemResourceAndVersion(theCodeSystemResourcePid, theCodeSystemVersionPid);
|
||||
TermConcept concept = myConceptDao.findByCodeSystemAndCode(codeSystem, theCode);
|
||||
|
||||
Set<TermConcept> retVal = new HashSet<TermConcept>();
|
||||
retVal.add(concept);
|
||||
|
||||
fetchChildren(concept, retVal);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
|
||||
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
||||
TermConcept nextChild = nextChildLink.getChild();
|
||||
if (theSetToPopulate.add(nextChild)) {
|
||||
fetchChildren(nextChild, theSetToPopulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFetchIsA() {
|
||||
public void testFindCodesBelowA() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl("http://example.com/my_code_system");
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
|
@ -100,6 +100,51 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test {
|
|||
assertThat(codes, containsInAnyOrder("childAA", "childAAA", "childAAB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindCodesAbove() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl("http://example.com/my_code_system");
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, new ServletRequestDetails()).getId().toUnqualified();
|
||||
|
||||
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
cs.setResourceVersionId(table.getVersion());
|
||||
|
||||
TermConcept parentA = new TermConcept(cs, "ParentA");
|
||||
cs.getConcepts().add(parentA);
|
||||
|
||||
TermConcept childAA = new TermConcept(cs, "childAA");
|
||||
parentA.addChild(childAA);
|
||||
|
||||
TermConcept childAAA = new TermConcept(cs, "childAAA");
|
||||
childAA.addChild(childAAA);
|
||||
|
||||
TermConcept childAAB = new TermConcept(cs, "childAAB");
|
||||
childAA.addChild(childAAB);
|
||||
|
||||
TermConcept childAB = new TermConcept(cs, "childAB");
|
||||
parentA.addChild(childAB);
|
||||
|
||||
TermConcept parentB = new TermConcept(cs, "ParentB");
|
||||
cs.getConcepts().add(parentB);
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion("http://foo", cs);
|
||||
|
||||
Set<TermConcept> concepts;
|
||||
Set<String> codes;
|
||||
|
||||
concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "ChildAA");
|
||||
codes = toCodes(concepts);
|
||||
assertThat(codes, containsInAnyOrder("ParentA", "childAA"));
|
||||
|
||||
concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAAB");
|
||||
codes = toCodes(concepts);
|
||||
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDuplicateCodeSystemUri() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
id effectiveTime active moduleId definitionStatusId
|
||||
100005 20020131 0 900000000000207008 900000000000074008
|
||||
101009 20020131 1 900000000000207008 900000000000074008
|
||||
102002 20020131 1 900000000000207008 900000000000074008
|
||||
103007 20020131 1 900000000000207008 900000000000074008
|
||||
104001 20020131 1 900000000000207008 900000000000073002
|
||||
105000 20020131 1 900000000000207008 900000000000074008
|
||||
105000 20040731 0 900000000000207008 900000000000074008
|
||||
106004 20020131 1 900000000000207008 900000000000074008
|
||||
107008 20020131 1 900000000000207008 900000000000074008
|
|
@ -0,0 +1,10 @@
|
|||
id effectiveTime active moduleId conceptId languageCode typeId term caseSignificanceId
|
||||
101013 20020131 1 900000000000207008 126813005 en 900000000000013009 Neoplasm of anterior aspect of epiglottis 900000000000020002
|
||||
102018 20020131 1 900000000000207008 126814004 en 900000000000013009 Neoplasm of junctional region of epiglottis 900000000000020002
|
||||
103011 20020131 1 900000000000207008 126815003 en 900000000000013009 Neoplasm of lateral wall of oropharynx 900000000000020002
|
||||
104017 20020131 1 900000000000207008 126816002 en 900000000000013009 Neoplasm of posterior wall of oropharynx 900000000000020002
|
||||
105016 20020131 1 900000000000207008 126817006 en 900000000000013009 Neoplasm of esophagus 900000000000020002
|
||||
106015 20020131 1 900000000000207008 126818001 en 900000000000013009 Neoplasm of cervical esophagus 900000000000020002
|
||||
107012 20020131 1 900000000000207008 126819009 en 900000000000013009 Neoplasm of thoracic esophagus 900000000000020002
|
||||
108019 20020131 1 900000000000207008 126820003 en 900000000000013009 Neoplasm of abdominal esophagus 900000000000020002
|
||||
110017 20020131 1 900000000000207008 126822006 en 900000000000013009 Neoplasm of middle third of esophagus 900000000000020002
|
|
@ -0,0 +1 @@
|
|||
identifierSchemeId alternateIdentifier effectiveTime active moduleId referencedComponentId
|
|
@ -0,0 +1,10 @@
|
|||
id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId
|
||||
100022 20020131 1 900000000000207008 100000000 102272007 0 116680003 900000000000011006 900000000000451002
|
||||
100022 20090731 0 900000000000207008 100000000 102272007 0 116680003 900000000000011006 900000000000451002
|
||||
101021 20020131 1 900000000000207008 10000006 29857009 0 116680003 900000000000011006 900000000000451002
|
||||
102025 20020131 1 900000000000207008 10000006 9972008 0 116680003 900000000000011006 900000000000451002
|
||||
103024 20020131 1 900000000000207008 1000004 19130008 0 116680003 900000000000011006 900000000000451002
|
||||
103024 20030131 0 900000000000207008 1000004 19130008 0 116680003 900000000000011006 900000000000451002
|
||||
104029 20020131 1 900000000000207008 100001001 102272007 0 116680003 900000000000011006 900000000000451002
|
||||
104029 20090731 0 900000000000207008 100001001 102272007 0 116680003 900000000000011006 900000000000451002
|
||||
105028 20020131 1 900000000000207008 100002008 102272007 0 116680003 900000000000011006 900000000000451002
|
|
@ -0,0 +1,10 @@
|
|||
id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId
|
||||
3187444026 20140131 1 900000000000207008 425630003 400195000 0 42752001 900000000000010007 900000000000451002
|
||||
3187444026 20160131 0 900000000000207008 425630003 400195000 0 42752001 900000000000010007 900000000000451002
|
||||
3192499027 20140131 0 900000000000207008 425630003 105590001 0 246075003 900000000000010007 900000000000451002
|
||||
3574321020 20140131 1 900000000000207008 425630003 111189002 0 116680003 900000000000010007 900000000000451002
|
||||
3574321020 20160131 0 900000000000207008 425630003 111189002 0 116680003 900000000000010007 900000000000451002
|
||||
3829433029 20080731 1 900000000000207008 102977005 102976001 0 116680003 900000000000010007 900000000000451002
|
||||
3829434024 20080731 1 900000000000207008 413337008 306751006 0 116680003 900000000000010007 900000000000451002
|
||||
3829435020 20080731 1 900000000000207008 103085008 72909000 0 116680003 900000000000010007 900000000000451002
|
||||
3829436021 20080731 1 900000000000207008 103085008 259648002 0 116680003 900000000000010007 900000000000451002
|
|
@ -0,0 +1,10 @@
|
|||
id effectiveTime active moduleId conceptId languageCode typeId term caseSignificanceId
|
||||
2884452019 20040731 1 900000000000207008 410016009 en 900000000000550004 A decrease in lower leg circumference due to recurrent ulceration and fat necrosis causing loss of subcutaneous tissue in a patient with venous stasis disease 900000000000017005
|
||||
2884453012 20050731 1 900000000000207008 416118004 en 900000000000550004 Introduction of a substance to the body 900000000000017005
|
||||
2884454018 20030731 1 900000000000207008 125097000 en 900000000000550004 Domestic goat 900000000000017005
|
||||
2884455017 20030731 1 900000000000207008 125099002 en 900000000000550004 Domestic sheep species 900000000000017005
|
||||
2884455017 20110131 0 900000000000207008 125099002 en 900000000000550004 Domestic sheep species 900000000000017005
|
||||
2884456016 20030731 1 900000000000207008 122868007 en 900000000000550004 An implantation of a staple 900000000000017005
|
||||
2884457013 20030731 1 900000000000207008 125085001 en 900000000000550004 Equus subspecies 900000000000017005
|
||||
2884457013 20100731 0 900000000000207008 125085001 en 900000000000550004 Equus subspecies 900000000000017005
|
||||
2884458015 20030731 1 900000000000207008 125671007 en 900000000000550004 Disruption of continuity of tissue, not necessarily due to external forces; may be due to weakness in the tissue or excessive internal pressures 900000000000017005
|
Loading…
Reference in New Issue