Merge branch 'loinc_loader_update'

This commit is contained in:
James Agnew 2018-04-15 17:20:13 -04:00
commit c450d51440
37 changed files with 802 additions and 208 deletions

View File

@ -209,7 +209,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
for (int i = 0; i < theRequest.getEntry().size(); i++) { for (int i = 0; i < theRequest.getEntry().size(); i++) {
if (i % 100 == 0) { if (i % 100 == 0) {
ourLog.info("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size()); ourLog.debug("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size());
} }
Entry nextReqEntry = theRequest.getEntry().get(i); Entry nextReqEntry = theRequest.getEntry().get(i);

View File

@ -815,7 +815,7 @@ public class SearchBuilder implements ISearchBuilder {
continue; continue;
} }
predicate = join.<Object>get("myUri").as(String.class).in(toFind); predicate = join.get("myUri").as(String.class).in(toFind);
} else if (param.getQualifier() == UriParamQualifierEnum.BELOW) { } else if (param.getQualifier() == UriParamQualifierEnum.BELOW) {
predicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value)); predicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value));
@ -953,8 +953,8 @@ public class SearchBuilder implements ISearchBuilder {
Predicate lb = null; Predicate lb = null;
if (lowerBound != null) { if (lowerBound != null) {
Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.<Date>get("myValueLow"), lowerBound); Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBound);
Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.<Date>get("myValueHigh"), lowerBound); Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), lowerBound);
if (theRange.getLowerBound().getPrefix() == ParamPrefixEnum.STARTS_AFTER || theRange.getLowerBound().getPrefix() == ParamPrefixEnum.EQUAL) { if (theRange.getLowerBound().getPrefix() == ParamPrefixEnum.STARTS_AFTER || theRange.getLowerBound().getPrefix() == ParamPrefixEnum.EQUAL) {
lb = gt; lb = gt;
} else { } else {
@ -964,8 +964,8 @@ public class SearchBuilder implements ISearchBuilder {
Predicate ub = null; Predicate ub = null;
if (upperBound != null) { if (upperBound != null) {
Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.<Date>get("myValueLow"), upperBound); Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), upperBound);
Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.<Date>get("myValueHigh"), upperBound); Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBound);
if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) { if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) {
ub = lt; ub = lt;
} else { } else {
@ -2008,15 +2008,15 @@ public class SearchBuilder implements ISearchBuilder {
} }
private static List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) { private static List<Predicate> createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From<?, ResourceTable> from) {
List<Predicate> lastUpdatedPredicates = new ArrayList<Predicate>(); List<Predicate> lastUpdatedPredicates = new ArrayList<>();
if (theLastUpdated != null) { if (theLastUpdated != null) {
if (theLastUpdated.getLowerBoundAsInstant() != null) { if (theLastUpdated.getLowerBoundAsInstant() != null) {
ourLog.debug("LastUpdated lower bound: {}", new InstantDt(theLastUpdated.getLowerBoundAsInstant())); ourLog.debug("LastUpdated lower bound: {}", new InstantDt(theLastUpdated.getLowerBoundAsInstant()));
Predicate predicateLower = builder.greaterThanOrEqualTo(from.<Date>get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()); Predicate predicateLower = builder.greaterThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getLowerBoundAsInstant());
lastUpdatedPredicates.add(predicateLower); lastUpdatedPredicates.add(predicateLower);
} }
if (theLastUpdated.getUpperBoundAsInstant() != null) { if (theLastUpdated.getUpperBoundAsInstant() != null) {
Predicate predicateUpper = builder.lessThanOrEqualTo(from.<Date>get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()); Predicate predicateUpper = builder.lessThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getUpperBoundAsInstant());
lastUpdatedPredicates.add(predicateUpper); lastUpdatedPredicates.add(predicateUpper);
} }
} }
@ -2262,10 +2262,7 @@ public class SearchBuilder implements ISearchBuilder {
if (myNext == null) { if (myNext == null) {
fetchNext(); fetchNext();
} }
if (myNext == NO_MORE) { return myNext != NO_MORE;
return false;
}
return true;
} }
@Override @Override

View File

@ -0,0 +1,28 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptDesignation;
import org.springframework.data.jpa.repository.JpaRepository;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 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%
*/
public interface ITermConceptDesignationDao extends JpaRepository<TermConceptDesignation, Long> {
// nothing
}

View File

