Give more priority to the resources already picked
FEA: ItEr71S07FragmentationDeletionItEr70S09
This commit is contained in:
parent
e1b61e752a
commit
07ec454406
2 changed files with 180 additions and 8 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue