Change untilAllocating algorithm to use EffortDurations instead of hours
FEA: ItEr60S19TimeUnitDataType
This commit is contained in:
parent
a6d5ae9743
commit
87ff9eb29a
5 changed files with 80 additions and 104 deletions
|
|
@ -411,9 +411,8 @@ public class GenericResourceAllocation extends
|
|||
return GenericDayAssignment.class;
|
||||
}
|
||||
|
||||
public List<DayAssignment> createAssignmentsAtDay(
|
||||
List<Resource> resources, LocalDate day,
|
||||
ResourcesPerDay resourcesPerDay,
|
||||
public List<DayAssignment> createAssignmentsAtDay(List<Resource> resources,
|
||||
LocalDate day, ResourcesPerDay resourcesPerDay,
|
||||
final EffortDuration maxLimit) {
|
||||
final EffortDuration durations = min(
|
||||
calculateTotalToDistribute(day, resourcesPerDay), maxLimit);
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
@Override
|
||||
protected List<DayAssignment> createAssignmentsAtDay(
|
||||
ResourcesPerDayModification allocation, LocalDate day,
|
||||
Integer limit) {
|
||||
EffortDuration limit) {
|
||||
return allocation.createAssignmentsAtDay(day, limit);
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +228,7 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
protected boolean thereAreAvailableHoursFrom(
|
||||
LocalDate start,
|
||||
ResourcesPerDayModification resourcesPerDayModification,
|
||||
int hoursToAllocate) {
|
||||
EffortDuration effortToAllocate) {
|
||||
IWorkHours workHoursPerDay = getWorkHoursPerDay(resourcesPerDayModification);
|
||||
ResourcesPerDay resourcesPerDay = resourcesPerDayModification
|
||||
.getGoal();
|
||||
|
|
@ -236,7 +236,7 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
.getAvailability();
|
||||
availability.invalidUntil(start);
|
||||
return workHoursPerDay.thereAreHoursOn(availability,
|
||||
resourcesPerDay, hoursToAllocate);
|
||||
resourcesPerDay, effortToAllocate.roundToHours());
|
||||
}
|
||||
|
||||
private CombinedWorkHours getWorkHoursPerDay(
|
||||
|
|
@ -252,7 +252,7 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
|
|||
allocation.markAsUnsatisfied();
|
||||
}
|
||||
};
|
||||
return allocator.untilAllocating(hoursToAllocate);
|
||||
return allocator.untilAllocating(hours(hoursToAllocate));
|
||||
}
|
||||
|
||||
public void allocateOnTaskLength() {
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ public class SpecificResourceAllocation extends
|
|||
@Override
|
||||
protected List<SpecificDayAssignment> distributeForDay(
|
||||
LocalDate day, EffortDuration effort) {
|
||||
return Arrays.asList(SpecificDayAssignment.create(day,
|
||||
effort.roundToHours(), resource));
|
||||
return Arrays.asList(SpecificDayAssignment.create(day, effort,
|
||||
resource));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -212,7 +212,7 @@ public class SpecificResourceAllocation extends
|
|||
ResourcesPerDay resourcesPerDay, EffortDuration limit) {
|
||||
EffortDuration effort = calculateTotalToDistribute(day, resourcesPerDay);
|
||||
SpecificDayAssignment specific = SpecificDayAssignment.create(day,
|
||||
min(limit, effort).roundToHours(), resource);
|
||||
min(limit, effort), resource);
|
||||
List<DayAssignment> result = new ArrayList<DayAssignment>();
|
||||
result.add(specific);
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@
|
|||
|
||||
package org.navalplanner.business.planner.entities.allocationalgorithms;
|
||||
|
||||
import static org.navalplanner.business.workingday.EffortDuration.zero;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -32,9 +32,11 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.navalplanner.business.common.ProportionalDistributor;
|
||||
import org.navalplanner.business.planner.entities.DayAssignment;
|
||||
import org.navalplanner.business.planner.entities.ResourceAllocation;
|
||||
import org.navalplanner.business.planner.entities.Task;
|
||||
import org.navalplanner.business.workingday.EffortDuration;
|
||||
import org.navalplanner.business.workingday.ResourcesPerDay;
|
||||
|
||||
public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
||||
|
|
@ -58,17 +60,17 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
|||
}
|
||||
}
|
||||
|
||||
public LocalDate untilAllocating(int hoursToAllocate) {
|
||||
public LocalDate untilAllocating(EffortDuration effortToAllocate) {
|
||||
LocalDate taskStart = LocalDate.fromDateFields(task.getStartDate());
|
||||
LocalDate start = (task.getFirstDayNotConsolidated().compareTo(
|
||||
taskStart) >= 0) ? task.getFirstDayNotConsolidated()
|
||||
: taskStart;
|
||||
int i = 0;
|
||||
int maxDaysElapsed = 0;
|
||||
for (HoursPerAllocation each : hoursPerAllocation(start,
|
||||
hoursToAllocate)) {
|
||||
for (EffortPerAllocation each : effortPerAllocation(start,
|
||||
effortToAllocate)) {
|
||||
int daysElapsedForCurrent = untilAllocating(start, each.allocation,
|
||||
each.hours);
|
||||
each.duration);
|
||||
maxDaysElapsed = Math.max(maxDaysElapsed, daysElapsedForCurrent);
|
||||
i++;
|
||||
}
|
||||
|
|
@ -76,22 +78,21 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
|||
return start.plusDays(maxDaysElapsed);
|
||||
}
|
||||
|
||||
private List<HoursPerAllocation> hoursPerAllocation(LocalDate start,
|
||||
int toBeAssigned) {
|
||||
private List<EffortPerAllocation> effortPerAllocation(LocalDate start,
|
||||
EffortDuration toBeAssigned) {
|
||||
return new HoursPerAllocationCalculator(allocations)
|
||||
.calculateHoursPerAllocation(start, toBeAssigned);
|
||||
.calculateEffortsPerAllocation(start, toBeAssigned);
|
||||
}
|
||||
|
||||
private int untilAllocating(LocalDate start,
|
||||
ResourcesPerDayModification resourcesPerDayModification,
|
||||
Integer hoursToAllocate) {
|
||||
int hoursRemaining = hoursToAllocate;
|
||||
EffortDuration effortRemaining) {
|
||||
int day = 0;
|
||||
while (hoursRemaining > 0) {
|
||||
while (effortRemaining.compareTo(zero()) > 0) {
|
||||
LocalDate current = start.plusDays(day);
|
||||
int taken = assignForDay(resourcesPerDayModification, current,
|
||||
hoursRemaining);
|
||||
hoursRemaining = hoursRemaining - taken;
|
||||
EffortDuration taken = assignForDay(resourcesPerDayModification,
|
||||
current, effortRemaining);
|
||||
effortRemaining = effortRemaining.minus(taken);
|
||||
day++;
|
||||
}
|
||||
return day;
|
||||
|
|
@ -114,44 +115,45 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
|||
List<DayAssignment> dayAssignments);
|
||||
|
||||
protected abstract List<DayAssignment> createAssignmentsAtDay(
|
||||
ResourcesPerDayModification allocation, LocalDate day, Integer limit);
|
||||
ResourcesPerDayModification allocation, LocalDate day,
|
||||
EffortDuration limit);
|
||||
|
||||
protected abstract boolean thereAreAvailableHoursFrom(LocalDate start,
|
||||
ResourcesPerDayModification resourcesPerDayModification,
|
||||
int hoursToAllocate);
|
||||
EffortDuration remainingDuration);
|
||||
|
||||
protected abstract void markUnsatisfied(ResourceAllocation<?> beingModified);
|
||||
|
||||
private int assignForDay(
|
||||
private EffortDuration assignForDay(
|
||||
ResourcesPerDayModification resourcesPerDayModification,
|
||||
LocalDate day, int remaining) {
|
||||
LocalDate day, EffortDuration remaining) {
|
||||
List<DayAssignment> newAssignments = createAssignmentsAtDay(
|
||||
resourcesPerDayModification, day, remaining);
|
||||
resultAssignments.get(resourcesPerDayModification).addAll(
|
||||
newAssignments);
|
||||
return DayAssignment.sum(newAssignments).roundToHours();
|
||||
return DayAssignment.sum(newAssignments);
|
||||
}
|
||||
|
||||
private static class HoursPerAllocation {
|
||||
final int hours;
|
||||
private static class EffortPerAllocation {
|
||||
final EffortDuration duration;
|
||||
|
||||
final ResourcesPerDayModification allocation;
|
||||
|
||||
private HoursPerAllocation(int hours,
|
||||
private EffortPerAllocation(EffortDuration duration,
|
||||
ResourcesPerDayModification allocation) {
|
||||
this.hours = hours;
|
||||
this.duration = duration;
|
||||
this.allocation = allocation;
|
||||
}
|
||||
|
||||
public static List<HoursPerAllocation> wrap(
|
||||
public static List<EffortPerAllocation> wrap(
|
||||
List<ResourcesPerDayModification> allocations,
|
||||
List<Integer> hours) {
|
||||
Validate.isTrue(hours.size() == allocations.size());
|
||||
List<EffortDuration> durations) {
|
||||
Validate.isTrue(durations.size() == allocations.size());
|
||||
int i = 0;
|
||||
List<HoursPerAllocation> result = new ArrayList<HoursPerAllocation>();
|
||||
List<EffortPerAllocation> result = new ArrayList<EffortPerAllocation>();
|
||||
for(i = 0; i < allocations.size(); i++){
|
||||
result.add(new HoursPerAllocation(hours.get(i), allocations
|
||||
.get(i)));
|
||||
result.add(new EffortPerAllocation(durations.get(i),
|
||||
allocations.get(i)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -166,12 +168,12 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
|||
allocations);
|
||||
}
|
||||
|
||||
public List<HoursPerAllocation> calculateHoursPerAllocation(
|
||||
LocalDate start, int toAssign) {
|
||||
public List<EffortPerAllocation> calculateEffortsPerAllocation(
|
||||
LocalDate start, EffortDuration toAssign) {
|
||||
do {
|
||||
List<Integer> hours = calculateHours(toAssign);
|
||||
List<HoursPerAllocation> result = HoursPerAllocation.wrap(
|
||||
allocations, hours);
|
||||
List<EffortDuration> durations = divideEffort(toAssign);
|
||||
List<EffortPerAllocation> result = EffortPerAllocation.wrap(
|
||||
allocations, durations);
|
||||
List<ResourcesPerDayModification> unsatisfied = getUnsatisfied(
|
||||
start, result);
|
||||
if (unsatisfied.isEmpty()) {
|
||||
|
|
@ -186,77 +188,53 @@ public abstract class AllocatorForSpecifiedResourcesPerDayAndHours {
|
|||
}
|
||||
|
||||
private List<ResourcesPerDayModification> getUnsatisfied(
|
||||
LocalDate start, List<HoursPerAllocation> hoursPerAllocations) {
|
||||
LocalDate start, List<EffortPerAllocation> hoursPerAllocations) {
|
||||
List<ResourcesPerDayModification> cannotSatisfy = new ArrayList<ResourcesPerDayModification>();
|
||||
for (HoursPerAllocation each : hoursPerAllocations) {
|
||||
for (EffortPerAllocation each : hoursPerAllocations) {
|
||||
if (!thereAreAvailableHoursFrom(start, each.allocation,
|
||||
each.hours)) {
|
||||
each.duration)) {
|
||||
cannotSatisfy.add(each.allocation);
|
||||
}
|
||||
}
|
||||
return cannotSatisfy;
|
||||
}
|
||||
|
||||
private List<Integer> calculateHours(int toAssign) {
|
||||
BigDecimal[] limits = new BigDecimal[allocations.size()];
|
||||
BigDecimal sumAll = sumAll();
|
||||
for (int i = 0; i < limits.length; i++) {
|
||||
BigDecimal amount = allocations.get(i).getGoal().getAmount();
|
||||
limits[i] = amount.divide(sumAll, RoundingMode.DOWN).multiply(
|
||||
new BigDecimal(toAssign));
|
||||
}
|
||||
final int remainder = toAssign - sumIntegerParts(limits);
|
||||
return distributeRemainder(limits, remainder);
|
||||
private List<EffortDuration> divideEffort(EffortDuration toBeDivided) {
|
||||
ProportionalDistributor distributor = ProportionalDistributor
|
||||
.create(createShares());
|
||||
int[] secondsDivided = distributor.distribute(toBeDivided
|
||||
.getSeconds());
|
||||
return asDurations(secondsDivided);
|
||||
}
|
||||
|
||||
private List<Integer> distributeRemainder(BigDecimal[] decimals,
|
||||
final int remainder) {
|
||||
for (int i = 0; i < remainder; i++) {
|
||||
int position = positionOfBiggestDecimalPart(decimals);
|
||||
decimals[position] = new BigDecimal(decimals[position]
|
||||
.intValue() + 1);
|
||||
}
|
||||
return asIntegers(decimals);
|
||||
}
|
||||
|
||||
private List<Integer> asIntegers(BigDecimal[] decimals) {
|
||||
Integer[] result = new Integer[decimals.length];
|
||||
private int[] createShares() {
|
||||
int[] result = new int[allocations.size()];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = decimals[i].intValue();
|
||||
}
|
||||
return Arrays.asList(result);
|
||||
}
|
||||
|
||||
private int positionOfBiggestDecimalPart(BigDecimal[] decimals) {
|
||||
int result = 0;
|
||||
BigDecimal currentBiggestDecimalPart = new BigDecimal(0);
|
||||
for (int i = 0; i < decimals.length; i++) {
|
||||
BigDecimal fractionalPart = decimals[i]
|
||||
.subtract(new BigDecimal(decimals[i].intValue()));
|
||||
if (currentBiggestDecimalPart.compareTo(fractionalPart) < 0) {
|
||||
currentBiggestDecimalPart = fractionalPart;
|
||||
result = i;
|
||||
}
|
||||
result[i] = normalize(allocations.get(i).getGoal()
|
||||
.getAmount());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int sumIntegerParts(BigDecimal[] decimals) {
|
||||
int sum = 0;
|
||||
for (BigDecimal decimal : decimals) {
|
||||
sum += decimal.intValue();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private BigDecimal sumAll() {
|
||||
BigDecimal result = new BigDecimal(0);
|
||||
for (ResourcesPerDayModification r : allocations) {
|
||||
result = result.add(r.getGoal().getAmount());
|
||||
private List<EffortDuration> asDurations(int[] secondsDivided) {
|
||||
List<EffortDuration> result = new ArrayList<EffortDuration>();
|
||||
for (int each : secondsDivided) {
|
||||
result.add(EffortDuration.seconds(each));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized amount for {@link ProportionalDistributor}. For
|
||||
* example, for 2.03, 203 is returned.
|
||||
*
|
||||
* @param amount
|
||||
* @return
|
||||
*/
|
||||
private int normalize(BigDecimal amount) {
|
||||
return amount.movePointRight(2).intValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@
|
|||
|
||||
package org.navalplanner.business.planner.entities.allocationalgorithms;
|
||||
|
||||
import static org.navalplanner.business.workingday.EffortDuration.hours;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
|
@ -42,6 +40,7 @@ import org.navalplanner.business.planner.entities.SpecificResourceAllocation;
|
|||
import org.navalplanner.business.resources.daos.IResourceDAO;
|
||||
import org.navalplanner.business.resources.entities.Criterion;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
import org.navalplanner.business.workingday.EffortDuration;
|
||||
import org.navalplanner.business.workingday.ResourcesPerDay;
|
||||
|
||||
public abstract class ResourcesPerDayModification extends
|
||||
|
|
@ -73,9 +72,9 @@ public abstract class ResourcesPerDayModification extends
|
|||
|
||||
@Override
|
||||
public List<DayAssignment> createAssignmentsAtDay(LocalDate day,
|
||||
int limit) {
|
||||
EffortDuration limit) {
|
||||
return genericAllocation.createAssignmentsAtDay(getResources(),
|
||||
day, getGoal(), hours(limit));
|
||||
day, getGoal(), limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -125,9 +124,9 @@ public abstract class ResourcesPerDayModification extends
|
|||
|
||||
@Override
|
||||
public List<DayAssignment> createAssignmentsAtDay(LocalDate day,
|
||||
int limit) {
|
||||
EffortDuration limit) {
|
||||
return resourceAllocation.createAssignmentsAtDay(day, getGoal(),
|
||||
hours(limit));
|
||||
limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -211,7 +210,7 @@ public abstract class ResourcesPerDayModification extends
|
|||
public abstract void applyAllocationUntil(LocalDate endExclusive);
|
||||
|
||||
public abstract List<DayAssignment> createAssignmentsAtDay(LocalDate day,
|
||||
int limit);
|
||||
EffortDuration limit);
|
||||
|
||||
public abstract AvailabilityTimeLine getAvailability();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue