ItEr56S14GraficaInferiorPantallaCargaRecursos: Added load chart to resource load view.

This commit is contained in:
Manuel Rego Casasnovas 2010-05-13 13:18:06 +02:00 committed by Javier Moran Rua
parent 2849fecd8c
commit a28163c015
8 changed files with 370 additions and 9 deletions

View file

@ -25,6 +25,7 @@ import static org.zkoss.ganttz.i18n.I18nHelper._;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.zkoss.ganttz.IChartVisibilityChangedListener;
import org.zkoss.ganttz.data.resourceload.LoadTimeLine;
import org.zkoss.ganttz.timetracker.TimeTracker;
import org.zkoss.ganttz.timetracker.TimeTrackerComponent;
@ -93,11 +94,14 @@ public class ResourcesLoadPanel extends HtmlMacroComponent {
private Component loadChart;
private boolean visibleChart = true;
private WeakReferencedListeners<IChartVisibilityChangedListener> chartVisibilityListeners = WeakReferencedListeners
.create();
public ResourcesLoadPanel(List<LoadTimeLine> groups,
TimeTracker timeTracker, Component componentOnWhichGiveFeedback,
Component loadChart) {
TimeTracker timeTracker, Component componentOnWhichGiveFeedback) {
this.componentOnWhichGiveFeedback = componentOnWhichGiveFeedback;
this.loadChart = loadChart;
init(groups, timeTracker);
}
@ -435,4 +439,29 @@ public class ResourcesLoadPanel extends HtmlMacroComponent {
nameFilterListener.addListener(iFilterChangedListener);
}
public void changeChartVisibility(boolean visible) {
visibleChart = visible;
chartVisibilityListeners
.fireEvent(new IListenerNotification<IChartVisibilityChangedListener>() {
@Override
public void doNotify(
IChartVisibilityChangedListener listener) {
listener.chartVisibilityChanged(visibleChart);
}
});
}
public boolean isVisibleChart() {
return visibleChart;
}
public void addChartVisibilityListener(
IChartVisibilityChangedListener chartVisibilityChangedListener) {
chartVisibilityListeners.addListener(chartVisibilityChangedListener);
}
public void setLoadChart(Component loadChart) {
this.loadChart = loadChart;
}
}

View file

@ -87,7 +87,9 @@ resourcesLoadPanel = self;
</center>
</borderlayout>
</center>
<south height="170px" collapsible="true" title="Graphics">
<south height="170px" collapsible="true" title="Graphics"
sclass="scheduling-graphics" id="graphics"
onOpen="resourcesLoadPanel.changeChartVisibility(event.open);">
<div id="insertionPointChart" />
</south>
</borderlayout>

View file

@ -21,6 +21,7 @@
package org.navalplanner.business.planner.daos;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.hibernate.Criteria;
@ -29,6 +30,7 @@ import org.joda.time.LocalDate;
import org.navalplanner.business.common.daos.GenericDAOHibernate;
import org.navalplanner.business.planner.entities.DayAssignment;
import org.navalplanner.business.planner.entities.DerivedDayAssignment;
import org.navalplanner.business.resources.entities.Resource;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Repository;
@ -64,4 +66,14 @@ public class DayAssignmentDAO extends GenericDAOHibernate<DayAssignment, Long>
}
return criteria.list();
}
@Override
public List<DayAssignment> findByResources(List<Resource> resources) {
if (resources.isEmpty()) {
return Collections.emptyList();
}
return getSession().createCriteria(DayAssignment.class).add(
Restrictions.in("resource", resources)).list();
}
}

View file

