package uk.ac.ox.zoo.seeg.abraid.mp.modeloutputhandler.web; import org.joda.time.DateTime; import org.joda.time.DateTimeUtils; import org.junit.Test; import org.kubek2k.springockito.annotations.ReplaceWithMock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.web.WebAppConfiguration; import uk.ac.ox.zoo.seeg.abraid.mp.common.dao.DiseaseGroupDao; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.DiseaseOccurrence; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.DiseaseOccurrenceStatus; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.ModelRun; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.ModelRunStatus; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.DiseaseService; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.ModelRunService; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.workflow.support.MachineWeightingPredictor; import uk.ac.ox.zoo.seeg.abraid.mp.common.web.RasterFilePathFactory; import uk.ac.ox.zoo.seeg.abraid.mp.testutils.AbstractSpringIntegrationTests; import uk.ac.ox.zoo.seeg.abraid.mp.testutils.SpringockitoWebContextLoader; import java.io.File; import java.util.List; import static ch.lambdaj.Lambda.*; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.core.IsEqual.equalTo; import static org.hamcrest.core.IsNull.notNullValue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; /** * Integration tests for the DiseaseOccurrenceHandler class. * * Copyright (c) 2014 University of Oxford */ @ContextConfiguration(loader = SpringockitoWebContextLoader.class, locations = { "file:ModelOutputHandler/web/WEB-INF/abraid-servlet-beans.xml", "file:ModelOutputHandler/web/WEB-INF/applicationContext.xml" }) @WebAppConfiguration("file:ModelOutputHandler/web") @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class BatchingHandlerIntegrationTest extends AbstractSpringIntegrationTests { private static final String LARGE_RASTER_FILENAME = "ModelOutputHandler/test/uk/ac/ox/zoo/seeg/abraid/mp/modeloutputhandler/web/testdata/batching/prediction_raster.tif"; private static final String ADMIN_RASTER_FILENAME = "ModelOutputHandler/test/uk/ac/ox/zoo/seeg/abraid/mp/modeloutputhandler/web/testdata/batching/admin.tif"; @Autowired private ModelRunService modelRunService; @Autowired private BatchingHandler batchingHandler; @Autowired private DiseaseService diseaseService; @Autowired @ReplaceWithMock private MachineWeightingPredictor machineWeightingPredictor; @Autowired @ReplaceWithMock private RasterFilePathFactory rasterFilePathFactory; @Autowired private DiseaseGroupDao diseaseGroupDao; @Test public void handleFirstBatch() throws Exception { // Arrange DateTime now = DateTime.now(); DateTimeUtils.setCurrentMillisFixed(now.getMillis()); int diseaseGroupId = 87; DateTime batchStartDate = new DateTime("2014-02-24"); // Occurrence date for earliest READY occurrence DateTime batchEndDate = new DateTime("2014-02-26").minusMillis(1); ModelRun modelRun = createAndSaveTestModelRun(diseaseGroupId, batchStartDate, batchEndDate, null); // As this is the first batch, there was no training data available, so no prediction can be made. when(machineWeightingPredictor.findMachineWeighting(any(DiseaseOccurrence.class))).thenReturn(null); // Act batchingHandler.handle(modelRun); // Assert List<DiseaseOccurrence> occurrences = diseaseService.getDiseaseOccurrencesByDiseaseGroupId(diseaseGroupId); // As this is the first batch, all of them should have final weighting (and final weighting excluding spatial) // set to null. for (DiseaseOccurrence occurrence : occurrences) { assertThat(occurrence.getFinalWeighting()).isNull(); assertThat(occurrence.getFinalWeightingExcludingSpatial()).isNull(); } // 29 occurrences were batched: 16 of them were sent to the Data Validator i.e. they have status IN_REVIEW // and a non-null environmental suitability, but 4 of them were ineligible points so are READY with an // environmental suitability. The remaining occurrences are 16 that are AWAITING_BATCHING as a result of // the batching initialisation, and 3 that were already DISCARDED_FAILED_QC. assertOccurrences(occurrences, DiseaseOccurrenceStatus.IN_REVIEW, 16 + 9, 16 + 9); assertOccurrences(occurrences, DiseaseOccurrenceStatus.READY, 4, 4); assertOccurrences(occurrences, DiseaseOccurrenceStatus.AWAITING_BATCHING, 16, 0); assertOccurrences(occurrences, DiseaseOccurrenceStatus.DISCARDED_FAILED_QC, 3, 0); // And the model run should have been updated correctly modelRun = modelRunService.getModelRunByName(modelRun.getName()); assertThat(modelRun.getBatchingCompletedDate()).isEqualTo(now); assertThat(modelRun.getBatchOccurrenceCount()).isEqualTo(29); } @Test public void handleSecondBatch() throws Exception { // Arrange DateTime now = DateTime.now(); DateTimeUtils.setCurrentMillisFixed(now.getMillis()); int diseaseGroupId = 87; DateTime batchStartDate = new DateTime("2014-02-24"); // Occurrence date for earliest READY occurrence DateTime batchEndDate = new DateTime("2014-02-26").minusMillis(1); createAndSaveTestModelRun(diseaseGroupId, batchStartDate, batchEndDate, DateTime.now().minusWeeks(1)); ModelRun modelRun2 = createAndSaveTestModelRun(diseaseGroupId, batchStartDate, batchEndDate, null); // Act batchingHandler.handle(modelRun2); // Assert List<DiseaseOccurrence> occurrences = diseaseService.getDiseaseOccurrencesByDiseaseGroupId(diseaseGroupId); // As this is the second batch, batching initialisation has not been performed, and therefore: // - the final weightings remain not-null // - the statuses remain as-is (in particular, none have been set to AWAITING_BATCHING) for (DiseaseOccurrence occurrence : occurrences) { assertThat(occurrence.getStatus()).isNotEqualTo(DiseaseOccurrenceStatus.AWAITING_BATCHING); // In the test data, occurrences without status READY have null weightings already, so ignore them if (occurrence.getStatus().equals(DiseaseOccurrenceStatus.READY)) { assertThat(occurrence.getFinalWeighting()).isNotNull(); assertThat(occurrence.getFinalWeightingExcludingSpatial()).isNotNull(); } } assertOccurrences(occurrences, DiseaseOccurrenceStatus.READY, 45, 0); assertOccurrences(occurrences, DiseaseOccurrenceStatus.IN_REVIEW, 0, 0); assertOccurrences(occurrences, DiseaseOccurrenceStatus.DISCARDED_FAILED_QC, 3, 0); // The model run should have been updated correctly modelRun2 = modelRunService.getModelRunByName(modelRun2.getName()); assertThat(modelRun2.getBatchingCompletedDate()).isEqualTo(now); assertThat(modelRun2.getBatchOccurrenceCount()).isEqualTo(0); } private ModelRun createAndSaveTestModelRun(int diseaseGroupId, DateTime batchStartDate, DateTime batchEndDate, DateTime batchingCompletionDate) { String name = Double.toString(Math.random()); ModelRun modelRun = new ModelRun( name, diseaseGroupDao.getById(diseaseGroupId), "host", DateTime.now().minusDays(1), DateTime.now(), DateTime.now()); modelRun.setStatus(ModelRunStatus.COMPLETED); modelRun.setResponseDate(DateTime.now()); modelRun.setBatchStartDate(batchStartDate); modelRun.setBatchEndDate(batchEndDate); modelRun.setBatchingCompletedDate(batchingCompletionDate); modelRunService.saveModelRun(modelRun); flushAndClear(); when(rasterFilePathFactory.getFullMeanPredictionRasterFile(eq(modelRun))) .thenReturn(new File(LARGE_RASTER_FILENAME)); when(rasterFilePathFactory.getAdminRaster(0)) .thenReturn(new File(ADMIN_RASTER_FILENAME)); when(rasterFilePathFactory.getAdminRaster(1)) .thenReturn(new File(ADMIN_RASTER_FILENAME)); when(rasterFilePathFactory.getAdminRaster(2)) .thenReturn(new File(ADMIN_RASTER_FILENAME)); return modelRun; } private void assertOccurrences(List<DiseaseOccurrence> occurrences, DiseaseOccurrenceStatus status, int expectedSize, int expectedSizeWithEnvironmentalSuitability) { List<DiseaseOccurrence> filteredOccurrences = findOccurrencesByStatus(occurrences, status); assertThat(filteredOccurrences).hasSize(expectedSize); assertThat(getOccurrencesWithEnvironmentalSuitability(filteredOccurrences)) .hasSize(expectedSizeWithEnvironmentalSuitability); } private List<DiseaseOccurrence> findOccurrencesByStatus(List<DiseaseOccurrence> occurrences, DiseaseOccurrenceStatus status) { return select(occurrences, having(on(DiseaseOccurrence.class).getStatus(), equalTo(status))); } private List<DiseaseOccurrence> getOccurrencesWithEnvironmentalSuitability(List<DiseaseOccurrence> occurrences) { return select(occurrences, having(on(DiseaseOccurrence.class).getEnvironmentalSuitability(), notNullValue())); } }