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 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="measurement") public class OmopMeasurement extends BaseResourceEntity { /** * */ private static final String RES_TYPE = "Observation"; public static final Long SYSTOLIC_CONCEPT_ID = 3004249L; public static final Long DIASTOLIC_CONCEPT_ID = 3012888L; @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="measurement_occurrence_seq_gen") @SequenceGenerator(name="measurement_occurrence_seq_gen", sequenceName="measurement_occurrence_id_seq", allocationSize=1) @Column(name = "measurement_id") @Access(AccessType.PROPERTY) private Long id; @ManyToOne(cascade={CascadeType.MERGE}) @JoinColumn(name="person_id", nullable=false) @NotNull private PersonComplement person; @Column(name="measurement_source_value") private String sourceValue; @ManyToOne(cascade={CascadeType.MERGE}) @JoinColumn(name="measurement_concept_id", nullable=false) @NotNull private Concept measurementConcept; @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 = "unit_concept_id") private Concept unit; @Column(name = "range_low") private Double rangeLow; @Column(name = "range_high") private Double rangeHigh; @Column(name = "measurement_date", nullable = false) @Temporal(TemporalType.DATE) @NotNull private Date date; @Column(name = "measurement_time") // @Temporal(TemporalType.TIME) private String time; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "visit_occurrence_id") private VisitOccurrence visitOccurrence; @ManyToOne(cascade = { CascadeType.MERGE }, fetch = FetchType.LAZY) @JoinColumn(name = "measurement_type_concept_id") private Concept type; @Override public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Person getPerson() { return person; } public void setPerson(PersonComplement person) { this.person = person; } public String getSourceValue() { return sourceValue; } public void setSourceValue(String sourceValue) { this.sourceValue = sourceValue; } public Concept getMeasurementConcept() { return measurementConcept; } public void setMeasurementConcept(Concept measurementConcept) { this.measurementConcept = measurementConcept; } 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 getUnit() { return unit; } public void setUnit(Concept unit) { this.unit = unit; } public Double getRangeLow() { return rangeLow; } public void setRangeLow(Double rangeLow) { this.rangeLow =rangeLow; } public Double getRangeHigh() { return rangeHigh; } public void setRangeHigh(Double rangeHigh) { this.rangeHigh = rangeHigh; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } public VisitOccurrence getVisitOccurrence() { return visitOccurrence; } public void setVisitOccurrence (VisitOccurrence visitOccurrence) { this.visitOccurrence = visitOccurrence; } public Concept getType() { return type; } public void setType(Concept type) { this.type = type; } @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 OmopMeasurement origMeasurement = (OmopMeasurement) ocm.loadEntityBySource(OmopMeasurement.class, "OmopMeasurement", "sourceValue", obs.getId().getIdPart()); if (origMeasurement == null) this.setSourceValue(obs.getId().getIdPart()); else this.setId(origMeasurement.getId()); } String code = obs.getCode().getCodingFirstRep().getCode(); Long obsConceptRef = ocm.get(code); setMeasurementConcept(new Concept()); if(obsConceptRef != null){ getMeasurementConcept().setId(obsConceptRef); } else { getMeasurementConcept().setId(0L); System.out.println("Measurement code not recognized: "+code+". System: "+obs.getCode().getCodingFirstRep().getSystem()); } /* Set the value of the observation */ IDatatype value = obs.getValue(); if (value instanceof QuantityDt) { // Long unitId = ocm.get(((QuantityDt) value).getUnit(), OmopConceptMapping.UCUM_CODE, // OmopConceptMapping.UCUM_CODE_STANDARD, OmopConceptMapping.UCUM_CODE_CUSTOM); 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); } if (!obs.getReferenceRangeFirstRep().isEmpty()) this.rangeHigh = obs.getReferenceRangeFirstRep().getHigh().getValue().doubleValue(); if (!obs.getReferenceRangeFirstRep().isEmpty()) this.rangeLow = obs.getReferenceRangeFirstRep().getLow().getValue().doubleValue(); } else if (value instanceof CodeableConceptDt) { // Long valueAsConceptId = ocm.get(((CodeableConceptDt) value).getCodingFirstRep().getCode(), // OmopConceptMapping.CLINICAL_FINDING); Long valueAsConceptId = ocm.get(((CodeableConceptDt) value).getCodingFirstRep().getCode()); if (valueAsConceptId != null) { this.valueAsConcept = new Concept(); this.valueAsConcept.setId(valueAsConceptId); } } 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")) { getType().setId(44818701L); } else if (catCode.getCode().equalsIgnoreCase("laboratory")) { getType().setId(44818702L); } } } return this; } }