// Copyright © 2015 HSL <https://www.hsl.fi>
// This program is dual-licensed under the EUPL v1.2 and AGPLv3 licenses.
package fi.hsl.parkandride.core.service.reporting;
import fi.hsl.parkandride.back.RegionRepository;
import fi.hsl.parkandride.core.back.UtilizationRepository;
import fi.hsl.parkandride.core.domain.*;
import fi.hsl.parkandride.core.service.*;
import org.joda.time.LocalDate;
import javax.inject.Inject;
import java.util.Iterator;
import java.util.Set;
import static com.google.common.collect.Iterators.filter;
import static fi.hsl.parkandride.core.domain.Permission.REPORT_GENERATE;
import static fi.hsl.parkandride.core.service.AuthenticationService.authorize;
import static fi.hsl.parkandride.core.service.AuthenticationService.getLimitedOperatorId;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toSet;
import static org.springframework.util.CollectionUtils.isEmpty;
public abstract class AbstractReportService extends ReportServiceSupport implements ReportService {
private final String reportName;
@Inject
ExcelUtil excelUtil;
protected AbstractReportService(String reportName, FacilityService facilityService, OperatorService operatorService, ContactService contactService, HubService hubService, UtilizationRepository utilizationRepository, TranslationService translationService, RegionRepository regionRepository, FacilityHistoryService facilityHistoryService) {
super(facilityService, operatorService, contactService, hubService, utilizationRepository, translationService, regionRepository, facilityHistoryService);
this.reportName = reportName;
}
/**
* Generates the Excel report and converts it to bytes.
* Verifies that the current user has permission to generate reports.
* Do not override.
*/
@Override
@TransactionalRead
public byte[] generateReport(User currentUser, ReportParameters reportParameters) {
authorize(currentUser, REPORT_GENERATE);
ReportContext ctx = new ReportContext(this, getLimitedOperatorId(currentUser));
return generateReport(ctx, reportParameters).toBytes();
}
/**
* Get the name of the report this service produces.
*/
@Override
public String reportName() {
return reportName;
}
protected abstract Excel generateReport(ReportContext reportContext, ReportParameters params);
protected final UtilizationSearch toUtilizationSearch(ReportParameters parameters, final ReportContext ctx) {
UtilizationSearch search = new UtilizationSearch();
search.start = parameters.startDate.toDateTimeAtStartOfDay();
if (parameters.endDate == null) {
search.end = new LocalDate().plusDays(1).toDateTimeAtStartOfDay().millisOfDay().withMaximumValue();
} else {
search.end = parameters.endDate.toDateTimeAtStartOfDay().millisOfDay().withMaximumValue();
}
if (!isEmpty(parameters.capacityTypes)) {
search.capacityTypes = parameters.capacityTypes;
}
if (!isEmpty(parameters.usages)) {
search.usages = parameters.usages;
}
boolean emptyResults = false;
if (!isEmpty(parameters.hubs)) {
Set<Long> facilityIds = parameters.hubs.stream().map(hubId -> ctx.facilitiesByHubId.getOrDefault(hubId, emptyList())).flatMap(ids -> ids.stream()).map(f -> f.id).collect(toSet());
emptyResults |= facilityIds.isEmpty();
search.facilityIds.addAll(facilityIds);
}
if (!isEmpty(parameters.operators)) {
Set<Long> facilityIds = parameters.operators.stream().map(oprId -> ctx.facilityIdsByOperatorId.getOrDefault(oprId, emptyList())).flatMap(ids -> ids.stream()).collect(toSet());
emptyResults |= facilityIds.isEmpty();
search.facilityIds.addAll(facilityIds);
}
if (emptyResults) {
search.facilityIds.clear();
search.facilityIds.add(-1L);
} else if (ctx.allowedOperatorId != null) {
if (isEmpty(parameters.facilities)) {
// only fetch facilities that the user is allowed to see
search.facilityIds = ctx.facilities.keySet();
} else {
// remove all facilities from search that the user is not allowed to see
parameters.facilities.retainAll(ctx.facilities.keySet());
}
}
return search;
}
protected Iterator<Utilization> addFilters(Iterator<Utilization> iter, ReportContext ctx, ReportParameters parameters) {
if (ctx.allowedOperatorId != null) {
iter = filter(iter, u -> ctx.facilities.containsKey(u.facilityId));
}
if (!isEmpty(parameters.operators)) {
iter = filter(iter, u -> parameters.operators.contains(ctx.facilities.get(u.facilityId).operatorId));
}
if (!isEmpty(parameters.hubs)) {
iter = filter(iter, u -> ctx.hubsByFacilityId.getOrDefault(u.facilityId, emptyList()).stream().filter(h -> parameters.hubs.contains(h.id)).findFirst().isPresent());
}
if (!isEmpty(parameters.regions)) {
iter = filter(iter, u -> parameters.regions.contains(ctx.regionByFacilityId.get(u.facilityId).id));
}
if (!isEmpty(parameters.facilities)) {
iter = filter(iter, u -> parameters.facilities.contains(u.facilityId));
}
return iter;
}
static class BasicUtilizationReportKey {
CapacityType capacityType;
Usage usage;
Long targetId;
public BasicUtilizationReportKey() {
}
public BasicUtilizationReportKey(Utilization u) {
capacityType = u.capacityType;
usage = u.usage;
targetId = u.facilityId;
}
@Override
public int hashCode() {
return capacityType.hashCode() ^ targetId.hashCode() ^ usage.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
BasicUtilizationReportKey other = (BasicUtilizationReportKey) obj;
if (capacityType != other.capacityType)
return false;
if (usage != other.usage)
return false;
if (!targetId.equals(other.targetId))
return false;
return true;
}
}
}