package uk.ac.ox.zoo.seeg.abraid.mp.modeloutputhandler.web; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.springframework.transaction.annotation.Transactional; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.*; 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.DiseaseOccurrenceValidationService; import java.util.List; /** * Handles batching of disease occurrences. Specifically, if a batch end date is specified in the model run, it sets the * "validation parameters" (e.g. environmental suitability, distance from disease extent) for the disease occurrences up * until the end date. * * Copyright (c) 2014 University of Oxford */ public class BatchingHandlerHelper { private static final Logger LOGGER = Logger.getLogger(BatchingHandlerHelper.class); private static final String STARTING_HANDLING_LOG_MESSAGE = "Model run %d: starting batching handling"; private static final String INITIAL_BATCH_LOG_MESSAGE = "Model run %d: this is the initial batch, so setting " + "status to AWAITING_BATCHING and final weighting to null for %d occurrence(s) of disease group %d (%s)"; private static final String VALIDATION_LOG_MESSAGE = "Model run %d: setting validation parameters for %d occurrence(s) of disease group %d (%s) " + "(batch start date %s, batch end date %s)"; private static final String VALIDATION_COMPLETED_LOG_MESSAGE = "Model run %d: setting validation parameters completed"; private static final String LOG_DATE_FORMAT = "dd MMM yyyy"; private static final String NO_HANDLING_LOG_MESSAGE = "Model run %d: no batching handling to do"; private DiseaseService diseaseService; private ModelRunService modelRunService; private DiseaseOccurrenceValidationService diseaseOccurrenceValidationService; public BatchingHandlerHelper(DiseaseService diseaseService, ModelRunService modelRunService, DiseaseOccurrenceValidationService diseaseOccurrenceValidationService) { this.diseaseService = diseaseService; this.modelRunService = modelRunService; this.diseaseOccurrenceValidationService = diseaseOccurrenceValidationService; } /** * Handles disease occurrences for disease group setup (i.e. automatic model runs not yet enabled). * @param modelRun The model run. * @return The date that batching initialisation started, or null if batching was not initialised by this call. * This is to allow a subsequent call to continueBatchingInitialisation. */ @Transactional(rollbackFor = Exception.class) public DateTime handle(ModelRun modelRun) { // Reload the model run, because this may be a new transaction and we may need to save the model run later on modelRun = modelRunService.getModelRunByName(modelRun.getName()); DiseaseGroup diseaseGroup = modelRun.getDiseaseGroup(); DateTime batchingInitialisationDate = null; // If disease group is in the setup phase, set validation parameters on a batch of disease occurrences if (isBatchingRequired(modelRun, diseaseGroup)) { LOGGER.info(String.format(STARTING_HANDLING_LOG_MESSAGE, modelRun.getId())); batchingInitialisationDate = initialiseBatchingIfNecessary(modelRun, diseaseGroup); handleBatch(modelRun, diseaseGroup); } else { LOGGER.info(String.format(NO_HANDLING_LOG_MESSAGE, modelRun.getId())); } return batchingInitialisationDate; } /** * Ensures that all relevant occurrences are initialised, in particular any occurrences that were acquired while * batching initialisation was taking place. * @param diseaseGroupId The disease group ID. * @param batchingInitialisationDate The date that batching initialisation started, or null if batching was * not initialised by an earlier call to the handle() method. */ @Transactional(rollbackFor = Exception.class) public void continueBatchingInitialisation(Integer diseaseGroupId, DateTime batchingInitialisationDate) { if (batchingInitialisationDate != null) { List<DiseaseOccurrence> occurrences = getDiseaseOccurrencesForBatchingInitialisation(diseaseGroupId); for (DiseaseOccurrence diseaseOccurrence : occurrences) { if (!batchingInitialisationDate.isAfter(diseaseOccurrence.getCreatedDate())) { initialiseOccurrenceForBatching(diseaseOccurrence); } } } } private boolean isBatchingRequired(ModelRun modelRun, DiseaseGroup diseaseGroup) { return (modelRun.getStatus() == ModelRunStatus.COMPLETED) && (modelRun.getBatchStartDate() != null) && (modelRun.getBatchEndDate() != null) && !diseaseGroup.isAutomaticModelRunsEnabled(); } private DateTime initialiseBatchingIfNecessary(ModelRun modelRun, DiseaseGroup diseaseGroup) { // If no batch of disease occurrences has completed for this disease group, initialise the batching process by // changing the status of all READY occurrences to AWAITING_BATCHING and setting their final weightings to null DateTime batchingInitialisationDate = null; Integer diseaseGroupId = diseaseGroup.getId(); if (!modelRunService.hasBatchingEverCompleted(diseaseGroupId)) { batchingInitialisationDate = DateTime.now(); List<DiseaseOccurrence> occurrences = getDiseaseOccurrencesForBatchingInitialisation(diseaseGroupId); LOGGER.info(String.format(INITIAL_BATCH_LOG_MESSAGE, modelRun.getId(), occurrences.size(), diseaseGroupId, diseaseGroup.getName())); for (DiseaseOccurrence occurrence : occurrences) { initialiseOccurrenceForBatching(occurrence); } } return batchingInitialisationDate; } private void handleBatch(ModelRun modelRun, DiseaseGroup diseaseGroup) { // Ensure that the batch start date is at the very start of the day DateTime batchStartDateWithMinimumTime = getBatchStartDateWithMinimumTime(modelRun.getBatchStartDate()); // Ensure that the batch end date is at the very end of the day DateTime batchEndDateWithMaximumTime = getBatchEndDateWithMaximumTime(modelRun.getBatchEndDate()); // Get the occurrences that we want to batch, and then set their validation parameters List<DiseaseOccurrence> occurrences = diseaseService.getDiseaseOccurrencesForBatching(diseaseGroup.getId(), batchStartDateWithMinimumTime, batchEndDateWithMaximumTime); LOGGER.info(String.format(VALIDATION_LOG_MESSAGE, modelRun.getId(), occurrences.size(), diseaseGroup.getId(), diseaseGroup.getName(), batchStartDateWithMinimumTime.toString(LOG_DATE_FORMAT), batchEndDateWithMaximumTime.toString(LOG_DATE_FORMAT))); setValidationParametersForOccurrencesBatch(occurrences); setModelRunBatchingParameters(modelRun, occurrences.size()); LOGGER.info(String.format(VALIDATION_COMPLETED_LOG_MESSAGE, modelRun.getId())); } private List<DiseaseOccurrence> getDiseaseOccurrencesForBatchingInitialisation(int diseaseGroupId) { return diseaseService.getDiseaseOccurrencesForBatchingInitialisation(diseaseGroupId); } private DateTime getBatchStartDateWithMinimumTime(DateTime batchStartDate) { return batchStartDate.withTimeAtStartOfDay(); } private DateTime getBatchEndDateWithMaximumTime(DateTime batchEndDate) { return batchEndDate.withTimeAtStartOfDay().plusDays(1).minusMillis(1); } private void initialiseOccurrenceForBatching(DiseaseOccurrence occurrence) { occurrence.setStatus(DiseaseOccurrenceStatus.AWAITING_BATCHING); occurrence.setFinalWeighting(null); occurrence.setFinalWeightingExcludingSpatial(null); diseaseService.saveDiseaseOccurrence(occurrence); } private void setValidationParametersForOccurrencesBatch(List<DiseaseOccurrence> occurrences) { if (occurrences.size() > 0) { diseaseOccurrenceValidationService.addValidationParameters(occurrences); for (DiseaseOccurrence occurrence : occurrences) { diseaseService.saveDiseaseOccurrence(occurrence); } } } private void setModelRunBatchingParameters(ModelRun modelRun, int batchedOccurrenceCount) { modelRun.setBatchingCompletedDate(DateTime.now()); modelRun.setBatchOccurrenceCount(batchedOccurrenceCount); modelRunService.saveModelRun(modelRun); } }