From 40593ab4d2979d2f3011ee437e2d3c6d48835f7f Mon Sep 17 00:00:00 2001 From: Celestino Bellone Date: Wed, 6 Apr 2016 20:25:22 +0200 Subject: [PATCH] #107 - download sponsor scan report (cherry picked from commit feb2957) --- .../controller/api/AttendeeApiController.java | 5 +- .../api/admin/EventApiController.java | 62 +++++++++++++++++-- .../repository/SponsorScanRepository.java | 4 ++ .../partials/event/fragment/event-header.html | 2 +- .../js/admin/ng-app/admin-application.js | 4 ++ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/main/java/alfio/controller/api/AttendeeApiController.java b/src/main/java/alfio/controller/api/AttendeeApiController.java index 9c165fa222..a6b51fee65 100644 --- a/src/main/java/alfio/controller/api/AttendeeApiController.java +++ b/src/main/java/alfio/controller/api/AttendeeApiController.java @@ -19,6 +19,7 @@ import alfio.manager.AttendeeManager; import alfio.manager.support.SponsorAttendeeData; import alfio.manager.support.TicketAndCheckInResult; +import alfio.repository.SponsorScanRepository; import alfio.util.EventUtil; import alfio.util.Wrappers; import com.fasterxml.jackson.annotation.JsonCreator; @@ -33,7 +34,6 @@ import org.springframework.web.bind.annotation.*; import java.security.Principal; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -46,7 +46,6 @@ @Log4j2 public class AttendeeApiController { - private static final ZonedDateTime DEFAULT = ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC); private final AttendeeManager attendeeManager; @Autowired @@ -86,7 +85,7 @@ public ResponseEntity> getScannedBadges(@PathVariable( ZonedDateTime start = Optional.ofNullable(StringUtils.trimToNull(from)) .map(EventUtil.JSON_DATETIME_FORMATTER::parse) .flatMap(d -> Wrappers.safeSupplier(() -> ZonedDateTime.of(LocalDateTime.from(d), ZoneOffset.UTC))) - .orElse(DEFAULT); + .orElse(SponsorScanRepository.DEFAULT_TIMESTAMP); return attendeeManager.retrieveScannedAttendees(eventShortName, principal.getName(), start).map(ResponseEntity::ok).orElse(notFound()); } diff --git a/src/main/java/alfio/controller/api/admin/EventApiController.java b/src/main/java/alfio/controller/api/admin/EventApiController.java index 0aaa2b4bf6..05470f6ac9 100644 --- a/src/main/java/alfio/controller/api/admin/EventApiController.java +++ b/src/main/java/alfio/controller/api/admin/EventApiController.java @@ -29,9 +29,8 @@ import alfio.model.modification.*; import alfio.model.transaction.PaymentProxy; import alfio.model.user.Organization; -import alfio.repository.DynamicFieldTemplateRepository; -import alfio.repository.TicketCategoryDescriptionRepository; -import alfio.repository.TicketFieldRepository; +import alfio.model.user.Role; +import alfio.repository.*; import alfio.util.ValidationResult; import alfio.util.Validator; import com.opencsv.CSVReader; @@ -39,6 +38,7 @@ import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; +import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; @@ -82,6 +82,8 @@ public class EventApiController { private final TicketHelper ticketHelper; private final DynamicFieldTemplateRepository dynamicFieldTemplateRepository; private final UserManager userManager; + private final SponsorScanRepository sponsorScanRepository; + private final TicketRepository ticketRepository; @Autowired public EventApiController(EventManager eventManager, @@ -93,7 +95,9 @@ public EventApiController(EventManager eventManager, DescriptionsLoader descriptionsLoader, TicketHelper ticketHelper, DynamicFieldTemplateRepository dynamicFieldTemplateRepository, - UserManager userManager) { + UserManager userManager, + SponsorScanRepository sponsorScanRepository, + TicketRepository ticketRepository) { this.eventManager = eventManager; this.eventStatisticsManager = eventStatisticsManager; this.i18nManager = i18nManager; @@ -104,6 +108,8 @@ public EventApiController(EventManager eventManager, this.ticketHelper = ticketHelper; this.dynamicFieldTemplateRepository = dynamicFieldTemplateRepository; this.userManager = userManager; + this.sponsorScanRepository = sponsorScanRepository; + this.ticketRepository = ticketRepository; } @ExceptionHandler(DataAccessException.class) @@ -231,7 +237,7 @@ public String unbindTickets(@PathVariable("eventName") String eventName, @PathVa @RequestMapping("/events/{eventName}/export.csv") public void downloadAllTicketsCSV(@PathVariable("eventName") String eventName, HttpServletRequest request, HttpServletResponse response, Principal principal) throws IOException { List fields = Arrays.asList(Optional.ofNullable(request.getParameterValues("fields")).orElse(new String[] {})); - Event event = Optional.ofNullable(eventManager.getSingleEvent(eventName, principal.getName())).orElseThrow(IllegalArgumentException::new); + Event event = loadEvent(eventName, principal); Map categoriesMap = eventManager.loadTicketCategories(event).stream().collect(Collectors.toMap(TicketCategory::getId, Function.identity())); ZoneId eventZoneId = event.getZoneId(); @@ -277,6 +283,52 @@ public void downloadAllTicketsCSV(@PathVariable("eventName") String eventName, H } } + @RequestMapping("/events/{eventName}/sponsor-scan/export.csv") + public void downloadSponsorScanExport(@PathVariable("eventName") String eventName, HttpServletResponse response, Principal principal) throws IOException { + Event event = loadEvent(eventName, principal); + List fields = ticketFieldRepository.findAdditionalFieldsForEvent(event.getId()); + + response.setContentType("text/csv;charset=UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + eventName + "-sponsor-scan.csv"); + + try(ServletOutputStream out = response.getOutputStream()) { + for (int marker : BOM_MARKERS) { + out.write(marker); + } + CSVWriter writer = new CSVWriter(new OutputStreamWriter(out)); + List header = new ArrayList<>(); + header.add("Username"); + header.add("Timestamp"); + header.add("Full name"); + header.add("Email"); + header.addAll(fields.stream().map(TicketFieldConfiguration::getName).collect(Collectors.toList())); + writer.writeNext(header.toArray(new String[header.size()])); + userManager.findAllUsers(principal.getName()).stream() + .map(u -> Pair.of(u, userManager.getUserRole(u))) + .filter(p -> p.getRight() == Role.SPONSOR) + .flatMap(p -> sponsorScanRepository.loadSponsorData(event.getId(), p.getKey().getId(), SponsorScanRepository.DEFAULT_TIMESTAMP) + .stream() + .map(v -> Pair.of(v, ticketFieldRepository.findAllValuesForTicketId(v.getTicket().getId())))) + .map(p -> { + DetailedScanData data = p.getLeft(); + Map descriptions = p.getRight(); + return Pair.of(data, fields.stream().map(x -> descriptions.getOrDefault(x, "")).collect(Collectors.toList())); + }).map(p -> { + List line = new ArrayList<>(); + Ticket ticket = p.getLeft().getTicket(); + SponsorScan sponsorScan = p.getLeft().getSponsorScan(); + line.add(userManager.findUser(sponsorScan.getUserId()).getUsername()); + line.add(sponsorScan.getTimestamp().toString()); + line.add(ticket.getFullName()); + line.add(ticket.getEmail()); + line.addAll(p.getRight()); + return line.toArray(new String[line.size()]); + }).forEachOrdered(writer::writeNext); + writer.flush(); + out.flush(); + } + } + @RequestMapping("/events/{eventName}/fields") public List getAllFields(@PathVariable("eventName") String eventName) { List fields = new ArrayList<>(); diff --git a/src/main/java/alfio/repository/SponsorScanRepository.java b/src/main/java/alfio/repository/SponsorScanRepository.java index a29bb11bfe..dccfb1b4c5 100644 --- a/src/main/java/alfio/repository/SponsorScanRepository.java +++ b/src/main/java/alfio/repository/SponsorScanRepository.java @@ -21,6 +21,8 @@ import ch.digitalfondue.npjt.Query; import ch.digitalfondue.npjt.QueryRepository; +import java.time.Instant; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; @@ -28,6 +30,8 @@ @QueryRepository public interface SponsorScanRepository { + ZonedDateTime DEFAULT_TIMESTAMP = ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC); + @Query("select creation from sponsor_scan where user_id = :userId and event_id = :eventId and ticket_id = :ticketId") Optional getRegistrationTimestamp(@Bind("userId") int userId, @Bind("eventId") int eventId, @Bind("ticketId") int ticketId); diff --git a/src/main/webapp/resources/angular-templates/admin/partials/event/fragment/event-header.html b/src/main/webapp/resources/angular-templates/admin/partials/event/fragment/event-header.html index 088c0afb56..199a6ee254 100644 --- a/src/main/webapp/resources/angular-templates/admin/partials/event/fragment/event-header.html +++ b/src/main/webapp/resources/angular-templates/admin/partials/event/fragment/event-header.html @@ -3,9 +3,9 @@

{{event.displayName}} {{event.shortDescription}}

diff --git a/src/main/webapp/resources/js/admin/ng-app/admin-application.js b/src/main/webapp/resources/js/admin/ng-app/admin-application.js index 0095d29e2e..35c9a2940f 100644 --- a/src/main/webapp/resources/js/admin/ng-app/admin-application.js +++ b/src/main/webapp/resources/js/admin/ng-app/admin-application.js @@ -762,6 +762,10 @@ }); }; + $scope.downloadSponsorsScan = function() { + $window.open($window.location.pathname+"/api/events/"+parentScope.event.shortName+"/sponsor-scan/export.csv"); + }; + $scope.prepareFieldDescriptionEdit = function(baseObject, field) { angular.copy(field, baseObject); };