diff --git a/biz.ganttproject.core/src/biz/ganttproject/core/model/task/TaskDefaultColumn.java b/biz.ganttproject.core/src/biz/ganttproject/core/model/task/TaskDefaultColumn.java index 62380d6fb2..b3f5c2260a 100644 --- a/biz.ganttproject.core/src/biz/ganttproject/core/model/task/TaskDefaultColumn.java +++ b/biz.ganttproject.core/src/biz/ganttproject/core/model/task/TaskDefaultColumn.java @@ -50,7 +50,8 @@ public enum TaskDefaultColumn { ID(new ColumnList.ColumnStub("tpd10", null, false, -1, 20), Integer.class, "tableColID", Functions.NOT_EDITABLE), OUTLINE_NUMBER(new ColumnList.ColumnStub("tpd11", null, false, 4, 20), String.class, "tableColOutline", Functions.NOT_EDITABLE), COST(new ColumnList.ColumnStub("tpd12", null, false, -1, 20), Double.class, "tableColCost"), - RESOURCES(new ColumnList.ColumnStub("tpd13", null, false, -1, 20), String.class, "resources", Functions.NOT_EDITABLE); + RESOURCES(new ColumnList.ColumnStub("tpd13", null, false, -1, 20), String.class, "resources", Functions.NOT_EDITABLE), + LOAD(new ColumnList.ColumnStub("tpd14", null, false, -1, 20), Double.class, "tableColLoad", Functions.NOT_EDITABLE); public interface LocaleApi { String i18n(String key); diff --git a/ganttproject-tester/test/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmTest.java b/ganttproject-tester/test/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmTest.java new file mode 100644 index 0000000000..02126347fd --- /dev/null +++ b/ganttproject-tester/test/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmTest.java @@ -0,0 +1,86 @@ +/* +Copyright 2017 Christoph Schneider, BarD Software s.r.o + +This file is part of GanttProject, an opensource project management tool. + +GanttProject 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, either version 3 of the License, or +(at your option) any later version. + +GanttProject 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 GanttProject. If not, see . +*/ +package net.sourceforge.ganttproject.task.algorithm; + +import net.sourceforge.ganttproject.TestSetupHelper; +import net.sourceforge.ganttproject.TestSetupHelper.TaskManagerBuilder; +import net.sourceforge.ganttproject.resource.HumanResource; +import net.sourceforge.ganttproject.task.Task; +import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade; +import net.sourceforge.ganttproject.test.task.TaskTestCase; + +/** + * Tests for cost calculations + * + * @author schch (Christoph Schneider) + */ +public class LoadAlgorithmTest extends TaskTestCase { + public void testSupertaskLoad() { + Task supertask = createTask(); + Task subtask1 = createTask(); + Task subtask2 = createTask(); + TaskContainmentHierarchyFacade hierarchy = getTaskManager().getTaskHierarchy(); + hierarchy.move(subtask1, supertask); + hierarchy.move(subtask2, supertask); + + TaskManagerBuilder builder = TestSetupHelper.newTaskManagerBuilder(); + setTaskManager(builder.build()); + HumanResource joe = new HumanResource("Joe", 1, builder.getResourceManager()); + + HumanResource jane = new HumanResource("Jane", 1, builder.getResourceManager()); + + builder.getResourceManager().add(joe); + builder.getResourceManager().add(jane); + + subtask1.setDuration(subtask1.getManager().createLength(5)); + subtask2.setDuration(subtask1.getManager().createLength(10)); + assertEquals(0.0, supertask.getLoad().getValue()); + + subtask1.getAssignmentCollection().addAssignment(joe).setLoad(100f); + subtask1.getAssignmentCollection().addAssignment(jane).setLoad(50f); + assertEquals(7.5, supertask.getLoad().getValue()); + assertEquals(7.5, subtask1.getLoad().getValue()); + assertEquals(0.0, subtask2.getLoad().getValue()); + + subtask2.getAssignmentCollection().addAssignment(jane).setLoad(50f); + assertEquals(12.5, supertask.getLoad().getValue()); + assertEquals(7.5, subtask1.getLoad().getValue()); + assertEquals(5.0, subtask2.getLoad().getValue()); + } + + public void testTaskLoad() { + TaskManagerBuilder builder = TestSetupHelper.newTaskManagerBuilder(); + setTaskManager(builder.build()); + HumanResource joe = new HumanResource("Joe", 1, builder.getResourceManager()); + + HumanResource jane = new HumanResource("Jane", 1, builder.getResourceManager()); + + builder.getResourceManager().add(joe); + builder.getResourceManager().add(jane); + + Task t = createTask(); + t.setDuration(t.getManager().createLength(2)); + assertEquals(0.0, t.getLoad().getValue()); + + t.getAssignmentCollection().addAssignment(joe).setLoad(100f); + t.getAssignmentCollection().addAssignment(jane).setLoad(50f); + assertEquals(3.0, t.getLoad().getValue()); + } + +} diff --git a/ganttproject/data/resources/language/i18n.properties b/ganttproject/data/resources/language/i18n.properties index c85d3c6f6e..f9ee64a242 100644 --- a/ganttproject/data/resources/language/i18n.properties +++ b/ganttproject/data/resources/language/i18n.properties @@ -212,6 +212,7 @@ library = Libraries license = License list.separator = ', ' list.separator.last = ' and ' +load = Load lockDAV = Lock WebDAV resources (maximal locking time in minutes) longLanguage = English looknfeel = Appearance @@ -323,6 +324,7 @@ option.taskProperties.cost.calculated.label.yes = Calculated\: option.taskProperties.cost.calculated.label.no = Set explicitly\: option.taskProperties.customColumn.name.label = Column name option.taskProperties.customColumn.type.label = Column type +option.taskProperties.load.value.label = Sum of all loads\: option.taskProperties.main.earliestBegin.copyBeginDate = Copy begin date option.taskProperties.main.scheduling.automated.label = in scheduling algorithms \u25BE option.taskProperties.main.scheduling.automated.value.duration = Duration is immutable @@ -364,6 +366,7 @@ optionGroup.importer.msproject.mpx.label = MPX options optionGroup.resourceChartColors.label = Resource colors optionGroup.resourceRate.label = Resource payment rate optionGroup.task.cost.label = Task cost +optionGroup.task.load.label = Task load optionGroup.timelineLabels.label = Timeline labels optionGroup.rss.label = GanttProject news and updates optionGroup.webdav.lock.label = Locking parameters @@ -598,6 +601,7 @@ tableColDuration = Duration tableColEndDate = End date tableColID = ID tableColInfo = Alerts +tableColLoad = Load tableColName = Name tableColOutline = Outline number tableColPredecessors = Predecessors diff --git a/ganttproject/data/resources/language/i18n_de.properties b/ganttproject/data/resources/language/i18n_de.properties index dd74bacdfe..906165e0a0 100644 --- a/ganttproject/data/resources/language/i18n_de.properties +++ b/ganttproject/data/resources/language/i18n_de.properties @@ -207,6 +207,7 @@ length = Dauer library = Bibliothek license = Lizenz list.separator.last = ' und ' +load = Aufwand lockDAV = WebDAV-Ressourcen sperren (maximale Sperrdauer in Minuten) longLanguage = Deutsch looknfeel = Aussehen @@ -312,6 +313,7 @@ option.taskProperties.cost.calculated.label.yes = Berechnet\: option.taskProperties.cost.calculated.label.no = Explizit festgelegt\: option.taskProperties.customColumn.name.label = Spaltenname option.taskProperties.customColumn.type.label = Spaltentyp +option.taskProperties.load.value.label = Summe der Aufw\u00E4nde\: option.taskProperties.main.earliestBegin.copyBeginDate = Kopiere Anfangsdatum option.taskProperties.main.scheduling.automated.value.hint = diese Optionen k\u00F6nnen noch nicht konfiguriert werden option.taskProperties.main.scheduling.label = Planungsoptionen @@ -349,6 +351,7 @@ optionGroup.importer.msproject.mpx.label = MPX-Optionen optionGroup.resourceChartColors.label = Farben f\u00FCr Ressourcen optionGroup.resourceRate.label = Bezahlungsrate der Ressource optionGroup.task.cost.label = Aufgabenkosten +optionGroup.task.load.label = Gesamtaufwand optionGroup.timelineLabels.label = Beschriftungen auf Zeitachse optionGroup.rss.label = GanttProject News und Updates optionGroup.webdav.lock.label = Datei-Sperre Einstellungen @@ -545,6 +548,7 @@ tableColDuration = Dauer tableColEndDate = Ende tableColID = ID tableColInfo = Warnungen +tableColLoad = Aufwand tableColName = Vorgang tableColOutline = GliederungsNr. tableColPredecessors = Vorg\u00E4nger diff --git a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java index 44164dcb16..3d2bf9e4a7 100644 --- a/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java +++ b/ganttproject/src/biz/ganttproject/impex/csv/GanttCSVExport.java @@ -205,6 +205,9 @@ private void writeTasks(SpreadsheetWriter writer) throws IOException { case COST: writer.print(task.getCost().getValue().toPlainString()); break; + case LOAD: + writer.print(task.getLoad().getValue().toString()); + break; case INFO: case PRIORITY: case TYPE: diff --git a/ganttproject/src/net/sourceforge/ganttproject/GanttTreeTableModel.java b/ganttproject/src/net/sourceforge/ganttproject/GanttTreeTableModel.java index 9db99f3281..0c0a746e13 100644 --- a/ganttproject/src/net/sourceforge/ganttproject/GanttTreeTableModel.java +++ b/ganttproject/src/net/sourceforge/ganttproject/GanttTreeTableModel.java @@ -314,6 +314,9 @@ else if (t.isMilestone()) { case COST: res = t.getCost().getValue(); break; + case LOAD: + res = t.getLoad().getValue(); + break; case RESOURCES: List resources = Lists.transform(Arrays.asList(t.getAssignments()), new Function() { @Override diff --git a/ganttproject/src/net/sourceforge/ganttproject/gui/taskproperties/TaskAllocationsPanel.java b/ganttproject/src/net/sourceforge/ganttproject/gui/taskproperties/TaskAllocationsPanel.java index 974b87210d..a35608574a 100644 --- a/ganttproject/src/net/sourceforge/ganttproject/gui/taskproperties/TaskAllocationsPanel.java +++ b/ganttproject/src/net/sourceforge/ganttproject/gui/taskproperties/TaskAllocationsPanel.java @@ -58,6 +58,8 @@ public void setValue(Double value) { }; private final GPOptionGroup myCostGroup = new GPOptionGroup("task.cost", myCostIsCalculated, myCostValue); + private final GPOptionGroup myLoadGroup = new GPOptionGroup("task.load"); + private JTable myTable; public TaskAllocationsPanel(Task task, HumanResourceManager hrManager, RoleManager roleMgr) { @@ -78,7 +80,7 @@ public JPanel getComponent() { CommonPanel.setupComboBoxEditor(getTable().getColumnModel().getColumn(4), myRoleManager.getEnabledRoles()); JPanel tablePanel = CommonPanel.createTableAndActions(myTable, myModel); - String layoutDef = "(ROW weight=1.0 (LEAF name=resources weight=0.5) (LEAF name=cost weight=0.5))"; + String layoutDef = "(ROW weight=1.0 (LEAF name=resources weight=0.5) (COLUMN weight=0.5 (LEAF name=cost weight=0.5) (LEAF name=load weight=0.5)))"; JXMultiSplitPane result = new JXMultiSplitPane(); result.setDividerSize(0); @@ -87,6 +89,7 @@ public JPanel getComponent() { result.getMultiSplitLayout().setModel(modelRoot); result.add(tablePanel, "resources"); result.add(UIUtil.border(createCostPanel(), 10, UIUtil.LEFT), "cost"); + result.add(UIUtil.border(createLoadPanel(), 10, UIUtil.LEFT), "load"); return result; } @@ -122,6 +125,21 @@ public void changeValue(ChangeValueEvent event) { return result; } + private JComponent createLoadPanel() { + OptionsPageBuilder builder = new OptionsPageBuilder(); + + JPanel optionsPanel = new JPanel(); + optionsPanel.add(new JLabel(GanttLanguage.getInstance().getText("option.taskProperties.load.value.label"))); + optionsPanel.add(new JLabel(myTask.getLoad().getValue().toString())); + OptionsPageBuilder.TWO_COLUMN_LAYOUT.layout(optionsPanel, 1); + + UIUtil.createTitle(optionsPanel, builder.getI18N().getOptionGroupLabel(myLoadGroup)); + + JPanel result = new JPanel(new BorderLayout()); + result.add(optionsPanel, BorderLayout.NORTH); + return result; + } + public void commit() { if (myTable.isEditing()) { myTable.getCellEditor().stopCellEditing(); diff --git a/ganttproject/src/net/sourceforge/ganttproject/task/Task.java b/ganttproject/src/net/sourceforge/ganttproject/task/Task.java index ebf3dfecf1..9b00cf0737 100644 --- a/ganttproject/src/net/sourceforge/ganttproject/task/Task.java +++ b/ganttproject/src/net/sourceforge/ganttproject/task/Task.java @@ -186,4 +186,10 @@ public static interface Cost { boolean isSupertask(); List getAttachments(); + + public static interface Load { + Double getValue(); + } + + Load getLoad(); } diff --git a/ganttproject/src/net/sourceforge/ganttproject/task/TaskImpl.java b/ganttproject/src/net/sourceforge/ganttproject/task/TaskImpl.java index 4a396c4c59..362df23b1d 100644 --- a/ganttproject/src/net/sourceforge/ganttproject/task/TaskImpl.java +++ b/ganttproject/src/net/sourceforge/ganttproject/task/TaskImpl.java @@ -37,6 +37,7 @@ of the License, or (at your option) any later version. import net.sourceforge.ganttproject.task.algorithm.AlgorithmCollection; import net.sourceforge.ganttproject.task.algorithm.AlgorithmException; import net.sourceforge.ganttproject.task.algorithm.CostAlgorithmImpl; +import net.sourceforge.ganttproject.task.algorithm.LoadAlgorithmImpl; import net.sourceforge.ganttproject.task.algorithm.ShiftTaskTreeAlgorithm; import net.sourceforge.ganttproject.task.dependency.TaskDependencyException; import net.sourceforge.ganttproject.task.dependency.TaskDependencySlice; @@ -130,6 +131,8 @@ public class TaskImpl implements Task { private final CostImpl myCost = new CostImpl(); + private final LoadImpl myLoad = new LoadImpl(); + private boolean isUnplugged = false; public final static int NONE = 0; @@ -1277,4 +1280,19 @@ public void setCalculated(boolean calculated) { public Cost getCost() { return myCost; } + + private class LoadImpl implements Load { + + @Override + public Double getValue() { + return new LoadAlgorithmImpl().getCalculatedLoad(TaskImpl.this); + } + + } + + @Override + public Load getLoad() { + return myLoad; + } + } diff --git a/ganttproject/src/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmImpl.java b/ganttproject/src/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmImpl.java new file mode 100644 index 0000000000..8078cefd70 --- /dev/null +++ b/ganttproject/src/net/sourceforge/ganttproject/task/algorithm/LoadAlgorithmImpl.java @@ -0,0 +1,44 @@ +/* +Copyright 2017 Christoph Schneider, BarD Software s.r.o + +This file is part of GanttProject, an opensource project management tool. + +GanttProject 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, either version 3 of the License, or +(at your option) any later version. + +GanttProject 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 GanttProject. If not, see . +*/ +package net.sourceforge.ganttproject.task.algorithm; + +import net.sourceforge.ganttproject.task.ResourceAssignment; +import net.sourceforge.ganttproject.task.Task; +import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade; + +/** + * Algorithm for calculating task load + * + * @author schch (Christoph Schneider) + */ +public class LoadAlgorithmImpl { + public Double getCalculatedLoad(Task t) { + Double total = new Double(0.0); + TaskContainmentHierarchyFacade taskHierarchy = t.getManager().getTaskHierarchy(); + if (taskHierarchy.hasNestedTasks(t)) { + for (Task child : taskHierarchy.getNestedTasks(t)) { + total = total + child.getLoad().getValue(); + } + } + for (ResourceAssignment assignment : t.getAssignments()) { + total = total + assignment.getLoad() * t.getDuration().getLength() / 100.0; + } + return total; + } +} diff --git a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/PropertyFetcher.java b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/PropertyFetcher.java index 380912bab5..ce04a210dc 100755 --- a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/PropertyFetcher.java +++ b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/PropertyFetcher.java @@ -64,6 +64,7 @@ public void getTaskAttributes(Task t, Map id2value) { id2value.put(TaskDefaultColumn.OUTLINE_NUMBER.getStub().getID(), Joiner.on('.').join(outlinePath)); id2value.put(TaskDefaultColumn.ID.getStub().getID(), String.valueOf(t.getTaskID())); id2value.put(TaskDefaultColumn.COST.getStub().getID(), t.getCost().getValue().toPlainString()); + id2value.put(TaskDefaultColumn.LOAD.getStub().getID(), t.getLoad().getValue().toString()); CustomColumnsValues customValues = t.getCustomValues(); for (CustomPropertyDefinition def : myProject.getTaskCustomColumnManager().getDefinitions()) { diff --git a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/XmlSerializer.java b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/XmlSerializer.java index 34e73af439..af00776ae4 100644 --- a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/XmlSerializer.java +++ b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/XmlSerializer.java @@ -143,6 +143,7 @@ protected void writeTasks(final TaskManager taskManager, final TransformerHandle addAttribute("assigned-to", i18n("human"), attrs); addAttribute("notes", i18n("notes"), attrs); addAttribute("duration", i18n("duration"), attrs); + addAttribute("load", i18n("load"), attrs); startPrefixedElement("tasks", attrs, handler); TaskVisitor visitor = new TaskVisitor() { AttributesImpl myAttrs = new AttributesImpl(); @@ -172,6 +173,9 @@ protected String serializeTask(Task t, int depth) throws Exception { addAttribute("id", "tpd6", myAttrs); textElement("duration", myAttrs, String.valueOf(t.getDuration().getLength()), handler); + addAttribute("id", "tpd14", myAttrs); + textElement("load", myAttrs, String.valueOf(t.getLoad().getValue()), handler); + final List attachments = t.getAttachments(); for (int i = 0; i < attachments.size(); i++) { Document nextAttachment = attachments.get(i); diff --git a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/itext/ThemeImpl.java b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/itext/ThemeImpl.java index 53361230f2..28e71fa5d8 100644 --- a/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/itext/ThemeImpl.java +++ b/org.ganttproject.impex.htmlpdf/src/org/ganttproject/impex/htmlpdf/itext/ThemeImpl.java @@ -424,6 +424,7 @@ private void writeProperties(ArrayList orderedColumns, Map