@ -318,7 +318,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
for (int i = 0; i < theEntries.size(); i++) { for (int i = 0; i < theEntries.size(); i++) {
if (i % 100 == 0) { if (i % 100 == 0) {
ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size());
} }
BundleEntryComponent nextReqEntry = theEntries.get(i); BundleEntryComponent nextReqEntry = theEntries.get(i);

View File

@ -333,7 +333,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
for (int i = 0; i < theEntries.size(); i++) { for (int i = 0; i < theEntries.size(); i++) {
if (i % 100 == 0) { if (i % 100 == 0) {
ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size());
} }
BundleEntryComponent nextReqEntry = theEntries.get(i); BundleEntryComponent nextReqEntry = theEntries.get(i);

View File

@ -81,6 +81,9 @@ public class TermConcept implements Serializable {
@FieldBridge(impl = TermConceptPropertyFieldBridge.class) @FieldBridge(impl = TermConceptPropertyFieldBridge.class)
private Collection<TermConceptProperty> myProperties; private Collection<TermConceptProperty> myProperties;
@OneToMany(mappedBy = "myConcept", orphanRemoval = true)
private Collection<TermConceptDesignation> myDesignations;
@Id() @Id()
@SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID") @SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PID")
@ -123,6 +126,13 @@ public class TermConcept implements Serializable {
} }
} }
public TermConceptDesignation addDesignation() {
TermConceptDesignation designation = new TermConceptDesignation();
designation.setConcept(this);
getDesignations().add(designation);
return designation;
}
private TermConceptProperty addProperty(@Nonnull TermConceptPropertyTypeEnum thePropertyType, @Nonnull String thePropertyName, @Nonnull String thePropertyValue) { private TermConceptProperty addProperty(@Nonnull TermConceptPropertyTypeEnum thePropertyType, @Nonnull String thePropertyName, @Nonnull String thePropertyValue) {
Validate.notBlank(thePropertyName); Validate.notBlank(thePropertyName);
@ -189,6 +199,29 @@ public class TermConcept implements Serializable {
} }
} }
public List<Coding> getCodingProperties(String thePropertyName) {
List<Coding> retVal = new ArrayList<>();
for (TermConceptProperty next : getProperties()) {
if (thePropertyName.equals(next.getKey())) {
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
Coding coding = new Coding();
coding.setSystem(next.getCodeSystem());
coding.setCode(next.getValue());
coding.setDisplay(next.getDisplay());
retVal.add(coding);
}
}
}
return retVal;
}
public Collection<TermConceptDesignation> getDesignations() {
if (myDesignations == null) {
myDesignations = new ArrayList<>();
}
return myDesignations;
}
public String getDisplay() { public String getDisplay() {
return myDisplay; return myDisplay;
} }
@ -231,6 +264,14 @@ public class TermConcept implements Serializable {
return myProperties; return myProperties;
} }
public Integer getSequence() {
return mySequence;
}
public void setSequence(Integer theSequence) {
mySequence = theSequence;
}
public List<String> getStringProperties(String thePropertyName) { public List<String> getStringProperties(String thePropertyName) {
List<String> retVal = new ArrayList<>(); List<String> retVal = new ArrayList<>();
for (TermConceptProperty next : getProperties()) { for (TermConceptProperty next : getProperties()) {
@ -243,30 +284,6 @@ public class TermConcept implements Serializable {
return retVal; return retVal;
} }
public List<Coding> getCodingProperties(String thePropertyName) {
List<Coding> retVal = new ArrayList<>();
for (TermConceptProperty next : getProperties()) {
if (thePropertyName.equals(next.getKey())) {
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
Coding coding = new Coding();
coding.setSystem(next.getCodeSystem());
coding.setCode(next.getValue());
coding.setDisplay(next.getDisplay());
retVal.add(coding);
}
}
}
return retVal;
}
public Integer getSequence() {
return mySequence;
}
public void setSequence(Integer theSequence) {
mySequence = theSequence;
}
public String getStringProperty(String thePropertyName) { public String getStringProperty(String thePropertyName) {
List<String> properties = getStringProperties(thePropertyName); List<String> properties = getStringProperties(thePropertyName);
if (properties.size() > 0) { if (properties.size() > 0) {

View File

@ -0,0 +1,103 @@
package ca.uhn.fhir.jpa.entity;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 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 javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "TRM_CONCEPT_DESIG", uniqueConstraints = {
}, indexes = {
})
public class TermConceptDesignation implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CONCEPT"))
private TermConcept myConcept;
@Id()
@SequenceGenerator(name = "SEQ_CONCEPT_DESIG_PID", sequenceName = "SEQ_CONCEPT_DESIG_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_DESIG_PID")
@Column(name = "PID")
private Long myId;
@Column(name = "LANG", length = 500, nullable = true)
private String myLanguage;
@Column(name = "USE_SYSTEM", length = 500, nullable = true)
private String myUseSystem;
@Column(name = "USE_CODE", length = 500, nullable = true)
private String myUseCode;
@Column(name = "USE_DISPLAY", length = 500, nullable = true)
private String myUseDisplay;
@Column(name = "VAL", length = 500, nullable = false)
private String myValue;
public String getLanguage() {
return myLanguage;
}
public TermConceptDesignation setLanguage(String theLanguage) {
myLanguage = theLanguage;
return this;
}
public String getUseCode() {
return myUseCode;
}
public TermConceptDesignation setUseCode(String theUseCode) {
myUseCode = theUseCode;
return this;
}
public String getUseDisplay() {
return myUseDisplay;
}
public TermConceptDesignation setUseDisplay(String theUseDisplay) {
myUseDisplay = theUseDisplay;
return this;
}
public String getUseSystem() {
return myUseSystem;
}
public TermConceptDesignation setUseSystem(String theUseSystem) {
myUseSystem = theUseSystem;
return this;
}
public String getValue() {
return myValue;
}
public TermConceptDesignation setValue(String theValue) {
myValue = theValue;
return this;
}
public TermConceptDesignation setConcept(TermConcept theConcept) {
myConcept = theConcept;
return this;
}
}

View File

@ -43,22 +43,22 @@ public class TermConceptProperty implements Serializable {
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PROP_PID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PROP_PID")
@Column(name = "PID") @Column(name = "PID")
private Long myId; private Long myId;
@Column(name = "PROP_KEY", length = 200, nullable = false) @Column(name = "PROP_KEY", length = 500, nullable = false)
@NotBlank @NotBlank
private String myKey; private String myKey;
@Column(name = "PROP_VAL", length = 200, nullable = true) @Column(name = "PROP_VAL", length = 500, nullable = true)
private String myValue; private String myValue;
@Column(name = "PROP_TYPE", length = MAX_PROPTYPE_ENUM_LENGTH, nullable = false) @Column(name = "PROP_TYPE", length = MAX_PROPTYPE_ENUM_LENGTH, nullable = false)
private TermConceptPropertyTypeEnum myType; private TermConceptPropertyTypeEnum myType;
/** /**
* Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING}
*/ */
@Column(name = "PROP_CODESYSTEM", length = 200, nullable = true) @Column(name = "PROP_CODESYSTEM", length = 500, nullable = true)
private String myCodeSystem; private String myCodeSystem;
/** /**
* Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING}
*/ */
@Column(name = "PROP_DISPLAY", length = 200, nullable = true) @Column(name = "PROP_DISPLAY", length = 500, nullable = true)
private String myDisplay; private String myDisplay;
/** /**

View File

@ -168,6 +168,19 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
} }
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) {
String stripVersionIds;
String deliverLatestVersion;
try {
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
}
} catch (FHIRException theE) { } catch (FHIRException theE) {
throw new InternalErrorException(theE); throw new InternalErrorException(theE);
} }
@ -201,6 +214,19 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
} }
if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) {
String stripVersionIds;
String deliverLatestVersion;
try {
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
} catch (FHIRException theE) {
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
}
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
}
List<org.hl7.fhir.r4.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics"); List<org.hl7.fhir.r4.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
if (topicExts.size() > 0) { if (topicExts.size() > 0) {
IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive(); IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive();

View File

@ -67,6 +67,8 @@ public class CanonicalSubscription implements Serializable {
private CanonicalEventDefinition myTrigger; private CanonicalEventDefinition myTrigger;
@JsonProperty("emailDetails") @JsonProperty("emailDetails")
private EmailDetails myEmailDetails; private EmailDetails myEmailDetails;
@JsonProperty("restHookDetails")
private RestHookDetails myRestHookDetails;
/** /**
* For now we're using the R4 TriggerDefinition, but this * For now we're using the R4 TriggerDefinition, but this
@ -131,12 +133,10 @@ public class CanonicalSubscription implements Serializable {
return myHeaders; return myHeaders;
} }
public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) { public void setHeaders(String theHeaders) {
myHeaders = new ArrayList<>(); myHeaders = new ArrayList<>();
for (IPrimitiveType<String> next : theHeader) { if (isNotBlank(theHeaders)) {
if (isNotBlank(next.getValueAsString())) { myHeaders.add(theHeaders);
myHeaders.add(next.getValueAsString());
}
} }
} }
@ -160,6 +160,13 @@ public class CanonicalSubscription implements Serializable {
myPayloadString = thePayloadString; myPayloadString = thePayloadString;
} }
public RestHookDetails getRestHookDetails() {
if (myRestHookDetails == null) {
myRestHookDetails = new RestHookDetails();
}
return myRestHookDetails;
}
public Subscription.SubscriptionStatus getStatus() { public Subscription.SubscriptionStatus getStatus() {
return myStatus; return myStatus;
} }
@ -191,10 +198,12 @@ public class CanonicalSubscription implements Serializable {
} }
} }
public void setHeaders(String theHeaders) { public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
myHeaders = new ArrayList<>(); myHeaders = new ArrayList<>();
if (isNotBlank(theHeaders)) { for (IPrimitiveType<String> next : theHeader) {
myHeaders.add(theHeaders); if (isNotBlank(next.getValueAsString())) {
myHeaders.add(next.getValueAsString());
}
} }
} }
@ -230,6 +239,32 @@ public class CanonicalSubscription implements Serializable {
} }
} }
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
public static class RestHookDetails {
@JsonProperty("stripVersionId")
private boolean myStripVersionId;
@JsonProperty("deliverLatestVersion")
private boolean myDeliverLatestVersion;
public boolean isDeliverLatestVersion() {
return myDeliverLatestVersion;
}
public void setDeliverLatestVersion(boolean theDeliverLatestVersion) {
myDeliverLatestVersion = theDeliverLatestVersion;
}
public boolean isStripVersionId() {
return myStripVersionId;
}
public void setStripVersionId(boolean theStripVersionId) {
myStripVersionId = theStripVersionId;
}
}
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) @JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
public static class CanonicalEventDefinition { public static class CanonicalEventDefinition {

View File

@ -28,8 +28,10 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.*; import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IClientExecutable; import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -51,21 +53,26 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
} }
protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient) { protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient) {
IBaseResource payloadResource = theMsg.getPayload(getContext()); IBaseResource payloadResource = getAndMassagePayload(theMsg, theSubscription);
if (payloadResource == null) return;
doDelivery(theMsg, theSubscription, thePayloadType, theClient, payloadResource);
}
protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient, IBaseResource thePayloadResource) {
IClientExecutable<?, ?> operation; IClientExecutable<?, ?> operation;
switch (theMsg.getOperationType()) { switch (theMsg.getOperationType()) {
case CREATE: case CREATE:
if (payloadResource == null || payloadResource.isEmpty()) { if (thePayloadResource == null || thePayloadResource.isEmpty()) {
if (thePayloadType != null ) { if (thePayloadType != null ) {
operation = theClient.create().resource(payloadResource); operation = theClient.create().resource(thePayloadResource);
} else { } else {
sendNotification(theMsg); sendNotification(theMsg);
return; return;
} }
} else { } else {
if (thePayloadType != null ) { if (thePayloadType != null ) {
operation = theClient.update().resource(payloadResource); operation = theClient.update().resource(thePayloadResource);
} else { } else {
sendNotification(theMsg); sendNotification(theMsg);
return; return;
@ -73,16 +80,16 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
} }
break; break;
case UPDATE: case UPDATE:
if (payloadResource == null || payloadResource.isEmpty()) { if (thePayloadResource == null || thePayloadResource.isEmpty()) {
if (thePayloadType != null ) { if (thePayloadType != null ) {
operation = theClient.create().resource(payloadResource); operation = theClient.create().resource(thePayloadResource);
} else { } else {
sendNotification(theMsg); sendNotification(theMsg);
return; return;
} }
} else { } else {
if (thePayloadType != null ) { if (thePayloadType != null ) {
operation = theClient.update().resource(payloadResource); operation = theClient.update().resource(thePayloadResource);
} else { } else {
sendNotification(theMsg); sendNotification(theMsg);
return; return;
@ -101,7 +108,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
operation.encoded(thePayloadType); operation.encoded(thePayloadType);
} }
ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue()); ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), thePayloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue());
try { try {
operation.execute(); operation.execute();
@ -112,6 +119,27 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
} }
} }
protected IBaseResource getAndMassagePayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription) {
IBaseResource payloadResource = theMsg.getPayload(getContext());
if (theSubscription.getRestHookDetails().isDeliverLatestVersion()) {
IFhirResourceDao dao = getSubscriptionDao().getDao(payloadResource.getClass());
try {
payloadResource = dao.read(payloadResource.getIdElement().toVersionless());
} catch (ResourceGoneException e) {
ourLog.warn("Resource {} is deleted, not going to deliver for subscription {}", payloadResource.getIdElement(), theSubscription.getIdElement(getContext()));
return null;
}
}
IIdType resourceId = payloadResource.getIdElement();
if (theSubscription.getRestHookDetails().isStripVersionId()) {
resourceId = resourceId.toVersionless();
payloadResource.setId(resourceId);
}
return payloadResource;
}
@Override @Override
public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException { public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException {
CanonicalSubscription subscription = theMessage.getSubscription(); CanonicalSubscription subscription = theMessage.getSubscription();

View File

@ -63,6 +63,7 @@ import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType; import javax.persistence.PersistenceContextType;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -78,6 +79,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
@Autowired @Autowired
protected ITermConceptPropertyDao myConceptPropertyDao; protected ITermConceptPropertyDao myConceptPropertyDao;
@Autowired @Autowired
protected ITermConceptDesignationDao myConceptDesignationDao;
@Autowired
protected FhirContext myContext; protected FhirContext myContext;
@PersistenceContext(type = PersistenceContextType.TRANSACTION) @PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
@ -87,7 +90,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
private List<TermConceptParentChildLink> myConceptLinksToSaveLater = new ArrayList<>(); private List<TermConceptParentChildLink> myConceptLinksToSaveLater = new ArrayList<>();
@Autowired @Autowired
private ITermConceptParentChildLinkDao myConceptParentChildLinkDao; private ITermConceptParentChildLinkDao myConceptParentChildLinkDao;
private List<TermConcept> myConceptsToSaveLater = new ArrayList<>(); private List<TermConcept> myDeferredConcepts = Collections.synchronizedList(new ArrayList<>());
private List<ValueSet> myDeferredValueSets = Collections.synchronizedList(new ArrayList<>());
private List<ConceptMap> myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>());
@Autowired @Autowired
private DaoConfig myDaoConfig; private DaoConfig myDaoConfig;
private long myNextReindexPass; private long myNextReindexPass;
@ -103,6 +108,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
contains.setCode(theConcept.getCode()); contains.setCode(theConcept.getCode());
contains.setSystem(theCodeSystem); contains.setSystem(theCodeSystem);
contains.setDisplay(theConcept.getDisplay()); contains.setDisplay(theConcept.getDisplay());
for (TermConceptDesignation nextDesignation : theConcept.getDesignations()) {
contains
.addDesignation()
.setValue(nextDesignation.getValue())
.getUse()
.setSystem(nextDesignation.getUseSystem())
.setCode(nextDesignation.getUseCode())
.setDisplay(nextDesignation.getUseDisplay());
}
} }
} }
@ -146,11 +160,11 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
return retVal; return retVal;
} }
protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails); protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource);
protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap, RequestDetails theRequestDetails); protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap);
abstract void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails); abstract void createOrUpdateValueSet(ValueSet theValueSet);
@Override @Override
public void deleteCodeSystem(TermCodeSystem theCodeSystem) { public void deleteCodeSystem(TermCodeSystem theCodeSystem) {
@ -162,10 +176,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
myCodeSystemDao.save(cs); myCodeSystemDao.save(cs);
myCodeSystemDao.flush(); myCodeSystemDao.flush();
int i = 0;
for (TermCodeSystemVersion next : myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystem.getPid())) { for (TermCodeSystemVersion next : myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystem.getPid())) {
myConceptParentChildLinkDao.deleteByCodeSystemVersion(next.getPid()); myConceptParentChildLinkDao.deleteByCodeSystemVersion(next.getPid());
for (TermConcept nextConcept : myConceptDao.findByCodeSystemVersion(next.getPid())) { for (TermConcept nextConcept : myConceptDao.findByCodeSystemVersion(next.getPid())) {
myConceptPropertyDao.delete(nextConcept.getProperties()); myConceptPropertyDao.delete(nextConcept.getProperties());
myConceptDesignationDao.delete(nextConcept.getDesignations());
myConceptDao.delete(nextConcept); myConceptDao.delete(nextConcept);
} }
if (next.getCodeSystem().getCurrentVersion() == next) { if (next.getCodeSystem().getCurrentVersion() == next) {
@ -173,10 +189,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
myCodeSystemDao.save(next.getCodeSystem()); myCodeSystemDao.save(next.getCodeSystem());
} }
myCodeSystemVersionDao.delete(next); myCodeSystemVersionDao.delete(next);
if (i++ % 1000 == 0) {
myEntityManager.flush();
}
} }
myCodeSystemVersionDao.deleteForCodeSystem(theCodeSystem); myCodeSystemVersionDao.deleteForCodeSystem(theCodeSystem);
myCodeSystemDao.delete(theCodeSystem); myCodeSystemDao.delete(theCodeSystem);
myEntityManager.flush();
} }
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) { private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) {
@ -223,11 +244,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
haveIncludeCriteria = true; haveIncludeCriteria = true;
TermConcept code = findCode(system, nextCode); TermConcept code = findCode(system, nextCode);
if (code != null) { if (code != null) {
addedCodes.add(nextCode); addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, code);
ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains();
contains.setCode(nextCode);
contains.setSystem(system);
contains.setDisplay(code.getDisplay());
} }
} }
} }
@ -492,7 +509,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
saveConcept(theConcept); saveConcept(theConcept);
} else { } else {
myConceptsToSaveLater.add(theConcept); myDeferredConcepts.add(theConcept);
} }
for (TermConceptParentChildLink next : theConcept.getChildren()) { for (TermConceptParentChildLink next : theConcept.getChildren()) {
@ -507,10 +524,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
} }
} }
for (TermConceptProperty next : theConcept.getProperties()) {
myConceptPropertyDao.save(next);
}
} }
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
@ -523,20 +536,30 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
} }
} }
private void processDeferredConceptMaps() {
int count = Math.min(myDeferredConceptMaps.size(), 5);
for (ConceptMap nextConceptMap : new ArrayList<>(myDeferredConceptMaps.subList(0, count))) {
ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId());
createOrUpdateConceptMap(nextConceptMap);
myDeferredConceptMaps.remove(nextConceptMap);
}
ourLog.info("Saved {} deferred ConceptMap resources, have {} remaining", count, myDeferredConceptMaps.size());
}
private void processDeferredConcepts() { private void processDeferredConcepts() {
int codeCount = 0, relCount = 0; int codeCount = 0, relCount = 0;
StopWatch stopwatch = new StopWatch(); StopWatch stopwatch = new StopWatch();
int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size()); int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myDeferredConcepts.size());
ourLog.info("Saving {} deferred concepts...", count); ourLog.info("Saving {} deferred concepts...", count);
while (codeCount < count && myConceptsToSaveLater.size() > 0) { while (codeCount < count && myDeferredConcepts.size() > 0) {
TermConcept next = myConceptsToSaveLater.remove(0); TermConcept next = myDeferredConcepts.remove(0);
codeCount += saveConcept(next); codeCount += saveConcept(next);
} }
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, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); new Object[] {codeCount, myDeferredConcepts.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
} }
if (codeCount == 0) { if (codeCount == 0) {
@ -560,11 +583,21 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)});
} }
if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) { if ((myDeferredConcepts.size() + myConceptLinksToSaveLater.size()) == 0) {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database"); ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
} }
} }
private void processDeferredValueSets() {
int count = Math.min(myDeferredValueSets.size(), 5);
for (ValueSet nextValueSet : new ArrayList<>(myDeferredValueSets.subList(0, count))) {
ourLog.info("Creating ValueSet: {}", nextValueSet.getId());
createOrUpdateValueSet(nextValueSet);
myDeferredValueSets.remove(nextValueSet);
}
ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredConceptMaps.size());
}
private void processReindexing() { private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass && !ourForceSaveDeferredAlwaysForUnitTest) { if (System.currentTimeMillis() < myNextReindexPass && !ourForceSaveDeferredAlwaysForUnitTest) {
return; return;
@ -656,6 +689,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
retVal++; retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
myConceptDao.save(theConcept); myConceptDao.save(theConcept);
for (TermConceptProperty next : theConcept.getProperties()) {
myConceptPropertyDao.save(next);
}
for (TermConceptDesignation next : theConcept.getDesignations()) {
myConceptDesignationDao.save(next);
}
} }
ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId()); ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId());
@ -674,20 +715,31 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
public synchronized void saveDeferred() { public synchronized void saveDeferred() {
if (!myProcessDeferred) { if (!myProcessDeferred) {
return; return;
} else if (myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()) { } else if (myDeferredConcepts.isEmpty() && myConceptLinksToSaveLater.isEmpty()) {
processReindexing(); processReindexing();
return; return;
} }
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr); TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() { tt.execute(t -> {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
processDeferredConcepts(); processDeferredConcepts();
return null;
});
if (myDeferredValueSets.size() > 0) {
tt.execute(t -> {
processDeferredValueSets();
return null;
});
}
if (myDeferredConceptMaps.size() > 0) {
tt.execute(t -> {
processDeferredConceptMaps();
return null;
});
} }
});
} }
@Override @Override
@ -754,8 +806,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)"); ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
// Validate the code system // Validate the code system
ArrayList<String> conceptsStack = new ArrayList<String>(); ArrayList<String> conceptsStack = new ArrayList<>();
IdentityHashMap<TermConcept, Object> allConcepts = new IdentityHashMap<TermConcept, Object>(); IdentityHashMap<TermConcept, Object> allConcepts = new IdentityHashMap<>();
int totalCodeCount = 0; int totalCodeCount = 0;
for (TermConcept next : theCodeSystemVersion.getConcepts()) { for (TermConcept next : theCodeSystemVersion.getConcepts()) {
totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts); totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts);
@ -790,8 +842,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
ourLog.info("Done deleting old code system versions"); ourLog.info("Done deleting old code system versions");
if (myConceptsToSaveLater.size() > 0 || myConceptLinksToSaveLater.size() > 0) { if (myDeferredConcepts.size() > 0 || myConceptLinksToSaveLater.size() > 0) {
ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size()); ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myDeferredConcepts.size(), myConceptLinksToSaveLater.size());
} }
} }
@ -800,7 +852,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
public void storeNewCodeSystemVersion(CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { public void storeNewCodeSystemVersion(CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) {
Validate.notBlank(theCodeSystemResource.getUrl(), "theCodeSystemResource must have a URL"); Validate.notBlank(theCodeSystemResource.getUrl(), "theCodeSystemResource must have a URL");
IIdType csId = createOrUpdateCodeSystem(theCodeSystemResource, theRequestDetails); IIdType csId = createOrUpdateCodeSystem(theCodeSystemResource);
ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId); ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId);
Long codeSystemResourcePid = resource.getId(); Long codeSystemResourcePid = resource.getId();
@ -810,14 +862,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
theCodeSystemVersion.setResource(resource); theCodeSystemVersion.setResource(resource);
storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion); storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion);
for (ValueSet nextValueSet : theValueSets) { myDeferredConceptMaps.addAll(theConceptMaps);
createOrUpdateValueSet(nextValueSet, theRequestDetails); myDeferredValueSets.addAll(theValueSets);
}
for (ConceptMap nextConceptMap : theConceptMaps) {
createOrUpdateConceptMap(nextConceptMap, theRequestDetails);
}
} }
@Override @Override

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term;
* #L% * #L%
*/ */
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.hapi.validation.IValidationSupport; import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
@ -62,17 +61,17 @@ public class HapiTerminologySvcDstu2 extends BaseHapiTerminologySvcImpl {
} }
@Override @Override
protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
protected void createOrUpdateConceptMap(ConceptMap theNextConceptMap, RequestDetails theRequestDetails) { protected void createOrUpdateConceptMap(ConceptMap theNextConceptMap) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
protected void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails) { protected void createOrUpdateValueSet(ValueSet theValueSet) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -5,7 +5,6 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
@ -29,6 +28,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
@ -103,39 +103,52 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
} }
@Override @Override
protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
CodeSystem resourceToStore; CodeSystem resourceToStore;
try { try {
resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource); resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource);
} catch (FHIRException e) { } catch (FHIRException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
return myCodeSystemResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId();
} else {
return myCodeSystemResourceDao.update(resourceToStore).getId();
}
} }
@Override @Override
protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) {
String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl());
ConceptMap resourceToStore; ConceptMap resourceToStore;
try { try {
resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap); resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap);
} catch (FHIRException e) { } catch (FHIRException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
myConceptMapResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); if (isBlank(resourceToStore.getIdElement().getIdPart())) {
String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl());
myConceptMapResourceDao.update(resourceToStore, matchUrl);
} else {
myConceptMapResourceDao.update(resourceToStore);
}
} }
@Override @Override
protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet, RequestDetails theRequestDetails) { protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl());
ValueSet valueSetDstu3; ValueSet valueSetDstu3;
try { try {
valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet); valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet);
} catch (FHIRException e) { } catch (FHIRException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} }
myValueSetResourceDao.update(valueSetDstu3, matchUrl, theRequestDetails);
if (isBlank(valueSetDstu3.getIdElement().getIdPart())) {
String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl());
myValueSetResourceDao.update(valueSetDstu3, matchUrl);
} else {
myValueSetResourceDao.update(valueSetDstu3);
}
} }
@Override @Override

