diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadPeriod.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadPeriod.java index df42133a7..ab7afdb22 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadPeriod.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadPeriod.java @@ -42,15 +42,23 @@ public class LoadPeriod { private final LoadLevel loadLevel; + private final int totalResourceWorkHours; - public LoadPeriod(LocalDate start, LocalDate end, LoadLevel loadLevel) { + private final int assignedHours; + + public LoadPeriod(LocalDate start, LocalDate end, + int totalResourceWorkHours, int assignedHours, LoadLevel loadLevel) { Validate.notNull(start); Validate.notNull(end); Validate.notNull(loadLevel); + Validate.notNull(totalResourceWorkHours); + Validate.notNull(assignedHours); Validate.isTrue(!start.isAfter(end)); this.start = start; this.end = end; this.loadLevel = loadLevel; + this.totalResourceWorkHours = totalResourceWorkHours; + this.assignedHours = assignedHours; } public LocalDate getStart() { @@ -114,4 +122,11 @@ public class LoadPeriod { return loadLevel; } + public int getTotalResourceWorkHours() { + return totalResourceWorkHours; + } + + public int getAssignedHours() { + return assignedHours; + } } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java index 78e895e3d..442853a15 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimeLine.java @@ -20,23 +20,32 @@ package org.zkoss.ganttz.data.resourceload; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; import java.util.List; import org.apache.commons.lang.Validate; import org.joda.time.LocalDate; +import org.zkoss.ganttz.util.Interval; public class LoadTimeLine { private final String conceptName; private final List loadPeriods; + private final String type; + private final List children; + public LoadTimeLine(String conceptName, List loadPeriods) { Validate.notEmpty(conceptName); Validate.notNull(loadPeriods); this.loadPeriods = LoadPeriod.sort(loadPeriods); this.conceptName = conceptName; this.type = ""; + this.children = Collections + .unmodifiableList(new ArrayList()); } public LoadTimeLine(String conceptName, List loadPeriods, @@ -46,6 +55,21 @@ public class LoadTimeLine { this.loadPeriods = LoadPeriod.sort(loadPeriods); this.conceptName = conceptName; this.type = type; + this.children = Collections + .unmodifiableList(new ArrayList()); + } + + public LoadTimeLine(LoadTimeLine principal, List children) { + Validate.notEmpty(principal.getConceptName()); + Validate.notNull(principal.getLoadPeriods()); + this.loadPeriods = LoadPeriod.sort(principal.getLoadPeriods()); + this.conceptName = principal.getConceptName(); + this.type = principal.getType(); + Validate.notNull(children); + allChildrenAreNotEmpty(children); + this.children = Collections + .unmodifiableList(new ArrayList(children)); + } public List getLoadPeriods() { @@ -64,7 +88,7 @@ public class LoadTimeLine { return loadPeriods.get(loadPeriods.size() - 1); } - public LocalDate getStart() { + public LocalDate getStartPeriod() { if (isEmpty()) { return null; } @@ -75,7 +99,7 @@ public class LoadTimeLine { return loadPeriods.isEmpty(); } - public LocalDate getEnd() { + public LocalDate getEndPeriod() { if (isEmpty()) { return null; } @@ -85,4 +109,93 @@ public class LoadTimeLine { public String getType() { return this.type; } + + public static Interval getIntervalFrom(List timeLines) { + Validate.notEmpty(timeLines); + LocalDate start = null; + LocalDate end = null; + for (LoadTimeLine loadTimeLine : timeLines) { + Validate.notNull(loadTimeLine.getStart()); + start = min(start, loadTimeLine.getStart()); + Validate.notNull(loadTimeLine.getEnd()); + end = max(end, loadTimeLine.getEnd()); + } + return new Interval(toDate(start), toDate(end)); + } + + private static Date toDate(LocalDate localDate) { + return localDate.toDateTimeAtStartOfDay().toDate(); + } + + private static LocalDate max(LocalDate one, LocalDate other) { + if (one == null) { + return other; + } + if (other == null) { + return one; + } + return one.compareTo(other) > 0 ? one : other; + } + + private static LocalDate min(LocalDate one, LocalDate other) { + if (one == null) { + return other; + } + if (other == null) { + return one; + } + return one.compareTo(other) < 0 ? one : other; + } + + private static void allChildrenAreNotEmpty(List lines) { + for (LoadTimeLine l : lines) { + if (l.isEmpty()) { + throw new IllegalArgumentException(l + " is empty"); + } + if (l.hasChildren()) { + allChildrenAreNotEmpty(l.getChildren()); + } + } + } + + public boolean hasChildren() { + return (!children.isEmpty()); + } + + public List getChildren() { + return children; + } + + public List getAllChildren() { + List result = new ArrayList(); + for (LoadTimeLine child : children) { + result.addAll(child.getAllChildren()); + result.add(child); + } + return result; + } + + public LocalDate getStart() { + LocalDate result = getStartPeriod(); + for (LoadTimeLine loadTimeLine : getChildren()) { + LocalDate start = loadTimeLine.getStart(); + if (start != null) { + result = result == null || result.compareTo(start) > 0 ? start + : result; + } + } + return result; + } + + public LocalDate getEnd() { + LocalDate result = getEndPeriod(); + for (LoadTimeLine loadTimeLine : getChildren()) { + LocalDate end = loadTimeLine.getEnd(); + if (end != null) { + result = result == null || result.compareTo(end) < 0 ? end : result; + } + } + return result; + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimelinesGroup.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimelinesGroup.java deleted file mode 100644 index 415b29a88..000000000 --- a/ganttzk/src/main/java/org/zkoss/ganttz/data/resourceload/LoadTimelinesGroup.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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 . - */ - -package org.zkoss.ganttz.data.resourceload; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import org.apache.commons.lang.Validate; -import org.joda.time.LocalDate; -import org.zkoss.ganttz.util.Interval; - -public class LoadTimelinesGroup { - - public static Interval getIntervalFrom(List timeLines) { - Validate.notEmpty(timeLines); - LocalDate start = null; - LocalDate end = null; - for (LoadTimelinesGroup loadTimelinesGroup : timeLines) { - Validate.notNull(loadTimelinesGroup.getStart()); - start = min(start, loadTimelinesGroup.getStart()); - Validate.notNull(loadTimelinesGroup.getEnd()); - end = max(end, loadTimelinesGroup.getEnd()); - } - return new Interval(toDate(start), toDate(end)); - } - - private static Date toDate(LocalDate localDate) { - return localDate.toDateTimeAtStartOfDay().toDate(); - } - - private static LocalDate max(LocalDate one, LocalDate other) { - if (one == null) { - return other; - } - if (other == null) { - return one; - } - return one.compareTo(other) > 0 ? one : other; - } - - private static LocalDate min(LocalDate one, LocalDate other) { - if (one == null) { - return other; - } - if (other == null) { - return one; - } - return one.compareTo(other) < 0 ? one : other; - } - - private final LoadTimeLine principal; - - private final List children; - - public LoadTimelinesGroup(LoadTimeLine principal, - List children) { - Validate.notNull(principal); - Validate.notNull(children); - allChildrenAreNotEmpty(children); - this.principal = principal; - this.children = Collections - .unmodifiableList(new ArrayList(children)); - } - - private static void allChildrenAreNotEmpty( - List lines) { - for (LoadTimeLine l : lines) { - if (l.isEmpty()) { - throw new IllegalArgumentException(l + " is empty"); - } - } - } - - public LoadTimeLine getPrincipal() { - return principal; - } - - public List getChildren() { - return children; - } - - private List getAll() { - List result = new ArrayList(); - result.add(getPrincipal()); - result.addAll(getChildren()); - return result; - } - - public LocalDate getStart() { - LocalDate result = null; - for (LoadTimeLine loadTimeLine : getAll()) { - LocalDate start = loadTimeLine.getStart(); - result = result == null || result.compareTo(start) > 0 ? start - : result; - } - return result; - } - - public LocalDate getEnd() { - LocalDate result = null; - for (LoadTimeLine loadTimeLine : getAll()) { - LocalDate end = loadTimeLine.getEnd(); - result = result == null || result.compareTo(end) < 0 ? end - : result; - } - return result; - } - - public boolean isEmpty() { - return principal.isEmpty(); - } - -} - diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java index 0b20caa85..29da10831 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadComponent.java @@ -96,8 +96,15 @@ public class ResourceLoadComponent extends XulElement { Div result = new Div(); result.setClass(String.format("taskassignmentinterval %s", loadPeriod .getLoadLevel().getCategory())); - result.setTooltiptext("Load: " - + loadPeriod.getLoadLevel().getPercentage() + "%"); + + String load = "Load: " + loadPeriod.getLoadLevel().getPercentage() + + "% , "; + if (loadPeriod.getLoadLevel().getPercentage() == Integer.MAX_VALUE) { + load = ""; + } + result.setTooltiptext(load + + "total work hours: " + loadPeriod.getTotalResourceWorkHours() + + " , " + "assigned hours: " + loadPeriod.getAssignedHours()); result.setLeft(forCSS(getStartPixels(datesMapper, loadPeriod))); result.setWidth(forCSS(getWidthPixels(datesMapper, loadPeriod))); diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java index 2195cec49..adb2a2c41 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java @@ -20,6 +20,10 @@ package org.zkoss.ganttz.resourceload; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + import org.zkoss.ganttz.data.resourceload.LoadTimeLine; import org.zkoss.ganttz.util.MutableTreeModel; import org.zkoss.zk.ui.Component; @@ -31,6 +35,7 @@ import org.zkoss.zul.Div; import org.zkoss.zul.Label; import org.zkoss.zul.Popup; import org.zkoss.zul.Treecell; +import org.zkoss.zul.Treechildren; import org.zkoss.zul.Treeitem; import org.zkoss.zul.TreeitemRenderer; import org.zkoss.zul.Treerow; @@ -41,7 +46,8 @@ public class ResourceLoadLeftPane extends HtmlMacroComponent { private MutableTreeModel modelForTree; private final ResourceLoadList resourceLoadList; - public ResourceLoadLeftPane(MutableTreeModel modelForTree, + public ResourceLoadLeftPane( +MutableTreeModel modelForTree, ResourceLoadList resourceLoadList) { this.resourceLoadList = resourceLoadList; this.modelForTree = modelForTree; @@ -58,26 +64,31 @@ public class ResourceLoadLeftPane extends HtmlMacroComponent { private TreeitemRenderer getRendererForTree() { return new TreeitemRenderer() { @Override - public void render(Treeitem item, Object data) throws Exception { + public void render(Treeitem item, Object data) + throws Exception { LoadTimeLine line = (LoadTimeLine) data; - item.setOpen(true); + item.setOpen(false); + item.setValue(line); + Treerow row = new Treerow(); Treecell cell = new Treecell(); Component component = createComponent(line); item.appendChild(row); row.appendChild(cell); cell.appendChild(component); + collapse(line); addExpandedListener(item, line); } - private void addExpandedListener(Treeitem item, + private void addExpandedListener(final Treeitem item, final LoadTimeLine line) { item.addEventListener("onOpen", new EventListener() { @Override public void onEvent(Event event) throws Exception { OpenEvent openEvent = (OpenEvent) event; if (openEvent.isOpen()) { - expand(line); + List closed = calculatedClosedItems(item); + expand(line, closed); } else { collapse(line); } @@ -101,8 +112,46 @@ public class ResourceLoadLeftPane extends HtmlMacroComponent { resourceLoadList.collapse(line); } - private void expand(LoadTimeLine line) { - resourceLoadList.expand(line); + private void expand(LoadTimeLine line, List closed) { + resourceLoadList.expand(line, closed); + } + + private List calculatedClosedItems(Treeitem item) { + List result = new ArrayList(); + Treechildren treeChildren = item.getTreechildren(); + if (treeChildren != null) { + List myTreeItems = (List) treeChildren + .getChildren(); + Iterator iterator = myTreeItems.iterator(); + while (iterator.hasNext()) { + Treeitem child = (Treeitem) iterator.next(); + if (!child.isOpen()) { + result.addAll(getLineChildrenBy(child)); + } else { + result.addAll(calculatedClosedItems(child)); + } + } + } + return result; + } + + private List getLineChildrenBy(Treeitem item) { + List result = new ArrayList(); + LoadTimeLine line = getLineByTreeitem(item); + if (line != null) { + result.addAll(line.getAllChildren()); + } + return result; + } + + private LoadTimeLine getLineByTreeitem(Treeitem child) { + LoadTimeLine line = null; + try { + line = (LoadTimeLine) child.getValue(); + } catch (Exception e) { + return null; + } + return line; } private Tree getContainerTree() { @@ -115,7 +164,6 @@ public class ResourceLoadLeftPane extends HtmlMacroComponent { return result; } - private Component createSecondLevel(LoadTimeLine loadTimeLine) { Div result = createLabelWithName(loadTimeLine); result.setSclass("secondlevel"); diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java index 7b350f17e..e88e9771c 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadList.java @@ -70,15 +70,6 @@ public class ResourceLoadList extends HtmlMacroComponent implements } } - private List getChildrenOf(LoadTimeLine parent) { - List result = new ArrayList(); - final int length = timelinesTree.getChildCount(parent); - for (int i = 0; i < length; i++) { - result.add(timelinesTree.getChild(parent, i)); - } - return result; - } - private IZoomLevelChangedListener adjustTimeTrackerSizeListener() { return new IZoomLevelChangedListener() { @@ -103,7 +94,7 @@ public class ResourceLoadList extends HtmlMacroComponent implements } public void collapse(LoadTimeLine line) { - for (LoadTimeLine l : getChildrenOf(line)) { + for (LoadTimeLine l : line.getAllChildren()) { getComponentFor(l).detach(); } } @@ -114,10 +105,14 @@ public class ResourceLoadList extends HtmlMacroComponent implements return resourceLoadComponent; } - public void expand(LoadTimeLine line) { + public void expand(LoadTimeLine line, List closed) { ResourceLoadComponent parentComponent = getComponentFor(line); Component nextSibling = parentComponent.getNextSibling(); - for (LoadTimeLine loadTimeLine : getChildrenReverseOrderFor(line)) { + + List childrenToOpen = getChildrenReverseOrderFor(line); + childrenToOpen.removeAll(closed); + + for (LoadTimeLine loadTimeLine : childrenToOpen) { ResourceLoadComponent child = getComponentFor(loadTimeLine); insertBefore(child, nextSibling); nextSibling = child; @@ -126,7 +121,7 @@ public class ResourceLoadList extends HtmlMacroComponent implements } private List getChildrenReverseOrderFor(LoadTimeLine line) { - List childrenOf = getChildrenOf(line); + List childrenOf = line.getAllChildren(); Collections.reverse(childrenOf); return childrenOf; } 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 f321f5617..ee5f55529 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -24,7 +24,6 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.zkoss.ganttz.data.resourceload.LoadTimeLine; -import org.zkoss.ganttz.data.resourceload.LoadTimelinesGroup; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.TimeTrackerComponent; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; @@ -60,7 +59,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private ResourceLoadList resourceLoadList; - private final List groups; + private final List groups; private MutableTreeModel treeModel; @@ -68,7 +67,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private Listbox listZoomLevels; - public ResourcesLoadPanel(List groups, + public ResourcesLoadPanel(List groups, TimeTracker timeTracker) { this.groups = groups; this.timeTracker = timeTracker; @@ -145,18 +144,28 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private MutableTreeModel createModelForTree() { MutableTreeModel result = MutableTreeModel .create(LoadTimeLine.class); - for (LoadTimelinesGroup loadTimelinesGroup : this.groups) { - LoadTimeLine principal = loadTimelinesGroup.getPrincipal(); - result.addToRoot(principal); - result.add(principal, loadTimelinesGroup.getChildren()); + for (LoadTimeLine loadTimeLine : this.groups) { + result.addToRoot(loadTimeLine); + result = addNodes(result, loadTimeLine); } return result; } + + private MutableTreeModel addNodes( + MutableTreeModel tree, LoadTimeLine parent) { + if (!parent.getChildren().isEmpty()) { + tree.add(parent, parent.getChildren()); + for (LoadTimeLine loadTimeLine : parent.getChildren()) { + tree = addNodes(tree, loadTimeLine); + } + } + return tree; + } + private TimeTrackerComponent timeTrackerForResourcesLoadPanel( TimeTracker timeTracker) { return new TimeTrackerComponent(timeTracker) { - @Override protected void scrollHorizontalPercentage(int pixelsDisplacement) { response("", new AuInvoke(resourceLoadList, diff --git a/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadPeriodTest.java b/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadPeriodTest.java index 4f5873d86..f529ec6c6 100644 --- a/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadPeriodTest.java +++ b/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadPeriodTest.java @@ -54,35 +54,44 @@ public class LoadPeriodTest { private void givenExampleLoadPeriod(LocalDate start, LocalDate end, LoadLevel loadLevel) { - loadPeriod = new LoadPeriod(start, end, loadLevel); + loadPeriod = new LoadPeriod(start, end, totalHours, assignedHours, + loadLevel); } @Test(expected = IllegalArgumentException.class) public void aLoadPeriodMustHaveAStartDate() { - new LoadPeriod(null, new LocalDate(), correctLoadLevel()); + new LoadPeriod(null, new LocalDate(), totalHours, assignedHours, + correctLoadLevel()); } + private static final int totalHours = 100; + + private static final int assignedHours = 40; + private static LoadLevel correctLoadLevel() { return new LoadLevel(40); } @Test(expected = IllegalArgumentException.class) public void aLoadPeriodMustHaveAnEndDate() { - new LoadPeriod(new LocalDate(), null, correctLoadLevel()); + new LoadPeriod(new LocalDate(), null, totalHours, assignedHours, + correctLoadLevel()); } @Test(expected = IllegalArgumentException.class) public void theEndDateCantBeBeforeTheStartDate() { LocalDate start = new LocalDate(2009, 10, 4); LocalDate end = new LocalDate(2009, 10, 3); - new LoadPeriod(start, end, correctLoadLevel()); + new LoadPeriod(start, end, totalHours, assignedHours, + correctLoadLevel()); } @Test public void theEndDateCanBeTheSameThanTheStartDate() { LocalDate start = new LocalDate(2009, 10, 4); LocalDate end = new LocalDate(2009, 10, 4); - new LoadPeriod(start, end, correctLoadLevel()); + new LoadPeriod(start, end, totalHours, assignedHours, + correctLoadLevel()); } @Test @@ -128,7 +137,8 @@ public class LoadPeriodTest { private static LoadPeriod create(int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) { return new LoadPeriod(new LocalDate(startYear, startMonth, startDay), - new LocalDate(endYear, endMonth, endDay), correctLoadLevel()); + new LocalDate(endYear, endMonth, endDay), totalHours, + assignedHours, correctLoadLevel()); } @Test(expected = IllegalArgumentException.class) @@ -178,7 +188,7 @@ public class LoadPeriodTest { @Test(expected = IllegalArgumentException.class) public void aLoadPeriodMustHaveANotNullLoadLevel() { givenExampleLoadPeriod(); - new LoadPeriod(start, end, null); + new LoadPeriod(start, end, totalHours, assignedHours, null); } @Test diff --git a/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadTimelineTest.java b/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadTimelineTest.java index 3351b34b2..8f15209c0 100644 --- a/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadTimelineTest.java +++ b/ganttzk/src/test/java/org/zkoss/ganttz/data/resourceload/LoadTimelineTest.java @@ -62,7 +62,8 @@ public class LoadTimelineTest { conceptName = "bla"; loadTimeLine = new LoadTimeLine(conceptName, Arrays .asList(new LoadPeriod(new LocalDate(2009, 10, 5), - new LocalDate(2009, 10, 11), new LoadLevel(20)))); + new LocalDate(2009, 10, 11), 100, 20, + new LoadLevel(20)))); } @Test @@ -75,9 +76,9 @@ public class LoadTimelineTest { @Test public void aLoadTimelineSortsItsReceivedPeriods() { LoadPeriod l1 = new LoadPeriod(new LocalDate(2009, 10, 5), - new LocalDate(2009, 10, 11), new LoadLevel(20)); + new LocalDate(2009, 10, 11), 100, 20, new LoadLevel(20)); LoadPeriod l2 = new LoadPeriod(new LocalDate(2009, 5, 3), - new LocalDate(2009, 6, 3), new LoadLevel(20)); + new LocalDate(2009, 6, 3), 100, 20, new LoadLevel(20)); LoadTimeLine loadTimeLine = new LoadTimeLine("bla", Arrays.asList(l1, l2)); List loadPeriods = loadTimeLine.getLoadPeriods(); @@ -88,9 +89,9 @@ public class LoadTimelineTest { @Test(expected = IllegalArgumentException.class) public void theLoadPeriodsMustNotOverlap() { LoadPeriod l1 = new LoadPeriod(new LocalDate(2009, 10, 5), - new LocalDate(2009, 10, 11), new LoadLevel(20)); + new LocalDate(2009, 10, 11), 100, 20, new LoadLevel(20)); LoadPeriod l2 = new LoadPeriod(new LocalDate(2009, 5, 3), - new LocalDate(2009, 10, 10), new LoadLevel(20)); + new LocalDate(2009, 10, 10), 100, 20, new LoadLevel(20)); new LoadTimeLine("bla", Arrays.asList(l1, l2)); } 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 3c44cfe19..d2f4a554c 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 @@ -23,7 +23,7 @@ package org.navalplanner.web.resourceload; import java.util.List; import org.navalplanner.business.orders.entities.Order; -import org.zkoss.ganttz.data.resourceload.LoadTimelinesGroup; +import org.zkoss.ganttz.data.resourceload.LoadTimeLine; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; @@ -33,7 +33,7 @@ public interface IResourceLoadModel { void initGlobalView(Order filterBy); - List getLoadTimeLines(); + List getLoadTimeLines(); Interval getViewInterval(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java index 9915fe54b..324d86b7e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/LoadPeriodGenerator.java @@ -196,7 +196,8 @@ abstract class LoadPeriodGenerator { } public LoadPeriod build() { - return new LoadPeriod(start, end, new LoadLevel( + return new LoadPeriod(start, end, getTotalWorkHours(), + getHoursAssigned(), new LoadLevel( calculateLoadPercentage())); } 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 605c901ce..fb88ef6f4 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 @@ -20,6 +20,8 @@ package org.navalplanner.web.resourceload; +import static org.navalplanner.web.I18nHelper._; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -36,6 +38,7 @@ import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.SeveralModificators; import org.zkoss.zk.ui.util.Composer; +import org.zkoss.zul.Messagebox; /** * Controller for global resourceload view @@ -69,16 +72,28 @@ public class ResourceLoadController implements Composer { } public void reload() { - if (filterBy == null) { - resourceLoadModel.initGlobalView(); - } else { - resourceLoadModel.initGlobalView(filterBy); + try { + if (filterBy == null) { + resourceLoadModel.initGlobalView(); + } else { + resourceLoadModel.initGlobalView(filterBy); + } + + ResourcesLoadPanel resourcesLoadPanel = buildResourcesLoadPanel(); + this.parent.getChildren().clear(); + this.parent.appendChild(resourcesLoadPanel); + resourcesLoadPanel.afterCompose(); + addCommands(resourcesLoadPanel); + } catch (IllegalArgumentException e) { + try { + Messagebox + .show( + _("Some lines have not allocation periods.\nBelow it shows the load all company resources"), + _("Error"), Messagebox.OK, Messagebox.ERROR); + } catch (InterruptedException o) { + throw new RuntimeException(e); + } } - ResourcesLoadPanel resourcesLoadPanel = buildResourcesLoadPanel(); - this.parent.getChildren().clear(); - this.parent.appendChild(resourcesLoadPanel); - resourcesLoadPanel.afterCompose(); - addCommands(resourcesLoadPanel); } private void addCommands(ResourcesLoadPanel resourcesLoadPanel) { 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 ab5818a64..952cfd81c 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 @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -36,7 +37,9 @@ import java.util.Map.Entry; import org.joda.time.LocalDate; 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.IResourceAllocationDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; import org.navalplanner.business.planner.entities.GenericResourceAllocation; @@ -54,7 +57,6 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; import org.zkoss.ganttz.data.resourceload.LoadPeriod; import org.zkoss.ganttz.data.resourceload.LoadTimeLine; -import org.zkoss.ganttz.data.resourceload.LoadTimelinesGroup; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; @@ -65,6 +67,9 @@ public class ResourceLoadModel implements IResourceLoadModel { @Autowired private IResourceDAO resourcesDAO; + @Autowired + private IOrderElementDAO orderElementDAO; + @Autowired private IOrderDAO orderDAO; @@ -74,7 +79,7 @@ public class ResourceLoadModel implements IResourceLoadModel { @Autowired private IResourceAllocationDAO resourceAllocationDAO; - private List loadTimeLines; + private List loadTimeLines; private Interval viewInterval; private Order filterBy; @@ -94,9 +99,9 @@ public class ResourceLoadModel implements IResourceLoadModel { } private void doGlobalView() { - loadTimeLines = calculateLoadTimelinesGroups(); + loadTimeLines = calculateLoadTimeLines(); if (!loadTimeLines.isEmpty()) { - viewInterval = LoadTimelinesGroup.getIntervalFrom(loadTimeLines); + viewInterval = LoadTimeLine.getIntervalFrom(loadTimeLines); } else { viewInterval = new Interval(new Date(), plusFiveYears(new Date())); } @@ -109,8 +114,8 @@ public class ResourceLoadModel implements IResourceLoadModel { return calendar.getTime(); } - private List calculateLoadTimelinesGroups() { - List result = new ArrayList(); + private List calculateLoadTimeLines() { + List result = new ArrayList(); result.addAll(groupsFor(resourcesToShow())); result.addAll(groupsFor(genericAllocationsByCriterion())); return result; @@ -159,15 +164,16 @@ public class ResourceLoadModel implements IResourceLoadModel { return resourcesDAO.list(Resource.class); } - private List groupsFor( + private List groupsFor( Map> genericAllocationsByCriterion) { - List result = new ArrayList(); + List result = new ArrayList(); for (Entry> entry : genericAllocationsByCriterion .entrySet()) { List allocations = ResourceAllocation .sortedByStartDate(entry.getValue()); - LoadTimelinesGroup group = new LoadTimelinesGroup(createPrincipal( - entry.getKey(), allocations), new ArrayList()); + LoadTimeLine group = new LoadTimeLine(createPrincipal( + entry.getKey(), allocations), + new ArrayList()); if (!group.isEmpty()) { result.add(group); } @@ -188,10 +194,10 @@ public class ResourceLoadModel implements IResourceLoadModel { value); } - private List groupsFor(List allResources) { - List result = new ArrayList(); + private List groupsFor(List allResources) { + List result = new ArrayList(); for (Resource resource : allResources) { - LoadTimelinesGroup group = buildGroup(resource); + LoadTimeLine group = buildGroup(resource); if (!group.isEmpty()) { result.add(group); } @@ -199,18 +205,99 @@ public class ResourceLoadModel implements IResourceLoadModel { return result; } - private LoadTimelinesGroup buildGroup(Resource resource) { + private LoadTimeLine buildGroup(Resource resource) { List> sortedByStartDate = ResourceAllocation .sortedByStartDate(resourceAllocationDAO .findAllocationsRelatedTo(resource)); - return new LoadTimelinesGroup(buildTimeLine(resource, resource - .getShortDescription(), sortedByStartDate), buildSecondLevel( - resource, sortedByStartDate)); + LoadTimeLine result = new LoadTimeLine(buildTimeLine( + resource, resource.getShortDescription(), sortedByStartDate), + buildSecondLevel(resource, sortedByStartDate)); + return result; + } private List buildSecondLevel(Resource resource, List> sortedByStartDate) { List result = new ArrayList(); + Map>> byOrder = byOrder(sortedByStartDate); + + if (filter()) { + // build time lines for current order + result.addAll(buildTimeLinesForOrder(resource, byOrder + .get(filterBy))); + byOrder.remove(filterBy); + // build time lines for other orders + LoadTimeLine lineOthersOrders = buildTimeLinesForOtherOrders( + resource, byOrder); + if (lineOthersOrders != null) { + result.add(lineOthersOrders); + } + } else { + result.addAll(buildTimeLinesGroupForOrder(resource, byOrder)); + } + return result; + } + + private LoadTimeLine buildTimeLinesForOtherOrders(Resource resource, + Map>> byOrder) { + List> resourceAllocations = getAllSortedValues(byOrder); + if (resourceAllocations.isEmpty()) { + return null; + } + LoadTimeLine group = new LoadTimeLine(buildTimeLine( + resource, "Others ordes", resourceAllocations), + buildTimeLinesGroupForOrder(resource, byOrder)); + return group; + } + + private List buildTimeLinesGroupForOrder( + Resource resource, Map>> byOrder) { + List result = new ArrayList(); + for (Order order : byOrder.keySet()) { + result.add(new LoadTimeLine(buildTimeLine(resource, + order + .getName(), byOrder.get(order)), buildTimeLinesForOrder( + resource, byOrder.get(order)))); + } + return result; + } + + private List> getAllSortedValues( + Map>> byOrder) { + List> resourceAllocations = new ArrayList>(); + for (List> listAllocations : byOrder.values()) { + resourceAllocations.addAll(listAllocations); + } + return ResourceAllocation.sortedByStartDate(resourceAllocations); + } + + private void initializeIfNeeded( + Map>> result, Order order) { + if (!result.containsKey(order)) { + result.put(order, new ArrayList>()); + } + } + + @Transactional(readOnly = true) + public Map>> byOrder( + Collection> allocations) { + Map>> result = new HashMap>>(); + for (ResourceAllocation resourceAllocation : allocations) { + if (resourceAllocation.getTask() != null) { + OrderElement orderElement = resourceAllocation.getTask() + .getOrderElement(); + Order order = orderElementDAO + .loadOrderAvoidingProxyFor(orderElement); + initializeIfNeeded(result, order); + result.get(order).add(resourceAllocation); + } + } + return result; + } + + private List buildTimeLinesForOrder(Resource resource, + List> sortedByStartDate) { + List result = new ArrayList(); result.addAll(buildTimeLinesForEachTask(resource, onlySpecific(sortedByStartDate))); result.addAll(buildTimeLinesForEachCriterion(resource, @@ -244,27 +331,49 @@ public class ResourceLoadModel implements IResourceLoadModel { Resource resource, List sortdByStartDate) { Map, List> byCriterions = GenericResourceAllocation .byCriterions(sortdByStartDate); + List result = new ArrayList(); for (Entry, List> entry : byCriterions .entrySet()) { - LoadTimeLine timeLine = buildTimeLine(entry.getKey(), resource, - entry.getValue()); - if (!timeLine.isEmpty()) { - result.add(timeLine); + + Map>> byTask = ResourceAllocation + .byTask(entry.getValue()); + for (Entry>> entryTask : byTask.entrySet()) { + + Task task = entryTask.getKey(); + List resouceAllocations = getGenericResourceAllocation(entryTask + .getValue()); + LoadTimeLine timeLine = buildTimeLine(entry.getKey(), task, + resource, resouceAllocations); + if (!timeLine.isEmpty()) { + result.add(timeLine); + } + } } return result; } - private List buildTimeLinesForEachTask(Resource resource, + private List getGenericResourceAllocation( + List> list) { + List result = new ArrayList(); + for (ResourceAllocation resourceAllocation : list) { + if (resourceAllocation instanceof GenericResourceAllocation) { + result.add((GenericResourceAllocation) resourceAllocation); + } + } + return result; + } + + private List buildTimeLinesForEachTask( + Resource resource, List sortedByStartDate) { Map>> byTask = ResourceAllocation .byTask(sortedByStartDate); List secondLevel = new ArrayList(); for (Entry>> entry : byTask.entrySet()) { Task task = entry.getKey(); - LoadTimeLine timeLine = buildTimeLine(resource, - getTaskName(task), + LoadTimeLine timeLine = buildTimeLine(resource, task.getName(), entry.getValue(), "specific"); if (!timeLine.isEmpty()) { secondLevel.add(timeLine); @@ -274,22 +383,22 @@ public class ResourceLoadModel implements IResourceLoadModel { return secondLevel; } - private String getTaskName(Task task) { - TaskElement topMost = task.getTopMost(); - String prefix = topMost != task ? (topMost.getName() + " :: ") : ""; - return prefix + task.getName(); - } - private LoadTimeLine buildTimeLine(Collection criterions, + Task task, Resource resource, List allocationsSortedByStartDate) { LoadPeriodGeneratorFactory periodGeneratorFactory = LoadPeriodGenerator .onResourceSatisfying(resource, criterions); - return new LoadTimeLine(getName(criterions), PeriodsBuilder.build( - periodGeneratorFactory, allocationsSortedByStartDate), + return new LoadTimeLine(getName(criterions, task), PeriodsBuilder + .build(periodGeneratorFactory, allocationsSortedByStartDate), "generic"); } + public static String getName(Collection criterions,Task task) { + String prefix = task.getName(); + return (prefix + " :: " + getName(criterions)); + } + public static String getName(Collection criterions) { if (criterions.isEmpty()) { return _("[generic all workers]"); @@ -299,7 +408,7 @@ public class ResourceLoadModel implements IResourceLoadModel { for (Criterion criterion : criterions) { names[i++] = criterion.getName(); } - return Arrays.toString(names); + return (Arrays.toString(names)); } private LoadTimeLine buildTimeLine(Resource resource, String name, @@ -315,7 +424,7 @@ public class ResourceLoadModel implements IResourceLoadModel { } @Override - public List getLoadTimeLines() { + public List getLoadTimeLines() { return loadTimeLines; }