<commit_before>/*
 * Copyright (c) 2022 by <NAME> under the MIT License (MIT).
 * See project LICENSE file for the detailed terms and conditions.
 */

package de.bluewhale.sabi.webclient.controller;

import de.bluewhale.sabi.exception.BusinessException;
import de.bluewhale.sabi.model.AquariumTo;
import de.bluewhale.sabi.model.MeasurementTo;
import de.bluewhale.sabi.model.ParameterTo;
import de.bluewhale.sabi.model.UnitTo;
import de.bluewhale.sabi.webclient.CDIBeans.UserSession;
import de.bluewhale.sabi.webclient.apigateway.MeasurementService;
import de.bluewhale.sabi.webclient.apigateway.TankService;
import de.bluewhale.sabi.webclient.utils.MessageUtil;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.primefaces.model.charts.ChartData;
import org.primefaces.model.charts.line.LineChartDataSet;
import org.primefaces.model.charts.line.LineChartModel;
import org.primefaces.model.charts.line.LineChartOptions;
import org.primefaces.model.charts.optionconfig.title.Title;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.annotation.SessionScope;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Controller for the Report View as shown in reportView.xhtml
 *
 * @author <NAME>
 */
@Named
@SessionScope
@Slf4j
@Getter
@Setter
public class ReportView extends AbstractControllerTools implements Serializable {

    // Limit drawn graph to avoid steady visual narrowing of target range by global minimum or maximum measurements
    final static int MAX_GRAPH_RECORDS = 14;
    private static final String REPORT_VIEW_PAGE = "reportView";
    @Autowired
    TankService tankService;

    @Autowired
    MeasurementService measurementService;

    @Inject
    UserSession userSession;

    // View Modell
    private List<AquariumTo> tanks;
    private List<UnitTo> knownUnits;
    private Long selectedTankId;
    private Integer selectedUnitId;
    private LineChartModel lineModel_recent_measuredPoints;
    private LineChartModel lineModel90d;
    private LineChartModel lineModel365d;
    private List<MeasurementTo> measurementTos;


    public List<MeasurementTo> getDescSortedMeasurementTos() {
        if (measurementTos != null) {
            return measurementTos.stream().sorted(Comparator.comparing(MeasurementTo::getMeasuredOn).reversed()).collect(Collectors.toList());
        } else {
            return new ArrayList<MeasurementTo>();
        }
    }

