Package org.projectforge.statistics

Source Code of org.projectforge.statistics.TimesheetDisciplineChartBuilder

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2014 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition 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; version 3 of the License.
//
// This community edition 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/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.statistics;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

import org.jfree.chart.JFreeChart;
import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.projectforge.calendar.DayHolder;
import org.projectforge.charting.XYChartBuilder;
import org.projectforge.core.OrderDirection;
import org.projectforge.timesheet.TimesheetDO;
import org.projectforge.timesheet.TimesheetDao;
import org.projectforge.timesheet.TimesheetFilter;

/**
* Erzeugt wahlweise eins von zwei Diagrammen:<br/>
* <ol>
* <li>Ein Diagramm, welches über die letzten n Tage die kummulierten IST-Arbeitsstunden und als Soll-Wert die tatsächlich gebuchten
* Zeitberichte aufträgt. Dies wird in einem Differenz-XY-Diagramm visualisiert. Die Darstellung soll motivieren, dass Projektmitarbeiter
* ihre Zeitberichte möglichst zeitnah eintragen.</li>
* <li>Ein Diagramm, welches über die letzten n Tage die Tage visualisiert, die zwischen Zeitberichtsdatum und Zeitpunkt der tatsächlichen
* Buchung liegen.</li>
* </ol>
*
* @author Kai Reinhard (k.reinhard@micromata.de)
*
*/
public class TimesheetDisciplineChartBuilder
{
  private static final double PLANNED_AVERAGE_DIFFERENCE_BETWEEN_TIMESHEET_AND_BOOKING = 2.0; // days.

  private double planWorkingHours;

  private double actualWorkingHours;

  private BigDecimal averageDifferenceBetweenTimesheetAndBooking;

  /**
   * Calculated total plan working hours calculated after call of create #1.
   */
  public double getPlanWorkingHours()
  {
    return planWorkingHours;
  }

  /**
   * Calculated total actual working hours calculated after call of create #1.
   */
  public double getActualWorkingHours()
  {
    return actualWorkingHours;
  }

  /**
   * Gets the calculated average number of days between the date of a time sheet and the date of creation (after call create #2).
   */
  public BigDecimal getAverageDifferenceBetweenTimesheetAndBooking()
  {
    return averageDifferenceBetweenTimesheetAndBooking;
  }

  public double getPlannedAverageDifferenceBetweenTimesheetAndBooking()
  {
    return PLANNED_AVERAGE_DIFFERENCE_BETWEEN_TIMESHEET_AND_BOOKING;
  }

  /**
   * Ein Diagramm, welches über die letzten n Tage die kummulierten IST-Arbeitsstunden und als Soll-Wert die tatsächlich gebuchten
   * Zeitberichte aufträgt. Dies wird in einem Differenz-XY-Diagramm visualisiert. Die Darstellung soll motivieren, dass Projektmitarbeiter
   * ihre Zeitberichte möglichst zeitnah eintragen.
   * @param timesheetDao
   * @param userId
   * @param workingHoursPerDay
   * @param forLastNDays
   * @param shape e. g. new Ellipse2D.Float(-3, -3, 6, 6) or null, if no marker should be printed.
   * @param stroke e. g. new BasicStroke(3.0f).
   * @param showAxisValues
   * @return
   */
  public JFreeChart create(final TimesheetDao timesheetDao, final Integer userId, final double workingHoursPerDay,
      final short forLastNDays, final boolean showAxisValues)
  {
    final DayHolder dh = new DayHolder();
    final TimesheetFilter filter = new TimesheetFilter();
    filter.setStopTime(dh.getDate());
    dh.add(Calendar.DATE, -forLastNDays);
    filter.setStartTime(dh.getDate());
    filter.setUserId(userId);
    filter.setOrderType(OrderDirection.ASC);
    final List<TimesheetDO> list = timesheetDao.getList(filter);
    final TimeSeries sollSeries = new TimeSeries("Soll");
    final TimeSeries istSeries = new TimeSeries("Ist");
    planWorkingHours = 0;
    actualWorkingHours = 0;
    final Iterator<TimesheetDO> it = list.iterator();
    TimesheetDO current = null;
    if (it.hasNext() == true) {
      current = it.next();
    }
    for (int i = 0; i <= forLastNDays; i++) {
      while (current != null && (dh.isSameDay(current.getStartTime()) == true || current.getStartTime().before(dh.getDate()) == true)) {
        actualWorkingHours += ((double) current.getWorkFractionDuration()) / 3600000;
        if (it.hasNext() == true) {
          current = it.next();
        } else {
          current = null;
          break;
        }
      }
      if (dh.isWorkingDay() == true) {
        final BigDecimal workFraction = dh.getWorkFraction();
        if (workFraction != null) {
          planWorkingHours += workFraction.doubleValue() * workingHoursPerDay;
        } else {
          planWorkingHours += workingHoursPerDay;
        }
      }
      final Day day = new Day(dh.getDayOfMonth(), dh.getMonth() + 1, dh.getYear());
      sollSeries.add(day, planWorkingHours);
      istSeries.add(day, actualWorkingHours);
      dh.add(Calendar.DATE, 1);
    }
    final TimeSeriesCollection dataset = new TimeSeriesCollection();
    dataset.addSeries(sollSeries);
    dataset.addSeries(istSeries);
    final XYChartBuilder cb = new XYChartBuilder(null,  null,  null,  dataset, false);
    final XYDifferenceRenderer diffRenderer = new XYDifferenceRenderer(cb.getRedFill(), cb.getGreenFill(), true);
    diffRenderer.setSeriesPaint(0, cb.getRedMarker());
    diffRenderer.setSeriesPaint(1, cb.getGreenMarker());
    cb.setRenderer(0, diffRenderer).setStrongStyle(diffRenderer, false, sollSeries, istSeries);
    cb.setDateXAxis(true).setYAxis(true, "hours");
    return cb.getChart();
  }

