package ca.uhn.fhir.jpa.dao.dstu3; /* * #%L * HAPI FHIR JPA Server * %% * Copyright (C) 2014 - 2017 University Health Network * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import org.hl7.fhir.dstu3.hapi.validation.IValidationSupport.CodeValidationResult; import org.hl7.fhir.dstu3.hapi.validation.ValidationSupportChain; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.util.LogicUtil; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class); @Autowired private ITermCodeSystemVersionDao myCsvDao; @Autowired private IHapiTerminologySvc myTerminologySvc; @Autowired private ValidationSupportChain myValidationSupport; // private LookupCodeResult lookup(List<ValueSetExpansionContainsComponent> theContains, String theSystem, String theCode) { // for (ValueSetExpansionContainsComponent nextCode : theContains) { // // String system = nextCode.getSystem(); // String code = nextCode.getCode(); // if (theSystem.equals(system) && theCode.equals(code)) { // LookupCodeResult retVal = new LookupCodeResult(); // retVal.setSearchedForCode(code); // retVal.setSearchedForSystem(system); // retVal.setFound(true); // if (nextCode.getAbstractElement().getValue() != null) { // retVal.setCodeIsAbstract(nextCode.getAbstractElement().booleanValue()); // } // retVal.setCodeDisplay(nextCode.getDisplay()); // retVal.setCodeSystemVersion(nextCode.getVersion()); // retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement // return retVal; // } // // } // // return null; // } @Override public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem) { List<IIdType> valueSetIds; Set<Long> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode))); valueSetIds = new ArrayList<IIdType>(); for (Long next : ids) { valueSetIds.add(new IdType("CodeSystem", next)); } return valueSetIds; } @Override public LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) { boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); boolean haveCode = theCode != null && theCode.isEmpty() == false; boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; if (!haveCoding && !(haveSystem && haveCode)) { throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate"); } if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) { throw new InvalidRequestException("$lookup can only validate (system AND code) OR (coding.system AND coding.code)"); } String code; String system; if (haveCoding) { code = theCoding.getCode(); system = theCoding.getSystem(); } else { code = theCode.getValue(); system = theSystem.getValue(); } ourLog.info("Looking up {} / {}", system, code); if (myValidationSupport.isCodeSystemSupported(getContext(), system)) { ourLog.info("Code system {} is supported", system); CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null); if (result != null) { if (result.isOk()) { LookupCodeResult retVal = new LookupCodeResult(); retVal.setFound(true); retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); retVal.setCodeDisplay(result.asConceptDefinition().getDisplay()); retVal.setCodeSystemDisplayName("Unknown"); retVal.setCodeSystemVersion(""); return retVal; } } // HapiWorkerContext ctx = new HapiWorkerContext(getContext(), myValidationSupport); // ValueSetExpander expander = ctx.getExpander(); // ValueSet source = new ValueSet(); // source.getCompose().addInclude().setSystem(system).addConcept().setCode(code); // // ValueSetExpansionOutcome expansion; // try { // expansion = expander.expand(source); // } catch (Exception e) { // throw new InternalErrorException(e); // } // // if (expansion.getValueset() != null) { // List<ValueSetExpansionContainsComponent> contains = expansion.getValueset().getExpansion().getContains(); // LookupCodeResult result = lookup(contains, system, code); // if (result != null) { // return result; // } // } } // We didn't find it.. LookupCodeResult retVal = new LookupCodeResult(); retVal.setFound(false); retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); return retVal; } private List<TermConcept> toPersistedConcepts(List<ConceptDefinitionComponent> theConcept, TermCodeSystemVersion theCodeSystemVersion) { ArrayList<TermConcept> retVal = new ArrayList<TermConcept>(); for (ConceptDefinitionComponent next : theConcept) { if (isNotBlank(next.getCode())) { TermConcept termConcept = new TermConcept(); termConcept.setCode(next.getCode()); termConcept.setCodeSystem(theCodeSystemVersion); termConcept.setDisplay(next.getDisplay()); termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); retVal.add(termConcept); } } return retVal; } @Override protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { ResourceTable retVal = super.updateEntity(theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry); CodeSystem cs = (CodeSystem) theResource; if (cs != null && isNotBlank(cs.getUrl())) { String codeSystemUrl = cs.getUrl(); if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) { ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString()); Long codeSystemResourcePid = retVal.getId(); TermCodeSystemVersion persCs = myCsvDao.findByCodeSystemResourceAndVersion(codeSystemResourcePid, retVal.getVersion()); if (persCs != null) { ourLog.info("Code system version already exists in database"); } else { persCs = new TermCodeSystemVersion(); persCs.setResource(retVal); persCs.setResourceVersionId(retVal.getVersion()); persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, persCs); } } } return retVal; } }