From a28163c015b6fdeb5e17b411f9273a06e31ce790 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Thu, 13 May 2010 13:18:06 +0200 Subject: [PATCH] ItEr56S14GraficaInferiorPantallaCargaRecursos: Added load chart to resource load view. --- .../resourceload/ResourcesLoadPanel.java | 35 ++- .../web/ganttz/zul/resourcesLoadLayout.zul | 4 +- .../planner/daos/DayAssignmentDAO.java | 12 + .../planner/daos/IDayAssignmentDAO.java | 3 + .../planner/company/CompanyPlanningModel.java | 4 +- .../web/resourceload/IResourceLoadModel.java | 5 + .../resourceload/ResourceLoadController.java | 286 +++++++++++++++++- .../web/resourceload/ResourceLoadModel.java | 30 ++ 8 files changed, 370 insertions(+), 9 deletions(-) diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java index 9bad8586d..638ce4ab5 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -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 chartVisibilityListeners = WeakReferencedListeners + .create(); + public ResourcesLoadPanel(List 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() { + @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; + } + } \ No newline at end of file diff --git a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul index cde328466..0d38b3997 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul @@ -87,7 +87,9 @@ resourcesLoadPanel = self; - +
diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java index 77189dfe2..a2ef3adea 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/DayAssignmentDAO.java @@ -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 } return criteria.list(); } + + @Override + public List findByResources(List resources) { + if (resources.isEmpty()) { + return Collections.emptyList(); + } + return getSession().createCriteria(DayAssignment.class).add( + Restrictions.in("resource", resources)).list(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java index 4f5fc07d9..25479d31c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IDayAssignmentDAO.java @@ -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 listFilteredByDate(LocalDate init, LocalDate end); + public List findByResources(List resources); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java index 5b0f97c24..b7c7eaec0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java @@ -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"); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java index 16232094c..b7fa42529 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java @@ -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 getDayAssignments(); + + List getResources(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java index 92f34d183..4f5b479f3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java @@ -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 keepAliveZoomListeners = new ArrayList(); + + private List keepAliveChartVisibilityListeners = new ArrayList(); + 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 getLoad(Date start, Date finish) { + List dayAssignments = resourceLoadModel + .getDayAssignments(); + + SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); + SortedMap mapDayAssignments = calculateHoursAdditionByDayWithoutOverload(dayAssignmentGrouped); + + return mapDayAssignments; + } + + private SortedMap getOverload(Date start, + Date finish) { + List dayAssignments = resourceLoadModel + .getDayAssignments(); + + SortedMap> dayAssignmentGrouped = groupDayAssignmentsByDayAndResource(dayAssignments); + SortedMap mapDayAssignments = calculateHoursAdditionByDayJustOverload(dayAssignmentGrouped); + SortedMap 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 calculateHoursAdditionByDayWithoutOverload( + SortedMap> dayAssignmentGrouped) { + SortedMap map = new TreeMap(); + + 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 calculateHoursAdditionByDayJustOverload( + SortedMap> dayAssignmentGrouped) { + SortedMap map = new TreeMap(); + + 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 getCalendarMaximumAvailability( + Date start, Date finish) { + SortedMap mapDayAssignments = calculateHoursAdditionByDay( + resourceLoadModel.getResources(), start, finish); + + return mapDayAssignments; + } + + private SortedMap calculateHoursAdditionByDay( + List resources, Date start, Date finish) { + return new HoursByDayCalculator>>() { + + @Override + protected LocalDate getDayFor( + Entry> element) { + return element.getKey(); + } + + @Override + protected int getHoursFor( + Entry> element) { + LocalDate day = element.getKey(); + List resources = element.getValue(); + return sumHoursForDay(resources, day); + } + + }.calculate(getResourcesByDateBetween(resources, start, finish)); + } + + private Set>> getResourcesByDateBetween( + List resources, Date start, Date finish) { + LocalDate end = new LocalDate(finish); + Map> result = new HashMap>(); + for (LocalDate date = new LocalDate(start); date.compareTo(end) <= 0; date = date + .plusDays(1)) { + result.put(date, resources); + } + return result.entrySet(); + } + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java index a5d887986..b49749f31 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java @@ -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 getDayAssignments() { + return dayAssignmentDAO.findByResources(getResources()); + } + + @Override + @Transactional(readOnly = true) + public List getResources() { + List 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 {