ItEr29S06CUAsignacionGrupoRecursosAPlanificacionItEr28S06: Implementing the allocation of some hours on interval
This commit is contained in:
parent
4283cf6582
commit
f62b661159
4 changed files with 218 additions and 20 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue