package edu.gatech.i3l.fhir.dstu2.entities; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.validation.constraints.NotNull; 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.QuantityDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.StringDt; import edu.gatech.i3l.fhir.jpa.entity.BaseResourceEntity; import edu.gatech.i3l.fhir.jpa.entity.IResourceEntity; import edu.gatech.i3l.omop.mapping.OmopConceptMapping; @Entity @Table(name="observation") public class OmopObservation extends BaseResourceEntity { /** * */ private static final String RES_TYPE = "Observation"; @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="observation_occurrence_seq_gen") @SequenceGenerator(name="observation_occurrence_seq_gen", sequenceName="observation_occurrence_id_seq", allocationSize=1) @Column(name = "observation_id") @Access(AccessType.PROPERTY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "person_id", nullable = false) @NotNull private PersonComplement person; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "observation_concept_id", nullable = false) @NotNull private Concept observationConcept; @Column(name = "observation_date", nullable = false) @Temporal(TemporalType.DATE) @NotNull private Date date; @Column(name = "observation_time") // @Temporal(TemporalType.TIME) private String time; @Column(name = "value_as_string") private String valueAsString; @Column(name = "value_as_number") private Double valueAsNumber; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "value_as_concept_id") private Concept valueAsConcept; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "observation_type_concept_id", nullable = false) @NotNull private Concept type; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "provider_id") private Provider provider; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "visit_occurrence_id") private VisitOccurrence visitOccurrence; @Column(name = "observation_source_value") private String sourceValue; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "unit_concept_id") private Concept unit; @Column(name = "unit_source_value") private String unitSourceValue; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } public PersonComplement getPerson() { return person; } public void setPerson(PersonComplement person) { this.person = person; } public Concept getObservationConcept() { return observationConcept; } public void setObservationConcept(Concept observationConcept) { this.observationConcept = observationConcept; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getValueAsString() { return valueAsString; } public void setValueAsString(String valueAsString) { this.valueAsString = valueAsString; } public Double getValueAsNumber() { return valueAsNumber; } public void setValueAsNumber(Double valueAsNumber) { this.valueAsNumber = valueAsNumber; } public Concept getValueAsConcept() { return valueAsConcept; } public void setValueAsConcept(Concept valueAsConcept) { this.valueAsConcept = valueAsConcept; } public Concept getType() { return type; } public void setType(Concept type) { this.type = type; } public Provider getProvider() { return provider; } public void setProvider(Provider provider) { this.provider = provider; } public VisitOccurrence getVisitOccurrence() { return visitOccurrence; } public void setVisitOccurrence(VisitOccurrence visitOccurrence) { this.visitOccurrence = visitOccurrence; } public String getSourceValue() { return sourceValue; } public void setSourceValue(String sourceValue) { this.sourceValue = sourceValue; } public Concept getUnit() { return unit; } public void setUnit(Concept unit) { this.unit = unit; } public String getUnitSourceValue() { return unitSourceValue; } public void setUnitSourceValue(String unitSourceValue) { this.unitSourceValue = unitSourceValue; } @Override public FhirVersionEnum getFhirVersion() { return FhirVersionEnum.DSTU2; } @Override public String getResourceType() { return RES_TYPE; } @Override public InstantDt getUpdated() { // TODO Auto-generated method stub return null; } @Override public String translateSearchParam(String theSearchParam) { // TODO Auto-generated method stub return null; } @Override public IResource getRelatedResource() { // TODO Auto-generated method stub return null; } @Override public IResourceEntity constructEntityFromResource(IResource resource) { ca.uhn.fhir.model.dstu2.resource.Observation obs = (ca.uhn.fhir.model.dstu2.resource.Observation) resource; ResourceReferenceDt subjectReference = obs.getSubject(); if (subjectReference != null) { if ("Patient".equals(subjectReference.getReference().getResourceType())) { PersonComplement person = PersonComplement.searchAndUpdate(subjectReference); if (person == null) return null; // We must have a patient this.setPerson(person); } else { // Group, Device, and Location are not supported in OMOP v5 return null; } } else { // We must have subject reference. return null; } // We are writing to the database. Keep the source so we know where it is coming from OmopConceptMapping ocm = OmopConceptMapping.getInstance(); if (obs.getId() != null) { // See if we already have this in the source field. If so, // then we want update not create OmopObservation origObservation = (OmopObservation) ocm.loadEntityBySource(OmopObservation.class, "OmopObservation", "sourceValue", obs.getId().getIdPart()); if (origObservation == null) this.setSourceValue(obs.getId().getIdPart()); else this.setId(origObservation.getId()); } String code = obs.getCode().getCodingFirstRep().getCode(); Long obsConceptRef = ocm.get(code); setObservationConcept(new Concept()); if(obsConceptRef != null){ getObservationConcept().setId(obsConceptRef); } else { getObservationConcept().setId(0L); System.out.println("Observation code not recognized: "+code+". System: "+obs.getCode().getCodingFirstRep().getSystem()); } /* Set the value of the observation */ boolean isValueString = false; IDatatype value = obs.getValue(); if (value instanceof QuantityDt) { String unitCode = ((QuantityDt) value).getCode(); if (unitCode == null) { unitCode = ((QuantityDt) value).getUnit(); } Long unitId = ocm.get(unitCode); this.valueAsNumber = ((QuantityDt) value).getValue().doubleValue(); if (unitId != null) { this.unit = new Concept(); this.unit.setId(unitId); } } else if (value instanceof CodeableConceptDt) { Long valueAsConceptId = ocm.get(((CodeableConceptDt) value).getCodingFirstRep().getCode()); if (valueAsConceptId != null) { this.valueAsConcept = new Concept(); this.valueAsConcept.setId(valueAsConceptId); } } else if (value instanceof StringDt) { String valueAsString = ((StringDt) value).getValueAsString(); if (valueAsString != null) { this.valueAsString = valueAsString; isValueString = true; } } if (obs.getEffective() instanceof DateTimeDt) { this.date = ((DateTimeDt) obs.getEffective()).getValue(); SimpleDateFormat timeFormat = new SimpleDateFormat ("HH:mm:ss"); this.time = timeFormat.format(((DateTimeDt) obs.getEffective()).getValue()); } else if (obs.getEffective() instanceof PeriodDt) { // TODO: we need to handle period. We can probably use // we can use range_low and range_high. These are only available in Measurement } /* Set visit occurrence */ Long visitOccurrenceId = obs.getEncounter().getReference().getIdPartAsLong(); if (visitOccurrenceId != null) { this.visitOccurrence = new VisitOccurrence(); this.visitOccurrence.setId(visitOccurrenceId); } /* measture type concept id - this is required field in OMOP v5. */ setType (new Concept()); CodeableConceptDt obsCategory = obs.getCategory(); if (obsCategory.isEmpty()) { getType().setId(0L); } else { List<CodingDt> catCodes = obsCategory.getCoding(); for (CodingDt catCode : catCodes) { if (catCode.getCode().equalsIgnoreCase("exam")) { if (isValueString) getType().setId(38000281L); else getType().setId(38000280L); } else if (catCode.getCode().equalsIgnoreCase("laboratory")) { if (isValueString) getType().setId(38000278L); else getType().setId(38000277L); } else if (catCode.getCode().equalsIgnoreCase("survey")) { getType().setId(45905771L); } else if (catCode.getCode().equalsIgnoreCase("vital-signs")) { getType().setId(38000280L); } else { getType().setId(0L); } } } return this; } }