/**
*
*/
package edu.gatech.i3l.fhir.dstu2.entities;
import java.util.Date;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Condition;
import ca.uhn.fhir.model.dstu2.valueset.ConditionCategoryCodesEnum;
import ca.uhn.fhir.model.dstu2.valueset.ConditionVerificationStatusEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import edu.gatech.i3l.fhir.jpa.entity.BaseResourceEntity;
import edu.gatech.i3l.fhir.jpa.entity.IResourceEntity;
import edu.gatech.i3l.omop.enums.Omop4ConceptsFixedIds;
import edu.gatech.i3l.omop.mapping.OmopConceptMapping;
/**
* @author Myung Choi
*
*/
@Entity
@Table(name="condition_occurrence")
@Inheritance(strategy=InheritanceType.JOINED)
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
public class ConditionOccurrence extends BaseResourceEntity {
public static final String RESOURCE_TYPE = "Condition";
@Id
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="condition_occurrence_seq_gen")
@SequenceGenerator(name="condition_occurrence_seq_gen", sequenceName="condition_occurrence_id_seq", allocationSize=1)
@Column(name="condition_occurrence_id")
@Access(AccessType.PROPERTY)
private Long id;
@ManyToOne(cascade={CascadeType.MERGE})
@JoinColumn(name="person_id", nullable=false)
@NotNull
private PersonComplement person;
@ManyToOne(cascade={CascadeType.MERGE})
@JoinColumn(name="condition_concept_id", nullable=false)
@NotNull
private Concept conditionConcept;
@Column(name="condition_start_date", nullable=false)
@NotNull
private Date startDate;
@Column(name="condition_end_date", nullable=false)
private Date endDate;
@ManyToOne(cascade={CascadeType.MERGE})
@JoinColumn(name="condition_type_concept_id", nullable=false)
@NotNull
private Concept conditionTypeConcept;
@Column(name="stop_reason")
private String stopReason;
/**
* @omop
* @fhir Asserter:
* person who asserts this condition (Practitioner or Patient)
* @fhirVersion 0.4.0
* @omopVersion 4.0
*/
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="provider_id")
private Provider provider;
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="visit_occurrence_id")
private VisitOccurrence encounter;
@Column(name="condition_source_value")
private String sourceValue;
@ManyToOne(cascade={CascadeType.MERGE})
@JoinColumn(name="condition_source_concept_id")
private Concept sourceConcept;
public ConditionOccurrence() {
super();
}
public ConditionOccurrence(Long id, PersonComplement person, Concept conditionConcept, Date startDate, Date endDate,
Concept conditionTypeConcept, String stopReason, Provider provider, VisitOccurrence encounter,
String sourceValue, Concept sourceConcept) {
super();
this.id = id;
this.person = person;
this.conditionConcept = conditionConcept;
this.startDate = startDate;
this.endDate = endDate;
this.conditionTypeConcept = conditionTypeConcept;
this.stopReason = stopReason;
this.provider = provider;
this.encounter = encounter;
this.sourceValue = sourceValue;
this.sourceConcept = sourceConcept;
}
@Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getResourceType() {
return RESOURCE_TYPE;
}
public PersonComplement getPerson() {
return person;
}
public void setPerson(PersonComplement person) {
this.person = person;
}
public Concept getConditionConcept() {
return conditionConcept;
}
public void setConditionConcept(Concept conditionConcept) {
this.conditionConcept = conditionConcept;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public Concept getConditionTypeConcept() {
return conditionTypeConcept;
}
public void setConditionTypeConcept(Concept conditionTypeConcept) {
this.conditionTypeConcept = conditionTypeConcept;
}
public String getStopReason() {
return stopReason;
}
public void setStopReason(String stopReason) {
this.stopReason = stopReason;
}
public Provider getProvider() {
return provider;
}
public void setProvider(Provider provider) {
this.provider = provider;
}
public VisitOccurrence getEncounter() {
return encounter;
}
public void setEncounter(VisitOccurrence encounter) {
this.encounter = encounter;
}
public String getSourceValue() {
return sourceValue;
}
public void setSourceValue(String sourceValue) {
this.sourceValue = sourceValue;
}
public Concept getSourceConcept() {
return sourceConcept;
}
public void setSourceConcept(Concept sourceConcept) {
this.sourceConcept = sourceConcept;
}
@Override
public IResourceEntity constructEntityFromResource(IResource resource) {
if (resource instanceof Condition) {
Condition condition = (Condition) resource;
/* Set patient */
ResourceReferenceDt patientResource = condition.getPatient();
if (patientResource == null) return null; // We have to have a patient
// Long patientRef = patientResource.getReference().getIdPartAsLong();
PersonComplement person = PersonComplement.searchAndUpdate(patientResource);
if (person == null) return null; // We must have a patient
this.setPerson(person);
// Long patientRef = condition.getPatient().getReference().getIdPartAsLong();
// if(patientRef != null){
// this.person = new PersonComplement();
// this.person.setId(patientRef);
// }
// We are writing to the database. Keep the source so we know where it is coming from
OmopConceptMapping ocm = OmopConceptMapping.getInstance();
if (condition.getId() != null) {
// See if we already have this in the source field. If so,
// then we want update not create
ConditionOccurrence origCondition = (ConditionOccurrence) ocm.loadEntityBySource(ConditionOccurrence.class, "ConditionOccurrence", "sourceValue", condition.getId().getIdPart());
if (origCondition == null)
this.sourceValue = condition.getId().getIdPart();
else
this.setId(origCondition.getId());
}
Long conditionConceptRef = ocm.get(condition.getCode().getCodingFirstRep().getCode());
this.conditionConcept = new Concept();
if(conditionConceptRef != null){
this.conditionConcept.setId(conditionConceptRef);
} else {
this.conditionConcept.setId(0L);
System.out.println("Condition code not recognized: "+condition.getCode().getCodingFirstRep().getCode()+". System: "+condition.getCode().getCodingFirstRep().getSystem());
}
IDatatype onSetDate = condition.getOnset();
if (onSetDate instanceof DateTimeDt) {
DateTimeDt dateTimeDt = (DateTimeDt) onSetDate;
this.startDate = dateTimeDt.getValue();
} else if (onSetDate instanceof PeriodDt) {
PeriodDt periodDt = (PeriodDt) onSetDate;
this.startDate = periodDt.getStart();
this.endDate = periodDt.getEnd();
}
ResourceReferenceDt encounterResource = condition.getEncounter();
if (encounterResource != null) {
Long encounterReference = encounterResource.getReference().getIdPartAsLong();
if (encounterReference != null) {
VisitOccurrence visitOccurrence = VisitOccurrence
.searchAndUpdate(encounterReference, startDate, endDate, this.person);
if (visitOccurrence != null) {
this.setEncounter(visitOccurrence);
}
}
}
this.conditionTypeConcept = new Concept();
ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt<ConditionCategoryCodesEnum> condCategory = condition.getCategory();
CodingDt condCatCoding = condCategory.getCodingFirstRep();
if (condCatCoding != null) {
if (condCatCoding.getCode().equalsIgnoreCase(ConditionCategoryCodesEnum.COMPLAINT.getCode())) {
this.conditionTypeConcept.setId(Omop4ConceptsFixedIds.PATIENT_SELF_REPORT.getConceptId());
} else {
this.conditionTypeConcept.setId(Omop4ConceptsFixedIds.EHR_PROBLEM_ENTRY.getConceptId());
}
} else {
this.conditionTypeConcept.setId(Omop4ConceptsFixedIds.PRIMARY_CONDITION.getConceptId());
}
// this.stopReason = stopReason; NOTE: no FHIR parameter for
// stopReason.
IdDt asserterReference = condition.getAsserter().getReference();
ResourceReferenceDt asserterResRef = condition.getAsserter();
if (asserterResRef != null && asserterReference.getIdPartAsLong() != null && asserterReference.getResourceType() != null
&& asserterReference.getResourceType().equalsIgnoreCase(Provider.RESOURCE_TYPE)) {
Long providerId = asserterReference.getIdPartAsLong();
if (providerId != null) {
Provider provider = (Provider) OmopConceptMapping.getInstance().loadEntityById(Provider.class, providerId);
if (provider != null) {
this.setProvider(provider);
} else {
// See if we have received this earlier.
provider = (Provider) OmopConceptMapping.getInstance().loadEntityBySource(Provider.class, "Provider", "providerSourceValue", providerId.toString());
if (provider == null) {
this.provider = new Provider();
this.provider.setProviderName(asserterResRef.getDisplay().getValueAsString());
this.provider.setProviderSourceValue(providerId.toString());
} else {
this.setProvider(provider);
}
}
}
}
//this.sourceValue = "FHIRCREATE";
}
return this;
}
@Override
public Condition getRelatedResource() {
Condition condition = new Condition();
// Populate condition parameters.
// Refer to 4.3.3 at http://hl7-fhir.github.io/condition.html
condition.setId(this.getIdDt());
// Set patient reference to Patient (note: in dstu1, this was subject.)
ResourceReferenceDt patientReference = new ResourceReferenceDt(new IdDt(Person.RES_TYPE, this.person.getId()));
patientReference.setDisplay(this.person.getNameAsSingleString());
condition.setPatient(patientReference);
// Set encounter if exists.
if (encounter != null && encounter.getId() > 0) {
// FIXME: encounter resource not yet implemented.
// we just create this reference resource manually. When encounter
// is implemented, we
// will get it from visit_occurrence class.
ResourceReferenceDt encounterReference = new ResourceReferenceDt(new IdDt(VisitOccurrence.RES_TYPE, this.encounter.getId()));
condition.setEncounter(encounterReference);
}
// Set asserter if exists
// This can be either Patient or Practitioner.
if (provider != null && provider.getId() > 0) {
ResourceReferenceDt practitionerReference = new ResourceReferenceDt(new IdDt(Provider.RESOURCE_TYPE, provider.getId()));
practitionerReference.setDisplay(this.provider.getProviderName());
condition.setAsserter(practitionerReference);
}
// Set Code
// System.out.println("ConceptID:"+this.getConditionConcept().getId().toString());
// System.out.println("ConceptName:"+this.getConditionConcept().getName());
//
// Vocabulary myVoc = this.getConditionConcept().getVocabulary();
//
// System.out.println("VocabularyID:"+myVoc.getId());
// System.out.println("VocabularyName:"+myVoc.getName());
// First check if ICD codes are available.
String theSystem;
String theCode;
String theDisplay = "";
// if (this.sourceValue.startsWith("icd-9-cm:") == true) {
// theSystem = "http://hl7.org/fhir/sid/icd-9-cm";
// theCode = this.sourceValue.substring(9);
// } else {
theSystem = conditionConcept.getVocabulary().getSystemUri();
theCode = conditionConcept.getConceptCode();
theDisplay = conditionConcept.getName();
// }
CodeableConceptDt conditionCodeConcept = new CodeableConceptDt();
if (theSystem != "") {
// Create coding here. We have one coding in this condition as OMOP
// allows one coding concept per condition.
// In the future, if we want to allow multiple coding concepts here,
// we need to do it here.
CodingDt coding = new CodingDt(theSystem, theCode);
coding.setDisplay(theDisplay);
conditionCodeConcept.addCoding(coding);
}
// FHIR does not require the coding. If our System URI is not mappable
// from
// OMOP database, then coding would be empty. Set Text here. Even text
// is not
// required in FHIR. But, then no reason to have this condition, I
// think...
String theText = conditionConcept.getName() + ", " + conditionConcept.getVocabulary().getName() + ", "
+ conditionConcept.getConceptCode();
conditionCodeConcept.setText(theText);
condition.setCode(conditionCodeConcept);
// Set clinicalStatus
// We have clinicalStatus information in the FHIR extended table. This
// will
// be set in the extended class.
// Set severity
// We have this as well in the FHIR exteded table.
// Set onset[x]
// We may have only one date if this condition did not end. If ended, we
// have
// a period. First, check if endDate is available.
DateTimeDt startDateDt = new DateTimeDt(startDate);
if (endDate == null) {
// Date
condition.setOnset(startDateDt);
} else {
// Period
DateTimeDt endDateDt = new DateTimeDt(endDate);
PeriodDt periodDt = new PeriodDt();
periodDt.setStart(startDateDt);
periodDt.setEnd(endDateDt);
condition.setOnset(periodDt);
}
// Category
Concept myCat = this.getConditionConcept();
if (myCat.getId() == Omop4ConceptsFixedIds.PATIENT_SELF_REPORT.getConceptId()) {
condition.setCategory(ConditionCategoryCodesEnum.COMPLAINT);
} else {
condition.setCategory(ConditionCategoryCodesEnum.FINDING);
}
// VerficationStutus
condition.setVerificationStatus(ConditionVerificationStatusEnum.CONFIRMED);
return condition;
}
@Override
public FhirVersionEnum getFhirVersion() {
return FhirVersionEnum.DSTU2;
}
@Override
public InstantDt getUpdated() {
// TODO Auto-generated method stub
return null;
}
@Override
public String translateSearchParam(String link) {
switch (link) {
case Condition.SP_CODE:
return "conditionConcept.conceptCode";
case Condition.SP_ONSET:
return "startDate";
case Condition.SP_ENCOUNTER:
return "encounter";
case Condition.SP_PATIENT:
return "person";
// case Condition.SP_SUBJECT:
// return "person";
default:
break;
}
return link;
}
}