com.aurel.track.report.dashboard.AverageTimeToCloseItem.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.report.dashboard.AverageTimeToCloseItem.java

Source

/**
 * Genji Scrum Tool and Issue Tracker
 * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions
    
 * <a href="http://www.trackplus.com">Genji Scrum Tool</a>
 *
 * This program 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.
 *
 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id:$ */

package com.aurel.track.report.dashboard;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.joda.time.DateTime;
import org.joda.time.Days;

import com.aurel.track.Constants;
import com.aurel.track.admin.customize.category.filter.execute.FilterExecuterFacade;
import com.aurel.track.admin.customize.category.filter.execute.loadItems.FilterUpperConfigUtil;
import com.aurel.track.admin.customize.category.filter.execute.loadItems.LoadItemIDListItems;
import com.aurel.track.admin.customize.category.filter.execute.loadItems.LoadTreeFilterItems;
import com.aurel.track.admin.customize.category.filter.execute.loadItems.ReportBeanLoader;
import com.aurel.track.admin.customize.category.filter.execute.loadItems.TooManyItemsToLoadException;
import com.aurel.track.admin.customize.category.filter.tree.design.FilterUpperTO;
import com.aurel.track.admin.customize.lists.customOption.OptionBL;
import com.aurel.track.admin.customize.lists.systemOption.StatusBL;
import com.aurel.track.admin.customize.treeConfig.field.FieldBL;
import com.aurel.track.admin.project.ProjectBL;
import com.aurel.track.beans.TFieldBean;
import com.aurel.track.beans.TOptionBean;
import com.aurel.track.beans.TPersonBean;
import com.aurel.track.beans.TWorkItemBean;
import com.aurel.track.errors.ErrorData;
import com.aurel.track.fieldType.constants.SystemFields;
import com.aurel.track.item.history.HistoryLoaderBL;
import com.aurel.track.item.history.HistoryValues;
import com.aurel.track.item.history.HistoryLoaderBL.LONG_TEXT_TYPE;
import com.aurel.track.itemNavigator.ItemNavigatorBL;
import com.aurel.track.itemNavigator.QueryContext;
import com.aurel.track.json.JSONUtility;
import com.aurel.track.persist.ReportBeanHistoryLoader;
import com.aurel.track.plugin.DashboardDescriptor;
import com.aurel.track.report.dashboard.BurnDownChart.CONFIGURATION_PARAMETERS;
import com.aurel.track.report.dashboard.BurnDownChart.EFFORT_TYPE;
import com.aurel.track.report.dashboard.BurnDownChart.PROPERTY_PARAMS;
import com.aurel.track.report.dashboard.BurnDownChart.REPORTING_INTERVAL;
import com.aurel.track.report.dashboard.BurnDownChart.TIME_INTERVAL;
import com.aurel.track.report.dashboard.ProjectFilterDashboardView.DATASOURCE_TYPE;
import com.aurel.track.report.dashboard.TimePeriodDashboardView.TIMEPERIOD_PARAMETERS;
import com.aurel.track.report.datasource.IPluggableDatasource.CONTEXT_ATTRIBUTE;
import com.aurel.track.report.datasource.earnedValue.EarnedValueBL;
import com.aurel.track.report.datasource.earnedValue.EarnedValueDatasource;
import com.aurel.track.report.datasource.earnedValue.EarnedValueTimeSlice;
import com.aurel.track.report.execute.ReportBean;
import com.aurel.track.report.execute.ReportBeanWithHistory;
import com.aurel.track.resources.LocalizeUtil;
import com.aurel.track.util.CalendarUtil;
import com.aurel.track.util.DateTimeUtils;
import com.aurel.track.util.GeneralUtils;
import com.aurel.track.util.IntegerStringBean;
import com.aurel.track.util.StringArrayParameterUtils;

public class AverageTimeToCloseItem extends TimePeriodDashboardView {
    private static final Logger LOGGER = LogManager.getLogger(AverageTimeToCloseItem.class);

    private List<ReportBean> reportBeanList;
    private List<ReportBeanWithHistory> reportBeanWithHistoryList;
    private Date dateTo;
    private Date dateFrom;
    private Map<Integer, Double> hoursPerWorkingDayMap;

    static final int PLANNED_VALUE = 1;
    static final int ACTUAL_EFFORT = 2;
    static final int EARNED_VALUE = 3;
    static String ISO_FORMATTED_DATE = "yyyy-MM-dd";

    //Configuration page constants
    public static interface CONFIGURATION_PARAMETERS {
        static String STATUSES = "statuses";
        static String SELECTED_STATUS = "selectedStatus";
        static String TIME_INTERVALS = "timeIntervals";
        static String REPORTING_INTERVAL = "reportingInterval";
        static String SELECTED_REPORTING_INTERVAL = "selectedReportingInterval";
        static String TIME_FORMAT = "timeFormat";
        static String SELECTED_TIME_FORMAT = "selectedTimeFormat";
        static String Y_AXE = "yAxe";
        static String RESPONSE_TIME_LIMIT = "responseTimeLimitValue";
    }