  /**
   * Ein Diagramm, welches über die letzten n Tage die Tage visualisiert, die zwischen Zeitberichtsdatum und Zeitpunkt der tatsächlichen
   * Buchung liegen.
   * @param timesheetDao
   * @param userId
   * @param forLastNDays
   * @param shape e. g. new Ellipse2D.Float(-3, -3, 6, 6) or null, if no marker should be printed.
   * @param stroke e. g. new BasicStroke(3.0f).
   * @param showAxisValues
   * @return
   */
  public JFreeChart create(final TimesheetDao timesheetDao, final Integer userId, final short forLastNDays, final boolean showAxisValues)
  {
    final DayHolder dh = new DayHolder();
    final TimesheetFilter filter = new TimesheetFilter();
    filter.setStopTime(dh.getDate());
    dh.add(Calendar.DATE, -forLastNDays);
    filter.setStartTime(dh.getDate());
    filter.setUserId(userId);
    filter.setOrderType(OrderDirection.ASC);
    final List<TimesheetDO> list = timesheetDao.getList(filter);
    final TimeSeries planSeries = new TimeSeries("Soll");
    final TimeSeries actualSeries = new TimeSeries("Ist");
    final Iterator<TimesheetDO> it = list.iterator();
    TimesheetDO current = null;
    if (it.hasNext() == true) {
      current = it.next();
    }
    long numberOfBookedDays = 0;
    long totalDifference = 0;
    for (int i = 0; i <= forLastNDays; i++) {
      long difference = 0;
      long totalDuration = 0; // Weight for average.
      while (current != null && (dh.isSameDay(current.getStartTime()) == true || current.getStartTime().before(dh.getDate()) == true)) {
        final long duration = current.getWorkFractionDuration();
        difference += (current.getCreated().getTime() - current.getStartTime().getTime()) * duration;
        totalDuration += duration;
        if (it.hasNext() == true) {
          current = it.next();
        } else {
          current = null;
          break;
        }
      }
      final double averageDifference = difference > 0 ? ((double) difference) / totalDuration / 86400000 : 0; // In days.
      final Day day = new Day(dh.getDayOfMonth(), dh.getMonth() + 1, dh.getYear());
      if (averageDifference > 0) {
        planSeries.add(day, PLANNED_AVERAGE_DIFFERENCE_BETWEEN_TIMESHEET_AND_BOOKING); // plan average
        // (PLANNED_AVERAGE_DIFFERENCE_BETWEEN_TIMESHEET_AND_BOOKING
        // days).
        actualSeries.add(day, averageDifference);
        totalDifference += averageDifference;
        numberOfBookedDays++;
      }
      dh.add(Calendar.DATE, 1);
    }
    averageDifferenceBetweenTimesheetAndBooking = numberOfBookedDays > 0 ? new BigDecimal(totalDifference).divide(new BigDecimal(
        numberOfBookedDays), 1, RoundingMode.HALF_UP) : BigDecimal.ZERO;

    final TimeSeriesCollection dataset = new TimeSeriesCollection();
    dataset.addSeries(actualSeries);
    dataset.addSeries(planSeries);
    final XYChartBuilder cb = new XYChartBuilder(null,  null,  null,  dataset, false);
    final XYDifferenceRenderer diffRenderer = new XYDifferenceRenderer(cb.getRedFill(), cb.getGreenFill(), true);
    diffRenderer.setSeriesPaint(0, cb.getRedMarker());
    diffRenderer.setSeriesPaint(1, cb.getGreenMarker());
    cb.setRenderer(0, diffRenderer).setStrongStyle(diffRenderer, false, actualSeries, planSeries);
    cb.setDateXAxis(true).setYAxis(true, "days");
    return cb.getChart();
  }
}
TOP

Related Classes of org.projectforge.statistics.TimesheetDisciplineChartBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.