/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2012 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.planner.chart; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import org.joda.time.LocalDate; import org.libreplan.business.planner.entities.IEarnedValueCalculator; import org.libreplan.web.I18nHelper; import org.zkforge.timeplot.Plotinfo; import org.zkforge.timeplot.Timeplot; import org.zkforge.timeplot.geometry.TimeGeometry; import org.zkforge.timeplot.geometry.ValueGeometry; import org.zkoss.ganttz.util.Interval; /** * * @author Manuel Rego Casasnovas <mrego@igalia.com> * @author Diego Pino García <dpino@igalia.com> * * Abstract class with the common functionality for the earned value * chart. */ public abstract class EarnedValueChartFiller extends ChartFiller { public static boolean includes(Interval interval, LocalDate date) { LocalDate start = interval.getStart(); LocalDate end = interval.getFinish(); return start.compareTo(date) <= 0 && date.compareTo(end) < 0; } private IEarnedValueCalculator earnedValueCalculator; protected Map<EarnedValueType, SortedMap<LocalDate, BigDecimal>> indicators = new HashMap<EarnedValueType, SortedMap<LocalDate, BigDecimal>>(); private Interval indicatorsInterval; protected void setEarnedValueCalculator(IEarnedValueCalculator earnedValueCalculator) { this.earnedValueCalculator = earnedValueCalculator; } protected Plotinfo createPlotInfo(SortedMap<LocalDate, BigDecimal> map, Interval interval, String lineColor) { Plotinfo plotInfo = createPlotinfo(map, interval, true); plotInfo.setLineColor(lineColor); return plotInfo; } public void calculateValues(Interval interval) { this.indicatorsInterval = interval; // BCWS calculateBudgetedCostWorkScheduled(interval); // ACWP calculateActualCostWorkPerformed(interval); // BCWP calculateBudgetedCostWorkPerformed(interval); // CV calculateCostVariance(); // SV calculateScheduleVariance(); // BAC calculateBudgetAtCompletion(); // EAC calculateEstimateAtCompletion(); // VAC calculateVarianceAtCompletion(); // ETC calculateEstimatedToComplete(); // CPI calculateCostPerformanceIndex(); // SPI calculateSchedulePerformanceIndex(); } protected abstract void calculateBudgetedCostWorkScheduled(Interval interval); protected abstract void calculateActualCostWorkPerformed(Interval interval); protected abstract void calculateBudgetedCostWorkPerformed(Interval interval); protected abstract Set<EarnedValueType> getSelectedIndicators(); private void calculateCostVariance() { setIndicator(EarnedValueType.CV, earnedValueCalculator.calculateCostVariance( getIndicator(EarnedValueType.BCWP), getIndicator(EarnedValueType.ACWP))); } private void calculateScheduleVariance() { setIndicator(EarnedValueType.SV, earnedValueCalculator.calculateScheduleVariance( getIndicator(EarnedValueType.BCWP), getIndicator(EarnedValueType.BCWS))); } private void calculateSchedulePerformanceIndex() { setIndicator(EarnedValueType.SPI, earnedValueCalculator.calculateSchedulePerformanceIndex( getIndicator(EarnedValueType.BCWP), getIndicator(EarnedValueType.BCWS))); } private void calculateBudgetAtCompletion() { setIndicator( EarnedValueType.BAC, earnedValueCalculator .calculateBudgetAtCompletion(getIndicator(EarnedValueType.BCWS))); } private void calculateEstimateAtCompletion() { setIndicator(EarnedValueType.EAC, earnedValueCalculator.calculateEstimateAtCompletion( getIndicator(EarnedValueType.ACWP), getIndicator(EarnedValueType.BCWP), getIndicator(EarnedValueType.BAC))); } private void calculateVarianceAtCompletion() { setIndicator(EarnedValueType.VAC, earnedValueCalculator.calculateVarianceAtCompletion( getIndicator(EarnedValueType.BAC), getIndicator(EarnedValueType.EAC))); } private void calculateEstimatedToComplete() { setIndicator(EarnedValueType.ETC, earnedValueCalculator.calculateEstimatedToComplete( getIndicator(EarnedValueType.EAC), getIndicator(EarnedValueType.ACWP))); } private void calculateCostPerformanceIndex() { setIndicator(EarnedValueType.CPI, earnedValueCalculator.calculateCostPerformanceIndex( getIndicator(EarnedValueType.BCWP), getIndicator(EarnedValueType.ACWP))); } public SortedMap<LocalDate, BigDecimal> getIndicator(EarnedValueType indicator) { return indicators.get(indicator); } public BigDecimal getIndicator(EarnedValueType indicator, LocalDate date) { return indicators.get(indicator).get(date); } public void setIndicator(EarnedValueType type, SortedMap<LocalDate, BigDecimal> values) { indicators.put(type, values); } public void setIndicatorInInterval(EarnedValueType type, Interval interval, SortedMap<LocalDate, BigDecimal> values) { addZeroBeforeTheFirstValue(values); indicators.put(type, calculatedValueForEveryDay(values, interval)); } protected void addZeroBeforeTheFirstValue(SortedMap<LocalDate, BigDecimal> map) { if ( !map.isEmpty() ) { map.put(map.firstKey().minusDays(1), BigDecimal.ZERO); } } @Override public void fillChart(Timeplot chart, Interval interval, Integer size) { chart.getChildren().clear(); chart.invalidate(); resetMinimumAndMaximumValueForChart(); calculateValues(interval); List<Plotinfo> plotinfos = new ArrayList<Plotinfo>(); for (EarnedValueType indicator : getSelectedIndicators()) { Plotinfo plotinfo = createPlotInfo(indicators.get(indicator), interval, indicator.getColor()); plotinfos.add(plotinfo); } if ( plotinfos.isEmpty() ) { // If user doesn't select any indicator, it is needed to create // a default Plotinfo in order to avoid errors on Timemplot plotinfos.add(new Plotinfo()); } ValueGeometry valueGeometry = getValueGeometry(); TimeGeometry timeGeometry = getTimeGeometry(interval); for (Plotinfo plotinfo : plotinfos) { appendPlotinfo(chart, plotinfo, valueGeometry, timeGeometry); } chart.setWidth(size + "px"); chart.setHeight("150px"); } public Interval getIndicatorsDefinitionInterval() { return indicatorsInterval; } /** * Will try to use today if possible * @return Today if there are values defined for that date. The last day in * the interval otherwise */ public LocalDate initialDateForIndicatorValues() { Interval chartInterval = getIndicatorsDefinitionInterval(); LocalDate today = new LocalDate(); return includes(chartInterval, today) ? today : chartInterval.getFinish().minusDays(1); } /** * * @author Manuel Rego Casasnovas <mrego@igalia.com> * */ public enum EarnedValueType { BCWS(_("BCWS"), _("Budgeted Cost Work Scheduled"), "#0000FF"), ACWP( _("ACWP"), _("Actual Cost Work Performed"), "#FF0000"), BCWP( _("BCWP"), _("Budgeted Cost Work Performed"), "#00FF00"), CV( _("CV"), _("Cost Variance"), "#FF8800"), SV(_("SV"), _("Schedule Variance"), "#00FFFF"), BAC(_("BAC"), _("Budget At Completion"), "#FF00FF"), EAC(_("EAC"), _("Estimate At Completion"), "#880000"), VAC(_("VAC"), _("Variance At Completion"), "#000088"), ETC(_("ETC"), _("Estimate To Complete"), "#008800"), CPI(_("CPI"), _("Cost Performance Index"), "#888800"), SPI(_("SPI"), _("Schedule Performance Index"), "#008888") ; /** * Forces to mark the string as needing translation */ private static String _(String string) { return string; } private String acronym; private String name; private String color; private EarnedValueType(String acronym, String name, String color) { this.acronym = acronym; this.name = name; this.color = color; } public String getAcronym() { return I18nHelper._(acronym); } public String getName() { return I18nHelper._(name); } public String getColor() { return color; } } }