    protected int DEFAULT_HEIGHT = 400;
    protected int MIN_HEIGHT = 250;
    protected int MAX_HEIGHT = 900;

    public static interface PERIOD_TYPE {
        static int FROM_TO = 1;
        static int DAYS_BEFORE = 2;
    }

    public static interface TIME_INTERVAL {
        static int DAY = 1;
        static int WEEK = 2;
        static int MONTH = 3;
    }

    // Reporting interval constants
    public static interface REPORTING_INTERVAL {
        static int DAILY = 1;
        static int WEEKLY = 2;
        static int MONTHLY = 3;
    }

    // Reporting interval constants
    public static interface TIME_FORMAT {
        static int WORKING_DAY = 0;
        static int WORKING_HOURS = 1;
    }

    // Property parameters
    public static interface PROPERTY_PARAMS {
        static String REPORTING_INTERVAL_DAILY = "averageTimeToCloseItem.tooltip.reportingInterval.daily";
        static String REPORTING_INTERVAL_WEEKLY = "averageTimeToCloseItem.tooltip.reportingInterval.weekly";
        static String REPORTING_INTERVAL_MONTHLY = "averageTimeToCloseItem.tooltip.reportingInterval.monthly";
        static String TIME_FORMAT_WORKING_DAY = "averageTimeToCloseItem.tooltip.time.format.working.day";
        static String TIME_FORMAT_WORKING_HOURS = "averageTimeToCloseItem.tooltip.time.format.working.hours";
    }

    protected boolean isUseConfig() {
        return true;
    }

    @Override
    protected String encodeJSONExtraData(Integer dashboardID, Map<String, Object> session,
            Map<String, String> parameters, Integer projectID, Integer releaseID, Map<String, String> ajaxParams)
            throws TooManyItemsToLoadException {
        TPersonBean user = (TPersonBean) session.get(Constants.USER_KEY);
        Locale locale = user.getLocale();
        DashboardDescriptor dashboardDescriptor = getDescriptor();
        String bundleName = dashboardDescriptor.getBundleName();

        StringBuilder sb = new StringBuilder();

        String sessionPrefix = "dashboard" + "." + dashboardID;
        //the name where configParameters are stored
        String theParams = sessionPrefix + "Params";
        //to avoid caching if IE
        String userEnteredTitle = parameters.get("title");
        if (userEnteredTitle == null || "".equals(userEnteredTitle.trim())) {
            userEnteredTitle = "burnDownChart.label";
        }
        int height = parseInteger(parameters, CONFIGURATION_PARAMETERS.Y_AXE, DEFAULT_HEIGHT);
        Map<String, Object> configParameters = new HashMap<String, Object>();
        Iterator<String> it = parameters.keySet().iterator();
        while (it.hasNext()) {
            String key = it.next();
            configParameters.put(key, parameters.get(key));
        }
        //used in preparing the locale specific chart
        configParameters.put("locale", locale);
        configParameters.put("personBean", user);
        configParameters.put("projectID", projectID);
        configParameters.put("releaseID", releaseID);
        session.put(theParams, configParameters);
        JSONUtility.appendIntegerValue(sb, "height", height);

        dateTo = null;
        dateFrom = null;

        if ((configParameters.get("dateTo") != null && configParameters.get("dateTo") != null
                && !configParameters.get("dateTo").equals("null")
                && !configParameters.get("dateFrom").equals("null"))
                || (configParameters.get("daysBefore") != null)) {

            SimpleDateFormat dateFormat;
            dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            if (configParameters.get("daysBefore") != null) {

                Calendar calendar = Calendar.getInstance();
                int daysBefore = BasePluginDashboardBL.parseIntegerValue(configParameters,
                        TIMEPERIOD_PARAMETERS.DAYS_BEFORE, 0);
                dateTo = calendar.getTime();
                dateFrom = calendar.getTime();
                if (daysBefore != 0) {
                    calendar.add(Calendar.DATE, -daysBefore);
                    dateFrom = calendar.getTime();
                }
            } else {
                try {
                    dateFrom = dateFormat.parse(configParameters.get("dateFrom").toString());
                    dateTo = dateFormat.parse(configParameters.get("dateTo").toString());
                } catch (Exception ex) {
                    LOGGER.error(ExceptionUtils.getStackTrace(ex), ex);
                }
            }
            JSONUtility.appendStringValue(sb, "dateTo", dateFormat.format(dateTo).toString());
            JSONUtility.appendStringValue(sb, "dateFrom", dateFormat.format(dateFrom).toString());
            String chartData = generateJSONResponse(configParameters, user, locale);
            boolean chartDataIsEmpty = false;
            if (chartData == null || "[]".equals(chartData)) {
                chartData = "[]";
                chartDataIsEmpty = true;
            }
            JSONUtility.appendJSONValue(sb, "chartData", chartData);
            JSONUtility.appendBooleanValue(sb, "empty", chartDataIsEmpty);
            String xAxesLabel = "";
            if (Integer
                    .parseInt(configParameters.get("reportingInterval").toString()) == REPORTING_INTERVAL.WEEKLY) {
                xAxesLabel = LocalizeUtil.getLocalizedText("burnDownChart.xAxesLabelWeekly", locale, bundleName);
            } else {
                xAxesLabel = LocalizeUtil.getLocalizedText("burnDownChart.xAxesLabel", locale, bundleName);
            }

            int selectedTimeFormat = parseIntegerValue(configParameters, CONFIGURATION_PARAMETERS.TIME_FORMAT, 0);
            String yAxesLabel = "";
            if (selectedTimeFormat == TIME_FORMAT.WORKING_DAY) {
                yAxesLabel = LocalizeUtil.getLocalizedText("averageTimeToCloseItem.yAxesLabel.working.day", locale,
                        bundleName);
            } else {
                yAxesLabel = LocalizeUtil.getLocalizedText("averageTimeToCloseItem.yAxesLabel.working.hours",
                        locale, bundleName);
            }
            JSONUtility.appendStringValue(sb, "xAxesLabel", xAxesLabel);
            JSONUtility.appendStringValue(sb, "yAxesLabel", yAxesLabel);
            JSONUtility.appendStringValue(sb, "reportingInterval",
                    configParameters.get("reportingInterval").toString());
            JSONUtility.appendStringValue(sb, "locale", locale.toString());
        } else {
            JSONUtility.appendBooleanValue(sb, "empty", true);
            LOGGER.debug("Burn-Down Chart not configured yet!");
        }
        return sb.toString();
    }

