diff --git a/model/src/main/java/org/mskcc/smile/model/web/CrdbCrosswalkTriplet.java b/model/src/main/java/org/mskcc/smile/model/web/CrdbCrosswalkTriplet.java new file mode 100644 index 00000000..16d76a30 --- /dev/null +++ b/model/src/main/java/org/mskcc/smile/model/web/CrdbCrosswalkTriplet.java @@ -0,0 +1,79 @@ +package org.mskcc.smile.model.web; + +import com.fasterxml.jackson.core.JsonProcessingException; +import java.io.Serializable; +import java.util.ArrayList; +import javax.persistence.Entity; +import javax.persistence.Id; +import org.apache.commons.lang.builder.ToStringBuilder; + +@Entity +public class CrdbCrosswalkTriplet implements Serializable { + static String CMO_PATIENT_ID_PREFIX = "C-"; + + @Id + private String dmpId; + private String cmoId; + private String ptMrn; + + public CrdbCrosswalkTriplet() {} + + /** + * CrdbCrosswalkTriplet constructor + * @param crdbValues + * @throws JsonProcessingException + */ + public CrdbCrosswalkTriplet(ArrayList crdbValues) throws Exception { + // the crosswalk table may contain records that are missing fields (at least dmp) + // we need to handle cases where null is passed into this constructor + // Jackson null values to empty string "" does not work, so lets + // set null values to "" here in constructor + if (crdbValues.get(1) == null) { + this.dmpId = ""; + } else { + this.dmpId = crdbValues.get(1).toString(); + } + if (crdbValues.get(0) == null) { + this.cmoId = ""; + } else { + this.cmoId = crdbValues.get(0).toString(); + if (!this.cmoId.startsWith(CMO_PATIENT_ID_PREFIX)) { + this.cmoId = CMO_PATIENT_ID_PREFIX + this.cmoId; + } + } + if (crdbValues.get(2) == null) { + this.ptMrn = ""; + } else { + this.ptMrn = crdbValues.get(2).toString(); + } + } + + public String getDmpId() { + return dmpId; + } + + public void setDmpId(String dmpId) { + this.dmpId = dmpId; + } + + public String getCmoId() { + return cmoId; + } + + public void setCmoId(String cmoId) { + this.cmoId = cmoId; + } + + public String getPtMrn() { + return ptMrn; + } + + public void setPtMrn(String ptMrn) { + this.ptMrn = ptMrn; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/persistence/src/main/java/org/mskcc/smile/persistence/jpa/CrdbRepository.java b/persistence/src/main/java/org/mskcc/smile/persistence/jpa/CrdbRepository.java index 35c319c6..fe3ded79 100644 --- a/persistence/src/main/java/org/mskcc/smile/persistence/jpa/CrdbRepository.java +++ b/persistence/src/main/java/org/mskcc/smile/persistence/jpa/CrdbRepository.java @@ -1,6 +1,7 @@ package org.mskcc.smile.persistence.jpa; import org.mskcc.smile.model.internal.CrdbMappingModel; +import org.mskcc.smile.model.web.CrdbCrosswalkTriplet; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -16,4 +17,8 @@ public interface CrdbRepository extends CrudRepository { nativeQuery = true) Object getCmoPatientIdByInputId(@Param("inputId") String inputId); // another query option + @Query(value = "SELECT CMO_ID, DMP_ID, PT_MRN " + + "FROM CRDB_CMO_LOJ_DMP_MAP WHERE :inputId IN (DMP_ID, PT_MRN, CMO_ID)", + nativeQuery = true) + Object getCrdbCrosswalkTripletByInputId(@Param("inputId") String inputId); } diff --git a/service/src/main/java/org/mskcc/smile/service/CrdbMappingService.java b/service/src/main/java/org/mskcc/smile/service/CrdbMappingService.java index f184a6d5..4d1dc4d9 100644 --- a/service/src/main/java/org/mskcc/smile/service/CrdbMappingService.java +++ b/service/src/main/java/org/mskcc/smile/service/CrdbMappingService.java @@ -1,9 +1,11 @@ package org.mskcc.smile.service; import org.mskcc.smile.model.internal.CrdbMappingModel; +import org.mskcc.smile.model.web.CrdbCrosswalkTriplet; public interface CrdbMappingService { String getCmoPatientIdbyDmpId(String dmpId); String getCmoPatientIdByInputId(String inputId); CrdbMappingModel getCrdbMappingModelByInputId(String inputId) throws Exception; + CrdbCrosswalkTriplet getCrdbCrosswalkTripletByInputId(String inputId) throws Exception; } diff --git a/service/src/main/java/org/mskcc/smile/service/impl/CrdbMappingServiceImpl.java b/service/src/main/java/org/mskcc/smile/service/impl/CrdbMappingServiceImpl.java index afd76385..7496769a 100644 --- a/service/src/main/java/org/mskcc/smile/service/impl/CrdbMappingServiceImpl.java +++ b/service/src/main/java/org/mskcc/smile/service/impl/CrdbMappingServiceImpl.java @@ -8,6 +8,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.mskcc.smile.model.internal.CrdbMappingModel; +import org.mskcc.smile.model.web.CrdbCrosswalkTriplet; import org.mskcc.smile.persistence.jpa.CrdbRepository; import org.mskcc.smile.service.CrdbMappingService; import org.springframework.beans.factory.annotation.Autowired; @@ -80,6 +81,22 @@ public Object call() { return null; } + @Override + public CrdbCrosswalkTriplet getCrdbCrosswalkTripletByInputId(String inputId) throws Exception { + Callable task = new Callable() { + @Override + public Object call() { + return crdbRepository.getCrdbCrosswalkTripletByInputId(inputId); + } + }; + Object result = runQueryWithForcedTimeout(task); + if (result != null) { + ArrayList crdbValues = mapper.convertValue(result, ArrayList.class); + return new CrdbCrosswalkTriplet(crdbValues); + } + return null; + } + private Object runQueryWithForcedTimeout(Callable task) { Future future = executor.submit(task); try { diff --git a/web/src/main/java/org/mskcc/smile/web/CrosswalkController.java b/web/src/main/java/org/mskcc/smile/web/CrosswalkController.java new file mode 100644 index 00000000..96be7751 --- /dev/null +++ b/web/src/main/java/org/mskcc/smile/web/CrosswalkController.java @@ -0,0 +1,76 @@ +package org.mskcc.smile.web; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.mskcc.smile.model.web.CrdbCrosswalkTriplet; +import org.mskcc.smile.service.CrdbMappingService; +import org.mskcc.smile.service.exception.SmileWebServiceException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.PropertySource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@CrossOrigin(origins = "*") +@RequestMapping(value = "/") +@Api(tags = "crosswalk-controller", description = "Crosswalk Controller") +@PropertySource("classpath:/maven.properties") +public class CrosswalkController { + + @Value("${smile.schema_version}") + private String smileSchemaVersion; + + @Autowired + private CrdbMappingService crdbMappingService; + + @Autowired + public CrosswalkController(CrdbMappingService crdbMappingService) { + this.crdbMappingService = crdbMappingService; + } + + /** + * Returns a list of mrn-dmp-cmo patient id triplets given a list of MRNs. + * Only triplets that exist in the crdb crosswalk table are returned. + * @param mrns + * @return ResponseEntity + * @throws Exception + */ + @ApiOperation(value = "Returns a list of mrn-dmp-cmo patient id triplets given a list of MRNs.", + nickname = "fetchIDTripletListPOST") + @RequestMapping(value = "/crosswalk", + method = RequestMethod.POST, + produces = "application/json") + public ResponseEntity> fetchIDTripletListPOST(@ApiParam(value = + "List of MRNs", required = true, allowMultiple = true) + @RequestBody List mrns) throws Exception { + List tripletList = new ArrayList<>(); + for (String mrn: mrns) { + CrdbCrosswalkTriplet triplet = crdbMappingService.getCrdbCrosswalkTripletByInputId(mrn); + if (triplet != null) { + tripletList.add(triplet); + } + } + return ResponseEntity.ok() + .headers(responseHeaders()) + .body(tripletList); + } + + private HttpHeaders responseHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.set("smile-schema-version", smileSchemaVersion); + return headers; + } +}