View File

@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -28,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
/* /*
@ -95,21 +95,33 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
} }
@Override @Override
protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) {
if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl());
return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl, theRequestDetails).getId(); return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId();
} else {
return myCodeSystemResourceDao.update(theCodeSystemResource).getId();
}
} }
@Override @Override
protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) {
if (isBlank(theConceptMap.getIdElement().getIdPart())) {
String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl());
myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails); myConceptMapResourceDao.update(theConceptMap, matchUrl);
} else {
myConceptMapResourceDao.update(theConceptMap);
}
} }
@Override @Override
protected void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails) { protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) {
String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); if (isBlank(theValueSet.getIdElement().getIdPart())) {
myValueSetResourceDao.update(theValueSet, matchUrl, theRequestDetails); String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl());
myValueSetResourceDao.update(theValueSet, matchUrl);
} else {
myValueSetResourceDao.update(theValueSet);
}
} }
@Override @Override

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.term.loinc;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.term.IRecordHandler; import ca.uhn.fhir.jpa.term.IRecordHandler;
import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
@ -85,7 +86,7 @@ abstract class BaseHandler implements IRecordHandler {
} }
void addConceptMapEntry(ConceptMapping theMapping) { void addConceptMapEntry(ConceptMapping theMapping, String theCopyright) {
if (isBlank(theMapping.getSourceCode())) { if (isBlank(theMapping.getSourceCode())) {
return; return;
} }
@ -99,6 +100,13 @@ abstract class BaseHandler implements IRecordHandler {
conceptMap.setId(theMapping.getConceptMapId()); conceptMap.setId(theMapping.getConceptMapId());
conceptMap.setUrl(theMapping.getConceptMapUri()); conceptMap.setUrl(theMapping.getConceptMapUri());
conceptMap.setName(theMapping.getConceptMapName()); conceptMap.setName(theMapping.getConceptMapName());
conceptMap.setPublisher("Regentrief Institute, Inc.");
conceptMap.addContact()
.setName("Regentrief Institute, Inc.")
.addTelecom()
.setSystem(ContactPoint.ContactPointSystem.URL)
.setValue("https://loinc.org");
conceptMap.setCopyright(theCopyright);
myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap);
myConceptMaps.add(conceptMap); myConceptMaps.add(conceptMap);
} else { } else {
@ -164,6 +172,13 @@ abstract class BaseHandler implements IRecordHandler {
vs.setId(theValueSetId); vs.setId(theValueSetId);
vs.setName(theValueSetName); vs.setName(theValueSetName);
vs.setStatus(Enumerations.PublicationStatus.ACTIVE); vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
vs.setPublisher("Regenstrief Institute, Inc.");
vs.addContact()
.setName("Regenstrief Institute, Inc.")
.addTelecom()
.setSystem(ContactPoint.ContactPointSystem.URL)
.setValue("https://loinc.org");
vs.setCopyright("This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/");
myIdToValueSet.put(theValueSetId, vs); myIdToValueSet.put(theValueSetId, vs);
myValueSets.add(vs); myValueSets.add(vs);
} else { } else {

View File

@ -35,8 +35,8 @@ import static org.apache.commons.lang3.StringUtils.trim;
public class LoincDocumentOntologyHandler extends BaseHandler implements IRecordHandler { public class LoincDocumentOntologyHandler extends BaseHandler implements IRecordHandler {
public static final String DOCUMENT_ONTOLOGY_CODES_VS_ID = "DOCUMENT-ONTOLOGY-CODES-VS"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_ID = "loinc-document-ontology";
public static final String DOCUMENT_ONTOLOGY_CODES_VS_URI = "http://loinc.org/document-ontology-codes"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_URI = "http://loinc.org/vs/loinc-document-ontology";
public static final String DOCUMENT_ONTOLOGY_CODES_VS_NAME = "LOINC Document Ontology Codes"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_NAME = "LOINC Document Ontology Codes";
private final Map<String, TermConcept> myCode2Concept; private final Map<String, TermConcept> myCode2Concept;
private final TermCodeSystemVersion myCodeSystemVersion; private final TermCodeSystemVersion myCodeSystemVersion;

View File

@ -57,6 +57,13 @@ public class LoincHandler implements IRecordHandler {
TermConcept concept = new TermConcept(myCodeSystemVersion, code); TermConcept concept = new TermConcept(myCodeSystemVersion, code);
concept.setDisplay(display); concept.setDisplay(display);
if (!display.equalsIgnoreCase(shortName)) {
concept
.addDesignation()
.setUseDisplay("ShortName")
.setValue(shortName);
}
for (String nextPropertyName : myPropertyNames) { for (String nextPropertyName : myPropertyNames) {
if (!theRecord.toMap().containsKey(nextPropertyName)) { if (!theRecord.toMap().containsKey(nextPropertyName)) {
continue; continue;
@ -67,7 +74,7 @@ public class LoincHandler implements IRecordHandler {
} }
} }
Validate.isTrue(!myCode2Concept.containsKey(code)); Validate.isTrue(!myCode2Concept.containsKey(code), "The code %s has appeared more than once", code);
myCode2Concept.put(code, concept); myCode2Concept.put(code, concept);
} }
} }

View File

@ -38,6 +38,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IR
public static final String LOINC_IEEE_CM_ID = "LOINC-IEEE-MEDICAL-DEVICE-CM"; public static final String LOINC_IEEE_CM_ID = "LOINC-IEEE-MEDICAL-DEVICE-CM";
public static final String LOINC_IEEE_CM_URI = "http://loinc.org/fhir/loinc-ieee-device-code-mappings"; public static final String LOINC_IEEE_CM_URI = "http://loinc.org/fhir/loinc-ieee-device-code-mappings";
public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings"; public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings";
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/IEEE Medical Device Code Mapping Table contains content from IEEE (http://ieee.org), copyright © 2017 IEEE.";
/** /**
* Constructor * Constructor
@ -68,7 +69,8 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IR
.setTargetCodeSystem(targetCodeSystemUri) .setTargetCodeSystem(targetCodeSystemUri)
.setTargetCode(ieeeCode) .setTargetCode(ieeeCode)
.setTargetDisplay(ieeeDisplayName) .setTargetDisplay(ieeeDisplayName)
.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL),
CM_COPYRIGHT);
} }

View File

@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.ValueSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim; import static org.apache.commons.lang3.StringUtils.trim;
public class LoincPartHandler implements IRecordHandler { public class LoincPartHandler implements IRecordHandler {
@ -50,15 +51,23 @@ public class LoincPartHandler implements IRecordHandler {
String partTypeName = trim(theRecord.get("PartTypeName")); String partTypeName = trim(theRecord.get("PartTypeName"));
String partName = trim(theRecord.get("PartName")); String partName = trim(theRecord.get("PartName"));
String partDisplayName = trim(theRecord.get("PartDisplayName")); String partDisplayName = trim(theRecord.get("PartDisplayName"));
String status = trim(theRecord.get("Status"));
if (!"ACTIVE".equals(status)) { // Per Dan's note, we include deprecated parts
return; // String status = trim(theRecord.get("Status"));
} // if (!"ACTIVE".equals(status)) {
// return;
// }
TermConcept concept = new TermConcept(myCodeSystemVersion, partNumber); TermConcept concept = new TermConcept(myCodeSystemVersion, partNumber);
concept.setDisplay(partName); concept.setDisplay(partName);
if (isNotBlank(partDisplayName)) {
concept.addDesignation()
.setConcept(concept)
.setUseDisplay("PartDisplayName")
.setValue(partDisplayName);
}
myCode2Concept.put(partDisplayName, concept); myCode2Concept.put(partDisplayName, concept);
} }

View File

@ -24,19 +24,19 @@ 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.IRecordHandler; 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.ValueSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.apache.commons.lang3.StringUtils.trim; import static org.apache.commons.lang3.StringUtils.trim;
public class LoincPartLinkHandler implements IRecordHandler { public class LoincPartLinkHandler implements IRecordHandler {
private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class);
private final Map<String, TermConcept> myCode2Concept; private final Map<String, TermConcept> myCode2Concept;
private final TermCodeSystemVersion myCodeSystemVersion; private final TermCodeSystemVersion myCodeSystemVersion;
private Long myPartCount;
public LoincPartLinkHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept) { public LoincPartLinkHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept) {
myCodeSystemVersion = theCodeSystemVersion; myCodeSystemVersion = theCodeSystemVersion;
@ -53,17 +53,23 @@ public class LoincPartLinkHandler implements IRecordHandler {
TermConcept loincConcept = myCode2Concept.get(loincNumber); TermConcept loincConcept = myCode2Concept.get(loincNumber);
TermConcept partConcept = myCode2Concept.get(partNumber); TermConcept partConcept = myCode2Concept.get(partNumber);
if (loincConcept==null) { if (loincConcept == null) {
ourLog.warn("No loinc code: {}", loincNumber); ourLog.warn("No loinc code: {}", loincNumber);
return; return;
} }
if (partConcept==null) { if (partConcept == null) {
ourLog.warn("No part code: {}", partNumber); if (myPartCount == null) {
myPartCount = myCode2Concept
.keySet()
.stream()
.filter(t->t.startsWith("LP"))
.count();
}
ourLog.debug("No part code: {} - Have {} part codes", partNumber, myPartCount);
return; return;
} }
// For now we're ignoring these // For now we're ignoring these
} }
private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class);
} }

View File

@ -33,13 +33,15 @@ import org.hl7.fhir.r4.model.ValueSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.trim; import static org.apache.commons.lang3.StringUtils.trim;
public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler { public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler {
public static final String LOINC_PART_MAP_ID = "LOINC-PART-MAP"; public static final String LOINC_PART_MAP_ID = "LOINC-PART-MAP";
public static final String LOINC_PART_MAP_URI = "http://loinc.org/fhir/loinc-part-map"; public static final String LOINC_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct";
public static final String LOINC_PART_MAP_NAME = "LOINC Part Map"; public static final String LOINC_PART_MAP_NAME = "LOINC Part Map";
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC Part File, LOINC/SNOMED CT Expression Association and Map Sets File, RELMA database and associated search index files include SNOMED Clinical Terms (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights are reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org. Under the terms of the Affiliate License, use of SNOMED CT in countries that are not IHTSDO Members is subject to reporting and fee payment obligations. However, IHTSDO agrees to waive the requirements to report and pay fees for use of SNOMED CT content included in the LOINC Part Mapping and LOINC Term Associations for purposes that support or enable more effective use of LOINC. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.";
private final Map<String, TermConcept> myCode2Concept; private final Map<String, TermConcept> myCode2Concept;
private final TermCodeSystemVersion myCodeSystemVersion; private final TermCodeSystemVersion myCodeSystemVersion;
private final List<ConceptMap> myConceptMaps; private final List<ConceptMap> myConceptMaps;
@ -68,7 +70,8 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I
String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice")); String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice"));
Enumerations.ConceptMapEquivalence equivalence; Enumerations.ConceptMapEquivalence equivalence;
switch (mapType) { switch (trim(defaultString(mapType))) {
case "":
case "Exact": case "Exact":
// 'equal' is more exact than 'equivalent' in the equivalence codes // 'equal' is more exact than 'equivalent' in the equivalence codes
equivalence = Enumerations.ConceptMapEquivalence.EQUAL; equivalence = Enumerations.ConceptMapEquivalence.EQUAL;
@ -80,7 +83,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I
equivalence = Enumerations.ConceptMapEquivalence.WIDER; equivalence = Enumerations.ConceptMapEquivalence.WIDER;
break; break;
default: default:
throw new InternalErrorException("Unknown MapType: " + mapType); throw new InternalErrorException("Unknown MapType '" + mapType + "' for PartNumber: " + partNumber);
} }
addConceptMapEntry( addConceptMapEntry(
@ -96,7 +99,9 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I
.setTargetDisplay(extCodeDisplayName) .setTargetDisplay(extCodeDisplayName)
.setTargetCodeSystemVersion(extCodeSystemVersion) .setTargetCodeSystemVersion(extCodeSystemVersion)
.setEquivalence(equivalence) .setEquivalence(equivalence)
.setCopyright(extCodeSystemCopyrightNotice)); .setCopyright(extCodeSystemCopyrightNotice),
CM_COPYRIGHT
);
} }

View File

@ -37,17 +37,27 @@ import static org.apache.commons.lang3.StringUtils.trim;
public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHandler { public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHandler {
public static final String RSNA_CODES_VS_ID = "RSNA-LOINC-CODES-VS"; public static final String RSNA_CODES_VS_ID = "loinc-rsna-radiology-playbook";
public static final String RSNA_CODES_VS_URI = "http://loinc.org/rsna-codes"; public static final String RSNA_CODES_VS_URI = "http://loinc.org/vs/loinc-rsna-radiology-playbook";
public static final String RSNA_CODES_VS_NAME = "RSNA Playbook"; public static final String RSNA_CODES_VS_NAME = "LOINC/RSNA Radiology Playbook";
public static final String RID_MAPPING_CM_ID = "LOINC-TO-RID-CODES-CM"; public static final String RID_MAPPING_CM_ID = "LOINC-TO-RID-CODES-CM";
public static final String RID_MAPPING_CM_URI = "http://loinc.org/rid-codes"; public static final String RID_MAPPING_CM_URI = "http://loinc.org/rid-codes";
public static final String RID_MAPPING_CM_NAME = "RSNA Playbook RID Codes Mapping"; public static final String RID_MAPPING_CM_NAME = "RSNA Playbook RID Codes Mapping";
public static final String RID_CS_URI = "http://rid"; public static final String RID_CS_URI = "http://www.radlex.org";
public static final String RPID_MAPPING_CM_ID = "LOINC-TO-RPID-CODES-CM"; public static final String RPID_MAPPING_CM_ID = "LOINC-TO-RPID-CODES-CM";
public static final String RPID_MAPPING_CM_URI = "http://loinc.org/rpid-codes"; public static final String RPID_MAPPING_CM_URI = "http://loinc.org/rpid-codes";
public static final String RPID_MAPPING_CM_NAME = "RSNA Playbook RPID Codes Mapping"; public static final String RPID_MAPPING_CM_NAME = "RSNA Playbook RPID Codes Mapping";
public static final String RPID_CS_URI = "http://rpid"; /*
* About these being the same - Per Dan:
* We had some discussion about this, and both
* RIDs (RadLex clinical terms) and RPIDs (Radlex Playbook Ids)
* belong to the same "code system" since they will never collide.
* The codesystem uri is "http://www.radlex.org". FYI, that's
* now listed on the FHIR page:
* https://www.hl7.org/fhir/terminologies-systems.html
*/
public static final String RPID_CS_URI = RID_CS_URI;
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/RSNA Radiology Playbook and the LOINC Part File contain content from RadLex® (http://rsna.org/RadLex.aspx), copyright © 2005-2017, The Radiological Society of North America, Inc., available at no cost under the license at http://www.rsna.org/uploadedFiles/RSNA/Content/Informatics/RadLex_License_Agreement_and_Terms_of_Use_V2_Final.pdf.";
private final Map<String, TermConcept> myCode2Concept; private final Map<String, TermConcept> myCode2Concept;
private final TermCodeSystemVersion myCodeSystemVersion; private final TermCodeSystemVersion myCodeSystemVersion;
private final Set<String> myPropertyNames; private final Set<String> myPropertyNames;
@ -116,6 +126,51 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand
case "Rad.Modality.Modality type": case "Rad.Modality.Modality type":
loincCodePropName = "rad-modality-modality-type"; loincCodePropName = "rad-modality-modality-type";
break; break;
case "Rad.Modality.Modality subtype":
loincCodePropName = "rad-modality-modality-subtype";
break;
case "Rad.Anatomic Location.Laterality":
loincCodePropName = "rad-anatomic-location-laterality";
break;
case "Rad.Anatomic Location.Laterality.Presence":
loincCodePropName = "rad-anatomic-location-laterality-presence";
break;
case "Rad.Guidance for.Action":
loincCodePropName = "rad-guidance-for-action";
break;
case "Rad.Guidance for.Approach":
loincCodePropName = "rad-guidance-for-approach";
break;
case "Rad.Guidance for.Object":
loincCodePropName = "rad-guidance-for-object";
break;
case "Rad.Guidance for.Presence":
loincCodePropName = "rad-guidance-for-presence";
break;
case "Rad.Maneuver.Maneuver type":
loincCodePropName = "rad-maneuver-maneuver-type";
break;
case "Rad.Pharmaceutical.Route":
loincCodePropName = "rad-pharmaceutical-route";
break;
case "Rad.Pharmaceutical.Substance Given":
loincCodePropName = "rad-pharmaceutical-substance-given";
break;
case "Rad.Reason for Exam":
loincCodePropName = "rad-reason-for-exam";
break;
case "Rad.Subject":
loincCodePropName = "rad-subject";
break;
case "Rad.Timing":
loincCodePropName = "rad-timing";
break;
case "Rad.View.Aggregation":
loincCodePropName = "rad-view-view-aggregation";
break;
case "Rad.View.View type":
loincCodePropName = "rad-view-view-type";
break;
default: default:
throw new InternalErrorException("Unknown PartTypeName: " + partTypeName); throw new InternalErrorException("Unknown PartTypeName: " + partTypeName);
} }
@ -138,7 +193,8 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand
.setTargetCodeSystem(RID_CS_URI) .setTargetCodeSystem(RID_CS_URI)
.setTargetCode(rid) .setTargetCode(rid)
.setTargetDisplay(preferredName) .setTargetDisplay(preferredName)
.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL),
CM_COPYRIGHT);
} }
// LOINC Term -> Radlex RPID code mappings // LOINC Term -> Radlex RPID code mappings
@ -154,7 +210,8 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand
.setTargetCodeSystem(RPID_CS_URI) .setTargetCodeSystem(RPID_CS_URI)
.setTargetCode(rpid) .setTargetCode(rpid)
.setTargetDisplay(longName) .setTargetDisplay(longName)
.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL),
CM_COPYRIGHT);
} }
} }

View File

@ -29,8 +29,8 @@ import java.util.Map;
public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsHandler { public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsHandler {
public static final String TOP_2000_SI_VS_ID = "TOP-2000-LABRESULTS-SI"; public static final String TOP_2000_SI_VS_ID = "top-2000-lab-observations-si";
public static final String TOP_2000_SI_VS_URI = "http://loinc.org/top-2000-lab-results-si"; public static final String TOP_2000_SI_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-si";
public static final String TOP_2000_SI_VS_NAME = "Top 2000 Lab Results SI"; public static final String TOP_2000_SI_VS_NAME = "Top 2000 Lab Results SI";
public LoincTop2000LabResultsSiHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { public LoincTop2000LabResultsSiHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) {

View File

@ -29,8 +29,8 @@ import java.util.Map;
public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsHandler { public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsHandler {
public static final String TOP_2000_US_VS_ID = "TOP-2000-LABRESULTS-US"; public static final String TOP_2000_US_VS_ID = "top-2000-lab-observations-us";
public static final String TOP_2000_US_VS_URI = "http://loinc.org/top-2000-lab-results-us"; public static final String TOP_2000_US_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-us";
public static final String TOP_2000_US_VS_NAME = "Top 2000 Lab Results US"; public static final String TOP_2000_US_VS_NAME = "Top 2000 Lab Results US";
public LoincTop2000LabResultsUsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { public LoincTop2000LabResultsUsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) {

View File

@ -40,4 +40,33 @@ public class JpaConstants {
*/ */
public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template"; public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template";
/**
* This extension URL indicates whether a REST HOOK delivery should
* include the version ID when delivering.
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids";
/**
* This extension URL indicates whether a REST HOOK delivery should
* reload the resource and deliver the latest version always. This
* could be useful for example if a resource which triggers a
* subscription gets updated many times in short succession and there
* is no value in delivering the older versions.
* <p>
* Note that if the resource is now deleted, this may cause
* the delivery to be cancelled altogether.
* </p>
*
* <p>
* This extension should be of type <code>boolean</code> and should be
* placed on the <code>Subscription.channel</code> element.
* </p>
*/
public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version";
} }

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder; import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.jpa.HibernatePersistenceProvider;
@ -104,7 +105,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
DataSource dataSource = ProxyDataSourceBuilder DataSource dataSource = ProxyDataSourceBuilder
.create(retVal) .create(retVal)
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS) .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
.countQuery(new ThreadQueryCountHolder()) .countQuery(new ThreadQueryCountHolder())
.build(); .build();

View File

@ -281,6 +281,7 @@ public abstract class BaseJpaTest {
@Override @Override
public Void doInTransaction(TransactionStatus theStatus) { public Void doInTransaction(TransactionStatus theStatus) {
entityManager.createQuery("DELETE from " + TermConceptProperty.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + TermConceptProperty.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + TermConceptDesignation.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + TermConcept.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + TermConcept.class.getSimpleName() + " d").executeUpdate();
for (TermCodeSystem next : entityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) { for (TermCodeSystem next : entityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) {
next.setCurrentVersion(null); next.setCurrentVersion(null);

View File

@ -18,6 +18,7 @@ import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -27,6 +28,11 @@ import static org.hamcrest.Matchers.empty;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@SuppressWarnings({"unchecked", "deprecation"}) @SuppressWarnings({"unchecked", "deprecation"})
@TestPropertySource(properties = {
// Since scheduled tasks can cause searches, which messes up the
// value returned by SearchBuilder.getLastHandlerMechanismForUnitTest()
"scheduling_disabled=true"
})
public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test { public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3UniqueSearchParamTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3UniqueSearchParamTest.class);

View File

@ -1,31 +1,19 @@
package ca.uhn.fhir.jpa.provider.dstu3; package ca.uhn.fhir.jpa.provider.dstu3;
import static org.hamcrest.Matchers.not; import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest;
import static org.hamcrest.Matchers.stringContainsInOrder; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import static org.junit.Assert.assertEquals; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import static org.junit.Assert.assertThat; import ca.uhn.fhir.util.TestUtil;
import static org.junit.Assert.fail; import org.hl7.fhir.dstu3.model.*;
import java.io.IOException;
import org.hl7.fhir.dstu3.model.BooleanType;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; import java.io.IOException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import static org.junit.Assert.*;
import ca.uhn.fhir.util.TestUtil;
public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDstu3Test { public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDstu3Test {
@ -58,11 +46,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
// With HTTP GET // With HTTP GET
respParam = ourClient respParam = ourClient
@ -78,11 +66,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
} }
@ -100,11 +88,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Accession ID", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
} }
@Test @Test
@ -141,11 +129,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
} }
@Test @Test
@ -181,11 +169,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue());
} }
@Test @Test
@ -259,11 +247,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst
ourLog.info(resp); ourLog.info(resp);
assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("name", respParam.getParameter().get(0).getName());
assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue());
assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("display", respParam.getParameter().get(1).getName());
assertEquals("Married", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue());
assertEquals("abstract", respParam.getParameter().get(2).getName()); assertEquals("abstract", respParam.getParameter().get(2).getName());
assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue()); assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue());
} }
@AfterClass @AfterClass