    @Override
    public String encodeJSONExtraDataConfig(Map<String, String> parameters, TPersonBean user, Integer entityId,
            Integer entityType) {
        StringBuilder sb = new StringBuilder();
        Locale locale = user.getLocale();
        //DateTimeUtils dateTimeUtils = DateTimeUtils.getInstance();
        DashboardDescriptor dashboardDescriptor = getDescriptor();
        String bundleName = dashboardDescriptor.getBundleName();

        sb.append(getDatasourceConfig(parameters, entityId, entityType, locale));
        //DataSourceDashboardBL.appendJSONExtraDataConfig_DataSource(sb,dashboardDescriptor,parameters,user,entityId,entityType);
        sb.append(getTimePeriodConfig(parameters, locale));

        JSONUtility.appendILabelBeanList(sb, CONFIGURATION_PARAMETERS.STATUSES, StatusBL.loadAll(locale));
        JSONUtility.appendStringList(sb, CONFIGURATION_PARAMETERS.SELECTED_STATUS,
                StringArrayParameterUtils.splitSelection(parameters.get(CONFIGURATION_PARAMETERS.SELECTED_STATUS)));

        //Reporting interval
        JSONUtility.appendIntegerStringBeanList(sb, CONFIGURATION_PARAMETERS.REPORTING_INTERVAL,
                getPossibleReportingIntervals(locale, bundleName));
        int reportingInterval = parseInteger(parameters, CONFIGURATION_PARAMETERS.REPORTING_INTERVAL,
                TIME_INTERVAL.MONTH);
        JSONUtility.appendIntegerValue(sb, CONFIGURATION_PARAMETERS.SELECTED_REPORTING_INTERVAL, reportingInterval);

        //Time format
        int timeFormat = parseInteger(parameters, CONFIGURATION_PARAMETERS.TIME_FORMAT, TIME_FORMAT.WORKING_DAY);
        JSONUtility.appendIntegerValue(sb, CONFIGURATION_PARAMETERS.SELECTED_TIME_FORMAT, timeFormat);

        JSONUtility.appendIntegerValue(sb, "timeFormatWorkingDayID", TIME_FORMAT.WORKING_DAY);
        JSONUtility.appendIntegerValue(sb, "timeFormatWorkingHoursID", TIME_FORMAT.WORKING_HOURS);

        JSONUtility.appendIntegerValue(sb, CONFIGURATION_PARAMETERS.Y_AXE,
                parseInteger(parameters, CONFIGURATION_PARAMETERS.Y_AXE, DEFAULT_HEIGHT));

        int responseTimeLimitValue = parseIntegerValue(parameters, CONFIGURATION_PARAMETERS.RESPONSE_TIME_LIMIT, 1);
        JSONUtility.appendIntegerValue(sb, CONFIGURATION_PARAMETERS.RESPONSE_TIME_LIMIT, responseTimeLimitValue);

        int prefWidth = 480;
        int prefHeight = 660;
        JSONUtility.appendIntegerValue(sb, "prefWidth", prefWidth);
        JSONUtility.appendIntegerValue(sb, "prefHeight", prefHeight);
        return sb.toString();
    }