@ -27,6 +27,7 @@ import org.joda.time.LocalDate;
import org.navalplanner.business.common.daos.IGenericDAO;
import org.navalplanner.business.planner.entities.DayAssignment;
import org.navalplanner.business.planner.entities.DerivedDayAssignment;
import org.navalplanner.business.resources.entities.Resource;
/**
* DAO interface for {@link DayAssignment}
@ -42,4 +43,6 @@ public interface IDayAssignmentDAO extends
List<DayAssignment> listFilteredByDate(LocalDate init, LocalDate end);
public List<DayAssignment> findByResources(List<Resource> resources);
}

View file

@ -321,7 +321,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel {
});
}
private void appendLoadChartAndLegend(Tabpanel loadChartPannel,
public static void appendLoadChartAndLegend(Tabpanel loadChartPannel,
Timeplot loadChart) {
Hbox hbox = new Hbox();
hbox.appendChild(getLoadChartLegend());
@ -334,7 +334,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel {
loadChartPannel.appendChild(hbox);
}
private org.zkoss.zk.ui.Component getLoadChartLegend() {
public static org.zkoss.zk.ui.Component getLoadChartLegend() {
Hbox hbox = new Hbox();
hbox.setClass("legend-container");
hbox.setAlign("center");

View file

@ -24,6 +24,7 @@ import java.util.Date;
import java.util.List;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.planner.entities.DayAssignment;
import org.navalplanner.business.planner.entities.TaskElement;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Resource;
@ -63,4 +64,8 @@ public interface IResourceLoadModel {
Date getEndDateFilter();
List<DayAssignment> getDayAssignments();
List<Resource> getResources();
}

View file

@ -22,18 +22,32 @@ package org.navalplanner.web.resourceload;
import static org.navalplanner.web.I18nHelper._;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;
import org.apache.commons.lang.Validate;
import org.joda.time.LocalDate;
import org.navalplanner.business.calendars.entities.BaseCalendar;
import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.planner.entities.DayAssignment;
import org.navalplanner.business.planner.entities.TaskElement;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.web.common.components.bandboxsearch.BandboxMultipleSearch;
import org.navalplanner.web.common.components.finders.FilterPair;
import org.navalplanner.web.planner.chart.Chart;
import org.navalplanner.web.planner.chart.ChartFiller;
import org.navalplanner.web.planner.company.CompanyPlanningModel;
import org.navalplanner.web.planner.order.BankHolidaysMarker;
import org.navalplanner.web.planner.order.IOrderPlanningGate;
import org.navalplanner.web.security.SecurityUtils;
@ -43,23 +57,34 @@ import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
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.IChartVisibilityChangedListener;
import org.zkoss.ganttz.data.resourceload.LoadTimeLine;
import org.zkoss.ganttz.resourceload.IFilterChangedListener;
import org.zkoss.ganttz.resourceload.ISeeScheduledOfListener;
import org.zkoss.ganttz.resourceload.ResourcesLoadPanel;
import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand;
import org.zkoss.ganttz.timetracker.TimeTracker;
import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener;
import org.zkoss.ganttz.timetracker.zoom.SeveralModificators;
import org.zkoss.ganttz.timetracker.zoom.ZoomLevel;
import org.zkoss.ganttz.util.Interval;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zk.ui.util.Composer;
import org.zkoss.zul.Button;
import org.zkoss.zul.Datebox;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Tab;
import org.zkoss.zul.Tabbox;
import org.zkoss.zul.Tabpanel;
import org.zkoss.zul.Tabpanels;
import org.zkoss.zul.Tabs;
/**
* Controller for global resourceload view
@ -93,6 +118,12 @@ public class ResourceLoadController implements Composer {
private boolean currentFilterByResources = true;
private boolean filterHasChanged = false;
private ZoomLevel zoomLevel;
private List<IZoomLevelChangedListener> keepAliveZoomListeners = new ArrayList<IZoomLevelChangedListener>();
private List<IChartVisibilityChangedListener> keepAliveChartVisibilityListeners = new ArrayList<IChartVisibilityChangedListener>();
public ResourceLoadController() {
}
@ -184,7 +215,7 @@ public class ResourceLoadController implements Composer {
}
private TimeTracker buildTimeTracker() {
ZoomLevel zoomLevel = (timeTracker == null) ? resourceLoadModel
zoomLevel = (timeTracker == null) ? resourceLoadModel
.calculateInitialZoomLevel() : timeTracker.getDetailLevel();
return new TimeTracker(resourceLoadModel.getViewInterval(), zoomLevel,
SeveralModificators.create(), SeveralModificators
@ -220,7 +251,8 @@ public class ResourceLoadController implements Composer {
}
} else {
resourcesLoadPanel = new ResourcesLoadPanel(resourceLoadModel
.getLoadTimeLines(), timeTracker, parent, buildLoadChart());
.getLoadTimeLines(), timeTracker, parent);
resourcesLoadPanel.setLoadChart(buildChart());
if(filterBy == null) {
addWorkersBandbox();
addTimeFilter();
@ -378,15 +410,263 @@ public class ResourceLoadController implements Composer {
}
private org.zkoss.zk.ui.Component buildLoadChart() {
private org.zkoss.zk.ui.Component buildChart() {
Tabbox chartComponent = new Tabbox();
chartComponent.setOrient("vertical");
chartComponent.setHeight("200px");
Tabs chartTabs = new Tabs();
chartTabs.appendChild(new Tab(_("Load")));
chartComponent.appendChild(chartTabs);
chartTabs.setWidth("124px");
Tabpanels chartTabpanels = new Tabpanels();
Tabpanel loadChartPannel = new Tabpanel();
// FIXME CSS problem and remove next line
// CompanyPlanningModel.appendLoadChartAndLegend(loadChartPannel,
// buildLoadChart());
loadChartPannel.appendChild(buildLoadChart());
chartTabpanels.appendChild(loadChartPannel);
chartComponent.appendChild(chartTabpanels);
return chartComponent;
}
private Timeplot buildLoadChart() {
Timeplot chartLoadTimeplot = createEmptyTimeplot();
Chart loadChart = new Chart(chartLoadTimeplot,
new ResourceLoadChartFiller(), timeTracker);
loadChart.setZoomLevel(zoomLevel);
if (resourcesLoadPanel.isVisibleChart()) {
loadChart.fillChart();
}
timeTracker.addZoomListener(fillOnZoomChange(loadChart));
resourcesLoadPanel
.addChartVisibilityListener(fillOnChartVisibilityChange(loadChart));
return chartLoadTimeplot;
}
private IZoomLevelChangedListener fillOnZoomChange(final Chart loadChart) {
IZoomLevelChangedListener zoomListener = new IZoomLevelChangedListener() {
@Override
public void zoomLevelChanged(ZoomLevel detailLevel) {
loadChart.setZoomLevel(detailLevel);
if (resourcesLoadPanel.isVisibleChart()) {
loadChart.fillChart();
}
}
};
keepAliveZoomListeners.add(zoomListener);
return zoomListener;
}
private IChartVisibilityChangedListener fillOnChartVisibilityChange(
final Chart loadChart) {
IChartVisibilityChangedListener chartVisibilityChangedListener = new IChartVisibilityChangedListener() {
@Override
public void chartVisibilityChanged(final boolean visible) {
if (visible) {
loadChart.fillChart();
}
}
};
keepAliveChartVisibilityListeners.add(chartVisibilityChangedListener);
return chartVisibilityChangedListener;
}
private Timeplot createEmptyTimeplot() {
Timeplot timeplot = new Timeplot();
timeplot.appendChild(new Plotinfo());
return timeplot;
}
private class ResourceLoadChartFiller extends ChartFiller {
@Override
public void fillChart(Timeplot chart, Interval interval, Integer size) {
chart.getChildren().clear();
chart.invalidate();
String javascript = "zkTasklist.timeplotcontainer_rescroll();";
Clients.evalJavaScript(javascript);
resetMinimumAndMaximumValueForChart();
Plotinfo plotInfoLoad = createPlotinfo(getLoad(interval.getStart(),
interval.getFinish()), interval);
plotInfoLoad
.setFillColor(CompanyPlanningModel.COLOR_ASSIGNED_LOAD_GLOBAL);
plotInfoLoad.setLineWidth(0);
Plotinfo plotInfoMax = createPlotinfo(
getCalendarMaximumAvailability(interval.getStart(),
interval.getFinish()), interval);
plotInfoMax
.setLineColor(CompanyPlanningModel.COLOR_CAPABILITY_LINE);
plotInfoMax.setFillColor("#FFFFFF");
plotInfoMax.setLineWidth(2);
Plotinfo plotInfoOverload = createPlotinfo(getOverload(interval
.getStart(), interval.getFinish()), interval);
plotInfoOverload
.setFillColor(CompanyPlanningModel.COLOR_OVERLOAD_GLOBAL);
plotInfoOverload.setLineWidth(0);
ValueGeometry valueGeometry = getValueGeometry();
TimeGeometry timeGeometry = getTimeGeometry(interval);
appendPlotinfo(chart, plotInfoLoad, valueGeometry, timeGeometry);
appendPlotinfo(chart, plotInfoMax, valueGeometry, timeGeometry);
appendPlotinfo(chart, plotInfoOverload, valueGeometry, timeGeometry);
chart.setWidth(size + "px");
chart.setHeight("150px");
}
private SortedMap<LocalDate, BigDecimal> getLoad(Date start, Date finish) {
List<DayAssignment> dayAssignments = resourceLoadModel
.getDayAssignments();
SortedMap<LocalDate, Map<Resource, Integer>> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments);
SortedMap<LocalDate, BigDecimal> mapDayAssignments = calculateHoursAdditionByDayWithoutOverload(dayAssignmentGrouped);
return mapDayAssignments;
}
private SortedMap<LocalDate, BigDecimal> getOverload(Date start,
Date finish) {
List<DayAssignment> dayAssignments = resourceLoadModel
.getDayAssignments();
SortedMap<LocalDate, Map<Resource, Integer>> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments);
SortedMap<LocalDate, BigDecimal> mapDayAssignments = calculateHoursAdditionByDayJustOverload(dayAssignmentGrouped);
SortedMap<LocalDate, BigDecimal> mapMaxAvailability = calculateHoursAdditionByDay(
resourceLoadModel.getResources(), start, finish);
for (LocalDate day : mapDayAssignments.keySet()) {
if ((day.compareTo(new LocalDate(start)) >= 0)
&& (day.compareTo(new LocalDate(finish)) <= 0)) {
BigDecimal overloadHours = mapDayAssignments.get(day);
BigDecimal maxHours = mapMaxAvailability.get(day);
mapDayAssignments.put(day, overloadHours.add(maxHours));
}
}
return mapDayAssignments;
}
private SortedMap<LocalDate, BigDecimal> calculateHoursAdditionByDayWithoutOverload(
SortedMap<LocalDate, Map<Resource, Integer>> dayAssignmentGrouped) {
SortedMap<LocalDate, Integer> map = new TreeMap<LocalDate, Integer>();
for (LocalDate day : dayAssignmentGrouped.keySet()) {
int result = 0;
for (Resource resource : dayAssignmentGrouped.get(day).keySet()) {
BaseCalendar calendar = resource.getCalendar();
int workableHours = SameWorkHoursEveryDay
.getDefaultWorkingDay().getCapacityAt(day);
if (calendar != null) {
workableHours = calendar.getCapacityAt(day);
}
int assignedHours = dayAssignmentGrouped.get(day).get(
resource);
if (assignedHours <= workableHours) {
result += assignedHours;
} else {
result += workableHours;
}
}
map.put(day, result);
}
return convertAsNeededByZoom(convertToBigDecimal(map));
}
private SortedMap<LocalDate, BigDecimal> calculateHoursAdditionByDayJustOverload(
SortedMap<LocalDate, Map<Resource, Integer>> dayAssignmentGrouped) {
SortedMap<LocalDate, Integer> map = new TreeMap<LocalDate, Integer>();
for (LocalDate day : dayAssignmentGrouped.keySet()) {
int result = 0;
for (Resource resource : dayAssignmentGrouped.get(day).keySet()) {
BaseCalendar calendar = resource.getCalendar();
int workableHours = SameWorkHoursEveryDay
.getDefaultWorkingDay().getCapacityAt(day);
if (calendar != null) {
workableHours = calendar.getCapacityAt(day);
}
int assignedHours = dayAssignmentGrouped.get(day).get(
resource);
if (assignedHours > workableHours) {
result += assignedHours - workableHours;
}
}
map.put(day, result);
}
return convertAsNeededByZoom(convertToBigDecimal(map));
}
private SortedMap<LocalDate, BigDecimal> getCalendarMaximumAvailability(
Date start, Date finish) {
SortedMap<LocalDate, BigDecimal> mapDayAssignments = calculateHoursAdditionByDay(
resourceLoadModel.getResources(), start, finish);
return mapDayAssignments;
}
private SortedMap<LocalDate, BigDecimal> calculateHoursAdditionByDay(
List<Resource> resources, Date start, Date finish) {
return new HoursByDayCalculator<Entry<LocalDate, List<Resource>>>() {
@Override
protected LocalDate getDayFor(
Entry<LocalDate, List<Resource>> element) {
return element.getKey();
}
@Override
protected int getHoursFor(
Entry<LocalDate, List<Resource>> element) {
LocalDate day = element.getKey();
List<Resource> resources = element.getValue();
return sumHoursForDay(resources, day);
}
}.calculate(getResourcesByDateBetween(resources, start, finish));
}
private Set<Entry<LocalDate, List<Resource>>> getResourcesByDateBetween(
List<Resource> resources, Date start, Date finish) {
LocalDate end = new LocalDate(finish);
Map<LocalDate, List<Resource>> result = new HashMap<LocalDate, List<Resource>>();
for (LocalDate date = new LocalDate(start); date.compareTo(end) <= 0; date = date
.plusDays(1)) {
result.put(date, resources);
}
return result.entrySet();
}
}
}

View file

@ -36,14 +36,18 @@ import java.util.Set;
import java.util.Map.Entry;
import org.joda.time.LocalDate;
import org.navalplanner.business.calendars.daos.IBaseCalendarDAO;
import org.navalplanner.business.calendars.entities.ResourceCalendar;
import org.navalplanner.business.common.BaseEntity;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
import org.navalplanner.business.orders.daos.IOrderDAO;
import org.navalplanner.business.orders.daos.IOrderElementDAO;
import org.navalplanner.business.orders.entities.Order;
import org.navalplanner.business.orders.entities.OrderElement;
import org.navalplanner.business.planner.daos.IDayAssignmentDAO;
import org.navalplanner.business.planner.daos.IResourceAllocationDAO;
import org.navalplanner.business.planner.daos.ITaskElementDAO;
import org.navalplanner.business.planner.entities.DayAssignment;
import org.navalplanner.business.planner.entities.GenericResourceAllocation;
import org.navalplanner.business.planner.entities.ResourceAllocation;
import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
@ -59,6 +63,7 @@ import org.navalplanner.business.users.entities.OrderAuthorization;
import org.navalplanner.business.users.entities.OrderAuthorizationType;
import org.navalplanner.business.users.entities.User;
import org.navalplanner.business.users.entities.UserRole;
import org.navalplanner.web.calendars.BaseCalendarModel;
import org.navalplanner.web.security.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@ -110,6 +115,12 @@ public class ResourceLoadModel implements IResourceLoadModel {
private Date initDateFilter;
private Date endDateFilter;
@Autowired
private IDayAssignmentDAO dayAssignmentDAO;
@Autowired
private IBaseCalendarDAO baseCalendarDAO;
@Override
@Transactional(readOnly = true)
public void initGlobalView(boolean filterByResources) {
@ -747,6 +758,25 @@ public class ResourceLoadModel implements IResourceLoadModel {
public Date getInitDateFilter() {
return initDateFilter;
}
@Transactional(readOnly = true)
public List<DayAssignment> getDayAssignments() {
return dayAssignmentDAO.findByResources(getResources());
}
@Override
@Transactional(readOnly = true)
public List<Resource> getResources() {
List<Resource> resources = resourcesToShow();
for (Resource resource : resources) {
resourcesDAO.reattach(resource);
ResourceCalendar calendar = resource.getCalendar();
baseCalendarDAO.reattach(calendar);
BaseCalendarModel.forceLoadBaseCalendar(calendar);
resource.getAssignments().size();
}
return resources;
}
}
class PeriodsBuilder {