package uk.ac.ox.zoo.seeg.abraid.mp.common.dao; import org.hibernate.Query; import org.hibernate.SessionFactory; import org.joda.time.DateTime; import org.springframework.stereotype.Repository; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.*; import java.util.Arrays; import java.util.List; import java.util.Set; /** * The DiseaseOccurrence entity's Data Access Object. * * Copyright (c) 2014 University of Oxford */ @Repository public class DiseaseOccurrenceDaoImpl extends AbstractDao<DiseaseOccurrence, Integer> implements DiseaseOccurrenceDao { // HQL fragments used to build queries to obtain disease occurrences private static final String DISEASE_EXTENT_QUERY = DiseaseOccurrence.DISEASE_OCCURRENCE_BASE_QUERY + "where d.diseaseGroup.id = :diseaseGroupId " + "and d.status = 'READY' " + "and d.location.adminUnitGlobalGaulCode is not null " + "and d.location.adminUnitTropicalGaulCode is not null "; private static final String DISEASE_EXTENT_WEIGHTING_CLAUSE = "and d.finalWeighting is not null " + "and (d.validationWeighting is null or d.validationWeighting >= :minimumValidationWeighting) "; private static final String DISEASE_EXTENT_OCCURRENCE_DATE_CLAUSE = "and d.occurrenceDate >= :minimumOccurrenceDate "; private static final String GOLD_STANDARD_OCCURRENCES_CLAUSE = "and d.alert in (from Alert where feed.provenance.name = '" + ProvenanceNames.MANUAL_GOLD_STANDARD + "') "; private static final String MODEL_RUN_REQUEST_QUERY = DiseaseOccurrence.DISEASE_OCCURRENCE_BASE_QUERY + "where d.diseaseGroup.id = :diseaseGroupId " + "and d.status = 'READY' " + "and d.location.isModelEligible is TRUE "; private static final String MODEL_RUN_REQUEST_FINAL_WEIGHTING_ABOVE_ZERO_CLAUSE = "and d.finalWeighting > 0 "; private static final String MODEL_RUN_REQUEST_ORDER_BY_CLAUSE = "order by d.occurrenceDate desc"; private static final int WEEKS_AGO_FOR_TRAINING_DATA_CUT_OFF_DATE = 52; public DiseaseOccurrenceDaoImpl(SessionFactory sessionFactory) { super(sessionFactory); } /** * Gets disease occurrences with the specified IDs. * @param diseaseOccurrenceIds The disease occurrence IDs. * @return The disease occurrences with the specified IDs. */ @Override public List<DiseaseOccurrence> getByIds(List<Integer> diseaseOccurrenceIds) { return listNamedQuery("getDiseaseOccurrencesByIds", "diseaseOccurrenceIds", diseaseOccurrenceIds); } /** * Gets all disease occurrences for the specified disease group. * @param diseaseGroupId The disease group's ID. * @return All disease occurrences for the specified disease group. */ @Override public List<DiseaseOccurrence> getByDiseaseGroupId(int diseaseGroupId) { return listNamedQuery("getDiseaseOccurrencesByDiseaseGroupId", "diseaseGroupId", diseaseGroupId); } /** * Gets all disease occurrences for the specified disease group and occurrence statuses. * @param diseaseGroupId The disease group's ID. * @param statuses One or more disease occurrence statuses. * @return All disease occurrences for the specified disease group and statuses. */ public List<DiseaseOccurrence> getByDiseaseGroupIdAndStatuses(int diseaseGroupId, DiseaseOccurrenceStatus... statuses) { List<DiseaseOccurrenceStatus> statusesList = Arrays.asList(statuses); return listNamedQuery("getDiseaseOccurrencesByDiseaseGroupIdAndStatuses", "diseaseGroupId", diseaseGroupId, "statuses", statusesList); } /** * Gets a list of occurrence points, for the specified validator disease group, for which the specified expert has * not yet submitted a review. Only SEEG users may view occurrences of disease groups before first model run prep. * Other external users may only view occurrences of disease groups with automatic model runs enabled. * @param expertId The id of the specified expert. * @param userIsSeeg Whether the expert is a member of SEEG, and therefore should review more occurrences. * @param validatorDiseaseGroupId The id of the validatorDiseaseGroup of interest. * @return The list of disease occurrence points to be displayed to the expert on the map. */ public List<DiseaseOccurrence> getDiseaseOccurrencesYetToBeReviewedByExpert(Integer expertId, boolean userIsSeeg, Integer validatorDiseaseGroupId) { return listNamedQuery("getDiseaseOccurrencesYetToBeReviewedByExpert", "expertId", expertId, "userIsSeeg", userIsSeeg, "validatorDiseaseGroupId", validatorDiseaseGroupId); } /** * Get disease occurrences (excluding bias occurrences) that match the specified disease group, location, alert * and occurrence start date. Used to check for the existence of a disease occurrence. * @param diseaseGroup The disease group. * @param location The location. * @param alert The alert. * @param occurrenceDate The occurrence date. * @return A list of matching disease occurrences. */ public List<DiseaseOccurrence> getDiseaseOccurrencesForExistenceCheck(DiseaseGroup diseaseGroup, Location location, Alert alert, DateTime occurrenceDate) { return listNamedQuery("getDiseaseOccurrencesForExistenceCheck", "diseaseGroup", diseaseGroup, "location", location, "alert", alert, "occurrenceDate", occurrenceDate); } /** * Gets disease occurrences for generating the disease extent for the specified disease group. * @param diseaseGroupId The ID of the disease group. * @param minimumValidationWeighting All disease occurrences must have a validation weighting greater than this * value, and must have a final weighting. If null, the validation and final weightings are ignored. * @param minimumOccurrenceDate All disease occurrences must have an occurrence date after this value. If null, * the occurrence date is ignored. * @param onlyUseGoldStandardOccurrences True if only "gold standard" occurrences should be retrieved, otherwise * false. * @return A list of disease occurrences. */ @Override @SuppressWarnings("unchecked") public List<DiseaseOccurrence> getDiseaseOccurrencesForDiseaseExtent( Integer diseaseGroupId, Double minimumValidationWeighting, DateTime minimumOccurrenceDate, boolean onlyUseGoldStandardOccurrences) { String queryString = DISEASE_EXTENT_QUERY; if (minimumValidationWeighting != null) { queryString += DISEASE_EXTENT_WEIGHTING_CLAUSE; } if (minimumOccurrenceDate != null) { queryString += DISEASE_EXTENT_OCCURRENCE_DATE_CLAUSE; } if (onlyUseGoldStandardOccurrences) { queryString += GOLD_STANDARD_OCCURRENCES_CLAUSE; } Query query = currentSession().createQuery(queryString); query.setParameter("diseaseGroupId", diseaseGroupId); if (minimumValidationWeighting != null) { query.setParameter("minimumValidationWeighting", minimumValidationWeighting); } if (minimumOccurrenceDate != null) { query.setParameter("minimumOccurrenceDate", minimumOccurrenceDate); } return query.list(); } /** * Gets disease occurrences currently in validation, for the specified disease group. * @param diseaseGroupId The ID of the disease group. * @return A list of disease occurrences currently being validated by experts. */ @Override public List<DiseaseOccurrence> getDiseaseOccurrencesInValidation(Integer diseaseGroupId) { return listNamedQuery("getDiseaseOccurrencesInValidation", "diseaseGroupId", diseaseGroupId); } /** * Gets disease occurrences for the specified disease group which are yet to have a final weighting assigned. * @param diseaseGroupId The ID of the disease group. * @param statuses A set of disease occurrence statuses from which to return occurrences. * @return A list of disease occurrences that need their final weightings to be set. */ @Override public List<DiseaseOccurrence> getDiseaseOccurrencesYetToHaveFinalWeightingAssigned( Integer diseaseGroupId, DiseaseOccurrenceStatus... statuses) { List<DiseaseOccurrenceStatus> statusList = Arrays.asList(statuses); return listNamedQuery("getDiseaseOccurrencesYetToHaveFinalWeightingAssigned", "diseaseGroupId", diseaseGroupId, "statuses", statusList); } /** * Gets disease occurrences for a request to run the model. * @param diseaseGroupId The ID of the disease group. * @param onlyUseGoldStandardOccurrences True if only "gold standard" occurrences should be retrieved, otherwise * false. * @return Disease occurrences for a request to run the model. */ @Override public List<DiseaseOccurrence> getDiseaseOccurrencesForModelRunRequest(Integer diseaseGroupId, boolean onlyUseGoldStandardOccurrences) { String queryString = MODEL_RUN_REQUEST_QUERY; if (onlyUseGoldStandardOccurrences) { queryString += GOLD_STANDARD_OCCURRENCES_CLAUSE; } else { queryString += MODEL_RUN_REQUEST_FINAL_WEIGHTING_ABOVE_ZERO_CLAUSE; } queryString += MODEL_RUN_REQUEST_ORDER_BY_CLAUSE; Query query = currentSession().createQuery(queryString); query.setParameter("diseaseGroupId", diseaseGroupId); return list(query); } /** * Gets the number of distinct locations from the new disease occurrences for the specified disease group. * A "new" occurrence has status READY, and a suitable created_date. * Occurrences must additionally satisfy one of: * + The distance from disease extent values is greater than minimum specified on the disease group (a new area). * + The environmental suitability values is less than max specified on the disease group (a new area). * @param diseaseGroupId The ID of the disease group. * @param locationsFromLastModelRun A list of location ids used in the last model run. * @param cutoffForAutomaticallyValidated Automatically validated occurrences must be newer than this date. * @param cutoffForManuallyValidated Manually validated occurrences must be newer than this date. * @param maxEnvironmentalSuitability The max environmental suitability of occurrences to consider. * @param minDistanceFromDiseaseExtent The minimum distance from disease extent of occurrences to consider. * @return The number of locations. */ @Override public long getDistinctLocationsCountForTriggeringModelRun(Integer diseaseGroupId, Set<Integer> locationsFromLastModelRun, DateTime cutoffForAutomaticallyValidated, DateTime cutoffForManuallyValidated, Double maxEnvironmentalSuitability, Double minDistanceFromDiseaseExtent) { Query query; if (locationsFromLastModelRun.isEmpty()) { query = getParameterisedNamedQuery( "getDistinctLocationsCountForTriggeringModelRunWithoutLastModelRunClause", "diseaseGroupId", diseaseGroupId, "cutoffForAutomaticallyValidated", cutoffForAutomaticallyValidated, "cutoffForManuallyValidated", cutoffForManuallyValidated, "maxEnvironmentalSuitability", maxEnvironmentalSuitability, "minDistanceFromDiseaseExtent", minDistanceFromDiseaseExtent); } else { query = getParameterisedNamedQuery( "getDistinctLocationsCountForTriggeringModelRunWithLastModelRunClause", "diseaseGroupId", diseaseGroupId, "cutoffForAutomaticallyValidated", cutoffForAutomaticallyValidated, "cutoffForManuallyValidated", cutoffForManuallyValidated, "maxEnvironmentalSuitability", maxEnvironmentalSuitability, "minDistanceFromDiseaseExtent", minDistanceFromDiseaseExtent, "locationsFromLastModelRun", locationsFromLastModelRun); } return (long) query.uniqueResult(); } /** * Gets statistics about the occurrences of the specified disease group. * @param diseaseGroupId The disease group ID. * @return The statistics. */ public DiseaseOccurrenceStatistics getDiseaseOccurrenceStatistics(int diseaseGroupId) { Query query = getParameterisedNamedQuery("getDiseaseOccurrenceStatistics", "diseaseGroupId", diseaseGroupId); return (DiseaseOccurrenceStatistics) query.uniqueResult(); } /** * Gets a list of disease occurrences for batching initialisation, for the specified disease group. * @param diseaseGroupId The disease group ID. * @return A list of disease occurrences. */ @SuppressWarnings("unchecked") @Override public List<DiseaseOccurrence> getDiseaseOccurrencesForBatchingInitialisation(int diseaseGroupId) { return listNamedQuery("getDiseaseOccurrencesForBatchingInitialisation", "diseaseGroupId", diseaseGroupId); } /** * Gets a list of disease occurrences for validation batching, for the specified disease group. * @param diseaseGroupId The disease group ID. * @param batchStartDate The start date of the batch. * @param batchEndDate The end date of the batch. * @return A list of disease occurrences. */ @SuppressWarnings("unchecked") @Override public List<DiseaseOccurrence> getDiseaseOccurrencesForBatching(int diseaseGroupId, DateTime batchStartDate, DateTime batchEndDate) { return listNamedQuery("getDiseaseOccurrencesForBatching", "diseaseGroupId", diseaseGroupId, "batchStartDate", batchStartDate, "batchEndDate", batchEndDate); } /** * Gets a list of recent disease occurrences that have been validated (they have a target expert weighting). * @param diseaseGroupId The disease group ID. * @return A list of disease occurrences. */ @Override public List<DiseaseOccurrence> getDiseaseOccurrencesForTrainingPredictor(int diseaseGroupId) { return listNamedQuery("getDiseaseOccurrencesForTrainingPredictor", "diseaseGroupId", diseaseGroupId, "cutOffDate", DateTime.now().minusWeeks(WEEKS_AGO_FOR_TRAINING_DATA_CUT_OFF_DATE)); } /** * Gets the number of occurrences that are eligible for being sent to the model, between the start and end dates. * This helps to estimate whether the number of occurrences in a batch will be sufficient for a model run. * @param diseaseGroupId The disease group ID. * @param startDate The start date. * @param endDate The end date. * @return The number of occurrences that are eligible for being sent to the model. This is all occurrences * except those that have been discarded, or points marked as ineligible. */ @Override public long getNumberOfOccurrencesEligibleForModelRun(int diseaseGroupId, DateTime startDate, DateTime endDate) { Query query = getParameterisedNamedQuery("getNumberOfDiseaseOccurrencesEligibleForModelRun", "diseaseGroupId", diseaseGroupId, "startDate", startDate, "endDate", endDate); return (long) query.uniqueResult(); } /** * Gets the number of bespoke bias occurrences that have been uploaded for use with a specified diseases group, * regardless of suitability. * @param diseaseGroup The disease group being modelled. * @return The number of bias occurrences. */ @Override public long getCountOfUnfilteredBespokeBiasOccurrences(DiseaseGroup diseaseGroup) { return (long) getParameterisedNamedQuery("getCountOfUnfilteredBespokeBiasOccurrences", "diseaseGroupId", diseaseGroup.getId()).uniqueResult(); } /** * Gets an estimate of number of bespoke bias occurrences that have been uploaded for use with a specified diseases * group, which are suitable for use in a model. This is only an estimate as the occurrence date filter that is * applied during model runs is not applied. * @param diseaseGroup The disease group being modelled. * @return The number of bias occurrences. */ @Override public long getEstimateCountOfFilteredBespokeBiasOccurrences(DiseaseGroup diseaseGroup) { return (long) getParameterisedNamedQuery("getEstimateCountOfFilteredBespokeBiasOccurrences", "diseaseGroupId", diseaseGroup.getId(), "isGlobal", diseaseGroup.isGlobal() ).uniqueResult(); } /** * Gets the estimate of number of occurrences that are available for use as a default/fallback bias set for a * specified disease group, which are suitable for use in a model. This is only an estimate as the occurrence date * filter that is applied during model runs is not applied. * @param diseaseGroup The disease group being modelled. * @return The number of bias occurrences. */ @Override public long getEstimateCountOfFilteredDefaultBiasOccurrences(DiseaseGroup diseaseGroup) { return (long) getParameterisedNamedQuery("getEstimateCountOfFilteredDefaultBiasOccurrences", "diseaseGroupId", diseaseGroup.getId(), "isGlobal", diseaseGroup.isGlobal(), "shouldFilterBiasDataByAgentType", diseaseGroup.shouldFilterBiasDataByAgentType(), "agentType", diseaseGroup.getAgentType() ).uniqueResult(); } /** * Gets the bespoke bias occurrences that are should be used with a model run (for sample bias). * @param diseaseGroup The disease group being modelled. * @param startDate The start date of the model run input data range. * @param endDate The end date of the model run input data range. * @return The bias occurrences. */ @Override public List<DiseaseOccurrence> getBespokeBiasOccurrencesForModelRun( DiseaseGroup diseaseGroup, DateTime startDate, DateTime endDate) { return listNamedQuery("getBespokeBiasOccurrencesForModelRun", "diseaseGroupId", diseaseGroup.getId(), "isGlobal", diseaseGroup.isGlobal(), "startDate", startDate, "endDate", endDate); } /** * Gets the default/fallback bias occurrences that are should be used with a model run (for sample bias). * This should be used when a bespoke dataset hasn't been provided. * @param diseaseGroup The disease group being modelled (will be excluded from bias set). * @param startDate The start date of the model run input data range. * @param endDate The end date of the model run input data range. * @return The bias occurrences. */ @Override public List<DiseaseOccurrence> getDefaultBiasOccurrencesForModelRun( DiseaseGroup diseaseGroup, DateTime startDate, DateTime endDate) { return listNamedQuery("getDefaultBiasOccurrencesForModelRun", "diseaseGroupId", diseaseGroup.getId(), "isGlobal", diseaseGroup.isGlobal(), "shouldFilterBiasDataByAgentType", diseaseGroup.shouldFilterBiasDataByAgentType(), "agentType", diseaseGroup.getAgentType(), "startDate", startDate, "endDate", endDate); } /** * Delete all of the occurrence that are labelled as bias for the specified disease group. * @param diseaseGroupId Disease group for which to remove the bias points * (i.e. bias_disease_group_id, not disease_group_id). */ @Override public void deleteDiseaseOccurrencesByBiasDiseaseId(int diseaseGroupId) { noResultNamedQuery("deleteDiseaseOccurrencesByBiasDiseaseId", "diseaseGroupId", diseaseGroupId); } }