From 4063b6d293dafd7bfb09a15c3ec13a7a24eaebe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Thu, 14 Apr 2011 02:42:12 +0200 Subject: [PATCH] Add method for specifying several days together in an interval FEA: ItEr74S04BugFixing --- .../entities/IAllocateHoursOnInterval.java | 25 +++++ .../planner/entities/ResourceAllocation.java | 96 +++++++++++++++---- .../GenericResourceAllocationTest.java | 6 +- .../SpecificResourceAllocationTest.java | 81 ++++++++++++++++ 4 files changed, 187 insertions(+), 21 deletions(-) diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/IAllocateHoursOnInterval.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/IAllocateHoursOnInterval.java index 050bb17fc..fb2c513c0 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/IAllocateHoursOnInterval.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/IAllocateHoursOnInterval.java @@ -20,6 +20,31 @@ */ package org.navalplanner.business.planner.entities; +import java.util.List; + +import org.navalplanner.business.workingday.EffortDuration; + public interface IAllocateHoursOnInterval { + void allocateHours(int hours); + + /** + *

+ * It tries to allocate the specified durations on the originally specified + * interval. It tries to fit them to the interval. If the specified list has + * less days than the days required by the interval, the end of the list is + * padded with zeroes. If the specified list has more days than the days + * required the trailing days are discarded. + *

+ *

+ * If the allocation is done within the bounds of the task, the durations + * specified outside the task's bounds are discarded. + *

+ *

+ * If for some day no allocation can't be done, i.e. the day is considered + * unavailable, the real assignment will be zero. + *

+ * @param durationsByDay + */ + void allocate(List durationsByDay); } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java index c569f2759..a0ddfb1af 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java @@ -720,13 +720,13 @@ public abstract class ResourceAllocation extends endDate); } - private List getDays(IntraDayDate startInclusive, + private Iterable getDays(IntraDayDate startInclusive, IntraDayDate endExclusive) { - Validate.notNull(startInclusive); - Validate.notNull(endExclusive); Validate.isTrue(startInclusive.compareTo(endExclusive) <= 0, "the end must be equal or posterior than start"); - return IntraDayDate.toList(startInclusive.daysUntil(endExclusive)); + Iterable daysUntil = startInclusive + .daysUntil(endExclusive); + return daysUntil; } private final class AllocateResourcesPerDayOnInterval implements @@ -817,6 +817,21 @@ public abstract class ResourceAllocation extends allocationInterval, duration); allocationInterval.resetAssignments(assignmentsCreated); } + + @Override + public void allocate(List durationsByDay) { + allocateDurationsByDay(allocationInterval, durationsByDay); + } + } + + private void allocateDurationsByDay(AllocationInterval interval, + List durationsByDay) { + List rightSlice = interval + .getRightSlice(durationsByDay); + AvailabilityTimeLine availability = getAvailability(); + List assignments = createAssignments(interval, availability, + rightSlice.toArray(new EffortDuration[0])); + interval.resetAssignments(assignments); } @Override @@ -829,6 +844,15 @@ public abstract class ResourceAllocation extends public void allocateHours(int hours) { allocateTheWholeAllocation(interval, hours(hours)); } + + @Override + public void allocate(List durationsByDay) { + List rightSlice = interval + .getRightSlice(durationsByDay); + AvailabilityTimeLine availability = getAvailability(); + createAssignments(interval, availability, + rightSlice.toArray(new EffortDuration[0])); + } }; } @@ -842,6 +866,11 @@ public abstract class ResourceAllocation extends public void allocateHours(int hours) { allocateTheWholeAllocation(interval, hours(hours)); } + + @Override + public void allocate(List durationsByDay) { + allocateDurationsByDay(interval, durationsByDay); + } }; } @@ -863,24 +892,31 @@ public abstract class ResourceAllocation extends private List createAssignments(AllocationInterval interval, EffortDuration durationToAssign) { - List assignmentsCreated = new ArrayList(); AvailabilityTimeLine availability = getAvailability(); - List days = getDays(interval.getStartInclusive(), + Iterable days = getDays(interval.getStartInclusive(), interval.getEndExclusive()); EffortDuration[] durationsEachDay = secondsDistribution( availability, days, durationToAssign); + return createAssignments(interval, availability, durationsEachDay); + } + + private List createAssignments(AllocationInterval interval, + AvailabilityTimeLine availability, + EffortDuration[] durationsEachDay) { + List result = new ArrayList(); int i = 0; - for (PartialDay day : days) { + for (PartialDay day : getDays(interval.getStartInclusive(), + interval.getEndExclusive())) { // if all days are not available, it would try to assign // them anyway, preventing it with a check if (availability.isValid(day.getDate())) { - assignmentsCreated.addAll(distributeForDay(day.getDate(), + result.addAll(distributeForDay(day.getDate(), durationsEachDay[i])); } i++; } - return onlyNonZeroHours(assignmentsCreated); + return onlyNonZeroHours(result); } private AvailabilityTimeLine getAvailability() { @@ -905,7 +941,7 @@ public abstract class ResourceAllocation extends } private EffortDuration[] secondsDistribution( - AvailabilityTimeLine availability, List days, + AvailabilityTimeLine availability, Iterable days, EffortDuration duration) { List shares = new ArrayList(); for (PartialDay each : days) { @@ -985,11 +1021,18 @@ public abstract class ResourceAllocation extends class AllocationInterval { + private IntraDayDate originalStart; + + private IntraDayDate originalEnd; + private final IntraDayDate start; private final IntraDayDate end; - private AllocationInterval(IntraDayDate start, IntraDayDate end) { + AllocationInterval(IntraDayDate originalStart, + IntraDayDate originalEnd, IntraDayDate start, IntraDayDate end) { + this.originalStart = originalStart; + this.originalEnd = originalEnd; IntraDayDate startConsideringConsolidated = task .hasConsolidations() ? IntraDayDate .max(task.getFirstDayNotConsolidated(), start) : start; @@ -998,17 +1041,33 @@ public abstract class ResourceAllocation extends this.end = IntraDayDate.max(this.start, end); } + AllocationInterval(IntraDayDate start, IntraDayDate end) { + this(start, end, start, end); + } + + AllocationInterval(LocalDate startInclusive, LocalDate endExclusive) { + this(IntraDayDate.startOfDay(startInclusive), IntraDayDate + .startOfDay(endExclusive)); + } + + public List getRightSlice(List original) { + List result = new ArrayList( + original); + final int numberOfDaysToFill = originalStart + .numberOfDaysUntil(originalEnd); + for (int i = 0; i < numberOfDaysToFill - original.size(); i++) { + result.add(zero()); + } + return result.subList(originalStart.numberOfDaysUntil(start), + result.size() - end.numberOfDaysUntil(originalEnd)); + } + + public void resetAssignments(List assignmentsCreated) { resetAssigmentsFittingAllocationDatesToResultingAssignments(this, assignmentsCreated); } - public AllocationInterval(LocalDate startInclusive, - LocalDate endExclusive) { - this(IntraDayDate.startOfDay(startInclusive), IntraDayDate - .startOfDay(endExclusive)); - } - public IntraDayDate getStartInclusive() { return this.start; } @@ -1034,7 +1093,8 @@ public abstract class ResourceAllocation extends AllocationIntervalInsideTask(IntraDayDate startInclusive, IntraDayDate endExclusive) { - super(IntraDayDate.max(startInclusive, getTask() + super(startInclusive, endExclusive, IntraDayDate.max( + startInclusive, getTask() .getFirstDayNotConsolidated()), IntraDayDate.min( endExclusive, task.getIntraDayEndDate())); } diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java index 3b1a80b94..5169c0449 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/GenericResourceAllocationTest.java @@ -840,9 +840,9 @@ public class GenericResourceAllocationTest { final int hoursOnSubinterval = 3; int daysSubinterval = 2; - genericResourceAllocation.forResources(workers).onIntervalWithinTask(start, - start.plusDays(daysSubinterval)).allocateHours( - hoursOnSubinterval); + genericResourceAllocation.forResources(workers) + .onIntervalWithinTask(start, start.plusDays(daysSubinterval)) + .allocateHours(hoursOnSubinterval); assertThat(genericResourceAllocation.getAssignedHours(), equalTo(hoursOnSubinterval + (days - daysSubinterval) * workableHoursDay)); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java index 0d4262efc..69be03884 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/SpecificResourceAllocationTest.java @@ -39,6 +39,7 @@ import static org.navalplanner.business.test.planner.entities.DayAssignmentMatch import static org.navalplanner.business.test.planner.entities.DayAssignmentMatchers.haveResourceAllocation; import static org.navalplanner.business.workingday.EffortDuration.hours; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -316,6 +317,73 @@ public class SpecificResourceAllocationTest { assertThat(specificResourceAllocation.getAssignments(), haveHours(5, 5)); } + @Test + public void theHoursForEachDayCanBeAssigned() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start, + start.plusDays(4)).allocate( + Arrays.asList(hours(4), hours(8), hours(4), hours(8))); + assertThat(specificResourceAllocation.getAssignments(), + haveHours(4, 8, 4, 8)); + } + + @Test + public void ifLessDaysAreSpecifiedTheInitialDaysAreAllocated() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start, + start.plusDays(4)).allocate( + Arrays.asList(hours(4), hours(8), hours(4))); + assertThat(specificResourceAllocation.getAssignments(), + haveHours(4, 8, 4)); + } + + @Test + public void ifMoreDaysAreSpecifiedTheInitialDaysAreAllocated() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start, + start.plusDays(4)) + .allocate( + Arrays.asList(hours(4), hours(8), hours(4), hours(4), + hours(3))); + assertThat(specificResourceAllocation.getAssignments(), + haveHours(4, 8, 4, 4)); + } + + @Test + public void theDaysSpecifiedOutsideBoundsAreDiscarded() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start.minusDays(2), + start.plusDays(1)).allocate( + Arrays.asList(hours(2), hours(3), hours(4))); + assertThat(specificResourceAllocation.getAssignments(), haveHours(4)); + } + + @Test + public void combineOutsideBoundsAndZeroPadding() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start.minusDays(2), + start.plusDays(1)).allocate(Arrays.asList(hours(2), hours(3))); + assertThat(specificResourceAllocation.getAssignments(), haveHours()); + } + + @Test + public void theDaysSpecifiedOutsideTheTaskAreDiscarded() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onIntervalWithinTask(start.minusDays(1), + start.plusDays(4)).allocate( + Arrays.asList(hours(10), hours(4), hours(8), hours(4), + hours(4), hours(3))); + List assigments = specificResourceAllocation + .getAssignments(); + assertThat(assigments, haveHours(4, 8, 4, 4)); + } + @Test public void theIntervalWithinTaskCanBeMadeOfIntraDayDates() { LocalDate start = new LocalDate(2000, 2, 4); @@ -447,6 +515,19 @@ public class SpecificResourceAllocationTest { haveHours(8, 8, 8, 8, 4)); } + @Test + public void canAllocateOutsideTheBoundsSpecifyingTheHoursForEachDay() { + LocalDate start = new LocalDate(2000, 2, 4); + givenSpecificResourceAllocation(start, 4); + specificResourceAllocation.onInterval( + IntraDayDate.startOfDay(start.minusDays(1)), + IntraDayDate.create(start.plusDays(4), hours(4))).allocate( + Arrays.asList(hours(8), hours(2), hours(8), hours(8), hours(8), + hours(4))); + assertThat(specificResourceAllocation.getAssignments(), + haveHours(8, 2, 8, 8, 8, 4)); + } + @Test public void allocatingZeroHoursAtTheEndShrinksTheAllocation() { LocalDate start = new LocalDate(2000, 2, 4);