nu.yona.server.admin.DashboardController.java Source code

Java tutorial

Introduction

Here is the source code for nu.yona.server.admin.DashboardController.java

Source

/*******************************************************************************
 * Copyright (c) 2017 Stichting Yona Foundation This Source Code Form is subject to the terms of the Mozilla Public License, v.
 * 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
 *******************************************************************************/
package nu.yona.server.admin;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.BuildProperties;
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 nu.yona.server.properties.YonaProperties;
import nu.yona.server.subscriptions.entities.UserAnonymizedRepository;
import nu.yona.server.subscriptions.entities.UserRepository;

@Controller
@RequestMapping(value = "/dashboard")
public class DashboardController {
    private static final LocalDate LONG_AGO = LocalDate.of(2000, 1, 1);

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserAnonymizedRepository userAnonymizedRepository;

    @Autowired
    private YonaProperties yonaProperties;

    @Autowired
    private BuildProperties buildProperties;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    @Transactional
    public String getIndexPage(Model model) {
        List<Integer> intervalEndOffsets = Arrays.asList(1, 2, 7, 14, 30, 60);
        List<HistoryInterval> intervals = determineIntervals(intervalEndOffsets);

        List<Integer> appOpenedCounts = calculateAppOpenedCounts(intervals);
        List<Integer> lastMonitoredActivityCounts = calculateLastMonitoredActivityCounts(intervals);
        model.addAttribute("maxNumOfUsers", yonaProperties.getMaxUsers());
        model.addAttribute("totalNumOfUsers", userRepository.count());
        model.addAttribute("numOfUsersWithConfirmedNumbers",
                userRepository.countByMobileNumberConfirmationCodeIsNull());
        model.addAttribute("numOfUsersWithUnconfirmedNumbers",
                userRepository.countByMobileNumberConfirmationCodeIsNotNull());
        model.addAttribute("numOfUsersWithUnconfirmedNumbersInvitedOnBuddyRequest",
                userRepository.countByMobileNumberConfirmationCodeIsNotNullAndIsCreatedOnBuddyRequest(true));
        model.addAttribute("numOfUsersWithUnconfirmedNumbersFreeSignUp",
                userRepository.countByMobileNumberConfirmationCodeIsNotNullAndIsCreatedOnBuddyRequest(false));
        model.addAttribute("appOpenedCounts", appOpenedCounts);
        model.addAttribute("appOpenedPercentages", absoluteValuesToPercentages(appOpenedCounts));
        model.addAttribute("lastMonitoredActivityCounts", lastMonitoredActivityCounts);
        model.addAttribute("lastMonitoredActivityPercentages",
                absoluteValuesToPercentages(lastMonitoredActivityCounts));
        model.addAttribute("buildNumber", buildProperties.get("buildNumber"));

        return "dashboard";
    }

    private List<HistoryInterval> determineIntervals(List<Integer> intervalEndOffsets) {
        HistoryInterval.setNextStart(LocalDate.now());
        List<HistoryInterval> intervals = intervalEndOffsets.stream().map(HistoryInterval::nextInstance)
                .collect(Collectors.toList());
        intervals.add(HistoryInterval.nextInstance(LONG_AGO));
        return intervals;
    }

    private List<Integer> calculateAppOpenedCounts(List<HistoryInterval> intervals) {
        return calculateCounts(intervals, i -> userRepository.countByAppLastOpenedDateBetween(i.end, i.start),
                userRepository.countByAppLastOpenedDateIsNull());
    }

    private List<Integer> calculateLastMonitoredActivityCounts(List<HistoryInterval> intervals) {
        return calculateCounts(intervals,
                i -> userAnonymizedRepository.countByLastMonitoredActivityDateBetween(i.end, i.start),
                userAnonymizedRepository.countByLastMonitoredActivityDateIsNull());
    }

    private List<Integer> calculateCounts(List<HistoryInterval> intervals,
            Function<HistoryInterval, Integer> countRetriever, int neverUsedCount) {
        List<Integer> appOpenedCounts = intervals.stream().map(countRetriever).collect(Collectors.toList());
        appOpenedCounts.add(neverUsedCount);

        return appOpenedCounts;
    }

    private List<Integer> absoluteValuesToPercentages(List<Integer> values) {
        int sum = values.stream().reduce(0, (a, b) -> a + b);
        return values.stream().map(v -> Math.round(((float) v) / sum * 100)).collect(Collectors.toList());
    }

    @FunctionalInterface
    private interface Getter {
        int count(LocalDate start, LocalDate end);
    }

    /**
     * This interval is used to reason backward in time, so the end is more recent than the start. Note that the interval is used
     * for "between" queries, which include both start and end, so an interval with an equal start and end date covers just one
     * day.
     */
    private static class HistoryInterval {
        final LocalDate start;
        final LocalDate end;
        private static LocalDate nextStart;

        HistoryInterval(LocalDate start, LocalDate end) {
            this.start = start;
            this.end = end;
        }

        static HistoryInterval nextInstance(int endOffset) {
            LocalDate end = LocalDate.now().minusDays(endOffset - 1L);
            return nextInstance(end);
        }

        private static HistoryInterval nextInstance(LocalDate end) {
            HistoryInterval newInterval = new HistoryInterval(nextStart, end);
            nextStart = newInterval.end.minusDays(1);
            return newInterval;
        }

        static void setNextStart(LocalDate nextStart) {
            HistoryInterval.nextStart = nextStart;
        }
    }
}