package uk.ac.ox.zoo.seeg.abraid.mp.publicsite.web.admin; import org.apache.commons.lang.builder.CompareToBuilder; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.Country; 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.service.core.DiseaseService; import uk.ac.ox.zoo.seeg.abraid.mp.common.service.core.GeometryService; import uk.ac.ox.zoo.seeg.abraid.mp.publicsite.domain.DiseaseOccurrenceSpreadTable; import java.util.*; import static ch.lambdaj.Lambda.*; import static org.hamcrest.core.IsEqual.equalTo; /** * Helper class for generating a disease occurrence spread table for a particular disease group. * * Copyright (c) 2014 University of Oxford */ public class DiseaseOccurrenceSpreadHelper { private DiseaseService diseaseService; private GeometryService geometryService; private static final String DISEASE_GROUP_DOES_NOT_EXIST_MESSAGE = "This disease group does not exist."; private static final String NO_OCCURRENCES_MESSAGE = "This disease group has no relevant occurrences."; public DiseaseOccurrenceSpreadHelper(DiseaseService diseaseService, GeometryService geometryService) { this.diseaseService = diseaseService; this.geometryService = geometryService; } /** * Gets a disease occurrence spread table for the specified disease group. This is a count of disease occurrences * (excluding country-level points) by the country they occur in (rows) and year (columns). * @param diseaseGroupId The disease group ID. * @return A disease occurrence spread table. */ public DiseaseOccurrenceSpreadTable getDiseaseOccurrenceSpreadTable(int diseaseGroupId) { if (diseaseService.getDiseaseGroupById(diseaseGroupId) == null) { return new DiseaseOccurrenceSpreadTable(DISEASE_GROUP_DOES_NOT_EXIST_MESSAGE); } // Get disease occurrences by disease group and multiple statuses, excluding country-level points. List<DiseaseOccurrence> occurrences = getOccurrencesForTable(diseaseGroupId); if (occurrences.size() > 0) { List<Country> countries = getCountries(); Set<Integer> years = getUniqueOccurrenceYears(occurrences); int[][] tableArray = getOccurrencesTableArray(occurrences, countries, years); return convertArrayToTable(countries, years, tableArray); } else { return new DiseaseOccurrenceSpreadTable(NO_OCCURRENCES_MESSAGE); } } private List<DiseaseOccurrence> getOccurrencesForTable(int diseaseGroupId) { List<DiseaseOccurrence> occurrences = diseaseService.getDiseaseOccurrencesByDiseaseGroupIdAndStatuses( diseaseGroupId, DiseaseOccurrenceStatus.READY, DiseaseOccurrenceStatus.IN_REVIEW, DiseaseOccurrenceStatus.AWAITING_BATCHING ); return filter( having(on(DiseaseOccurrence.class).getLocation().isModelEligible(), equalTo(true)), occurrences ); } private List<Country> getCountries() { List<Country> countries = geometryService.getAllCountries(); Collections.sort(countries, new Comparator<Country>() { @Override public int compare(Country o1, Country o2) { return new CompareToBuilder() .append(o2.isForMinDataSpread(), o1.isForMinDataSpread()) // descending (i.e. true before false) .append(o1.getName(), o2.getName()) // ascending .toComparison(); } }); return countries; } private Set<Integer> getUniqueOccurrenceYears(List<DiseaseOccurrence> occurrences) { // Use a TreeSet to keep the set sorted Set<Integer> years = new TreeSet<>(); for (DiseaseOccurrence occurrence : occurrences) { years.add(occurrence.getOccurrenceDate().getYear()); } return years; } private int[][] getOccurrencesTableArray(List<DiseaseOccurrence> occurrences, List<Country> countries, Set<Integer> years) { // Map country GAUL codes to consecutive table rows Map<Integer, Integer> countryMap = new HashMap<>(); Integer row = 0; for (Country country : countries) { countryMap.put(country.getGaulCode(), row); row++; } // Map occurrence years to consecutive table columns Map<Integer, Integer> yearMap = new HashMap<>(); Integer column = 0; for (int year : years) { yearMap.put(year, column); column++; } // Add occurrences to the table (which is an array for ease of incrementing) int[][] tableArray = new int[countryMap.size()][yearMap.size()]; for (DiseaseOccurrence occurrence : occurrences) { row = countryMap.get(occurrence.getLocation().getCountryGaulCode()); column = yearMap.get(occurrence.getOccurrenceDate().getYear()); if (row != null && column != null) { // Increment the number of occurrences for this country and year tableArray[row][column]++; } } return tableArray; } private DiseaseOccurrenceSpreadTable convertArrayToTable(List<Country> countries, Set<Integer> years, int[][] tableArray) { DiseaseOccurrenceSpreadTable table = new DiseaseOccurrenceSpreadTable(years); int row = 0; for (Country country : countries) { table.addRow(country, toList(tableArray[row])); row++; } return table; } private List<Integer> toList(int[] array) { List<Integer> list = new ArrayList<>(); for (int item : array) { list.add(item); } return list; } }