FundingByLocationController extends GenericOCDSController { @ApiOperation(value = "Planned funding by location by year. Returns the total amount of planning.budget" + " available per planning.budget.projectLocation, grouped by year. " + "This will return full location information, including geocoding data." + "Responds only to the procuring entity id filter: tender.procuringEntity._id") @RequestMapping(value = "/api/plannedFundingByLocation", method = { RequestMethod.POST, RequestMethod.GET }, produces = "application/json") public List<DBObject> plannedFundingByLocation(@ModelAttribute @Valid final YearFilterPagingRequest filter) { DBObject vars = new BasicDBObject(); vars.put("numberOfLocations", new BasicDBObject("$size", "$planning.budget.projectLocation")); vars.put("planningBudget", "$planning.budget.amount.amount"); DBObject in = new BasicDBObject("$divide", Arrays.asList("$$planningBudget", "$$numberOfLocations")); DBObject let = new BasicDBObject(); let.put("vars", vars); let.put("in", in); DBObject dividedTotal = new BasicDBObject("$let", let); DBObject project = new BasicDBObject(); project.put("planning.budget.projectLocation", 1); project.put("cntprj", new BasicDBObject("$literal", 1)); project.put("planning.budget.amount.amount", 1); project.put("dividedTotal", dividedTotal); addYearlyMonthlyProjection(filter, project, "$planning.bidPlanProjectDateApprove"); Aggregation agg = newAggregation( match(where("planning").exists(true).and("planning.budget.projectLocation.0").exists(true) .andOperator(getProcuringEntityIdCriteria(filter), getYearFilterCriteria(filter, "planning.bidPlanProjectDateApprove"))), new CustomProjectionOperation(project), unwind("$planning.budget.projectLocation"), match(where("planning.budget.projectLocation.geometry.coordinates.0").exists(true)), project(getYearlyMonthlyGroupingFields(filter)) .and("dividedTotal").as("dividedTotal") .and("cntprj").as("cntprj") .and("planning.budget.projectLocation").as("projectLocation"), group(getYearlyMonthlyGroupingFields(filter, Keys.PLANNING_BUDGET_PROJECTLOCATION)) .sum("dividedTotal").as(Keys.TOTAL_PLANNED_AMOUNT) .sum("cntprj").as(Keys.RECORDS_COUNT), getSortByYearMonth(filter) ); AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, "release", DBObject.class); List<DBObject> tagCount = results.getMappedResults(); return tagCount; }  @ApiOperation(value = "Total estimated funding (tender.value) grouped by " + "tender.items.deliveryLocation and also grouped by year." + " The endpoint also returns the count of tenders for each location. " + "It responds to all filters. The year is calculated based on tender.tenderPeriod.startDate") @RequestMapping(value = "/api/fundingByTenderDeliveryLocation", method = { RequestMethod.POST, RequestMethod.GET }, produces = "application/json") List<DBObject> fundingByTenderDeliveryLocation(
            @ModelAttribute @Valid final YearFilterPagingRequest filter); @ApiOperation("Calculates percentage of releases with tender with at least one specified delivery location," + " that is the array tender.items.deliveryLocation has to have items." + "Filters out stub tenders, therefore tender.tenderPeriod.startDate has to exist.") @RequestMapping(value = "/api/qualityFundingByTenderDeliveryLocation", method = { RequestMethod.POST, RequestMethod.GET }, produces = "application/json") List<DBObject> qualityFundingByTenderDeliveryLocation(
            @ModelAttribute @Valid final YearFilterPagingRequest filter); @ApiOperation(value = "Planned funding by location by year. Returns the total amount of planning.budget" + " available per planning.budget.projectLocation, grouped by year. " + "This will return full location information, including geocoding data." + "Responds only to the procuring entity id filter: tender.procuringEntity._id") @RequestMapping(value = "/api/plannedFundingByLocation", method = { RequestMethod.POST, RequestMethod.GET }, produces = "application/json") List<DBObject> plannedFundingByLocation(@ModelAttribute @Valid final YearFilterPagingRequest filter); @ApiOperation("Calculates percentage of releases with planning with at least one specified location," + " that is the array planning.budget.projectLocation has to be initialzied." + "Filters out stub planning, therefore planning.budget.amount has to exist." + "Responds only to the procuring entity id filter: tender.procuringEntity._id") @RequestMapping(value = "/api/qualityPlannedFundingByLocation", method = { RequestMethod.POST, RequestMethod.GET }, produces = "application/json") List<DBObject> qualityPlannedFundingByLocation(
            @ModelAttribute @Valid final DefaultFilterPagingRequest filter);  }
@Test public void plannedFundingByLocation() throws Exception { final List<DBObject> plannedFundingByLocation = fundingByLocationController .plannedFundingByLocation(new YearFilterPagingRequest()); final DBObject first = plannedFundingByLocation.get(0); int year = (int) first.get(FundingByLocationController.Keys.YEAR); BasicDBObject projectLocation = (BasicDBObject) first .get(FundingByLocationController.Keys.PLANNING_BUDGET_PROJECTLOCATION); BasicDBObject geometry = (BasicDBObject) projectLocation.get("geometry"); String geometryType = (String) geometry.get("type"); List<Double> coordinates = (List<Double>) geometry.get("coordinates"); double totalPlannedAmount = (double) first.get(FundingByLocationController.Keys.TOTAL_PLANNED_AMOUNT); Assert.assertEquals(2016, year); Assert.assertEquals("Point", geometryType); Assert.assertEquals(new ArrayList<>(Arrays.asList(47.411, 28.36)), coordinates); Assert.assertEquals(1000.0, totalPlannedAmount, 0); }