    public String generateJSONResponse(Map configParameters, TPersonBean personBean, Locale locale)
            throws TooManyItemsToLoadException {
        int datasourceType = parseInteger(configParameters, "selectedDatasourceType", 1);
        Integer projectOrReleaseID = null;
        Integer filterID = null;
        if (configParameters.get("projectID") != null || configParameters.get("releaseID") != null) {
            if (configParameters.get("releaseID") != null) {
                projectOrReleaseID = Integer.valueOf(configParameters.get("releaseID").toString());
            } else {
                projectOrReleaseID = Integer.valueOf(configParameters.get("projectID").toString());
                projectOrReleaseID = projectOrReleaseID * (-1);
            }
            datasourceType = DATASOURCE_TYPE.PROJECT_RELEASE;
        } else {
            if (datasourceType == 1) {
                projectOrReleaseID = parseInteger(configParameters, "selectedProjectOrRelease", 1);
            } else {
                filterID = parseInteger(configParameters, "selectedQueryID", 1);
            }
        }

        Map<String, Object> contextMap = new HashMap<String, Object>();
        contextMap.put("fromIssueNavigator", false);
        contextMap.put("useProjectSpecificID", false);
        reportBeanList = getReportBeanList(datasourceType, projectOrReleaseID, filterID, contextMap, true, true,
                true, personBean, locale);
        reportBeanWithHistoryList = ReportBeanHistoryLoader.getReportBeanWithHistoryList(reportBeanList, locale,
                false, false, true, new Integer[] { SystemFields.INTEGER_STATE }, true, true, true, true, false,
                personBean.getObjectID(), null, dateFrom, dateTo, true, LONG_TEXT_TYPE.ISPLAIN);
        SortedMap<Date, EarnedValueTimeSlice> earnedValueTimeSliceMap = new TreeMap<Date, EarnedValueTimeSlice>();
        int reportingInterval = parseIntegerValue(configParameters, CONFIGURATION_PARAMETERS.REPORTING_INTERVAL, 1);
        int selectedTimeFormat = parseIntegerValue(configParameters, CONFIGURATION_PARAMETERS.TIME_FORMAT, 1);
        int responseTimeLimitValue = parseIntegerValue(configParameters,
                CONFIGURATION_PARAMETERS.RESPONSE_TIME_LIMIT, 1);
        if (!reportBeanWithHistoryList.isEmpty()) {
            LOGGER.debug(
                    "Number of reportBeanWithHistoryList after removing items with no budget or overlapping start/end dates "
                            + reportBeanWithHistoryList.size());
            Set<Integer> projectIDs = getProjectIDs(reportBeanWithHistoryList);
            hoursPerWorkingDayMap = ProjectBL.getHoursPerWorkingDayMap(projectIDs);
            Integer[] selectedStatuses = getSelectedStatuses(configParameters);
            SortedMap<Integer, SortedMap<Integer, Double>> yearToIntervalToAverageValue = createYearToIntervalToAvgTimeMap(
                    reportingInterval, GeneralUtils.createSetFromIntegerArr(selectedStatuses), selectedTimeFormat);
            BurnDownChartBL.addZerosForEmptyIntervals(dateFrom, dateTo, reportingInterval,
                    yearToIntervalToAverageValue, false);
            SortedMap<Date, Object> dateToAvgTime = transformPeriodsToDates(yearToIntervalToAverageValue,
                    reportingInterval);
            String retValue = convertResultMapToJsonResponse(dateToAvgTime, reportingInterval,
                    responseTimeLimitValue);
            return retValue;
        }
        return null;
    }

    private String convertResultMapToJsonResponse(SortedMap<Date, Object> dateToAvgTime, int reportingInterval,
            int responseTimeLimitValue) {
        StringBuilder sb = new StringBuilder();
        boolean isEmpty = true;
        if (dateToAvgTime != null) {
            sb.append("[");
            for (Map.Entry<Date, Object> entry : dateToAvgTime.entrySet()) {
                if (isEmpty) {
                    isEmpty = false;
                }
                Date date = entry.getKey();
                Double avgValue = (Double) entry.getValue();
                sb.append("{");
                JSONUtility.appendStringValue(sb, "date", getDateBasedOnReportInterval(date, reportingInterval));
                JSONUtility.appendDoubleValue(sb, "avgTime", avgValue);
                JSONUtility.appendIntegerValue(sb, "respTimeLimitValue", responseTimeLimitValue, true);

                sb.append("},");
            }
            if (sb != null && sb.length() > 0) {
                if (sb.toString().endsWith(",")) {
                    sb = new StringBuilder(sb.substring(0, sb.length() - 1));
                }
            }
            sb.append("]");
        }
        if (isEmpty) {
            sb = new StringBuilder();
            sb.append("[]");
        }
        return sb.toString();
    }