    public String requestData() {

        if (selectedTankId == null || selectedUnitId == null) {
            MessageUtil.info("messages", "common.no_such_data.t", userSession.getLocale());
            return REPORT_VIEW_PAGE;
        }

        try {
            measurementTos = measurementService.getMeasurementsForUsersTankFilteredByUnit(userSession.getSabiBackendToken(), selectedTankId, selectedUnitId);

            if (measurementTos == null || measurementTos.isEmpty()) {
                return REPORT_VIEW_PAGE;
            }

            // Ensure that measured points are sorted
            Comparator<MeasurementTo> measuredOnComperator = Comparator.comparing(MeasurementTo::getMeasuredOn);
            Collections.sort(measurementTos, measuredOnComperator);

            // reinitialize lineModel, old Data free for GC
            lineModel_recent_measuredPoints = new LineChartModel();
            lineModel90d = new LineChartModel();
            lineModel365d = new LineChartModel();

            //Options
            LineChartOptions options = new LineChartOptions();
            Title title = new Title();
            title.setDisplay(true);
            String chartTitle = String.format(MessageUtil.getFromMessageProperties("reportview.chart.h", userSession.getLocale()),
                    getTankNameForId(selectedTankId, tanks));
            title.setText(chartTitle);
            options.setTitle(title);

            lineModel_recent_measuredPoints.setOptions(options);
            lineModel90d.setOptions(options);
            lineModel365d.setOptions(options);

            LineChartDataSet dataSet_recent_measuredPoints = new LineChartDataSet();
            List<Object> values_recent_measuredPoints = new ArrayList<>();
            List<String> labels_recent_measuredPoints = new ArrayList<>();

            LineChartDataSet dataSet_last90days = new LineChartDataSet();
            List<Object> values_last90days = new ArrayList<>();
            List<String> labels_last90days = new ArrayList<>();

            LineChartDataSet dataSet_last365days = new LineChartDataSet();
            List<Object> values_last365days = new ArrayList<>();
            List<String> labels_last365days = new ArrayList<>();

            // Slice available data to to display in charts. As the source set has been presorted we don't need to sort again.
            List<MeasurementTo> measurementTos90d = measurementTos.stream().filter(item -> item.getMeasuredOn().isAfter(LocalDateTime.now().minusDays(90l))).collect(Collectors.toList());
            List<MeasurementTo> measurementTos365d = measurementTos.stream().filter(item -> item.getMeasuredOn().isAfter(LocalDateTime.now().minusDays(365l))).collect(Collectors.toList());

            // =======================================================================================
            // Graph Modell for last recent MAX_GRAPH_RECORDS measured points, with interpolated lines
            // =======================================================================================
            int size = measurementTos.size();
            for (MeasurementTo measurement : measurementTos) {
                // we want to display only the lastest MAX_GRAPH_RECORDS of the list
                if (size == 0) {
                    break;
                }
                if (size <= MAX_GRAPH_RECORDS) {
                    values_recent_measuredPoints.add(measurement.getMeasuredValue());
                    labels_recent_measuredPoints.add(measurement.getMeasuredOn().toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
                }
                size--;
            }

            populteDataSet(dataSet_recent_measuredPoints,values_recent_measuredPoints,labels_recent_measuredPoints,lineModel_recent_measuredPoints,true);

            // =============================================================================
            // Graph Modell for last 90 days, lines not connected if we have null value gaps
            // =============================================================================

            // LocalDate startAYearBack = LocalDate.now().minusDays(365l);
            for (int dayOffset = 90; dayOffset > 0; dayOffset--) {

                LocalDate date = LocalDate.now().minusDays(dayOffset);
                int dayOfMonth = date.getDayOfMonth();

                // Lable start of each month
                if (dayOfMonth == 1) {
                    String monthi18nDisplayName = date.getMonth().getDisplayName(TextStyle.FULL, userSession.getLocale());
                    labels_last90days.add(monthi18nDisplayName);
                } else {
                    labels_last90days.add(Integer.toString(dayOfMonth));
                }

                // Add a value if one exists for the day (we take only the first value for given date)
                Optional<MeasurementTo> optMeasurementTo = measurementTos90d.stream().filter(item -> item.getMeasuredOn().toLocalDate().isEqual(date)).findFirst();
                if (optMeasurementTo.isPresent()) {
                    values_last90days.add(optMeasurementTo.get().getMeasuredValue());
                } else {
                    values_last90days.add(null);
                }
            }

            populteDataSet(dataSet_last90days,values_last90days,labels_last90days,lineModel90d, false);

            // =============================================================================
            // Graph Modell for last 365 days, lines not connected if we have null value gaps
            // =============================================================================

            // LocalDate startAYearBack = LocalDate.now().minusDays(365l);
            for (int dayOffset = 365; dayOffset > 0; dayOffset--) {

                LocalDate date = LocalDate.now().minusDays(dayOffset);
                int dayOfMonth = date.getDayOfMonth();

                // Lable start of each month
                // because of https://github.com/primefaces/primefaces/issues/9339
                // I choosed to use the lable on two subsequent datapoints to make
                // sure that the smart chartJS render algorithm prints at least one of them in this case
                if (dayOfMonth < 8) {
                    String monthi18nDisplayName = date.getMonth().getDisplayName(TextStyle.FULL, userSession.getLocale());
                    String shortYear = " '"+Integer.toString(date.getYear() - 2000);
                    labels_last365days.add(monthi18nDisplayName+shortYear);
                } else {
                    labels_last365days.add(Strings.EMPTY);
                }

                // Add a value if one exists for the day (we take only the first value for given date)
                Optional<MeasurementTo> optMeasurementTo = measurementTos365d.stream().filter(item -> item.getMeasuredOn().toLocalDate().isEqual(date)).findFirst();
                if (optMeasurementTo.isPresent()) {
                    values_last365days.add(optMeasurementTo.get().getMeasuredValue());
                } else {
                    values_last365days.add(null);
                }<commit_msg>bugfix branch contained only the half solution.<commit_after>/*
 * Copyright (c) 2022 by <NAME> under the MIT License (MIT).
 * See project LICENSE file for the detailed terms and conditions.
 */

package de.bluewhale.sabi.webclient.controller;

import de.bluewhale.sabi.exception.BusinessException;
import de.bluewhale.sabi.model.AquariumTo;
import de.bluewhale.sabi.model.MeasurementTo;
import de.bluewhale.sabi.model.ParameterTo;
import de.bluewhale.sabi.model.UnitTo;
import de.bluewhale.sabi.webclient.CDIBeans.UserSession;
import de.bluewhale.sabi.webclient.apigateway.MeasurementService;
import de.bluewhale.sabi.webclient.apigateway.TankService;
import de.bluewhale.sabi.webclient.utils.MessageUtil;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.primefaces.model.charts.ChartData;
import org.primefaces.model.charts.line.LineChartDataSet;
import org.primefaces.model.charts.line.LineChartModel;
import org.primefaces.model.charts.line.LineChartOptions;
import org.primefaces.model.charts.optionconfig.title.Title;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.annotation.SessionScope;

import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Controller for the Report View as shown in reportView.xhtml
 *
 * @author <NAME>
 */
@Named
@SessionScope
@Slf4j
@Getter
@Setter
public class ReportView extends AbstractControllerTools implements Serializable {

    // Limit drawn graph to avoid steady visual narrowing of target range by global minimum or maximum measurements
    final static int MAX_GRAPH_RECORDS = 14;
    private static final String REPORT_VIEW_PAGE = "reportView";
    @Autowired
    TankService tankService;

    @Autowired
    MeasurementService measurementService;

    @Inject
    UserSession userSession;

    // View Modell
    private List<AquariumTo> tanks;
    private List<UnitTo> knownUnits;
    private Long selectedTankId;
    private Integer selectedUnitId;
    private LineChartModel lineModel_recent_measuredPoints;
    private LineChartModel lineModel90d;
    private LineChartModel lineModel365d;
    private List<MeasurementTo> measurementTos;


    public List<MeasurementTo> getDescSortedMeasurementTos() {
        if (measurementTos != null) {
            return measurementTos.stream().sorted(Comparator.comparing(MeasurementTo::getMeasuredOn).reversed()).collect(Collectors.toList());
        } else {
            return new ArrayList<MeasurementTo>();
        }
    }

    public String requestData() {

        if (selectedTankId == null || selectedUnitId == null) {
            MessageUtil.info("messages", "common.no_such_data.t", userSession.getLocale());
            return REPORT_VIEW_PAGE;
        }

        try {
            measurementTos = measurementService.getMeasurementsForUsersTankFilteredByUnit(userSession.getSabiBackendToken(), selectedTankId, selectedUnitId);

            if (measurementTos == null || measurementTos.isEmpty()) {
                return REPORT_VIEW_PAGE;
            }

            // Ensure that measured points are sorted
            Comparator<MeasurementTo> measuredOnComperator = Comparator.comparing(MeasurementTo::getMeasuredOn);
            Collections.sort(measurementTos, measuredOnComperator);

            // reinitialize lineModel, old Data free for GC
            lineModel_recent_measuredPoints = new LineChartModel();
            lineModel90d = new LineChartModel();
            lineModel365d = new LineChartModel();

            //Options
            LineChartOptions options = new LineChartOptions();
            Title title = new Title();
            title.setDisplay(true);
            String chartTitle = String.format(MessageUtil.getFromMessageProperties("reportview.chart.h", userSession.getLocale()),
                    getTankNameForId(selectedTankId, tanks));
            title.setText(chartTitle);
            options.setTitle(title);

            lineModel_recent_measuredPoints.setOptions(options);
            lineModel90d.setOptions(options);
            lineModel365d.setOptions(options);

            LineChartDataSet dataSet_recent_measuredPoints = new LineChartDataSet();
            List<Object> values_recent_measuredPoints = new ArrayList<>();
            List<String> labels_recent_measuredPoints = new ArrayList<>();

            LineChartDataSet dataSet_last90days = new LineChartDataSet();
            List<Object> values_last90days = new ArrayList<>();
            List<String> labels_last90days = new ArrayList<>();

            LineChartDataSet dataSet_last365days = new LineChartDataSet();
            List<Object> values_last365days = new ArrayList<>();
            List<String> labels_last365days = new ArrayList<>();

            // Slice available data to to display in charts. As the source set has been presorted we don't need to sort again.
            List<MeasurementTo> measurementTos90d = measurementTos.stream().filter(item -> item.getMeasuredOn().isAfter(LocalDateTime.now().minusDays(90l))).collect(Collectors.toList());
            List<MeasurementTo> measurementTos365d = measurementTos.stream().filter(item -> item.getMeasuredOn().isAfter(LocalDateTime.now().minusDays(365l))).collect(Collectors.toList());

            // =======================================================================================
            // Graph Modell for last recent MAX_GRAPH_RECORDS measured points, with interpolated lines
            // =======================================================================================
            int size = measurementTos.size();
            for (MeasurementTo measurement : measurementTos) {
                // we want to display only the lastest MAX_GRAPH_RECORDS of the list
                if (size == 0) {
                    break;
                }
                if (size <= MAX_GRAPH_RECORDS) {
                    values_recent_measuredPoints.add(measurement.getMeasuredValue());
                    labels_recent_measuredPoints.add(measurement.getMeasuredOn().toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
                }
                size--;
            }

            populteDataSet(dataSet_recent_measuredPoints,values_recent_measuredPoints,labels_recent_measuredPoints,lineModel_recent_measuredPoints,true);

            // =============================================================================
            // Graph Modell for last 90 days, lines not connected if we have null value gaps
            // =============================================================================

            // Lable start of each month
            // As of https://github.com/primefaces/primefaces/issues/9339
            // I chose to use the lable on several subsequent datapoints to make
            // sure that the smart chartJS render algorithm prints at least one of them in this case
            for (int dayOffset = 90; dayOffset > 0; dayOffset--) {

                LocalDate date = LocalDate.now().minusDays(dayOffset);
                int dayOfMonth = date.getDayOfMonth();

                // Lable start of each month
                if (dayOfMonth < 3) {
                    String monthi18nDisplayName = date.getMonth().getDisplayName(TextStyle.FULL, userSession.getLocale());
                    labels_last90days.add(monthi18nDisplayName);
                } else {
                    labels_last90days.add(Integer.toString(dayOfMonth));
                }

                // Add a value if one exists for the day (we take only the first value for given date)
                Optional<MeasurementTo> optMeasurementTo = measurementTos90d.stream().filter(item -> item.getMeasuredOn().toLocalDate().isEqual(date)).findFirst();
                if (optMeasurementTo.isPresent()) {
                    values_last90days.add(optMeasurementTo.get().getMeasuredValue());
                } else {
                    values_last90days.add(null);
                }
            }

            populteDataSet(dataSet_last90days,values_last90days,labels_last90days,lineModel90d, false);

            // =============================================================================
            // Graph Modell for last 365 days, lines not connected if we have null value gaps
            // =============================================================================

            for (int dayOffset = 365; dayOffset > 0; dayOffset--) {

                LocalDate date = LocalDate.now().minusDays(dayOffset);
                int dayOfMonth = date.getDayOfMonth();

                // Lable start of each month
                // As of https://github.com/primefaces/primefaces/issues/9339
                // I chose to use the lable on several subsequent datapoints to make
                // sure that the smart chartJS render algorithm prints at least one of them in this case
                if (dayOfMonth < 8) {
                    String monthi18nDisplayName = date.getMonth().getDisplayName(TextStyle.FULL, userSession.getLocale());
                    String shortYear = " '"+Integer.toString(date.getYear() - 2000);
                    labels_last365days.add(monthi18nDisplayName+shortYear);
                } else {
                    labels_last365days.add(Strings.EMPTY);
                }

                // Add a value if one exists for the day (we take only the first value for given date)
                Optional<MeasurementTo> optMeasurementTo = measurementTos365d.stream().filter(item -> item.getMeasuredOn().toLocalDate().isEqual(date)).findFirst();
                if (optMeasurementTo.isPresent()) {
                    values_last365days.add(optMeasurementTo.get().getMeasuredValue());
                } else {
                    values_last365days.add(null);
                }
{'fraction_non_alphanumeric': 0.13267277681446618, 'fraction_numerical': 0.011790933861778548, 'mean_word_length': 2.819489120151372, 'pattern_counts': {'":': 0, '<': 56, '<?xml version=': 0, '>': 63, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5399739', 'n_tokens_mistral': 5454, 'n_tokens_neox': 5000, 'n_words': 1122}