package uk.ac.ox.zoo.seeg.abraid.mp.publicsite.web.admin; import ch.lambdaj.group.Group; import org.joda.time.LocalDate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.HealthMapReportEntry; 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.common.service.core.ReportingService; import uk.ac.ox.zoo.seeg.abraid.mp.common.web.AbstractController; import java.util.*; import static ch.lambdaj.Lambda.*; /** * Controller for the admin reporting pages. * Copyright (c) 2015 University of Oxford */ @Controller public class ReportingController extends AbstractController { private static final String ADMIN_REPORTING_BASE_URL = "/admin/report"; private ReportingService reportingService; private final GeometryService geometryService; private final DiseaseService diseaseService; @Autowired public ReportingController(ReportingService reportingService, GeometryService geometryService, DiseaseService diseaseService) { this.reportingService = reportingService; this.geometryService = geometryService; this.diseaseService = diseaseService; } /** * Get the HealthMap disease report. * @param model The page template model. * @return The page template. */ @Secured({ "ROLE_ADMIN" }) @RequestMapping(value = ADMIN_REPORTING_BASE_URL + "/healthMapByDisease", method = RequestMethod.GET) public String getHealthMapDiseaseReport(Model model) { return getHealthMapReport( model, reportingService.getHealthMapDiseaseReportEntries(), diseaseService.getDiseaseGroupNamesForHealthMapReport(), false); } /** * Get the HealthMap country report. * @param model The page template model. * @return The page template. */ @Secured({ "ROLE_ADMIN" }) @RequestMapping(value = ADMIN_REPORTING_BASE_URL + "/healthMapByCountry", method = RequestMethod.GET) public String getHealthMapCountryReport(Model model) { return getHealthMapReport( model, reportingService.getHealthMapCountryReportEntries(), geometryService.getCountryNamesForHealthMapReport(), true); } /** * Setup the template data for a HealthMap report. */ private String getHealthMapReport( Model model, List<HealthMapReportEntry> data, List<String> qualifiers, boolean includeOtherRow) { List<String> months = getMonths(); Map<String, Map<String, HealthMapReportEntry>> processed = process(data, qualifiers, months, includeOtherRow); List<String> columns = new ArrayList<>(months); columns.add("Total"); List<String> rows = new ArrayList<>(qualifiers); if (includeOtherRow) { rows.add("Other"); } rows.add("Total"); model.addAttribute("data", processed); model.addAttribute("months", columns); model.addAttribute("qualifiers", rows); return "admin/healthMapReport"; } /** * Index the data by month and qualifier, fill in the gaps with 0, add "Other" and "Total" rows/columns. */ private Map<String, Map<String, HealthMapReportEntry>> process( List<HealthMapReportEntry> data, List<String> qualifiers, List<String> months, boolean includeOtherRow) { Map<String, Map<String, HealthMapReportEntry>> unprocessed = indexData(data, months); Map<String, Map<String, HealthMapReportEntry>> processed = new HashMap<>(); for (String month : months) { Map<String, HealthMapReportEntry> processedForMonth = new HashMap<>(); processed.put(month, processedForMonth); // Add the known qualifiers for (String qualifier : qualifiers) { Map<String, HealthMapReportEntry> unprocessedForMonth = unprocessed.get(month); if (unprocessedForMonth.containsKey(qualifier)) { processedForMonth.put(qualifier, unprocessedForMonth.get(qualifier)); } else { processedForMonth.put(qualifier, new HealthMapReportEntry(month, qualifier, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L)); } } // Add other row if (includeOtherRow) { HealthMapReportEntry otherEntry = sumDataForUnknownQualifiers(month, qualifiers, unprocessed); processedForMonth.put("Other", otherEntry); } // Add totals row HealthMapReportEntry totalEntry = sumDataForAllQualifiers(month, unprocessed); processedForMonth.put("Total", totalEntry); } // Add totals column Map<String, HealthMapReportEntry> processedTotals = new HashMap<>(); for (String qualifier : qualifiers) { processedTotals.put(qualifier, sumDataForAllMonths(months, qualifier, processed)); } if (includeOtherRow) { processedTotals.put("Other", sumDataForAllMonths(months, "Other", processed)); } processedTotals.put("Total", sumDataForAllMonths(months, "Total", processed)); processed.put("Total", processedTotals); return processed; } private Map<String, Map<String, HealthMapReportEntry>> indexData( List<HealthMapReportEntry> data, List<String> months) { Group<HealthMapReportEntry> dataByMonth = group(data, by(on(HealthMapReportEntry.class).getMonth())); Map<String, Map<String, HealthMapReportEntry>> indexed = new HashMap<>(); for (String month : months) { Map<String, HealthMapReportEntry> forMonth = index(dataByMonth.find(month), on(HealthMapReportEntry.class).getQualifier()); indexed.put(month, forMonth); } return indexed; } private HealthMapReportEntry sumDataForAllMonths( List<String> months, String qualifier, Map<String, Map<String, HealthMapReportEntry>> data) { return sumData("Total", qualifier, months, Arrays.asList(qualifier), data); } private HealthMapReportEntry sumDataForAllQualifiers( String month, Map<String, Map<String, HealthMapReportEntry>> data) { List<String> allQualifiers = new ArrayList<>(data.get(month).keySet()); return sumData(month, "Total", Arrays.asList(month), allQualifiers, data); } private HealthMapReportEntry sumDataForUnknownQualifiers( String month, List<String> knownQualifiers, Map<String, Map<String, HealthMapReportEntry>> data) { List<String> unknownQualifiers = new ArrayList<>(data.get(month).keySet()); unknownQualifiers.removeAll(knownQualifiers); return sumData(month, "Other", Arrays.asList(month), unknownQualifiers, data); } private HealthMapReportEntry sumData(String outputMonth, String outputQualifier, List<String> months, List<String> qualifiers, Map<String, Map<String, HealthMapReportEntry>> data) { Long dataCountryCount = 0L; Long dataAdmin1Count = 0L; Long dataAdmin2Count = 0L; Long dataPreciseCount = 0L; Long locationCountryCount = 0L; Long locationAdmin1Count = 0L; Long locationAdmin2Count = 0L; Long locationPreciseCount = 0L; for (String month : months) { Map<String, HealthMapReportEntry> forMonth = data.get(month); for (String qualifier : qualifiers) { HealthMapReportEntry entry = forMonth.get(qualifier); dataCountryCount = dataCountryCount + entry.getDataCountryCount(); dataAdmin1Count = dataAdmin1Count + entry.getDataAdmin1Count(); dataAdmin2Count = dataAdmin2Count + entry.getDataAdmin2Count(); dataPreciseCount = dataPreciseCount + entry.getDataPreciseCount(); locationCountryCount = locationCountryCount + entry.getLocationCountryCount(); locationAdmin1Count = locationAdmin1Count + entry.getLocationAdmin1Count(); locationAdmin2Count = locationAdmin2Count + entry.getLocationAdmin2Count(); locationPreciseCount = locationPreciseCount + entry.getLocationPreciseCount(); } } return new HealthMapReportEntry(outputMonth, outputQualifier, dataCountryCount, dataAdmin1Count, dataAdmin2Count, dataPreciseCount, locationCountryCount, locationAdmin1Count, locationAdmin2Count, locationPreciseCount); } private List<String> getMonths() { List<String> months = new ArrayList<>(); LocalDate date = new LocalDate("2015-02-01"); while (date.isBefore(LocalDate.now())) { months.add(date.toString("YYYY-MM")); date = date.plusMonths(1); } return months; } }