View File

@ -182,6 +182,15 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
return ids; return ids;
} }
@Test
@Ignore
public void test() throws IOException {
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse?_count=50&status=completed&questionnaire=ARIncenterAbsRecord&_lastUpdated=%3E"+UrlUtil.escapeUrlParam("=2018-01-01")+"&context.organization=O3435");
ourLog.info("*** MAKING QUERY");
ourHttpClient.execute(get);
System.exit(0);
}
@Test @Test
public void testBundleCreate() throws Exception { public void testBundleCreate() throws Exception {
IGenericClient client = myClient; IGenericClient client = myClient;

View File

@ -2,8 +2,10 @@ package ca.uhn.fhir.jpa.subscription.r4;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test; import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.annotation.Update;
@ -172,6 +174,37 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
} }
@Test
public void testRestHookSubscriptionApplicationJsonDisableVersionIdInDelivery() throws Exception {
String payload = "application/json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
subscription1
.getChannel()
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
subscription1
.getChannel()
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
myClient.update().resource(subscription1).execute();
waitForQueueToDrain();
Observation observation1 = sendObservation(code, "SNOMED-CT");
// Should see 1 subscription notification
waitForQueueToDrain();
waitForSize(0, ourCreatedObservations);
waitForSize(1, ourUpdatedObservations);
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
assertEquals(observation1.getIdElement().getIdPart(), ourUpdatedObservations.get(0).getIdElement().getIdPart());
assertEquals(null, ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
}
@Test @Test
public void testRestHookSubscriptionApplicationJson() throws Exception { public void testRestHookSubscriptionApplicationJson() throws Exception {
String payload = "application/json"; String payload = "application/json";
@ -191,6 +224,8 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test {
waitForSize(1, ourUpdatedObservations); waitForSize(1, ourUpdatedObservations);
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0)); assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId()); Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
Assert.assertNotNull(subscriptionTemp); Assert.assertNotNull(subscriptionTemp);

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
@ -13,6 +14,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.r4.model.ValueSet;
import org.junit.After;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -42,6 +44,14 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermCodeSystemVersion cs = new TermCodeSystemVersion(); TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table); cs.setResource(table);
TermConcept parent;
parent = new TermConcept(cs, "ParentWithNoChildrenA");
cs.getConcepts().add(parent);
parent = new TermConcept(cs, "ParentWithNoChildrenB");
cs.getConcepts().add(parent);
parent = new TermConcept(cs, "ParentWithNoChildrenC");
cs.getConcepts().add(parent);
TermConcept parentA = new TermConcept(cs, "ParentA"); TermConcept parentA = new TermConcept(cs, "ParentA");
cs.getConcepts().add(parentA); cs.getConcepts().add(parentA);
@ -56,6 +66,11 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
TermConcept childAAB = new TermConcept(cs, "childAAB"); TermConcept childAAB = new TermConcept(cs, "childAAB");
childAAB.addPropertyString("propA", "valueAAB"); childAAB.addPropertyString("propA", "valueAAB");
childAAB.addPropertyString("propB", "foo"); childAAB.addPropertyString("propB", "foo");
childAAB.addDesignation()
.setUseSystem("D1S")
.setUseCode("D1C")
.setUseDisplay("D1D")
.setValue("D1V");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA); childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB"); TermConcept childAB = new TermConcept(cs, "childAB");
@ -190,9 +205,75 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
ValueSet outcome = myTermSvc.expandValueSet(vs); ValueSet outcome = myTermSvc.expandValueSet(vs);
codes = toCodesContains(outcome.getExpansion().getContains()); codes = toCodesContains(outcome.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB")); assertThat(codes, containsInAnyOrder("ParentWithNoChildrenA", "ParentWithNoChildrenB", "ParentWithNoChildrenC", "ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB"));
} }
@Test
public void testPropertiesAndDesignationsPreservedInExpansion() {
createCodeSystem();
List<String> codes;
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(CS_URL);
include.addConcept().setCode("childAAB");
ValueSet outcome = myTermSvc.expandValueSet(vs);
codes = toCodesContains(outcome.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAB"));
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
assertEquals("childAAB", concept.getCode());
assertEquals("http://example.com/my_code_system", concept.getSystem());
assertEquals(null, concept.getDisplay());
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
assertEquals("D1V", concept.getDesignation().get(0).getValue());
}
@After
public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Test
public void testCreatePropertiesAndDesignationsWithDeferredConcepts() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true);
createCodeSystem();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
myTermSvc.saveDeferred();
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(CS_URL);
include.addConcept().setCode("childAAB");
ValueSet outcome = myTermSvc.expandValueSet(vs);
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAB"));
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
assertEquals("childAAB", concept.getCode());
assertEquals("http://example.com/my_code_system", concept.getSystem());
assertEquals(null, concept.getDisplay());
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
assertEquals("D1V", concept.getDesignation().get(0).getValue());
}
@Test @Test
public void testFindCodesAbove() { public void testFindCodesAbove() {
IIdType id = createCodeSystem(); IIdType id = createCodeSystem();

View File

@ -108,7 +108,6 @@ where res_id in (
drop table hfj_history_tag cascade constraints; drop table hfj_history_tag cascade constraints;
drop table hfj_forced_id cascade constraints; drop table hfj_forced_id cascade constraints;
drop table HFJ_SUBSCRIPTION_STATS cascade constraints;
drop table hfj_res_link cascade constraints; drop table hfj_res_link cascade constraints;
drop table hfj_spidx_coords cascade constraints; drop table hfj_spidx_coords cascade constraints;
drop table hfj_spidx_date cascade constraints; drop table hfj_spidx_date cascade constraints;

View File

@ -451,7 +451,7 @@
<jetty_version>9.4.8.v20171121</jetty_version> <jetty_version>9.4.8.v20171121</jetty_version>
<jsr305_version>3.0.2</jsr305_version> <jsr305_version>3.0.2</jsr305_version>
<!--<hibernate_version>5.2.10.Final</hibernate_version>--> <!--<hibernate_version>5.2.10.Final</hibernate_version>-->
<hibernate_version>5.2.12.Final</hibernate_version> <hibernate_version>5.2.16.Final</hibernate_version>
<hibernate_validator_version>5.4.1.Final</hibernate_validator_version> <hibernate_validator_version>5.4.1.Final</hibernate_validator_version>
<!-- Update lucene version when you update hibernate-search version --> <!-- Update lucene version when you update hibernate-search version -->
<hibernate_search_version>5.7.1.Final</hibernate_search_version> <hibernate_search_version>5.7.1.Final</hibernate_search_version>
@ -998,7 +998,7 @@
<dependency> <dependency>
<groupId>org.javassist</groupId> <groupId>org.javassist</groupId>
<artifactId>javassist</artifactId> <artifactId>javassist</artifactId>
<version>3.20.0-GA</version> <version>3.22.0-GA</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>

View File

@ -6,6 +6,46 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="3.4.0" date="TBD">
<action type="add">
The version of a few dependencies have been bumped to the
latest versions (dependent HAPI modules listed in brackets):
<![CDATA[
<ul>
<li>Hibernate (JPA): 5.2.12.Final -&gt; 5.2.16.Final</li>
<li>Javassist (JPA): 3.20.0-GA -&gt; 3.22.0-GA</li>
</ul>
]]>
</action>
<action type="fix">
When performing a FHIR resource update in the JPA server
where the update happens within a transaction, and the
resource being updated contains placeholder IDs, and
the resource has not actually changed, a new version was
created even though there was not actually any change.
This particular combination of circumstances seems very
specific and improbable, but it is quite common for some
types of solutions (e.g. mapping HL7v2 data) so this
fix can prevent significant wasted space in some cases.
</action>
<action type="fix">
JPA server index tables did not have a column length specified
on the resource type column. This caused the default of 255 to
be used, which wasted a lot of space since resource names are all
less than 30 chars long and a single resource can have 10-100+
index rows depending on configuration. This has now been set
to a much more sensible 30.
</action>
<action type="fix">
The LOINC uploader for the JPA Terminology Server has been
significantly beefed up so that it now takes in the full
set of LOINC distribution artifacts, and creates not only
the LOINC CodeSystem but a complete set of concept properties,
a number of LOINC ValueSets, and a number of LOINC ConceptMaps.
This work was sponsored by the Regenstrief Institute. Thanks
to Regenstrief for their support!
</action>
</release>
<release version="3.3.0" date="2018-03-29"> <release version="3.3.0" date="2018-03-29">
<action type="add"> <action type="add">
This release corrects an inefficiency in the JPA Server, but requires a schema This release corrects an inefficiency in the JPA Server, but requires a schema