/**
* This file is part of alf.io.
*
* alf.io is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* alf.io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.manager;
import alfio.manager.user.UserManager;
import alfio.model.*;
import alfio.model.modification.EventWithStatistics;
import alfio.model.modification.TicketCategoryWithStatistic;
import alfio.model.modification.TicketWithStatistic;
import alfio.repository.*;
import alfio.util.EventUtil;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import static alfio.util.OptionalWrapper.optionally;
import static java.util.stream.Collectors.toList;
@Component
public class EventStatisticsManager {
private final EventRepository eventRepository;
private final EventDescriptionRepository eventDescriptionRepository;
private final TicketRepository ticketRepository;
private final TicketCategoryRepository ticketCategoryRepository;
private final TicketCategoryDescriptionRepository ticketCategoryDescriptionRepository;
private final TicketReservationRepository ticketReservationRepository;
private final SpecialPriceRepository specialPriceRepository;
private final UserManager userManager;
private final TransactionRepository transactionRepository;
@Autowired
public EventStatisticsManager(EventRepository eventRepository,
EventDescriptionRepository eventDescriptionRepository,
TicketRepository ticketRepository,
TicketCategoryRepository ticketCategoryRepository,
TicketCategoryDescriptionRepository ticketCategoryDescriptionRepository,
TicketReservationRepository ticketReservationRepository,
SpecialPriceRepository specialPriceRepository,
UserManager userManager,
TransactionRepository transactionRepository) {
this.eventRepository = eventRepository;
this.eventDescriptionRepository = eventDescriptionRepository;
this.ticketRepository = ticketRepository;
this.ticketCategoryRepository = ticketCategoryRepository;
this.ticketCategoryDescriptionRepository = ticketCategoryDescriptionRepository;
this.ticketReservationRepository = ticketReservationRepository;
this.specialPriceRepository = specialPriceRepository;
this.userManager = userManager;
this.transactionRepository = transactionRepository;
}
EventWithStatistics fillWithStatistics(Event event) {
return new EventWithStatistics(event, eventDescriptionRepository.findByEventId(event.getId()), loadTicketCategoriesWithStats(event), ticketRepository.countReleasedTickets(event.getId()));
}
private List<Event> getAllEvents(String username) {
return userManager.findUserOrganizations(username)
.parallelStream()
.flatMap(o -> eventRepository.findByOrganizationId(o.getId()).stream())
.collect(Collectors.toList());
}
public List<EventStatistic> getAllEventsWithStatisticsFilteredBy(String username, Predicate<Event> predicate) {
List<Event> events = getAllEvents(username).stream().filter(predicate).collect(toList());
Map<Integer, Event> mappedEvent = events.stream().collect(Collectors.toMap(Event::getId, Function.identity()));
if(!mappedEvent.isEmpty()) {
List<EventStatisticView> stats = eventRepository.findStatisticsFor(mappedEvent.keySet());
return stats.stream().map(stat -> new EventStatistic(mappedEvent.get(stat.getEventId()), stat)).sorted().collect(Collectors.toList());
} else {
return Collections.emptyList();
}
}
@Cacheable
public List<EventStatistic> getAllEventsWithStatistics(String username) {
return getAllEventsWithStatisticsFilteredBy(username, (e) -> true);
}
TicketCategoryWithStatistic loadTicketCategoryWithStats(int categoryId, Event event) {
final TicketCategory tc = ticketCategoryRepository.getById(categoryId, event.getId());
return new TicketCategoryWithStatistic(tc,
loadModifiedTickets(event.getId(), tc.getId()),
specialPriceRepository.findAllByCategoryId(tc.getId()),
event,
descriptionForTicketCategory(tc.getId()));
}
private Map<String, String> descriptionForTicketCategory(int ticketCategory) {
return ticketCategoryDescriptionRepository.findByTicketCategoryId(ticketCategory).stream().collect(Collectors.toMap(TicketCategoryDescription::getLocale, TicketCategoryDescription::getDescription));
}
public List<TicketCategoryWithStatistic> loadTicketCategoriesWithStats(Event event) {
return loadTicketCategories(event).stream()
.map(tc -> new TicketCategoryWithStatistic(tc, loadModifiedTickets(tc.getEventId(), tc.getId()),
specialPriceRepository.findAllByCategoryId(tc.getId()), event,
descriptionForTicketCategory(tc.getId())))
.sorted()
.collect(toList());
}
public List<TicketCategory> loadTicketCategories(Event event) {
return ticketCategoryRepository.findByEventId(event.getId());
}
public EventWithStatistics getSingleEventWithStatistics(String eventName, String username) {
return fillWithStatistics(getSingleEvent(eventName, username));
}
private Event getSingleEvent(String eventName, String username) {
final Event event = eventRepository.findByShortName(eventName);
checkOwnership(event, username, event.getOrganizationId());
return event;
}
private void checkOwnership(Event event, String username, int organizationId) {
Validate.isTrue(organizationId == event.getOrganizationId(), "invalid organizationId");
userManager.findUserOrganizations(username)
.stream()
.filter(o -> o.getId() == organizationId)
.findAny()
.orElseThrow(IllegalArgumentException::new);
}
private List<TicketWithStatistic> loadModifiedTickets(int eventId, int categoryId) {
Event event = eventRepository.findById(eventId);
return ticketRepository.findAllModifiedTickets(eventId, categoryId).stream()
.map(t -> new TicketWithStatistic(t, event, ticketReservationRepository.findReservationById(t.getTicketsReservationId()),
event.getZoneId(), optionally(() -> transactionRepository.loadByReservationId(t.getTicketsReservationId()))))
.sorted()
.collect(Collectors.toList());
}
public Predicate<Event> noSeatsAvailable() {
return event -> {
EventWithStatistics eventWithStatistics = fillWithStatistics(event);
return eventWithStatistics.getTicketCategories().stream().allMatch(tc -> EventUtil.determineAvailableSeats(tc, eventWithStatistics) == 0);
};
}
}