refs/refs/ETL-German-FHIR-Core/src/main/java/org/miracum/etl/fhirtoomop/mapper/ProcedureMapper.java
lines 204–216
1141 lines · java
1package org.miracum.etl.fhirtoomop.mapper; 3import static org.miracum.etl.fhirtoomop.Constants.CONCEPT_EHR; 4import static org.miracum.etl.fhirtoomop.Constants.FHIR_RESOURCE_ACCEPTABLE_EVENT_STATUS_LIST; 5import static org.miracum.etl.fhirtoomop.Constants.OMOP_DOMAIN_DRUG; 6import static org.miracum.etl.fhirtoomop.Constants.OMOP_DOMAIN_MEASUREMENT; 7import static org.miracum.etl.fhirtoomop.Constants.OMOP_DOMAIN_OBSERVATION; 8import static org.miracum.etl.fhirtoomop.Constants.OMOP_DOMAIN_PROCEDURE; 9import static org.miracum.etl.fhirtoomop.Constants.SOURCE_VOCABULARY_ID_PROCEDURE_BODYSITE; 10import static org.miracum.etl.fhirtoomop.Constants.SOURCE_VOCABULARY_ID_PROCEDURE_DICOM; 11import static org.miracum.etl.fhirtoomop.Constants.VOCABULARY_OPS; 12import static org.miracum.etl.fhirtoomop.Constants.VOCABULARY_SNOMED; 14import com.google.common.base.Strings; 15import io.micrometer.core.instrument.Counter; 16import java.sql.Timestamp; 17import java.time.LocalDate; 18import java.time.LocalDateTime; 19import java.util.ArrayList; 20import java.util.Arrays; 21import java.util.Collections; 23import lombok.extern.slf4j.Slf4j; 24import org.apache.commons.lang3.tuple.Pair; 25import org.hl7.fhir.r4.model.CodeableConcept; 26import org.hl7.fhir.r4.model.Coding; 27import org.hl7.fhir.r4.model.Procedure; 28import org.miracum.etl.fhirtoomop.DbMappings; 29import org.miracum.etl.fhirtoomop.config.FhirSystems; 30import org.miracum.etl.fhirtoomop.mapper.helpers.FindOmopConcepts; 31import org.miracum.etl.fhirtoomop.mapper.helpers.MapperMetrics; 32import org.miracum.etl.fhirtoomop.mapper.helpers.ResourceCheckDataAbsentReason; 33import org.miracum.etl.fhirtoomop.mapper.helpers.ResourceFhirReferenceUtils; 34import org.miracum.etl.fhirtoomop.mapper.helpers.ResourceOmopReferenceUtils; 35import org.miracum.etl.fhirtoomop.mapper.helpers.ResourceOnset; 36import org.miracum.etl.fhirtoomop.model.OmopModelWrapper; 37import org.miracum.etl.fhirtoomop.model.OpsStandardDomainLookup; 38import org.miracum.etl.fhirtoomop.model.omop.Concept; 39import org.miracum.etl.fhirtoomop.model.omop.DeviceExposure; 40import org.miracum.etl.fhirtoomop.model.omop.DrugExposure; 41import org.miracum.etl.fhirtoomop.model.omop.Measurement; 42import org.miracum.etl.fhirtoomop.model.omop.OmopObservation; 43import org.miracum.etl.fhirtoomop.model.omop.ProcedureOccurrence; 44import org.miracum.etl.fhirtoomop.model.omop.SourceToConceptMap; 45import org.miracum.etl.fhirtoomop.repository.service.DeviceExposureMapperServiceImpl; 46import org.miracum.etl.fhirtoomop.repository.service.OmopConceptServiceImpl; 47import org.miracum.etl.fhirtoomop.repository.service.ProcedureMapperServiceImpl; 48import org.springframework.beans.factory.annotation.Autowired; 49import org.springframework.lang.Nullable; 50import org.springframework.stereotype.Component; 53 * The ProcedureMapper class describes the business logic of transforming a FHIR Procedure resource 61public class ProcedureMapper implements FhirMapper<Procedure> { 63 private static final FhirSystems fhirSystems = new FhirSystems(); 65 private final Boolean bulkload; 66 private final DbMappings dbMappings; 67 private final List<String> listOfProcedureVocabularyId = 68 Arrays.asList(SOURCE_VOCABULARY_ID_PROCEDURE_DICOM, VOCABULARY_OPS, VOCABULARY_SNOMED); 70 private static final Counter noStartDateCounter = 71 MapperMetrics.setNoStartDateCounter("stepProcessProcedures"); 72 private static final Counter noPersonIdCounter = 73 MapperMetrics.setNoPersonIdCounter("stepProcessProcedures"); 74 private static final Counter invalidCodeCounter = 75 MapperMetrics.setInvalidCodeCounter("stepProcessProcedures"); 76 private static final Counter noCodeCounter = 77 MapperMetrics.setNoCodeCounter("stepProcessProcedures"); 78 private static final Counter noFhirReferenceCounter = 79 MapperMetrics.setNoFhirReferenceCounter("stepProcessProcedures"); 80 private static final Counter deletedFhirReferenceCounter = 81 MapperMetrics.setDeletedFhirRessourceCounter("stepProcessProcedures"); 83 @Autowired OmopConceptServiceImpl omopConceptService; 84 @Autowired ResourceOmopReferenceUtils omopReferenceUtils; 85 @Autowired ProcedureMapperServiceImpl procedureService; 86 @Autowired DeviceExposureMapperServiceImpl deviceExposureService; 87 @Autowired ResourceFhirReferenceUtils fhirReferenceUtils; 88 @Autowired ResourceCheckDataAbsentReason checkDataAbsentReason; 89 @Autowired FindOmopConcepts findOmopConcepts; 92 * Constructor for objects of the class ProcedureMapper. 94 * @param bulkload parameter which indicates whether the Job should be run as bulk load or 96 * @param dbMappings collections for the intermediate storage of data from OMOP CDM in RAM 99 public ProcedureMapper(Boolean bulkload, DbMappings dbMappings) { 100 this.bulkload = bulkload; 101 this.dbMappings = dbMappings; 105 * Maps a FHIR Procedure resource to procedure_occurrence table in OMOP CDM. 107 * @param srcProcedure FHIR Procedure resource 108 * @param isDeleted a flag, whether the FHIR resource is deleted in the source 109 * @return OmopModelWrapper cache of newly created OMOP CDM records from the FHIR Procedure 113 public OmopModelWrapper map(Procedure srcProcedure, boolean isDeleted) { 115 var wrapper = new OmopModelWrapper(); 117 var procedureLogicId = fhirReferenceUtils.extractId(srcProcedure); 118 var procedureSourceIdentifier = fhirReferenceUtils.extractResourceFirstIdentifier(srcProcedure); 119 if (Strings.isNullOrEmpty(procedureLogicId) 120 && Strings.isNullOrEmpty(procedureSourceIdentifier)) { 121 log.warn("No [Identifier] or [Id] found. [Procedure] resource is invalid. Skip resource"); 122 noFhirReferenceCounter.increment(); 126 String procedureId = ""; 127 if (!Strings.isNullOrEmpty(procedureLogicId)) { 128 procedureId = srcProcedure.getId(); 131 if (bulkload.equals(Boolean.FALSE)) { 132 deleteExistingProcedureEntry(procedureLogicId, procedureSourceIdentifier); 134 deletedFhirReferenceCounter.increment(); 135 log.info("Found a deleted [Procedure] resource {}. Deleting from OMOP DB.", procedureId); 140 var statusElement = srcProcedure.getStatusElement(); 141 var statusValue = checkDataAbsentReason.getValue(statusElement); 142 if (Strings.isNullOrEmpty(statusValue) 143 || !FHIR_RESOURCE_ACCEPTABLE_EVENT_STATUS_LIST.contains(statusValue)) { 145 "The [status]: {} of {} is not acceptable for writing into OMOP CDM. Skip resource.", 151 var personId = getPersonId(srcProcedure, procedureLogicId, procedureId); 152 if (personId == null) { 153 log.warn("No matching [Person] found for [Procedure]: {}. Skip resource", procedureId); 154 noPersonIdCounter.increment(); 158 var procedureCodings = getProcedureCodings(srcProcedure, procedureLogicId); 159 if (procedureCodings.isEmpty()) { 160 log.warn("No [Code] found in [Procedure]: {}. Skip resource", procedureId); 161 noCodeCounter.increment(); 165 var procedureOnset = getProcedureOnset(srcProcedure); 166 if (procedureOnset.getStartDateTime() == null) { 168 "Unable to determine [Performed DateTime] for [Procedure]: {}. Skip resource", 170 noStartDateCounter.increment(); 174 var visitOccId = getVisitOccId(srcProcedure, personId, procedureId); 176 createProcedureMapping( 179 procedureOnset.getStartDateTime(), 183 procedureSourceIdentifier, 187 var usedCodesCodeableConcepts = srcProcedure.getUsedCode(); 188 if (!usedCodesCodeableConcepts.isEmpty()) { 189 var deviceExposures = 190 createDeviceExposure( 195 procedureSourceIdentifier, 196 usedCodesCodeableConcepts, 198 wrapper.setDeviceExposure(deviceExposures); 204 private List<Coding> extractDeviceCode(List<CodeableConcept> usedCodesCodeableConcepts) { 206 List<Coding> devicesCodes = new ArrayList<>(); 207 for (var usedCodesCodeableConcept : usedCodesCodeableConcepts) { 208 var usedCodesCodings = usedCodesCodeableConcept.getCoding(); 209 if (usedCodesCodings.isEmpty()) { 213 usedCodesCodings.forEach(devicesCodes::add); 218 private List<DeviceExposure> createDeviceExposure( 221 ResourceOnset procedureOnset, 222 String procedureLogicId, 223 String procedureSourceIdentifier, 224 List<CodeableConcept> usedCodesCodeableConcepts, 225 String procedureId) { 226 var deviceCodings = extractDeviceCode(usedCodesCodeableConcepts); 227 if (deviceCodings.isEmpty()) { 229 return Collections.emptyList(); 231 List<DeviceExposure> deviceExposures = new ArrayList<>(); 232 for (var deviceCoding : deviceCodings) { 233 var deviceCode = checkDataAbsentReason.getValue(deviceCoding.getCodeElement()); 234 if (Strings.isNullOrEmpty(deviceCode)) { 238 var startDateTime = procedureOnset.getStartDateTime(); 239 var endDateTime = procedureOnset.getEndDateTime(); 241 findOmopConcepts.getConcepts( 242 deviceCoding, startDateTime.toLocalDate(), bulkload, dbMappings, procedureId); 243 if (deviceConcept == null) { 247 DeviceExposure.builder() 249 .visitOccurrenceId(visitOccId) 250 .fhirIdentifier(procedureSourceIdentifier) 251 .fhirLogicalId(procedureLogicId) 252 .deviceExposureStartDate(startDateTime.toLocalDate()) 253 .deviceExposureStartDatetime(startDateTime) 254 .deviceExposureEndDate(endDateTime == null ? null : endDateTime.toLocalDate()) 255 .deviceExposureEndDatetime(endDateTime) 256 .deviceTypeConceptId(CONCEPT_EHR) 257 .deviceConceptId(deviceConcept.getConceptId()) 258 .deviceSourceConceptId(deviceConcept.getConceptId()) 259 .deviceSourceValue(deviceCode) 262 addToList(deviceExposures, deviceExposure); 264 return deviceExposures; 267 private void addToList(List<DeviceExposure> deviceExposures, DeviceExposure deviceExposure) { 268 if (deviceExposures.isEmpty()) { 269 deviceExposures.add(deviceExposure); 272 if (deviceExposures.contains(deviceExposure)) { 275 deviceExposures.add(deviceExposure); 279 * Creates a new record of the procedure_occurrence table in OMOP CDM for the processed FHIR 280 * Procedure resource. 282 * @param procedureCodings a list of Coding elements from Procedure FHIR resource 283 * @param procedureStartDatetime date time of the FHIR Procedure resource 284 * @param personId person_id of the referenced FHIR Patient resource 285 * @param visitOccId visit_occurrence_id of the referenced FHIR Encounter resource 286 * @param procedureLogicId logical id of the FHIR Procedure resource 287 * @param procedureSourceIdentifier identifier of the FHIR Procedure resource 288 * @param bodySite anatomical body site information from procedure FHIR resource 290 private void createProcedureMapping( 291 OmopModelWrapper wrapper, 292 List<Coding> procedureCodings, 293 LocalDateTime procedureStartDatetime, 296 String procedureLogicId, 297 String procedureSourceIdentifier, 298 Procedure srcProcedure, 299 String procedureId) { 301 var codingSize = procedureCodings.size(); 302 if (codingSize == 1) { 303 setProcedureConceptsUsingSingleCoding( 305 procedureStartDatetime, 306 procedureCodings.get(0), 311 procedureSourceIdentifier, 314 setProcedureConceptsUsingMultipleCodings( 316 procedureStartDatetime, 322 procedureSourceIdentifier, 328 * @param procedureStartDatetime 331 * @param procedureLogicId 332 * @param procedureSourceIdentifier 333 * @param procedureCoding 336 private void setProcedureConceptsUsingSingleCoding( 337 OmopModelWrapper wrapper, 338 LocalDateTime procedureStartDatetime, 339 Coding procedureCoding, 340 Procedure srcProcedure, 343 String procedureLogicId, 344 String procedureSourceIdentifier, 345 String procedureId) { 347 List<Pair<String, List<OpsStandardDomainLookup>>> opsStandardMapPairList = null; 348 SourceToConceptMap dicomConcept = null; 349 Concept snomedConcept = null; 351 var procedureCodeExist = 352 checkIfAnyProcedureCodesExist(procedureCoding, listOfProcedureVocabularyId); 353 if (!procedureCodeExist) { 357 var procedureVocabularyId = findOmopConcepts.getOmopVocabularyId(procedureCoding.getSystem()); 359 var procedureBodySiteLocalization = 360 getBodySiteLocalization( 361 srcProcedure, procedureCoding, procedureStartDatetime.toLocalDate(), procedureId); 363 if (procedureVocabularyId.equals(VOCABULARY_OPS)) { 366 opsStandardMapPairList = 367 getValidOpsCodes(procedureCoding, procedureStartDatetime.toLocalDate(), procedureId); 369 if (opsStandardMapPairList.isEmpty()) { 372 for (var singlePair : opsStandardMapPairList) { 378 procedureBodySiteLocalization, 379 procedureStartDatetime, 381 procedureSourceIdentifier, 387 } else if (procedureVocabularyId.equals(SOURCE_VOCABULARY_ID_PROCEDURE_DICOM)) { 391 findOmopConcepts.getCustomConcepts( 392 procedureCoding.getCode(), procedureVocabularyId, dbMappings); 393 if (dicomConcept == null) { 402 procedureBodySiteLocalization, 403 procedureStartDatetime, 405 procedureSourceIdentifier, 410 } else if (procedureVocabularyId.equals(VOCABULARY_SNOMED)) { 414 findOmopConcepts.getConcepts( 416 procedureStartDatetime.toLocalDate(), 421 if (snomedConcept == null) { 430 procedureBodySiteLocalization, 431 procedureStartDatetime, 433 procedureSourceIdentifier, 441 * @param procedureStartDatetime 444 * @param procedureLogicId 445 * @param procedureSourceIdentifier 447 * @param snomedCoding 450 private void setProcedureConceptsUsingMultipleCodings( 451 OmopModelWrapper wrapper, 452 LocalDateTime procedureStartDatetime, 453 List<Coding> procedureCodings, 454 Procedure srcProcedure, 457 String procedureLogicId, 458 String procedureSourceIdentifier, 459 String procedureId) { 461 Coding uncheckedOpsCoding = null; 462 Coding uncheckedDicomCoding = null; 463 Coding uncheckedSnomedCoding = null; 464 Coding procedureCoding = null; 466 for (var uncheckedCoding : procedureCodings) { 467 var procedureVocabularyId = findOmopConcepts.getOmopVocabularyId(uncheckedCoding.getSystem()); 468 if (procedureVocabularyId.equals(VOCABULARY_OPS)) { 469 uncheckedOpsCoding = uncheckedCoding; 471 if (procedureVocabularyId.equals(SOURCE_VOCABULARY_ID_PROCEDURE_DICOM)) { 472 uncheckedDicomCoding = uncheckedCoding; 474 if (procedureVocabularyId.equals(VOCABULARY_SNOMED)) { 475 uncheckedSnomedCoding = uncheckedCoding; 478 if (uncheckedOpsCoding == null 479 && uncheckedDicomCoding == null 480 && uncheckedSnomedCoding == null) { 485 var opsStandardMapPairList = 486 getValidOpsCodes(uncheckedOpsCoding, procedureStartDatetime.toLocalDate(), procedureId); 489 SourceToConceptMap dicomConcept = null; 490 if (uncheckedDicomCoding != null) { 492 findOmopConcepts.getCustomConcepts( 493 uncheckedDicomCoding.getCode(), 494 findOmopConcepts.getOmopVocabularyId(uncheckedDicomCoding.getSystem()), 499 findOmopConcepts.getConcepts( 500 uncheckedSnomedCoding, 501 procedureStartDatetime.toLocalDate(), 506 if (opsStandardMapPairList.isEmpty() && snomedConcept == null && dicomConcept == null) { 508 } else if (!opsStandardMapPairList.isEmpty()) { 510 procedureCoding = uncheckedOpsCoding; 511 } else if (dicomConcept != null) { 513 procedureCoding = uncheckedDicomCoding; 514 } else if (snomedConcept != null) { 516 procedureCoding = uncheckedSnomedCoding; 519 setProcedureConceptsUsingSingleCoding( 521 procedureStartDatetime, 527 procedureSourceIdentifier, 532 * Processes information from FHIR Procedure resource and transforms them into records OMOP CDM 535 * @param singlePair one pair of OPS code and its OMOP standard concept_id and domain information 536 * @param omopConcept extracted Concept from OMOP 537 * @param dicomConcept source_to_concept_map entry for DICOM code 538 * @param wrapper the OMOP model wrapper 539 * @param procedureDate date of the FHIR Procedure resource 540 * @param procedureLogicId logical id of the FHIR Procedure resource 541 * @param procedureSourceIdentifier identifier of the FHIR Procedure resource 542 * @param personId person_id of the referenced FHIR Patient resource 543 * @param visiOccId visit_occurrence_id of the referenced FHIR Encounter resource 545 private void procedureProcessor( 546 @Nullable Pair<String, List<OpsStandardDomainLookup>> opsStandardPair, 547 @Nullable Concept snomedConcept, 548 @Nullable SourceToConceptMap dicomConcept, 549 OmopModelWrapper wrapper, 550 Pair<String, Integer> procedureBodySiteLocalization, 551 LocalDateTime procedureStartDatetime, 552 String procedureLogicId, 553 String procedureSourceIdentifier, 556 String procedureId) { 558 if (opsStandardPair == null && snomedConcept == null && dicomConcept == null) { 562 if (opsStandardPair != null) { 563 var opsCode = opsStandardPair.getLeft(); 564 var opsStandardMaps = opsStandardPair.getRight(); 566 for (var opsStandardMap : opsStandardMaps) { 569 procedureBodySiteLocalization, 570 procedureStartDatetime, 572 procedureSourceIdentifier, 576 opsStandardMap.getStandardConceptId(), 577 opsStandardMap.getSourceConceptId(), 578 opsStandardMap.getStandardDomainId(), 582 } else if (dicomConcept != null) { 585 procedureBodySiteLocalization, 586 procedureStartDatetime, 588 procedureSourceIdentifier, 591 dicomConcept.getSourceCode(), 592 dicomConcept.getTargetConceptId(), 593 dicomConcept.getTargetConceptId(), 594 OMOP_DOMAIN_PROCEDURE, 600 procedureBodySiteLocalization, 601 procedureStartDatetime, 603 procedureSourceIdentifier, 606 snomedConcept.getConceptCode(), 607 snomedConcept.getConceptId(), 608 snomedConcept.getConceptId(), 609 snomedConcept.getDomainId(), 614 /** Write procedure information into correct OMOP tables based on their domains. */ 615 private void setProcedure( 616 OmopModelWrapper wrapper, 617 Pair<String, Integer> procedureBodySiteLocalization, 618 LocalDateTime procedureStartDatetime, 619 String procedureLogicId, 620 String procedureSourceIdentifier, 623 String procedureCode, 624 Integer procedureConceptId, 625 Integer procedureSourceConceptId, 627 String procedureId) { 629 case OMOP_DOMAIN_PROCEDURE: 632 procedureStartDatetime, 634 procedureSourceConceptId, 638 procedureBodySiteLocalization, 640 procedureSourceIdentifier); 642 wrapper.getProcedureOccurrence().add(procedure); 645 case OMOP_DOMAIN_OBSERVATION: 648 procedureStartDatetime, 650 procedureSourceConceptId, 654 procedureBodySiteLocalization, 656 procedureSourceIdentifier); 658 wrapper.getObservation().add(observation); 661 case OMOP_DOMAIN_DRUG: 664 procedureStartDatetime, 666 procedureSourceConceptId, 671 procedureSourceIdentifier); 673 wrapper.getDrugExposure().add(drug); 676 case OMOP_DOMAIN_MEASUREMENT: 679 procedureStartDatetime, 681 procedureSourceConceptId, 685 procedureBodySiteLocalization, 687 procedureSourceIdentifier); 689 wrapper.getMeasurement().add(measurement); 693 // throw new UnsupportedOperationException(String.format("Unsupported domain %s", 696 "[Unsupported domain] {} of code in [Procedure]: {}. Skip resource.", 703 private ProcedureOccurrence setUpProcedure( 704 LocalDateTime procedureStartDatetime, 705 Integer procedureConceptId, 706 Integer procedureSourceConceptId, 707 String procedureCode, 710 Pair<String, Integer> procedureBodySiteLocalization, 711 String procedureLogicId, 712 String procedureSourceIdentifier) { 714 var newProcedureOccurrence = 715 ProcedureOccurrence.builder() 717 .procedureDate(procedureStartDatetime.toLocalDate()) 718 .procedureDatetime(procedureStartDatetime) 719 .visitOccurrenceId(visitOccId) 720 .procedureSourceConceptId(procedureSourceConceptId) 721 .procedureConceptId(procedureConceptId) 722 .procedureTypeConceptId(CONCEPT_EHR) 723 .procedureSourceValue(procedureCode) 724 .fhirLogicalId(procedureLogicId) 725 .fhirIdentifier(procedureSourceIdentifier) 728 if (procedureBodySiteLocalization != null) { 729 newProcedureOccurrence.setModifierSourceValue(procedureBodySiteLocalization.getLeft()); 730 newProcedureOccurrence.setModifierConceptId(procedureBodySiteLocalization.getRight()); 733 return newProcedureOccurrence; 736 private OmopObservation setUpObservation( 737 LocalDateTime procedureStartDatetime, 738 Integer procedureConceptId, 739 Integer procedureSourceConceptId, 740 String procedureCode, 743 Pair<String, Integer> procedureBodySiteLocalization, 744 String procedureLogicId, 745 String procedureSourceIdentifier) { 748 OmopObservation.builder() 750 .observationDate(procedureStartDatetime.toLocalDate()) 751 .observationDatetime(procedureStartDatetime) 752 .visitOccurrenceId(visitOccId) 753 .observationSourceConceptId(procedureSourceConceptId) 754 .observationConceptId(procedureConceptId) 755 .observationTypeConceptId(CONCEPT_EHR) 756 .observationSourceValue(procedureCode) 757 .fhirLogicalId(procedureLogicId) 758 .fhirIdentifier(procedureSourceIdentifier) 761 if (procedureBodySiteLocalization != null) { 762 newObservation.setQualifierSourceValue(procedureBodySiteLocalization.getLeft()); 763 newObservation.setQualifierConceptId(procedureBodySiteLocalization.getRight()); 766 return newObservation; 769 private Measurement setUpMeasurement( 770 LocalDateTime procedureStartDatetime, 771 Integer procedureConceptId, 772 Integer procedureSourceConceptId, 773 String procedureCode, 776 Pair<String, Integer> procedureBodySiteLocalization, 777 String procedureLogicId, 778 String procedureSourceIdentifier) { 781 Measurement.builder() 783 .measurementDate(procedureStartDatetime.toLocalDate()) 784 .measurementDatetime(procedureStartDatetime) 785 .visitOccurrenceId(visitOccId) 786 .measurementSourceConceptId(procedureSourceConceptId) 787 .measurementConceptId(procedureConceptId) 788 .measurementTypeConceptId(CONCEPT_EHR) 789 .measurementSourceValue(procedureCode) 790 .fhirLogicalId(procedureLogicId) 791 .fhirIdentifier(procedureSourceIdentifier) 794 if (procedureBodySiteLocalization != null) { 795 newMeasurement.setValueSourceValue(procedureBodySiteLocalization.getLeft()); 796 newMeasurement.setValueAsConceptId(procedureBodySiteLocalization.getRight()); 799 return newMeasurement; 802 private DrugExposure setUpDrug( 803 LocalDateTime procedureStartDatetime, 804 Integer procedureConceptId, 805 Integer procedureSourceConceptId, 806 String procedureCode, 809 String procedureLogicId, 810 String procedureSourceIdentifier) { 812 return DrugExposure.builder() 814 .drugExposureStartDate(procedureStartDatetime.toLocalDate()) 815 .drugExposureStartDatetime(procedureStartDatetime) 816 .drugExposureEndDate(procedureStartDatetime.toLocalDate()) 817 .visitOccurrenceId(visitOccId) 818 .drugSourceConceptId(procedureSourceConceptId) 819 .drugConceptId(procedureConceptId) 820 .drugTypeConceptId(CONCEPT_EHR) 821 .drugSourceValue(procedureCode) 822 .fhirLogicalId(procedureLogicId) 823 .fhirIdentifier(procedureSourceIdentifier) 828 * Extract valid pairs of OPS code and its OMOP concept_id and domain information as a list 831 * @param procedureDate the date of procedure 832 * @return a list of valid pairs of OPS code and its OMOP concept_id and domain information 834 private List<Pair<String, List<OpsStandardDomainLookup>>> getValidOpsCodes( 835 Coding opsCoding, LocalDate procedureDate, String procedureId) { 836 if (opsCoding == null) { 837 return Collections.emptyList(); 840 List<Pair<String, List<OpsStandardDomainLookup>>> validOpsStandardConceptMaps = 842 List<OpsStandardDomainLookup> opsStandardMap = 843 findOmopConcepts.getOpsStandardConcepts( 844 opsCoding, procedureDate, bulkload, dbMappings, procedureId); 845 if (opsStandardMap.isEmpty()) { 846 return Collections.emptyList(); 849 validOpsStandardConceptMaps.add(Pair.of(opsCoding.getCode(), opsStandardMap)); 851 return validOpsStandardConceptMaps; 855 * Set procedure_occurrence modifier information. 857 * @param procedureOccurrence the new record of the procedure_occurrence 858 * @param procedureLocalization Localization coding from the FHIR Procedure resource 859 * @param procedureEffective date time of the FHIR Procedure resource 861 public void setProcedureModifier( 862 ProcedureOccurrence procedureOccurrence, 863 Pair<String, Integer> procedureBodySiteLocalization) { 865 if (procedureBodySiteLocalization == null) { 868 procedureOccurrence.setModifierSourceValue(procedureBodySiteLocalization.getLeft()); 869 procedureOccurrence.setModifierConceptId(procedureBodySiteLocalization.getRight()); 873 * Returns the person_id of the referenced FHIR Patient resource for the processed FHIR Procedure 876 * @param srcProcedure FHIR Procedure resource 877 * @param procedureLogicId logical id of the FHIR Procedure resource 878 * @return person_id of the referenced FHIR Patient resource from person table in OMOP CDM 880 private Long getPersonId(Procedure srcProcedure, String procedureLogicId, String procedureId) { 881 var patientReferenceIdentifier = fhirReferenceUtils.getSubjectReferenceIdentifier(srcProcedure); 882 var patientReferenceLogicalId = fhirReferenceUtils.getSubjectReferenceLogicalId(srcProcedure); 883 return omopReferenceUtils.getPersonId( 884 patientReferenceIdentifier, patientReferenceLogicalId, procedureLogicId, procedureId); 888 * Returns the visit_occurrence_id of the referenced FHIR Encounter resource for the processed 889 * FHIR Procedure resource. 891 * @param srcProcedure FHIR Procedure resource 892 * @param personId person_id of the referenced FHIR Patient resource 893 * @param procedureLogicId logical id of the FHIR Procedure resource 894 * @return visit_occurrence_id of the referenced FHIR Encounter resource from visit_occurrence 897 private Long getVisitOccId(Procedure srcProcedure, Long personId, String procedureId) { 898 var encounterReferenceIdentifier = 899 fhirReferenceUtils.getEncounterReferenceIdentifier(srcProcedure); 900 var encounterReferenceLogicalId = 901 fhirReferenceUtils.getEncounterReferenceLogicalId(srcProcedure); 904 omopReferenceUtils.getVisitOccId( 905 encounterReferenceIdentifier, encounterReferenceLogicalId, personId, procedureId); 907 if (visitOccId == null) { 908 log.debug("No matching [Encounter] found for [Procedure]: {}.", procedureId); 915 * Extracts date time information from the FHIR Procedure resource. 917 * @param srcProcedure FHIR Procedure resource 918 * @return date time of the FHIR Procedure resource 920 private ResourceOnset getProcedureOnset(Procedure srcProcedure) { 921 var resourceOnset = new ResourceOnset(); 923 if (srcProcedure.hasPerformedDateTimeType() 924 && !srcProcedure.getPerformedDateTimeType().isEmpty()) { 926 var performedDateTimeType = srcProcedure.getPerformedDateTimeType(); 927 var performedDateTime = checkDataAbsentReason.getValue(performedDateTimeType); 928 if (performedDateTime != null) { 929 resourceOnset.setStartDateTime(performedDateTime); 930 return resourceOnset; 934 if (srcProcedure.hasPerformedPeriod() && !srcProcedure.getPerformedPeriod().isEmpty()) { 935 var performedPeriodElement = srcProcedure.getPerformedPeriod(); 937 var performedPeriod = checkDataAbsentReason.getValue(performedPeriodElement); 938 if (performedPeriod == null) { 939 return resourceOnset; 942 if (!performedPeriod.getStartElement().isEmpty()) { 943 resourceOnset.setStartDateTime( 944 new Timestamp(performedPeriod.getStartElement().getValue().getTime()) 947 if (!performedPeriod.getEndElement().isEmpty()) { 948 resourceOnset.setEndDateTime( 949 new Timestamp(performedPeriod.getEndElement().getValue().getTime()).toLocalDateTime()); 952 return resourceOnset; 956 * Extracts the procedure codings from the FHIR Procedure resource as a list. 958 * @param srcProcedure FHIR Procedure resource 959 * @param procedureLogicId logical id of the FHIR Procedure resource 960 * @return a list of procedure codings from the FHIR Procedure resource 962 private List<Coding> getProcedureCodings(Procedure srcProcedure, String procedureLogicId) { 963 List<Coding> codingList = new ArrayList<>(); 964 var procedureCodings = srcProcedure.getCode().getCoding(); 965 if (procedureCodings.size() == 1) { 966 var procedureCoding = procedureCodings.get(0); 967 if (checkIfCodeExist(procedureCoding)) { 968 codingList.add(procedureCodings.get(0)); 971 return Collections.emptyList(); 974 if (procedureCodings.size() > 1) { 975 for (var coding : procedureCodings) { 976 if (checkIfCodeExist(coding)) { 977 codingList.add(coding); 982 return Collections.emptyList(); 986 * Extract Coding from Procedure FHIR resource based on the vocabulary ID in OMOP. 988 * @param procedureCodings a list of Coding elements from Procedure FHIR resource 989 * @param vocabularyId vocabulary Id in OMOP based on the used system URL in Coding 990 * @return a Coding from Procedure FHIR 992 private Coding getCoding(List<Coding> procedureCodings, String vocabularyId) { 994 procedureCodings.stream() 998 .getOmopVocabularyId(procedureCoding.getSystem()) 999 .equals(vocabularyId)) 1001 if (codingOptional.isPresent()) { 1002 return codingOptional.get(); 1007 private Pair<String, Integer> getBodySiteLocalization( 1008 Procedure srcProcedure, Coding procedureCoding, LocalDate procedureDate, String procedureId) { 1009 var siteLocalization = getOpsSiteLocalization(procedureCoding); 1010 var procedureBodySite = getBodySite(srcProcedure); 1011 if (siteLocalization == null && procedureBodySite == null) { 1013 } else if (siteLocalization != null) { 1014 var opsSiteLocalizationConcept = 1015 findOmopConcepts.getCustomConcepts( 1016 siteLocalization.getCode(), SOURCE_VOCABULARY_ID_PROCEDURE_BODYSITE, dbMappings); 1017 return Pair.of(siteLocalization.getCode(), opsSiteLocalizationConcept.getTargetConceptId()); 1019 var procedureBodySiteConcept = 1020 findOmopConcepts.getConcepts( 1021 procedureBodySite, procedureDate, bulkload, dbMappings, procedureId); 1022 if (procedureBodySiteConcept == null) { 1025 return Pair.of(procedureBodySite.getCode(), procedureBodySiteConcept.getConceptId()); 1029 * Extracts OPS site localization information from the FHIR Procedure resource. 1031 * @param opsCoding OPS coding from the FHIR Procedure resource 1032 * @return OPS site localization from FHIR Procedure resource 1034 private Coding getOpsSiteLocalization(Coding opsCoding) { 1035 if (opsCoding == null) { 1038 var opsSiteLocalizationExtension = 1039 opsCoding.getExtensionByUrl(fhirSystems.getSiteLocalizationExtension()); 1040 if (opsSiteLocalizationExtension == null) { 1043 var opsSiteLocalizationType = opsSiteLocalizationExtension.getValue(); 1044 if (opsSiteLocalizationType == null) { 1048 var opsSiteLocalizationCoding = opsSiteLocalizationType.castToCoding(opsSiteLocalizationType); 1049 var opsSiteLocalizationCode = opsSiteLocalizationCoding.getCode(); 1050 if (Strings.isNullOrEmpty(opsSiteLocalizationCode)) { 1053 return opsSiteLocalizationCoding; 1057 * Extracts body site information from the FHIR Procedure resource. 1059 * @param srcProcedure FHIR Procedure resource 1060 * @return body site from FHIR Procedure resource 1062 private Coding getBodySite(Procedure srcProcedure) { 1064 if (!srcProcedure.hasBodySite() || srcProcedure.getBodySite().isEmpty()) { 1068 var bodySiteCodingOptional = 1069 srcProcedure.getBodySite().get(0).getCoding().stream() 1070 .filter(code -> code.getSystem().equalsIgnoreCase(fhirSystems.getSnomed())) 1072 if (!bodySiteCodingOptional.isPresent()) { 1076 var bodySiteCoding = bodySiteCodingOptional.get(); 1077 var bodySiteCode = bodySiteCoding.getCode(); 1078 if (Strings.isNullOrEmpty(bodySiteCode)) { 1081 return bodySiteCoding; 1085 * Check if the used procedure code exists in FHIR Procedure resource 1087 * @param procedureCoding procedure codings from the FHIR Procedure resource 1088 * @return a boolean value 1090 private boolean checkIfCodeExist(Coding procedureCoding) { 1091 var codeElement = procedureCoding.getCodeElement(); 1092 if (codeElement.isEmpty()) { 1095 var procedureCode = checkDataAbsentReason.getValue(codeElement); 1096 if (Strings.isNullOrEmpty(procedureCode)) { 1104 * Check if the used procedure code exists in OMOP 1106 * @param procedureCoding Coding element from Procedure FHIR resource 1107 * @param vocabularyId vocabulary Id in OMOP based on the used system URL in Coding 1108 * @return a boolean value 1110 private boolean checkIfAnyProcedureCodesExist(Coding procedureCoding, List<String> vocabularyId) { 1111 if (procedureCoding == null) { 1114 var codingVocabularyId = findOmopConcepts.getOmopVocabularyId(procedureCoding.getSystem()); 1115 return vocabularyId.contains(codingVocabularyId); 1118 private boolean checkIfSpecificProcedureCodesExist(Coding procedureCoding, String vocabularyId) { 1119 if (procedureCoding == null) { 1122 var codingVocabularyId = findOmopConcepts.getOmopVocabularyId(procedureCoding.getSystem()); 1123 return codingVocabularyId.equals(vocabularyId); 1127 * Deletes FHIR Procedure resources from OMOP CDM tables using fhir_logical_id and fhir_identifier 1129 * @param procedureLogicId logical id of the FHIR Procedure resource 1130 * @param procedureSourceIdentifier identifier of the FHIR Procedure resource 1132 private void deleteExistingProcedureEntry( 1133 String procedureLogicId, String procedureSourceIdentifier) { 1134 if (!Strings.isNullOrEmpty(procedureLogicId)) { 1135 procedureService.deleteExistingProceduresByFhirLogicalId(procedureLogicId); 1137 procedureService.deleteExistingProceduresByFhirIdentifier(procedureSourceIdentifier);