Give more priority to the resources already picked

FEA: ItEr71S07FragmentationDeletionItEr70S09
This commit is contained in:
Óscar González Fernández 2011-02-24 18:01:41 +01:00
parent e1b61e752a
commit 07ec454406
2 changed files with 180 additions and 8 deletions

View file

@ -26,11 +26,15 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections.ComparatorUtils;
import org.apache.commons.lang.Validate;
import org.joda.time.LocalDate;
import org.navalplanner.business.calendars.entities.Capacity;
@ -103,6 +107,15 @@ public class EffortDistributor {
return result;
}
static List<Resource> resources(
Collection<? extends ResourceWithAssignedDuration> collection) {
List<Resource> result = new ArrayList<Resource>();
for (ResourceWithAssignedDuration each : collection) {
result.add(each.resource);
}
return result;
}
static Map<Resource, ResourceWithAssignedDuration> byResource(
Collection<? extends ResourceWithAssignedDuration> durations) {
Map<Resource, ResourceWithAssignedDuration> result = new HashMap<Resource, ResourceWithAssignedDuration>();
@ -244,6 +257,30 @@ public class EffortDistributor {
public int compareTo(ResourceWithAvailableCapacity o) {
return available.compareTo(o.available);
}
@SuppressWarnings("unchecked")
static Comparator<ResourceWithAvailableCapacity> getComparatorConsidering(
final Set<Resource> lastResourcesUsed) {
return ComparatorUtils.chainedComparator(
new Comparator<ResourceWithAvailableCapacity>() {
@Override
public int compare(ResourceWithAvailableCapacity o1,
ResourceWithAvailableCapacity o2) {
boolean resource1Used = lastResourcesUsed
.contains(o1.resource);
boolean resource2Used = lastResourcesUsed
.contains(o2.resource);
return asInt(resource1Used) - asInt(resource2Used);
}
int asInt(boolean b) {
return b ? 1 : 0;
}
}, ComparatorUtils.naturalComparator());
}
}
private final List<ResourceWithDerivedData> resources;
@ -252,6 +289,8 @@ public class EffortDistributor {
private final IResourceSelector resourceSelector;
private Set<Resource> resourcesAlreadyPicked = new HashSet<Resource>();
public EffortDistributor(List<Resource> resources,
IAssignedEffortForResource assignedHoursForResource) {
this(resources, assignedHoursForResource, null);
@ -269,6 +308,19 @@ public class EffortDistributor {
public List<ResourceWithAssignedDuration> distributeForDay(LocalDate date,
EffortDuration totalDuration) {
return withCaptureOfResourcesPicked(distributeForDay_(date,
totalDuration));
}
private List<ResourceWithAssignedDuration> withCaptureOfResourcesPicked(
List<ResourceWithAssignedDuration> result) {
resourcesAlreadyPicked.addAll(ResourceWithAssignedDuration
.resources(result));
return result;
}
private List<ResourceWithAssignedDuration> distributeForDay_(
LocalDate date, EffortDuration totalDuration) {
List<ResourceWithDerivedData> resourcesAssignable = resourcesAssignableAt(date);
List<ResourceWithAssignedDuration> withoutOvertime = assignAllPossibleWithoutOvertime(
date, totalDuration, resourcesAssignable);
@ -282,7 +334,8 @@ public class EffortDistributor {
date, remaining,
ResourceWithAssignedDuration.sumAssignedEffort(withoutOvertime,
assignedEffortForResource), resourcesAssignable);
return ResourceWithAssignedDuration.join(withoutOvertime, withOvertime);
return ResourceWithAssignedDuration
.join(withoutOvertime, withOvertime);
}
private List<ResourceWithDerivedData> resourcesAssignableAt(LocalDate day) {
@ -298,7 +351,7 @@ public class EffortDistributor {
private List<ResourceWithAssignedDuration> assignAllPossibleWithoutOvertime(
LocalDate date, EffortDuration totalDuration,
List<ResourceWithDerivedData> resourcesAssignable) {
List<ResourceWithAvailableCapacity> fromMoreToLessCapacity = resourcesFromMoreToLessCapacityAvailable(
List<ResourceWithAvailableCapacity> fromMoreToLessCapacity = resourcesFromMoreDesirableToLess(
resourcesAssignable, date);
EffortDuration remaining = totalDuration;
List<ResourceWithAssignedDuration> result = new ArrayList<ResourceWithAssignedDuration>();
@ -315,14 +368,16 @@ public class EffortDistributor {
return result;
}
private List<ResourceWithAvailableCapacity> resourcesFromMoreToLessCapacityAvailable(
private List<ResourceWithAvailableCapacity> resourcesFromMoreDesirableToLess(
List<ResourceWithDerivedData> resourcesAssignable, LocalDate date) {
List<ResourceWithAvailableCapacity> result = new ArrayList<ResourceWithAvailableCapacity>();
for (ResourceWithDerivedData each : resourcesAssignable) {
result.add(each.withAvailableCapacityOn(date,
assignedEffortForResource));
}
Collections.sort(result, Collections.reverseOrder());
Collections.sort(result, Collections
.reverseOrder(ResourceWithAvailableCapacity
.getComparatorConsidering(resourcesAlreadyPicked)));
return result;
}

View file

@ -42,11 +42,15 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.easymock.IAnswer;
import org.easymock.classextension.EasyMock;
import org.joda.time.Interval;
import org.joda.time.LocalDate;
import org.joda.time.Period;
@ -178,13 +182,60 @@ public class GenericResourceAllocationTest {
workers.add(worker3);
}
private static class LoadSpec {
public static LoadSpec withHours(int hours) {
return new LoadSpec(hours(hours));
}
private final EffortDuration defaultLoad;
private LoadSpec(EffortDuration defaultLoad) {
Validate.notNull(defaultLoad);
this.defaultLoad = defaultLoad;
}
private Map<LocalDate, EffortDuration> exceptions = new HashMap<LocalDate, EffortDuration>();
LoadSpec withException(LocalDate date, EffortDuration loadAtThatDate) {
Validate.notNull(date);
Validate.notNull(loadAtThatDate);
exceptions.put(date, loadAtThatDate);
return this;
}
EffortDuration getLoad(LocalDate date) {
if (exceptions.containsKey(date)) {
return exceptions.get(date);
}
return defaultLoad;
}
}
private Worker createWorkerWithLoad(ResourceCalendar resourceCalendar,
int hours) {
return createWorkerWithLoad(resourceCalendar,
new LoadSpec(hours(hours)));
}
private Worker createWorkerWithLoad(ResourceCalendar resourceCalendar,
final LoadSpec loadSpec) {
Worker result = createNiceMock(Worker.class);
expect(result.getCalendar()).andReturn(resourceCalendar).anyTimes();
expect(
result.getAssignedDurationDiscounting(isA(Object.class),
isA(LocalDate.class))).andReturn(hours(hours)).anyTimes();
isA(LocalDate.class))).andAnswer(
new IAnswer<EffortDuration>() {
@Override
public EffortDuration answer() throws Throwable {
Object[] currentArguments = EasyMock
.getCurrentArguments();
LocalDate date = (LocalDate) currentArguments[1];
return loadSpec.getLoad(date);
}
}).anyTimes();
expect(result.getSatisfactionsFor(isA(ICriterion.class))).andReturn(
satisfactionsForPredefinedCriterions(result)).anyTimes();
replay(result);
@ -225,6 +276,12 @@ public class GenericResourceAllocationTest {
}
private void givenWorkersWithLoads(int hours1, int hours2, int hours3) {
givenWorkersWithLoads(LoadSpec.withHours(hours1),
LoadSpec.withHours(hours2), LoadSpec.withHours(hours3));
}
private void givenWorkersWithLoads(LoadSpec load1, LoadSpec load2,
LoadSpec load3) {
ResourceCalendar[] calendars;
if (workerCalendars == null) {
calendars = new ResourceCalendar[] { null, null, null };
@ -232,9 +289,9 @@ public class GenericResourceAllocationTest {
calendars = new ResourceCalendar[] { workerCalendars.get(0),
workerCalendars.get(1), workerCalendars.get(2) };
}
worker1 = createWorkerWithLoad(calendars[0], hours1);
worker2 = createWorkerWithLoad(calendars[1], hours2);
worker3 = createWorkerWithLoad(calendars[2], hours3);
worker1 = createWorkerWithLoad(calendars[0], load1);
worker2 = createWorkerWithLoad(calendars[1], load2);
worker3 = createWorkerWithLoad(calendars[2], load3);
buildWorkersList();
}
@ -540,6 +597,66 @@ public class GenericResourceAllocationTest {
assertThat(assignmentsWorker3, haveHours(7, 7, 7, 7));
}
@Test
public void itTakesIntoAccountTheLoadForEachDay() {
final int TASK_DURATION_DAYS = 4;
givenBaseCalendarWithoutExceptions(8);
LocalDate start = new LocalDate(2006, 10, 5);
givenTaskWithStartAndEnd(toInterval(start,
Period.days(TASK_DURATION_DAYS)));
givenGenericResourceAllocationForTask(task);
givenWorkersWithLoads(
LoadSpec.withHours(3)
.withException(start.plusDays(1), hours(1))
.withException(start.plusDays(3), hours(8)),
LoadSpec.withHours(12).withException(start.plusDays(3), zero()),
LoadSpec.withHours(1)
.withException(start.plusDays(1), hours(3))
.withException(start.plusDays(3), hours(8)));
genericResourceAllocation.forResources(workers).allocate(
ResourcesPerDay.amount(1));
List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
.getOrderedAssignmentsFor(worker1);
assertThat(assignmentsWorker1, haveHours(1, 7, 1));
List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
.getOrderedAssignmentsFor(worker2);
assertThat(assignmentsWorker2, haveHours(8));
List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
.getOrderedAssignmentsFor(worker3);
assertThat(assignmentsWorker3, haveHours(7, 1, 7));
}
@Test
public void previouslyPickedResourcesHaveMorePriority() {
final int TASK_DURATION_DAYS = 4;
givenBaseCalendarWithoutExceptions(8);
LocalDate start = new LocalDate(2006, 10, 5);
givenTaskWithStartAndEnd(toInterval(start,
Period.days(TASK_DURATION_DAYS)));
givenGenericResourceAllocationForTask(task);
givenWorkersWithLoads(
LoadSpec.withHours(0)
.withException(start.plusDays(3), hours(4)),
LoadSpec.withHours(12),
LoadSpec.withHours(1)
.withException(start.plusDays(3), hours(0)));
genericResourceAllocation.forResources(workers).allocate(
ResourcesPerDay.amount(1));
List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
.getOrderedAssignmentsFor(worker3);
assertThat(assignmentsWorker3, haveHours(4));
List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
.getOrderedAssignmentsFor(worker1);
assertThat(assignmentsWorker1, haveHours(8, 8, 8, 4));
List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
.getOrderedAssignmentsFor(worker2);
assertThat(assignmentsWorker2, haveHours());
}
@Test
public void doesntSurpassTheExtraHours() {
final int TASK_DURATION_DAYS = 4;