/* * This file is part of LibrePlan * * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.web.montecarlo; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.joda.time.LocalDate; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import org.zkoss.zk.ui.util.GenericForwardComposer; import org.zkoss.zul.CategoryModel; import org.zkoss.zul.Chart; import org.zkoss.zul.Datebox; import org.zkoss.zul.Decimalbox; import org.zkoss.zul.SimpleCategoryModel; /** * Generates a BarChart 3D with the results of a MonteCarlo computation. * The window also shows a set of Datebox controllers that allow the user to specify a start and end date * and calculate the probability density between both values. * * @author Diego Pino Garcia <dpino@igalia.com> */ @Component @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class MonteCarloGraphController extends GenericForwardComposer { private Chart monteCarloChart; private Datebox dateboxStartDateProbability; private Datebox dateboxEndDateProbability; private Decimalbox dbIntervalProbability; private List<LocalDate> dates; private Map<LocalDate, BigDecimal> monteCarloValues; public MonteCarloGraphController() { } public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { super.doAfterCompose(comp); self.setAttribute("monteCarloGraphController", this, true); } public interface IOnClose { void monteCarloGraphClosed(); } private IOnClose onClose = null; public void generateMonteCarloGraph(String orderName, Map<LocalDate, BigDecimal> data, boolean byWeek, IOnClose onClose) { this.onClose = onClose; CategoryModel xyModel; initializeMonteCarloValues(data); // Generate MonteCarlo chart if (byWeek) { xyModel = generateMonteCarloGraphByWeek(orderName, groupByWeek(data)); } else { xyModel = generateMonteCarloGraphByDay(orderName, data); } monteCarloChart.setModel(xyModel); // Initialize dates for calculating probability density LocalDate first = getFirstDate(); LocalDate last = getLastDate(); dateboxStartDateProbability.setValue(toDate(first)); dateboxEndDateProbability.setValue(toDate(last)); dbIntervalProbability.setValue(calculateProbabilityDensity(first, last)); } private void initializeMonteCarloValues(Map<LocalDate, BigDecimal> data) { monteCarloValues = data; initializeDates(data); } private void initializeDates(Map<LocalDate, BigDecimal> monteCarloValues) { dates = new ArrayList<>(monteCarloValues.keySet()); Collections.sort(dates); } private CategoryModel generateMonteCarloGraphByDay(String orderName, Map<LocalDate, BigDecimal> data) { CategoryModel result = new SimpleCategoryModel(); LocalDate first = getFirstDate(); LocalDate last = getLastDate(); for (LocalDate i = first; i.compareTo(last) <= 0; i = i.plusDays(1)) { String labelDate = i.toString(); result.setValue(orderName, labelDate, data.get(i)); } return result; } private LocalDate getFirstDate() { return dates.get(0); } private LocalDate getLastDate() { return dates.get(dates.size() - 1); } private Date toDate(LocalDate date) { return date.toDateTimeAtStartOfDay().toDate(); } private String weekLabelFor(LocalDate date) { return "W" + date.getWeekOfWeekyear(); } private String weekAndYearLabelFor(LocalDate date) { return "W" + date.getWeekOfWeekyear() + "-" + date.getYear(); } private CategoryModel generateMonteCarloGraphByWeek(String orderName, Map<String, BigDecimal> data) { CategoryModel result = new SimpleCategoryModel(); LocalDate first = getFirstDate(); LocalDate last = getLastDate(); for (LocalDate i = first; i.compareTo(last) <= 0; i = i.plusDays(1)) { plotWeekValue(result, orderName, weekLabelFor(i), data.get(weekAndYearLabelFor(i))); } return result; } private void plotWeekValue(CategoryModel model, String orderName, String date, BigDecimal probability) { if (probability == null) { probability = BigDecimal.ZERO; } model.setValue(orderName, date, probability); } private Map<String, BigDecimal> groupByWeek(Map<LocalDate, BigDecimal> data) { Map<String, BigDecimal> result = new HashMap<>(); // Group values of each date by week for (LocalDate date: data.keySet()) { String weekLabel = weekAndYearLabelFor(date); BigDecimal value = result.get(weekLabel); value = (value != null) ? value.add(data.get(date)) : data.get(date); result.put(weekLabel, value); } return result; } private BigDecimal calculateProbabilityDensity(LocalDate start, LocalDate end) { BigDecimal result = BigDecimal.ZERO; for (LocalDate i = start; i.compareTo(end) <= 0; i = i.plusDays(1) ) { BigDecimal value = monteCarloValues.get(i); if (value == null) { continue; } result = result.add(value); } return result; } public void showProbabilityDensity(Datebox startDatebox, Datebox endDatebox) { LocalDate start = (startDatebox.getValue() != null) ? new LocalDate(startDatebox.getValue()) : getFirstDate(); LocalDate end = (endDatebox.getValue() != null) ? new LocalDate(endDatebox.getValue()) : getLastDate(); BigDecimal probabilityDensity = calculateProbabilityDensity(start, end); dbIntervalProbability.setValue(probabilityDensity); } public void onCancel() { cancel(); } public void cancel() { self.setVisible(false); if (onClose != null) { onClose.monteCarloGraphClosed(); } } }