Skip to content

Commit

Permalink
Merge pull request #600 from ao508/dev-update-handler
Browse files Browse the repository at this point in the history
Support patient ID swap to an existing patient
  • Loading branch information
divyamadala30 authored Feb 18, 2022
2 parents 6295d3f + d39e783 commit 8d5cfdd
Show file tree
Hide file tree
Showing 19 changed files with 742 additions and 55 deletions.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Please follow these checks if any changes were made to any classes in the web, s
Updates were made to the mocked incoming request data and/or mocked published request data:
- [ ] [cmo-metadb test data](https://github.com/mskcc/cmo-metadb/tree/master/service/src/test/resources/data)
- [ ] [cmo-metadb-common test data](https://github.com/mskcc/cmo-metadb-common/tree/master/src/test/resources/data)
- [ ] [cmo-metadb-label-generator test data](https://github.com/mskcc/cmo-metadb-label-generator/tree/master/src/test/resources/data)

**Code checks:**
- [ ] Endpoints were tested to ensure their integrity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@NodeEntity
public class SampleMetadata implements Serializable, Comparable<SampleMetadata> {
public class SampleMetadata implements Serializable, Comparable<SampleMetadata>, Cloneable {
@Id @GeneratedValue
@JsonIgnore
private Long id;
Expand Down Expand Up @@ -382,4 +382,9 @@ public int compareTo(SampleMetadata sampleMetadata) {
public String toString() {
return ToStringBuilder.reflectionToString(this);
}

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // return shallow copy
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,19 @@ MetadbPatient findPatientByCmoPatientId(
+ "RETURN p.metaDbPatientId")
UUID findPatientIdBySample(@Param("metaDbSampleId") UUID metaDbSampleId);

@Query("MATCH (p: Patient)<-[:IS_ALIAS]-(pa: PatientAlias {value: $oldCmoPatientId, namespace: 'cmoId'}) "
+ "SET pa.value = $newCmoPatientId "
@Query("MATCH (p: Patient)<-[:IS_ALIAS]-(pa: PatientAlias {value: $oldCmoId, namespace: 'cmoId'}) "
+ "SET pa.value = $newCmoId "
+ "RETURN p")
MetadbPatient updateCmoPatientIdInPatientNode(@Param("oldCmoPatientId") String oldCmoPatientId,
@Param("newCmoPatientId") String newCmoPatientId);
MetadbPatient updateCmoPatientIdInPatientNode(@Param("oldCmoId") String oldCmoId,
@Param("newCmoId") String newCmoId);

@Query("MATCH (s: Sample)<-[:IS_ALIAS]-(sa: SampleAlias {value: $value, namespace: $namespace}) "
+ "MATCH (s)<-[:HAS_SAMPLE]-(p: Patient) "
+ "RETURN p")
MetadbPatient findPatientByNamespaceValue(
@Param("namespace") String namespace, @Param("value") String value);

@Query("MATCH (p: Patient {metaDbPatientId: $patient.metaDbPatientId})"
+ "<-[:IS_ALIAS]-(pa: PatientAlias) DETACH DELETE p, pa")
void deletePatientAndAliases(@Param("patient") MetadbPatient patient);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,9 @@ List<MetadbSample> findAllSamplesByCategoryAndCmoPatientId(
List<SampleMetadata> findSampleMetadataHistoryByNamespaceValue(
@Param("namespace") String namespace, @Param("value") String value);

@Query("MATCH (s: Sample {metaDbSampleId: $metaDbSampleId}) "
+ "MATCH (p: Patient {metaDbPatientId: $metaDbPatientId}) "
+ "CREATE (s)<-[:HAS_SAMPLE]-(p)")
void updateSamplePatientRelationship(@Param("metaDbSampleId") UUID metaDbSampleId,
@Param("metaDbPatientId") UUID metaDbPatientId);
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<cmo_metadb_messaging_java.version>1.2.1.RELEASE</cmo_metadb_messaging_java.version>
<!-- metadb common centralized config properties -->
<cmo_metadb_common.group>com.github.mskcc</cmo_metadb_common.group>
<cmo_metadb_common.version>1.2.1.RELEASE</cmo_metadb_common.version>
<cmo_metadb_common.version>1.2.2.RELEASE</cmo_metadb_common.version>
<!-- metadb expected schema version -->
<metadb.schema_version>v2.0</metadb.schema_version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface MetadbPatientService {
MetadbPatient getPatientByCmoPatientId(String cmoPatientId);
UUID getPatientIdBySample(UUID metadbSampleId);
MetadbPatient updateCmoPatientId(String oldCmoPatientId, String newCmoPatientId);
void deletePatient(MetadbPatient patient);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ List<PublishedMetadbSample> getPublishedMetadbSamplesByCmoPatientId(String cmoPa
MetadbSample getClinicalSampleByDmpId(String dmpId) throws Exception;
List<MetadbSample> getSamplesByCategoryAndCmoPatientId(String cmoPatientId,
String sampleCategory) throws Exception;
void updateSamplePatientRelationship(UUID metaDbSampleId, UUID metaDbPatientId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public class MessageHandlingServiceImpl implements MessageHandlingService {
@Value("${metadb.correct_cmoptid_topic}")
private String CORRECT_CMOPTID_TOPIC;

@Value("${request_reply.cmo_label_generator_topic}")
private String CMO_LABEL_GENERATOR_REQREPLY_TOPIC;

@Value("${num.new_request_handler_threads}")
private int NUM_NEW_REQUEST_HANDLERS;

Expand Down Expand Up @@ -117,31 +120,66 @@ public void run() {
Map<String, String> idCorrectionMap =
correctCmoPatientIdQueue.poll(100, TimeUnit.MILLISECONDS);
if (idCorrectionMap != null) {
String oldCmoPatientId = idCorrectionMap.get("oldId");
String newCmoPatientId = idCorrectionMap.get("newId");

// get samples by old cmo patient id before updating the
// cmo patient id for the given patient alias/patient node
List<MetadbSample> samples =
sampleService.getSamplesByCmoPatientId(oldCmoPatientId);
MetadbPatient updatedPatient = patientService.updateCmoPatientId(
oldCmoPatientId, newCmoPatientId);

for (MetadbSample sample: samples) {
SampleMetadata latestMetadata = sample.getLatestSampleMetadata();
latestMetadata.setCmoPatientId(newCmoPatientId);
String oldCmoPtId = idCorrectionMap.get("oldId");
String newCmoPtId = idCorrectionMap.get("newId");

List<MetadbSample> samplesByOldCmoPatient =
sampleService.getSamplesByCmoPatientId(oldCmoPtId);
List<MetadbSample> samplesByNewCmoPatient =
sampleService.getSamplesByCmoPatientId(newCmoPtId);
MetadbPatient patientByNewId = patientService.getPatientByCmoPatientId(newCmoPtId);

// update the cmo patient id for each sample linked to the "old" patient
// and the metadata as well
for (MetadbSample sample : samplesByOldCmoPatient) {
SampleMetadata updatedMetadata = sample.getLatestSampleMetadata();
updatedMetadata.setCmoPatientId(newCmoPtId);

// research samples need a new label as well
if (sample.getSampleCategory().equals("research")) {
LOG.info("Updating patient ID prefix embedded in CMO sample label "
+ "for research sample: " + latestMetadata.getPrimaryId());
String newCmoSampleLabel = latestMetadata.getCmoSampleName()
.replaceAll(oldCmoPatientId, newCmoPatientId);
latestMetadata.setCmoSampleName(newCmoSampleLabel);
LOG.info("Requesting new CMO sample label for sample: "
+ updatedMetadata.getPrimaryId());
Message reply = messagingGateway.request(CMO_LABEL_GENERATOR_REQREPLY_TOPIC,
mapper.writeValueAsString(updatedMetadata));
String newCmoSampleLabel = new String(reply.getData(),
StandardCharsets.UTF_8);
updatedMetadata.setCmoSampleName(newCmoSampleLabel);
}
// now update sample with the target patient we want to swap to
sample.updateSampleMetadata(updatedMetadata);

// update the sample-to-patient relationship if swapping to a different
// patient node. if still using the same node the samples are already linked
// to then there's no need to override the patient currently set for the sample
if (patientByNewId != null) {
sample.setPatient(patientByNewId);
sampleService.updateSamplePatientRelationship(sample.getMetaDbSampleId(),
patientByNewId.getMetaDbPatientId());
}
sample.setPatient(updatedPatient);
sample.updateSampleMetadata(latestMetadata);
LOG.info("Persisting update for sample to database");
sampleService.saveMetadbSample(sample);
}

// delete old patient node if we are swapping to an existing patient node
// otherwise simply update the existing patient node with the new cmo id
if (patientByNewId != null) {
LOG.info("Deleting Patient node (and its relationships) for old ID: "
+ oldCmoPtId);
MetadbPatient patientByOldId =
patientService.getPatientByCmoPatientId(oldCmoPtId);
patientService.deletePatient(patientByOldId);
} else {
patientService.updateCmoPatientId(oldCmoPtId, newCmoPtId);
}

// sanity check the counts before and after the swaps
Integer expectedCount = samplesByOldCmoPatient.size()
+ samplesByNewCmoPatient.size();
List<MetadbSample> samplesAfterSwap =
sampleService.getSamplesByCmoPatientId(newCmoPtId);
if (expectedCount != samplesAfterSwap.size()) {
LOG.error("Expected sample count after patient ID swap does not match actual"
+ " count: " + expectedCount + " != " + samplesAfterSwap.size());
}
}
if (interrupted && correctCmoPatientIdQueue.isEmpty()) {
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.mskcc.cmo.metadb.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.mskcc.cmo.metadb.model.MetadbPatient;
Expand All @@ -9,6 +8,7 @@
import org.mskcc.cmo.metadb.service.MetadbPatientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class PatientServiceImpl implements MetadbPatientService {
Expand All @@ -17,6 +17,7 @@ public class PatientServiceImpl implements MetadbPatientService {
private MetadbPatientRepository patientRepository;

@Override
@Transactional(rollbackFor = {Exception.class})
public MetadbPatient savePatientMetadata(MetadbPatient patient) {
MetadbPatient result = patientRepository.save(patient);
patient.setMetaDbPatientId(result.getMetaDbPatientId());
Expand All @@ -39,11 +40,17 @@ public UUID getPatientIdBySample(UUID metadbSampleId) {
}

@Override
@Transactional(rollbackFor = {Exception.class})
public MetadbPatient updateCmoPatientId(String oldCmoPatientId, String newCmoPatientId) {
if (getPatientByCmoPatientId(oldCmoPatientId) == null) {
return null;
}
return patientRepository.updateCmoPatientIdInPatientNode(oldCmoPatientId, newCmoPatientId);
}

@Override
@Transactional(rollbackFor = {Exception.class})
public void deletePatient(MetadbPatient patient) {
patientRepository.deletePatientAndAliases(patient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.mskcc.cmo.common.MetadbJsonComparator;
import org.mskcc.cmo.metadb.model.MetadbPatient;
Expand Down Expand Up @@ -51,7 +49,7 @@ public MetadbSample saveMetadbSample(MetadbSample
sample.setMetaDbSampleId(newSampleId);
return sample;
} else {
existingSample.addSampleMetadata(sample.getLatestSampleMetadata());
existingSample.updateSampleMetadata(sample.getLatestSampleMetadata());
sampleRepository.save(existingSample);
return existingSample;
}
Expand Down Expand Up @@ -203,4 +201,10 @@ public List<MetadbSample> getSamplesByCategoryAndCmoPatientId(String cmoPatientI
}
return samples;
}

@Override
@Transactional(rollbackFor = {Exception.class})
public void updateSamplePatientRelationship(UUID metaDbSampleId, UUID metaDbPatientId) {
sampleRepository.updateSamplePatientRelationship(metaDbSampleId, metaDbPatientId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.mskcc.cmo.metadb.service;

import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mskcc.cmo.metadb.model.MetadbPatient;
import org.mskcc.cmo.metadb.model.MetadbRequest;
import org.mskcc.cmo.metadb.model.MetadbSample;
import org.mskcc.cmo.metadb.model.SampleMetadata;
import org.mskcc.cmo.metadb.persistence.neo4j.MetadbPatientRepository;
import org.mskcc.cmo.metadb.persistence.neo4j.MetadbRequestRepository;
import org.mskcc.cmo.metadb.persistence.neo4j.MetadbSampleRepository;
import org.mskcc.cmo.metadb.service.util.RequestDataFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.testcontainers.containers.Neo4jContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

/**
*
* @author ochoaa
*/
@Testcontainers
@DataNeo4jTest
@Import(MockDataUtils.class)
public class CorrectCmoPatientIdHandlerTest {
@Autowired
private MockDataUtils mockDataUtils;

@Autowired
private MetadbRequestService requestService;

@Autowired
private MetadbSampleService sampleService;

@Autowired
private MetadbPatientService patientService;

@Container
private static final Neo4jContainer databaseServer = new Neo4jContainer<>()
.withEnv("NEO4J_dbms_security_procedures_unrestricted", "apoc.*,algo.*");

@TestConfiguration
static class Config {
@Bean
public org.neo4j.ogm.config.Configuration configuration() {
return new org.neo4j.ogm.config.Configuration.Builder()
.uri(databaseServer.getBoltUrl())
.credentials("neo4j", databaseServer.getAdminPassword())
.build();
}
}

private final MetadbRequestRepository requestRepository;
private final MetadbSampleRepository sampleRepository;
private final MetadbPatientRepository patientRepository;

/**
* Persists the Mock Request data to the test database.
* @throws Exception
*/
@Autowired
public CorrectCmoPatientIdHandlerTest(MetadbRequestRepository requestRepository,
MetadbSampleRepository sampleRepository, MetadbPatientRepository patientRepository) {
this.requestRepository = requestRepository;
this.sampleRepository = sampleRepository;
this.patientRepository = patientRepository;
}


/**
* Persists the Mock Request data to the test database.
* @throws Exception
*/
@Autowired
public void initializeMockDatabase() throws Exception {
// mock request id: MOCKREQUEST1_B
MockJsonTestData request1Data = mockDataUtils.mockedRequestJsonDataMap
.get("mockIncomingRequest1JsonDataWith2T2N");
MetadbRequest request1 = RequestDataFactory.buildNewLimsRequestFromJson(request1Data.getJsonString());
requestService.saveRequest(request1);
// mock request id: 145145_IM
MockJsonTestData request5Data = mockDataUtils.mockedRequestJsonDataMap
.get("mockIncomingRequest5JsonPtMultiSamples");
MetadbRequest request5 = RequestDataFactory.buildNewLimsRequestFromJson(request5Data.getJsonString());
requestService.saveRequest(request5);
}


/**
* Tests sample fetch before patient swap and after the patient id swap in the
* event that the patient already exists by the new id.
*/
@Test
public void testPatientIdSwapWithExistingPatient() throws Exception {
String oldCmoPatientId = "C-MP789JR";
String newCmoPatientId = "C-1MP6YY";


List<MetadbSample> samplesByNewCmoPatient = sampleService.getSamplesByCmoPatientId(newCmoPatientId);
System.out.println("Samples for new cmo patient id: " + samplesByNewCmoPatient.size());

String request1 = "MOCKREQUEST1_B";
String sampleId1 = "MOCKREQUEST1_B_1";
MetadbSample sample1 = sampleService.getResearchSampleByRequestAndIgoId(request1, sampleId1);

MetadbPatient newPatient = patientService.getPatientByCmoPatientId(newCmoPatientId);
SampleMetadata newSample1Metadata = sample1.getLatestSampleMetadata();
newSample1Metadata.setCmoPatientId(newCmoPatientId);
sample1.updateSampleMetadata(newSample1Metadata);
sample1.setPatient(newPatient);
sampleService.saveMetadbSample(sample1);
sampleService.updateSamplePatientRelationship(sample1.getMetaDbSampleId(),
newPatient.getMetaDbPatientId());

Integer expectedSampleCount = samplesByNewCmoPatient.size() + 1;
List<MetadbSample> samplesByNewCmoPatientAfterSwap =
sampleService.getSamplesByCmoPatientId(newCmoPatientId);
Assertions.assertThat(samplesByNewCmoPatientAfterSwap.size())
.isEqualTo(expectedSampleCount);
}
}
Loading

0 comments on commit 8d5cfdd

Please sign in to comment.