ItEr41S09RFSoporteRecursosVirtuaisItEr40S12: Prorating virtual workers so they receive more overload
This commit is contained in:
parent
d53a64f2bc
commit
d2333b6312
2 changed files with 209 additions and 28 deletions
|
|
@ -20,6 +20,7 @@
|
|||
package org.navalplanner.business.planner.entities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
|
@ -47,12 +48,26 @@ public class HoursDistributor {
|
|||
|
||||
private final List<IWorkHours> workHours;
|
||||
|
||||
private final List<Integer> capacities;
|
||||
|
||||
public HoursDistributor(List<Resource> resources) {
|
||||
this.resources = resources;
|
||||
this.workHours = new ArrayList<IWorkHours>();
|
||||
for (Resource resource : resources) {
|
||||
this.workHours.add(generateWorkHoursFor(resource));
|
||||
}
|
||||
this.capacities = new ArrayList<Integer>();
|
||||
for (Resource resource : resources) {
|
||||
this.capacities.add(getCapacityFor(resource));
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getCapacityFor(Resource resource) {
|
||||
if (resource.getCalendar() != null) {
|
||||
return resource.getCalendar().getCapacity();
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private final IWorkHours generateWorkHoursFor(Resource resource) {
|
||||
|
|
@ -65,31 +80,78 @@ public class HoursDistributor {
|
|||
|
||||
public List<ResourceWithAssignedHours> distributeForDay(LocalDate day,
|
||||
int totalHours) {
|
||||
List<ResourceWithAssignedHours> result = new ArrayList<ResourceWithAssignedHours>();
|
||||
List<Share> shares = currentSharesFor(day);
|
||||
ShareDivision currentDivision = ShareDivision.create(shares);
|
||||
List<ShareSource> shares = divisionAt(day);
|
||||
ShareDivision currentDivision = ShareSource.all(shares);
|
||||
ShareDivision newDivison = currentDivision.plus(totalHours);
|
||||
int[] differences = currentDivision.to(newDivison);
|
||||
for (int i = 0; i < differences.length; i++) {
|
||||
assert differences[i] >= 0;
|
||||
result.add(new ResourceWithAssignedHours(differences[i], resources
|
||||
.get(i)));
|
||||
return ShareSource.hoursForEachResource(shares, differences, resources);
|
||||
}
|
||||
|
||||
private static final ResourcesPerDay ONE = ResourcesPerDay.amount(1);
|
||||
|
||||
private static class ShareSource {
|
||||
|
||||
public static ShareDivision all(Collection<ShareSource> sources) {
|
||||
List<Share> shares = new ArrayList<Share>();
|
||||
for (ShareSource shareSource : sources) {
|
||||
shares.addAll(shareSource.shares);
|
||||
}
|
||||
return ShareDivision.create(shares);
|
||||
}
|
||||
|
||||
public static List<ResourceWithAssignedHours> hoursForEachResource(
|
||||
List<ShareSource> sources, int[] differences,
|
||||
List<Resource> resources) {
|
||||
List<ResourceWithAssignedHours> result = new ArrayList<ResourceWithAssignedHours>();
|
||||
int differencesIndex = 0;
|
||||
for (int i = 0; i < resources.size(); i++) {
|
||||
Resource resource = resources.get(i);
|
||||
ShareSource shareSource = sources.get(i);
|
||||
final int differencesToTake = shareSource.shares.size();
|
||||
int sum = sumDifferences(differences, differencesIndex,
|
||||
differencesToTake);
|
||||
differencesIndex += differencesToTake;
|
||||
result.add(new ResourceWithAssignedHours(sum, resource));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int sumDifferences(int[] differences, int start,
|
||||
final int toTake) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < toTake; i++) {
|
||||
sum += differences[start + i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private final List<Share> shares;
|
||||
|
||||
private ShareSource(List<Share> shares) {
|
||||
this.shares = shares;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<ShareSource> divisionAt(LocalDate day) {
|
||||
List<ShareSource> result = new ArrayList<ShareSource>();
|
||||
for (int i = 0; i < resources.size(); i++) {
|
||||
List<Share> shares = new ArrayList<Share>();
|
||||
Resource resource = resources.get(i);
|
||||
IWorkHours workHoursForResource = workHours.get(i);
|
||||
int alreadyAssignedHours = resource.getAssignedHours(day);
|
||||
Integer capacityEachOne = workHoursForResource.toHours(day, ONE);
|
||||
final int capacityUnits = capacities.get(i);
|
||||
assert capacityUnits >= 1;
|
||||
final int assignedForEach = alreadyAssignedHours / capacityUnits;
|
||||
final int remainder = alreadyAssignedHours % capacityUnits;
|
||||
for (int j = 0; j < capacityUnits; j++) {
|
||||
int assigned = assignedForEach + (j < remainder ? 1 : 0);
|
||||
shares.add(new Share(assigned - capacityEachOne));
|
||||
}
|
||||
result.add(new ShareSource(shares));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Share> currentSharesFor(LocalDate day) {
|
||||
List<Share> shares = new ArrayList<Share>();
|
||||
for (int i = 0; i < resources.size(); i++) {
|
||||
Resource resource = resources.get(i);
|
||||
IWorkHours workHoursForResource = workHours.get(i);
|
||||
int alreadyAssignedHours = resource.getAssignedHours(day);
|
||||
Integer workableHours = workHoursForResource.getCapacityAt(day);
|
||||
// a resource would have a zero share if all it's hours for a
|
||||
// given day are filled
|
||||
shares.add(new Share(alreadyAssignedHours - workableHours));
|
||||
}
|
||||
return shares;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
package org.navalplanner.business.test.planner.entities;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.getCurrentArguments;
|
||||
import static org.easymock.EasyMock.isA;
|
||||
import static org.easymock.classextension.EasyMock.createNiceMock;
|
||||
import static org.easymock.classextension.EasyMock.replay;
|
||||
|
|
@ -40,11 +41,13 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.easymock.IAnswer;
|
||||
import org.joda.time.Interval;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.joda.time.Period;
|
||||
import org.junit.Test;
|
||||
import org.navalplanner.business.calendars.entities.BaseCalendar;
|
||||
import org.navalplanner.business.calendars.entities.ResourceCalendar;
|
||||
import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay;
|
||||
import org.navalplanner.business.planner.entities.GenericDayAssignment;
|
||||
import org.navalplanner.business.planner.entities.GenericResourceAllocation;
|
||||
|
|
@ -52,6 +55,7 @@ import org.navalplanner.business.planner.entities.ResourcesPerDay;
|
|||
import org.navalplanner.business.planner.entities.Task;
|
||||
import org.navalplanner.business.resources.entities.Criterion;
|
||||
import org.navalplanner.business.resources.entities.Resource;
|
||||
import org.navalplanner.business.resources.entities.VirtualWorker;
|
||||
import org.navalplanner.business.resources.entities.Worker;
|
||||
|
||||
public class GenericResourceAllocationTest {
|
||||
|
|
@ -60,6 +64,7 @@ public class GenericResourceAllocationTest {
|
|||
private Set<Criterion> criterions;
|
||||
|
||||
private List<Worker> workers;
|
||||
private List<ResourceCalendar> workerCalendars = null;
|
||||
private Worker worker1;
|
||||
private Worker worker2;
|
||||
private Worker worker3;
|
||||
|
|
@ -129,19 +134,35 @@ public class GenericResourceAllocationTest {
|
|||
workers.add(worker3);
|
||||
}
|
||||
|
||||
private Worker createWorkerWithLoad(int hours) {
|
||||
private Worker createWorkerWithLoad(ResourceCalendar resourceCalendar,
|
||||
int hours) {
|
||||
Worker result = createNiceMock(Worker.class);
|
||||
setupCalendarIsNull(result);
|
||||
expect(result.getCalendar()).andReturn(resourceCalendar).anyTimes();
|
||||
expect(result.getAssignedHours(isA(LocalDate.class))).andReturn(hours)
|
||||
.anyTimes();
|
||||
replay(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void givenCalendarsForResources(int capacity1, int capacity2,
|
||||
int capacity3) {
|
||||
workerCalendars = new ArrayList<ResourceCalendar>();
|
||||
workerCalendars.add(createCalendar(ResourceCalendar.class, capacity1));
|
||||
workerCalendars.add(createCalendar(ResourceCalendar.class, capacity2));
|
||||
workerCalendars.add(createCalendar(ResourceCalendar.class, capacity3));
|
||||
}
|
||||
|
||||
private void givenWorkersWithLoads(int hours1, int hours2, int hours3) {
|
||||
worker1 = createWorkerWithLoad(hours1);
|
||||
worker2 = createWorkerWithLoad(hours2);
|
||||
worker3 = createWorkerWithLoad(hours3);
|
||||
ResourceCalendar[] calendars;
|
||||
if(workerCalendars ==null){
|
||||
calendars = new ResourceCalendar[] { null, null, null };
|
||||
} else {
|
||||
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);
|
||||
buildWorkersList();
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +171,37 @@ public class GenericResourceAllocationTest {
|
|||
}
|
||||
|
||||
private void givenBaseCalendarWithoutExceptions(int hoursPerDay) {
|
||||
BaseCalendar baseCalendar = createNiceMock(BaseCalendar.class);
|
||||
BaseCalendar baseCalendar = createCalendar(BaseCalendar.class,
|
||||
hoursPerDay);
|
||||
this.baseCalendar = baseCalendar;
|
||||
}
|
||||
|
||||
private <T extends BaseCalendar> T createCalendar(Class<T> klass,
|
||||
final int hoursPerDay) {
|
||||
BaseCalendar baseCalendar = createNiceMock(klass);
|
||||
expect(baseCalendar.getWorkableHours(isA(Date.class))).andReturn(
|
||||
hoursPerDay).anyTimes();
|
||||
expect(baseCalendar.getCapacityAt(isA(LocalDate.class))).andReturn(
|
||||
hoursPerDay).anyTimes();
|
||||
expect(
|
||||
baseCalendar.toHours(isA(LocalDate.class),
|
||||
isA(ResourcesPerDay.class))).andAnswer(
|
||||
new IAnswer<Integer>() {
|
||||
|
||||
@Override
|
||||
public Integer answer() throws Throwable {
|
||||
ResourcesPerDay resourcesPerDay = (ResourcesPerDay) getCurrentArguments()[1];
|
||||
return resourcesPerDay
|
||||
.asHoursGivenResourceWorkingDayOf(hoursPerDay);
|
||||
}
|
||||
})
|
||||
.anyTimes();
|
||||
if (baseCalendar instanceof ResourceCalendar) {
|
||||
ResourceCalendar resourceCalendar = (ResourceCalendar) baseCalendar;
|
||||
expect(resourceCalendar.getCapacity()).andReturn(1);
|
||||
}
|
||||
replay(baseCalendar);
|
||||
this.baseCalendar = baseCalendar;
|
||||
return klass.cast(baseCalendar);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -399,6 +444,80 @@ public class GenericResourceAllocationTest {
|
|||
assertThat(assignmentsWorker3, haveHours(5, 5, 5, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void virtualWorkersAreGivenMoreLoad() {
|
||||
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(8, 8, 8);
|
||||
givenVirtualWorkerWithCapacity(5);
|
||||
genericResourceAllocation.forResources(workers).allocate(
|
||||
ResourcesPerDay.amount(1));
|
||||
List<GenericDayAssignment> virtualWorkerAssignments = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(workers.get(workers.size() - 1));
|
||||
assertThat(virtualWorkerAssignments, haveHours(5, 5, 5, 5));
|
||||
List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker1);
|
||||
assertThat(assignmentsWorker1, haveHours(1, 1, 1, 1));
|
||||
List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker2);
|
||||
assertThat(assignmentsWorker2, haveHours(1, 1, 1, 1));
|
||||
List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker3);
|
||||
assertThat(assignmentsWorker3, haveHours(1, 1, 1, 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itWorksWithCalendarsReturningZeroHours() {
|
||||
final int TASK_DURATION_DAYS = 4;
|
||||
givenBaseCalendarWithoutExceptions(8);
|
||||
givenCalendarsForResources(4, 4, 0);
|
||||
LocalDate start = new LocalDate(2006, 10, 5);
|
||||
givenTaskWithStartAndEnd(toInterval(start, Period
|
||||
.days(TASK_DURATION_DAYS)));
|
||||
givenGenericResourceAllocationForTask(task);
|
||||
givenWorkersWithLoads(4, 4, 4);
|
||||
|
||||
genericResourceAllocation.forResources(workers).allocate(
|
||||
ResourcesPerDay.amount(1));
|
||||
|
||||
List<GenericDayAssignment> assignmentsWorker1 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker1);
|
||||
assertThat(assignmentsWorker1, haveHours(4, 4, 4, 4));
|
||||
List<GenericDayAssignment> assignmentsWorker2 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker2);
|
||||
assertThat(assignmentsWorker2, haveHours(4, 4, 4, 4));
|
||||
List<GenericDayAssignment> assignmentsWorker3 = genericResourceAllocation
|
||||
.getOrderedAssignmentsFor(worker3);
|
||||
assertThat(assignmentsWorker3, haveHours(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
private void givenVirtualWorkerWithCapacity(int capacity) {
|
||||
VirtualWorker worker = createNiceMock(VirtualWorker.class);
|
||||
final int fullLoadForAll = 8 * capacity;
|
||||
expect(worker.getAssignedHours(isA(LocalDate.class))).andReturn(
|
||||
fullLoadForAll)
|
||||
.anyTimes();
|
||||
expect(worker.getCalendar()).andReturn(createCalendar(capacity, 8))
|
||||
.anyTimes();
|
||||
replay(worker);
|
||||
workers.add(worker);
|
||||
}
|
||||
|
||||
private ResourceCalendar createCalendar(int capacity, int unit) {
|
||||
ResourceCalendar calendar = createNiceMock(ResourceCalendar.class);
|
||||
expect(calendar.getCapacityAt(isA(LocalDate.class))).andReturn(
|
||||
capacity * unit).anyTimes();
|
||||
expect(calendar.toHours(isA(LocalDate.class),
|
||||
isA(ResourcesPerDay.class))).andReturn(unit).anyTimes();
|
||||
expect(calendar.getCapacity()).andReturn(capacity).anyTimes();
|
||||
replay(calendar);
|
||||
return calendar;
|
||||
}
|
||||
|
||||
private static Interval toInterval(LocalDate start, Period period) {
|
||||
return new Interval(start.toDateTimeAtStartOfDay(), start.plus(period)
|
||||
.toDateTimeAtStartOfDay());
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue