Change untilAllocating algorithm to use EffortDurations instead of hours

FEA: ItEr60S19TimeUnitDataType
This commit is contained in:
Óscar González Fernández 2010-09-14 00:12:15 +02:00
parent a6d5ae9743
commit 87ff9eb29a
5 changed files with 80 additions and 104 deletions

View file

@ -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);

View file

@ -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() {

View file

@ -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;

View file

@ -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();
}
}
}

View file

@ -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();