    private SortedMap<Integer, SortedMap<Integer, ArrayList<ReportBeanWithHistory>>> createYearToIntervalToReportBeanListMap(
            int timeInterval, Set<Integer> finalStates) {
        SortedMap<Integer, SortedMap<Integer, ArrayList<ReportBeanWithHistory>>> yearToIntervalToReportBeanList = new TreeMap<Integer, SortedMap<Integer, ArrayList<ReportBeanWithHistory>>>();
        if (reportBeanWithHistoryList != null) {
            Calendar calendar = Calendar.getInstance();
            Calendar calendarEndDate = Calendar.getInstance();
            int calendarInterval = getCalendarInterval(timeInterval);

            for (ReportBeanWithHistory reportBean : reportBeanWithHistoryList) {
                TWorkItemBean workItemBean = reportBean.getWorkItemBean();
                calendar.setTime(dateFrom);
                calendarEndDate.setTime(dateTo);
                int yearValue = calendar.get(Calendar.YEAR);
                int intervalValue = calendar.get(calendarInterval);
                boolean isFirst = true;
                while (calendar.before(calendarEndDate) || isFirst) {
                    if (isFirst) {
                        isFirst = false;
                    } else {
                        calendar.add(calendarInterval, 1);
                    }
                    yearValue = calendar.get(Calendar.YEAR);
                    intervalValue = calendar.get(calendarInterval);
                    SortedMap<Integer, ArrayList<ReportBeanWithHistory>> intervalToReportBeans = yearToIntervalToReportBeanList
                            .get(Integer.valueOf(yearValue));
                    if (intervalToReportBeans == null) {
                        yearToIntervalToReportBeanList.put(new Integer(yearValue),
                                new TreeMap<Integer, ArrayList<ReportBeanWithHistory>>());
                        intervalToReportBeans = yearToIntervalToReportBeanList.get(Integer.valueOf(yearValue));
                    }
                    ArrayList<ReportBeanWithHistory> reportBeanList = intervalToReportBeans
                            .get(Integer.valueOf(intervalValue));
                    if (reportBeanList == null) {
                        reportBeanList = new ArrayList<ReportBeanWithHistory>();
                        intervalToReportBeans.put(Integer.valueOf(intervalValue), reportBeanList);
                    }
                    Integer stateID = getReportBeanStateID(reportBean);
                    Date lastStateChangeDate = getReportBeanLastStateChange(reportBean);
                    if (stateID == null || lastStateChangeDate == null) {
                        continue;
                    }
                    if (finalStates.contains(stateID)) {
                        if (timeInterval == TIME_INTERVAL.DAY) {
                            if (DateTimeUtils.compareTwoDatesWithoutTimeValue(workItemBean.getCreated(),
                                    calendar.getTime()) == 0
                                    && DateTimeUtils.compareTwoDatesWithoutTimeValue(lastStateChangeDate,
                                            calendar.getTime()) == 0) {
                                reportBeanList.add(reportBean);
                            }
                        } else {
                            Calendar actualReportinIntervalEndCalendar = Calendar.getInstance();
                            actualReportinIntervalEndCalendar.setTime(calendar.getTime());
                            actualReportinIntervalEndCalendar.add(calendarInterval, 1);
                            if (DateTimeUtils.greaterOrEqual(workItemBean.getCreated(), calendar.getTime())
                                    && DateTimeUtils.greater(lastStateChangeDate, calendar.getTime())
                                    && DateTimeUtils.lessOrEqual(lastStateChangeDate,
                                            actualReportinIntervalEndCalendar.getTime())) {
                                reportBeanList.add(reportBean);
                            }
                        }
                    }
                }
            }
        }
        return yearToIntervalToReportBeanList;
    }

    private SortedMap<Integer, SortedMap<Integer, Double>> createYearToIntervalToAvgTimeMap(Integer timeInterval,
            Set<Integer> finalStates, int selectedTimeFormat) {

        SortedMap<Integer, SortedMap<Integer, ArrayList<ReportBeanWithHistory>>> yearToIntervalToReportBeanList = createYearToIntervalToReportBeanListMap(
                timeInterval, finalStates);
        SortedMap<Integer, SortedMap<Integer, Double>> yearToIntervalToAverageValue = new TreeMap<Integer, SortedMap<Integer, Double>>();

        for (Map.Entry<Integer, SortedMap<Integer, ArrayList<ReportBeanWithHistory>>> yearToIntervalEntry : yearToIntervalToReportBeanList
                .entrySet()) {
            Integer year = yearToIntervalEntry.getKey();
            SortedMap<Integer, ArrayList<ReportBeanWithHistory>> intervalToReportBeanList = yearToIntervalEntry
                    .getValue();
            yearToIntervalToAverageValue.put(year, new TreeMap<Integer, Double>());
            for (Entry<Integer, ArrayList<ReportBeanWithHistory>> intervalToWorkItemsListEntry : intervalToReportBeanList
                    .entrySet()) {
                Integer interval = intervalToWorkItemsListEntry.getKey();
                ArrayList<ReportBeanWithHistory> reportBeanLists = intervalToWorkItemsListEntry.getValue();
                int count = 0;
                int sumOfDaysOrWorkingHours = 0;
                for (ReportBeanWithHistory reportBean : reportBeanLists) {
                    Integer stateID = getReportBeanStateID(reportBean);
                    Date lastStateChangeDate = getReportBeanLastStateChange(reportBean);
                    if (stateID == null || lastStateChangeDate == null) {
                        continue;
                    }
                    count++;
                    int valueToAdd = getNumberOfDaysBetweenDates(reportBean.getWorkItemBean().getCreated(),
                            lastStateChangeDate);
                    //It was closed on same day when it was created => 1 day
                    if (valueToAdd == 0) {
                        valueToAdd = 1;
                    }
                    if (selectedTimeFormat == TIME_FORMAT.WORKING_HOURS) {
                        Double workingDaysHourse = hoursPerWorkingDayMap
                                .get(reportBean.getWorkItemBean().getProjectID());
                        if (workingDaysHourse == null) {
                            workingDaysHourse = 8.0;
                        }
                        valueToAdd *= workingDaysHourse;
                    }
                    sumOfDaysOrWorkingHours += valueToAdd;
                }
                Double avg = 0.0;
                if (count > 0) {
                    avg = (double) sumOfDaysOrWorkingHours / count;
                }
                yearToIntervalToAverageValue.get(year).put(interval, avg);
            }
        }
        return yearToIntervalToAverageValue;
    }

    private Integer getReportBeanStateID(ReportBeanWithHistory reportBean) {
        Map<Integer, Map<Integer, HistoryValues>> historyMap = reportBean.getHistoryValuesMap();
        List<HistoryValues> historyValuesList = HistoryLoaderBL.getHistoryValuesList(historyMap, false);
        if (historyValuesList == null || historyValuesList.isEmpty()) {
            return null;
        }
        HistoryValues historyValues = historyValuesList.get(0);
        if (!SystemFields.INTEGER_STATE.equals(historyValues.getFieldID())) {
            return null;
        }
        Integer stateID = (Integer) historyValues.getNewValue();
        return stateID;
    }

    private Date getReportBeanLastStateChange(ReportBeanWithHistory reportBean) {
        Map<Integer, Map<Integer, HistoryValues>> historyMap = reportBean.getHistoryValuesMap();
        List<HistoryValues> historyValuesList = HistoryLoaderBL.getHistoryValuesList(historyMap, false);
        if (historyValuesList == null || historyValuesList.isEmpty()) {
            return null;
        }
        HistoryValues historyValues = historyValuesList.get(0);
        if (!SystemFields.INTEGER_STATE.equals(historyValues.getFieldID())) {
            return null;
        }
        Date lastStateChangeDate = historyValues.getLastEdit();
        return lastStateChangeDate;
    }

    public Double getMaxPlannedValue(SortedMap<Date, EarnedValueTimeSlice> earnedValueTimeSliceMap) {
        Double maxValue = -1.0;
        for (Map.Entry<Date, EarnedValueTimeSlice> entry : earnedValueTimeSliceMap.entrySet()) {
            Double d1 = entry.getValue().getPlannedValue();
            if (Double.compare(d1, maxValue) > 0) {
                maxValue = entry.getValue().getPlannedValue();
            }
        }
        return maxValue;
    }

    public Double getMaxEarnedValue(SortedMap<Date, EarnedValueTimeSlice> earnedValueTimeSliceMap) {
        Double maxValue = -1.0;
        for (Map.Entry<Date, EarnedValueTimeSlice> entry : earnedValueTimeSliceMap.entrySet()) {
            if (entry.getValue().getEarnedvalue() != null) {
                Double d1 = entry.getValue().getEarnedvalue();
                if (Double.compare(d1, maxValue) > 0) {
                    maxValue = entry.getValue().getEarnedvalue();
                }
            }
        }
        return maxValue;
    }

    private String convertResultMapToJSONData(SortedMap<Date, EarnedValueTimeSlice> earnedValueTimeSliceMap,
            Double maxValPlanned, Double maxEarned, int reportingInterval, int effortType) {
        StringBuilder local = new StringBuilder();
        local.append("[");
        for (Map.Entry<Date, EarnedValueTimeSlice> entry : earnedValueTimeSliceMap.entrySet()) {
            local.append("{");
            JSONUtility.appendStringValue(local, "date",
                    getDateBasedOnReportInterval(entry.getKey(), reportingInterval));
            Double plannedVal = entry.getValue().getPlannedValue();
            if (effortType == EFFORT_TYPE.TIME) {
                plannedVal = maxValPlanned - entry.getValue().getPlannedValue();
            }
            JSONUtility.appendDoubleValue(local, "plannedValue", plannedVal);
            Double earnValue = 0.0;
            if (entry.getValue().getEarnedvalue() != null) {
                earnValue = entry.getValue().getEarnedvalue();

            }
            if (effortType == EFFORT_TYPE.TIME) {
                earnValue = maxEarned - earnValue;
            }
            JSONUtility.appendDoubleValue(local, "earnedValue", earnValue, true);
            local.append("},");
        }
        if (local != null && local.length() > 0) {
            if (local.toString().endsWith(",")) {
                local = new StringBuilder(local.substring(0, local.length() - 1));
            }
        }
        local.append("]");
        return local.toString();
    }

    private String getDateBasedOnReportInterval(Date date, int reportingInterval) {
        String formattedDate = null;
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week = cal.get(Calendar.WEEK_OF_YEAR);
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH);
        if (reportingInterval == REPORTING_INTERVAL.DAILY) {
            SimpleDateFormat dtf = new SimpleDateFormat(ISO_FORMATTED_DATE);
            return dtf.format(date).toString();
        } else if (reportingInterval == REPORTING_INTERVAL.MONTHLY) {
            return year + "-" + month;
        } else if (reportingInterval == REPORTING_INTERVAL.WEEKLY) {
            return week + "/" + year;
        }
        return formattedDate;
    }

    public List<IntegerStringBean> getPossibleReportingIntervals(Locale locale, String bundleName) {
        List<IntegerStringBean> result = new ArrayList<IntegerStringBean>();
        result.add(new IntegerStringBean(
                LocalizeUtil.getLocalizedText(PROPERTY_PARAMS.REPORTING_INTERVAL_DAILY, locale, bundleName),
                new Integer(REPORTING_INTERVAL.DAILY)));
        result.add(new IntegerStringBean(
                LocalizeUtil.getLocalizedText(PROPERTY_PARAMS.REPORTING_INTERVAL_WEEKLY, locale, bundleName),
                new Integer(REPORTING_INTERVAL.WEEKLY)));
        result.add(new IntegerStringBean(
                LocalizeUtil.getLocalizedText(PROPERTY_PARAMS.REPORTING_INTERVAL_MONTHLY, locale, bundleName),
                new Integer(REPORTING_INTERVAL.MONTHLY)));

        return result;
    }

    public int parseIntegerValue(Map configParameters, String fieldName, int defaultValue) {
        int result = defaultValue;
        try {
            result = Integer.parseInt((String) configParameters.get(fieldName));
        } catch (Exception e) {
            LOGGER.debug("There is no value set for field: " + fieldName + " The default value will be used: "
                    + defaultValue);
        }
        return result;
    }

    protected List<ReportBean> getReportBeanList(Integer datasourceType, Integer projectOrReleaseID,
            Integer filterID, Map<String, Object> contextMap, boolean includeOpen, boolean includeClosed,
            boolean includeSubs, TPersonBean personBean, Locale locale) throws TooManyItemsToLoadException {
        List<ReportBean> reportBeanList = null;
        Boolean fromIssueNavigator = (Boolean) contextMap.get(CONTEXT_ATTRIBUTE.FROM_ISSUE_NAVIGATOR);
        Integer dashboardProjectOrReleaseID = (Integer) contextMap
                .get(CONTEXT_ATTRIBUTE.DASHBOARD_PROJECT_RELEASE_ID);
        if (fromIssueNavigator != null && fromIssueNavigator.booleanValue()) {
            List<Integer> workItemIDs = (List<Integer>) contextMap.get(CONTEXT_ATTRIBUTE.WORKITEMIDS);
            if (workItemIDs != null && !workItemIDs.isEmpty()) {
                reportBeanList = LoadItemIDListItems.getReportBeansByWorkItemIDs(
                        GeneralUtils.createIntArrFromIntegerList(workItemIDs), false, personBean.getObjectID(),
                        locale, true, true, true, true, true, true, false, true, false);
            } else {
                QueryContext queryContext = ItemNavigatorBL.loadLastQuery(personBean.getObjectID(), locale);
                if (queryContext != null) {
                    reportBeanList = ItemNavigatorBL.executeQuery(personBean, locale, queryContext);
                }
            }
        } else {
            if (dashboardProjectOrReleaseID != null) {
                FilterUpperTO filterUpperTO = FilterUpperConfigUtil
                        .getByProjectReleaseID(dashboardProjectOrReleaseID, true, includeOpen, includeClosed);
                reportBeanList = LoadTreeFilterItems.getTreeFilterReportBeansForReport(filterUpperTO, null,
                        filterID, personBean, locale);
            } else {
                //saves the parameters into the database
                if (datasourceType == null) {
                    LOGGER.warn("No datasourceType was selected");
                    return null;
                }
                if (datasourceType.intValue() == DATASOURCE_TYPE.PROJECT_RELEASE) {
                    if (projectOrReleaseID == null) {
                        LOGGER.warn("No project/release was selected");
                        return null;
                    } else {
                        FilterUpperTO filterUpperTO = FilterUpperConfigUtil
                                .getByProjectReleaseID(projectOrReleaseID, true, includeOpen, includeClosed);
                        reportBeanList = LoadTreeFilterItems.getTreeFilterReportBeansForReport(filterUpperTO, null,
                                filterID, personBean, locale);
                    }
                } else {
                    if (filterID == null) {
                        LOGGER.warn("No filter was selected");
                        return null;
                    } else {
                        reportBeanList = FilterExecuterFacade.getSavedFilterReportBeanList(filterID, locale,
                                personBean, new LinkedList<ErrorData>(), null, null, true);
                    }
                }
            }
        }
        return ReportBeanLoader.addISOValuesToReportBeans(reportBeanList, personBean.getObjectID(), locale);
    }

    private Integer[] getSelectedStatuses(Map configParameters) {
        String values = configParameters.get(CONFIGURATION_PARAMETERS.SELECTED_STATUS).toString();
        Integer[] integerValues = null;
        if (values != null) {
            String[] strArr = values.split(",");
            if (strArr != null && strArr.length > 0 && !"".equals(strArr[0])) {
                integerValues = new Integer[strArr.length];
                for (int i = 0; i < strArr.length; i++) {
                    try {
                        integerValues[i] = new Integer(strArr[i]);
                    } catch (Exception e) {
                        LOGGER.info("Converting the " + strArr[i] + " as the " + i
                                + "th parameter to Integer failed with " + e.getMessage(), e);
                        LOGGER.error(ExceptionUtils.getStackTrace(e), e);
                    }
                }
            }
        }
        return integerValues;
    }

    public static int getCalendarInterval(int timeInterval) {
        switch (timeInterval) {
        case TIME_INTERVAL.DAY:
            return Calendar.DAY_OF_YEAR;
        case TIME_INTERVAL.WEEK:
            return Calendar.WEEK_OF_YEAR;
        default:
            return Calendar.MONTH;
        }
    }

    /**
     * Returns number of free days from given interval, start date included always, endDate only if includeLastDay == true!
     * @param startDateParam
     * @param endDateParam
     * @param includeLastDay
     * @return
     */
    public static Integer getNumberOfDaysBetweenDates(Date startDateParam,
            Date endDateParam/*, boolean includeLastDay*/) {
        return (int) ((startDateParam.getTime() - endDateParam.getTime()) / (1000 * 60 * 60 * 24));
    }

    private Set<Integer> getProjectIDs(List<ReportBeanWithHistory> reportBeanWithHistoryList) {
        Set<Integer> projectIDs = new HashSet<Integer>();
        for (ReportBeanWithHistory reportBeanWithHistory : reportBeanWithHistoryList) {
            projectIDs.add(reportBeanWithHistory.getWorkItemBean().getProjectID());
        }
        return projectIDs;
    }

    /**
     * Add the time series to the timeSeriesCollection
     * SortedMap at first and second level (year and period)
     * (Sorted because the accumulated should be calculated in the right order)
     * @param timeSeriesCollection
     * @param yearToPeriodToOpenedWorkItemCountMap
     * @param selectedTimeInterval
    */
    public static SortedMap<Date, Object> transformPeriodsToDates(
            SortedMap/*<Integer, SortedMap<Integer, Object>>*/ yearToPeriodToValuesMap, int selectedTimeInterval) {
        SortedMap<Date, Object> dateToValue = new TreeMap<Date, Object>();
        for (Iterator iterator = yearToPeriodToValuesMap.keySet().iterator(); iterator.hasNext();) {
            Integer year = (Integer) iterator.next();
            SortedMap<Integer, Object> intervalToStatusChangeBeans = (SortedMap<Integer, Object>) yearToPeriodToValuesMap
                    .get(year);
            Iterator<Integer> periodIterator = intervalToStatusChangeBeans.keySet().iterator();
            while (periodIterator.hasNext()) {
                Integer period = periodIterator.next();
                Object periodValue = intervalToStatusChangeBeans.get(period);
                if (periodValue != null) {
                    dateToValue.put(createDate(period.intValue(), year.intValue(), selectedTimeInterval),
                            periodValue);
                }
            }
        }
        return dateToValue;
    }

    /**
     * Created a regular time period
     * @param period
     * @param year
     * @param timeInterval
     * @return
     */
    public static Date createDate(int period, int year, int timeInterval) {
        Calendar calendar = Calendar.getInstance();
        calendar.clear();
        calendar.setLenient(true);
        calendar.set(Calendar.YEAR, year);
        switch (timeInterval) {
        case TIME_INTERVAL.DAY:
            calendar.set(Calendar.DAY_OF_YEAR, period);
            break;
        case TIME_INTERVAL.WEEK:
            calendar.set(Calendar.WEEK_OF_YEAR, period);
            break;
        default:
            calendar.set(Calendar.MONTH, period);
        }
        return calendar.getTime();
    }
}