ItEr29S06CUAsignacionGrupoRecursosAPlanificacionItEr28S06: Implementing the allocation of some hours on interval

This commit is contained in:
Óscar González Fernández 2009-10-08 00:27:00 +02:00
parent 4283cf6582
commit f62b661159
4 changed files with 218 additions and 20 deletions

View file

@ -64,7 +64,7 @@ public abstract class DayAssignment extends BaseEntity {
}
public static <T extends DayAssignment> Map<LocalDate, List<T>> byDay(
Collection<T> assignments) {
Collection<? extends T> assignments) {
Map<LocalDate, List<T>> result = new HashMap<LocalDate, List<T>>();
for (T t : assignments) {
LocalDate day = t.getDay();

View file

@ -28,6 +28,7 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang.Validate;
import org.hibernate.validator.NotNull;
@ -295,6 +296,34 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
return result;
}
void allocate(LocalDate startInclusive, LocalDate endExclusive, int hours) {
Validate.isTrue(hours >= 0);
Validate.isTrue(startInclusive.compareTo(endExclusive) <= 0,
"the end must be equal or posterior than start");
List<T> assignmentsCreated = new ArrayList<T>();
List<LocalDate> days = getDays(startInclusive, endExclusive);
int[] hoursEachDay = hoursDistribution(days, hours);
int i = 0;
for (LocalDate day : getDays(startInclusive, endExclusive)) {
assignmentsCreated.addAll(distributeForDay(day,
hoursEachDay[i++]));
}
removingAssignments(getAssignments(startInclusive, endExclusive));
addingAssignments(assignmentsCreated);
setResourcesPerDay(calculateResourcesPerDayFromAssignments());
}
private int[] hoursDistribution(List<LocalDate> days, int hoursToSum) {
List<Share> shares = new ArrayList<Share>();
for (LocalDate day : days) {
shares.add(new Share(-getWorkHoursPerDay()
.getWorkableHours(day)));
}
ShareDivision original = ShareDivision.create(shares);
ShareDivision newShare = original.plus(hoursToSum);
return original.to(newShare);
}
protected abstract List<T> distributeForDay(LocalDate day,
int totalHours);
@ -317,6 +346,21 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
return resourcesPerDay.asHoursGivenResourceWorkingDayOf(workableHours);
}
private ResourcesPerDay calculateResourcesPerDayFromAssignments() {
Map<LocalDate, List<DayAssignment>> byDay = DayAssignment
.byDay(getAssignments());
int sumTotalHours = 0;
int sumWorkableHours = 0;
for (Entry<LocalDate, List<DayAssignment>> entry : byDay.entrySet()) {
sumWorkableHours += getWorkHoursPerDay().getWorkableHours(
entry.getKey())
* entry.getValue().size();
sumTotalHours += getAssignedHours(entry.getValue());
}
return ResourcesPerDay.calculateFrom(
sumTotalHours, sumWorkableHours);
}
private IWorkHours getWorkHoursPerDay() {
return getWorkHoursGivenTaskHours(getTaskWorkHoursLimit());
}
@ -413,34 +457,51 @@ public abstract class ResourceAllocation<T extends DayAssignment> extends
public int getAssignedHours(final Resource resource, LocalDate start,
LocalDate end) {
return getAssignedHours(start, end, new PredicateOnDayAssignment() {
LocalDate endExclusive) {
return getAssignedHours(filter(getAssignments(start, endExclusive),new PredicateOnDayAssignment() {
@Override
public boolean satisfiedBy(DayAssignment dayAssignment) {
return dayAssignment.isAssignedTo(resource);
}
});
}));
}
public int getAssignedHours(LocalDate start, LocalDate end) {
return getAssignedHours(start, end,
PredicateOnDayAssignment.ALWAYS_TRUE);
}
private int getAssignedHours(LocalDate start, LocalDate end,
PredicateOnDayAssignment predicate) {
int sum = 0;
public List<DayAssignment> getAssignments(LocalDate start,
LocalDate endExclusive) {
List<DayAssignment> result = new ArrayList<DayAssignment>();
for (DayAssignment dayAssignment : getAssignments()) {
if (dayAssignment.getDay().compareTo(end) >= 0) {
if (dayAssignment.getDay().compareTo(endExclusive) >= 0) {
break;
}
if (dayAssignment.includedIn(start, end)
&& predicate.satisfiedBy(dayAssignment)) {
sum += dayAssignment.getHours();
if (dayAssignment.includedIn(start, endExclusive)) {
result.add(dayAssignment);
}
}
return result;
}
public int getAssignedHours(LocalDate start, LocalDate endExclusive) {
return getAssignedHours(getAssignments(start, endExclusive));
}
private List<DayAssignment> filter(List<DayAssignment> assignments,
PredicateOnDayAssignment predicate) {
List<DayAssignment> result = new ArrayList<DayAssignment>();
for (DayAssignment dayAssignment : assignments) {
if (predicate.satisfiedBy(dayAssignment)) {
result.add(dayAssignment);
}
}
return result;
}
private int getAssignedHours(List<DayAssignment> assignments) {
int sum = 0;
for (DayAssignment dayAssignment : assignments) {
sum += dayAssignment.getHours();
}
return sum;
}

View file

@ -123,6 +123,31 @@ public class SpecificResourceAllocation extends
}
}
public interface IAllocateHoursOnInterval {
void allocateHours(int hours);
}
private class AllocateHoursOnInterval implements IAllocateHoursOnInterval {
private final LocalDate start;
private final LocalDate end;
AllocateHoursOnInterval(LocalDate start, LocalDate end) {
this.start = start;
this.end = end;
}
public void allocateHours(int hours) {
new SpecificAssignmentsAllocation().allocate(start, end, hours);
}
}
public IAllocateHoursOnInterval onInterval(LocalDate start, LocalDate end) {
Validate.isTrue(start.compareTo(end) <= 0,
"the end must be equal or posterior than start");
return new AllocateHoursOnInterval(start, end);
}
@Override
protected IWorkHours getWorkHoursGivenTaskHours(IWorkHours taskWorkHours) {
if (getResource().getCalendar() == null) {

View file

@ -25,13 +25,18 @@ import static org.easymock.EasyMock.isA;
import static org.easymock.classextension.EasyMock.createNiceMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.consecutiveDays;
import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.from;
import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.haveHours;
import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.haveResourceAllocation;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.easymock.IAnswer;
import org.easymock.classextension.EasyMock;
import org.joda.time.LocalDate;
import org.junit.Test;
import org.navalplanner.business.calendars.entities.BaseCalendar;
@ -55,7 +60,7 @@ public class SpecificResourceAllocationTest {
private int assignedHours = 0;
private void givenAssignedHours(int assignedHours){
private void givenAssignedHours(int assignedHours) {
this.assignedHours = assignedHours;
}
@ -68,10 +73,40 @@ public class SpecificResourceAllocationTest {
replay(this.calendar);
}
private void givenWorker(){
private void givenResourceCalendar(final int defaultAnswer, final Map<LocalDate, Integer> answersForDates){
this.calendar = createNiceMock(ResourceCalendar.class);
expect(this.calendar.getWorkableHours(isA(LocalDate.class))).andAnswer(new IAnswer<Integer>() {
@Override
public Integer answer() throws Throwable {
LocalDate date = (LocalDate) EasyMock.getCurrentArguments()[0];
if(answersForDates.containsKey(date)){
return answersForDates.get(date);
}
return defaultAnswer;
}
}).anyTimes();
expect(this.calendar.getWorkableHours(isA(Date.class)))
.andAnswer(new IAnswer<Integer>() {
@Override
public Integer answer() throws Throwable {
Date date = (Date) EasyMock.getCurrentArguments()[0];
LocalDate localDate = new LocalDate(date.getTime());
if(answersForDates.containsKey(localDate)){
return answersForDates.get(localDate);
}
return defaultAnswer;
}
}).anyTimes();
replay(this.calendar);
}
private void givenWorker() {
this.worker = createNiceMock(Worker.class);
expect(this.worker.getCalendar()).andReturn(calendar).anyTimes();
expect(this.worker.getAssignedHours(isA(LocalDate.class))).andReturn(assignedHours).anyTimes();
expect(this.worker.getAssignedHours(isA(LocalDate.class))).andReturn(
assignedHours).anyTimes();
replay(this.worker);
}
@ -138,4 +173,81 @@ public class SpecificResourceAllocationTest {
assertThat(specificResourceAllocation.getAssignments(), haveHours(4, 4));
}
@Test(expected = IllegalArgumentException.class)
public void cantAllocateOnAnWrongInterval() {
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
LocalDate dayBefore = start.plusDays(-1);
specificResourceAllocation.onInterval(start, dayBefore).allocateHours(
10);
}
@Test
public void canAllocateZeroHours() {
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
specificResourceAllocation.onInterval(start, start.plusDays(2))
.allocateHours(0);
}
@Test(expected = IllegalArgumentException.class)
public void cantAllocateNegativeHours() {
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
specificResourceAllocation.onInterval(start, start.plusDays(1))
.allocateHours(-1);
}
@Test
public void someHoursInAnIntervalCanBeAssigned() {
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
specificResourceAllocation.onInterval(start, start.plusDays(2))
.allocateHours(10);
assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5));
}
@Test
public void thePreviousAssignmentsAreReplacedWhenAllocationHoursOnInterval() {
givenResourceCalendarAlwaysReturning(3);
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
specificResourceAllocation.allocate(ResourcesPerDay.amount(1));
specificResourceAllocation.onInterval(start, start.plusDays(2))
.allocateHours(10);
assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5,
3, 3));
}
@Test
public void theResourcesPerDayAreRecalculatedWhenAllocationHoursOnInterval() {
givenResourceCalendarAlwaysReturning(3);
LocalDate start = new LocalDate(2000, 2, 4);
givenSpecificResourceAllocation(start, 4);
ResourcesPerDay original = ResourcesPerDay.amount(1);
specificResourceAllocation.allocate(original);
specificResourceAllocation.onInterval(start, start.plusDays(2))
.allocateHours(10);
ResourcesPerDay newResourcesPerDay = specificResourceAllocation
.getResourcesPerDay();
assertTrue("Expecting that the resources per day is increased",
newResourcesPerDay
.getAmount().compareTo(original.getAmount()) > 0);
}
@SuppressWarnings("serial")
@Test
public void theHoursAreDistributedTakingIntoAccountTheWorkableHours() {
final LocalDate start = new LocalDate(2000, 2, 4);
givenResourceCalendar(8, new HashMap<LocalDate, Integer>() {
{
put(start, 2);
}
});
givenSpecificResourceAllocation(start, 4);
specificResourceAllocation.onInterval(start, start.plusDays(2))
.allocateHours(10);
assertThat(specificResourceAllocation.getAssignments(), haveHours(2, 8));
}
}