Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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());
}

}
4 changes: 4 additions & 0 deletions ganttproject/data/resources/language/i18n.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -598,6 +601,7 @@ tableColDuration = Duration
tableColEndDate = End date
tableColID = ID
tableColInfo = Alerts
tableColLoad = Load
tableColName = Name
tableColOutline = Outline number
tableColPredecessors = Predecessors
Expand Down
4 changes: 4 additions & 0 deletions ganttproject/data/resources/language/i18n_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -545,6 +548,7 @@ tableColDuration = Dauer
tableColEndDate = Ende
tableColID = ID
tableColInfo = Warnungen
tableColLoad = Aufwand
tableColName = Vorgang
tableColOutline = GliederungsNr.
tableColPredecessors = Vorg\u00E4nger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> resources = Lists.transform(Arrays.asList(t.getAssignments()), new Function<ResourceAssignment, String>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)))";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the weights of cost and load to 0.1 -- this will group them in the upper part of the column closer to each other


JXMultiSplitPane result = new JXMultiSplitPane();
result.setDividerSize(0);
Expand All @@ -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;
}

Expand Down Expand Up @@ -122,6 +125,21 @@ public void changeValue(ChangeValueEvent event) {
return result;
}

private JComponent createLoadPanel() {
OptionsPageBuilder builder = new OptionsPageBuilder();

JPanel optionsPanel = new JPanel();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is either over- or under- engineered :)
GPOption and GPOptionGroup classes serve as UI-independent "properties", or as a model for UI components if you wish. OptionsPageBuilder can automatically build UI from options and groups, adds titles, makes them readonly depending on the model properties. If you use this machinery then it makes sense to utilize it completely and do not bother with UI components explicitly (and it is the recommended way, unless UI is more complex than property sheet -like)

In order to use it, remove all code at lines 130-140 and replace it with

return builder.createGroupComponent(myLoadGroup);

Also, add the option instance:

  private final DefaultDoubleOption myLoadOption = new DefaultDoubleOption("taskProperties.load.value");

and pass it to myLoadGroup in the group constructor call.

You're almost done, the remaining thing is to set the value of myLoadOption from myTask

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();
Expand Down
6 changes: 6 additions & 0 deletions ganttproject/src/net/sourceforge/ganttproject/task/Task.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,10 @@ public static interface Cost {
boolean isSupertask();

List<Document> getAttachments();

public static interface Load {
Double getValue();
}

Load getLoad();
}
18 changes: 18 additions & 0 deletions ganttproject/src/net/sourceforge/ganttproject/task/TaskImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, is it possible to cache the calculated result? If it is always calculated then we're going to use CPU heavily in big projects.

Probably a good place to store load, cache it and invalidate the cache is ResourceAssignmentCollectionImpl (field myAssignments) which is aware of assigned resources and their changes.

}

}

@Override
public Load getLoad() {
return myLoad;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pls replace tab with 2 spaces

}

}
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any sense to use primitive type while calculating? Not sure if it matters these days from performance point, but summing non-primitive Double's in the loops below must heavily and pointlessly use autoboxing and unboxing

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public void getTaskAttributes(Task t, Map<String, String> 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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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<Document> attachments = t.getAttachments();
for (int i = 0; i < attachments.size(); i++) {
Document nextAttachment = attachments.get(i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ private void writeProperties(ArrayList<Column> orderedColumns, Map<String, Strin
Paragraph p = new Paragraph(value, getSansRegular(12));
cell = new PdfPCell(p);
if (TaskDefaultColumn.COST.getStub().getID().equals(column.getID())
|| TaskDefaultColumn.LOAD.getStub().getID().equals(column.getID())
|| ResourceDefaultColumn.STANDARD_RATE.getStub().getID().equals(column.getID())
|| ResourceDefaultColumn.TOTAL_COST.getStub().getID().equals(column.getID())) {
cell.setHorizontalAlignment(PdfPCell.ALIGN_RIGHT);
Expand Down