Implement business logic to calculate Global Progress Indicator number 3.1.
FEA: ItEr75S27PerProjectDashboard
This commit is contained in:
parent
b7d4ff8622
commit
5a170c99a6
6 changed files with 246 additions and 9 deletions
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* This file is part of LibrePlan
|
||||
*
|
||||
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
|
||||
* Desenvolvemento Tecnolóxico de Galicia
|
||||
* Copyright (C) 2010-2011 Igalia, S.L.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.libreplan.business.planner.entities;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
import org.libreplan.business.planner.entities.DayAssignment;
|
||||
|
||||
/**
|
||||
* Computes aggregate values on a set{@link DayAssignment}.
|
||||
* <p>
|
||||
* @author Nacho Barrientos <nacho@igalia.com>
|
||||
*/
|
||||
public class AggregateOfDayAssignments {
|
||||
|
||||
public static AggregateOfDayAssignments createByDataRange(
|
||||
List<DayAssignment> assignments,
|
||||
Date start,
|
||||
Date end) {
|
||||
|
||||
Collections.sort(assignments, new Comparator<DayAssignment>() {
|
||||
@Override
|
||||
public int compare(DayAssignment arg0, DayAssignment arg1) {
|
||||
return arg0.getDay().compareTo(arg1.getDay());
|
||||
}
|
||||
});
|
||||
|
||||
return new AggregateOfDayAssignments(
|
||||
DayAssignment.getAtInterval(assignments,
|
||||
new LocalDate(start), new LocalDate(end)));
|
||||
}
|
||||
|
||||
private Set<DayAssignment> dayAssignments;
|
||||
|
||||
private AggregateOfDayAssignments(
|
||||
Collection<DayAssignment> assignments) {
|
||||
Validate.notNull(assignments);
|
||||
Validate.noNullElements(assignments);
|
||||
this.dayAssignments = new HashSet<DayAssignment>(
|
||||
assignments);
|
||||
}
|
||||
|
||||
public int getTotalHours() {
|
||||
int sum = 0;
|
||||
for (DayAssignment dayAssignment : dayAssignments) {
|
||||
sum += dayAssignment.getDuration().getHours();
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import static org.libreplan.business.workingday.EffortDuration.min;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -1085,4 +1086,11 @@ public class Task extends TaskElement implements ITaskPositionConstrained {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTheoreticalCompletedHoursUntilDate(Date date) {
|
||||
return AggregateOfDayAssignments.createByDataRange(
|
||||
this.getDayAssignments(),
|
||||
this.getStartDate(),
|
||||
date).getTotalHours();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -701,4 +701,10 @@ public abstract class TaskElement extends BaseEntity {
|
|||
return result;
|
||||
}
|
||||
|
||||
public abstract Integer getTheoreticalCompletedHoursUntilDate(Date date);
|
||||
|
||||
public BigDecimal getTheoreticalAdvancePercentageUntilDate(Date date) {
|
||||
BigDecimal result = new BigDecimal(this.getTheoreticalCompletedHoursUntilDate(date));
|
||||
return result.divide(new BigDecimal(this.getWorkHours()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ package org.libreplan.business.planner.entities;
|
|||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
|
@ -304,4 +305,13 @@ public class TaskGroup extends TaskElement {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTheoreticalCompletedHoursUntilDate(Date date) {
|
||||
int sum = 0;
|
||||
for(TaskElement each: taskElements) {
|
||||
sum += each.getTheoreticalCompletedHoursUntilDate(date);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -181,4 +181,10 @@ public class TaskMilestone extends TaskElement implements ITaskPositionConstrain
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getTheoreticalCompletedHoursUntilDate(Date date) {
|
||||
// TODO Auto-generated method stub FIXME
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,20 +34,25 @@ import static org.junit.Assert.assertThat;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.libreplan.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
|
||||
import static org.libreplan.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
|
||||
import static org.libreplan.business.test.planner.entities.DayAssignmentMatchers.haveHours;
|
||||
import static org.libreplan.business.workingday.EffortDuration.hours;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.easymock.IAnswer;
|
||||
import org.easymock.classextension.EasyMock;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.libreplan.business.IDataBootstrap;
|
||||
import org.libreplan.business.calendars.entities.AvailabilityTimeLine;
|
||||
import org.libreplan.business.calendars.entities.BaseCalendar;
|
||||
import org.libreplan.business.calendars.entities.Capacity;
|
||||
import org.libreplan.business.calendars.entities.ResourceCalendar;
|
||||
import org.libreplan.business.orders.entities.HoursGroup;
|
||||
import org.libreplan.business.orders.entities.Order;
|
||||
import org.libreplan.business.orders.entities.OrderLine;
|
||||
|
|
@ -57,10 +62,12 @@ import org.libreplan.business.planner.entities.SpecificResourceAllocation;
|
|||
import org.libreplan.business.planner.entities.Task;
|
||||
import org.libreplan.business.planner.entities.TaskElement;
|
||||
import org.libreplan.business.planner.limiting.entities.LimitingResourceQueueElement;
|
||||
import org.libreplan.business.resources.entities.Worker;
|
||||
import org.libreplan.business.scenarios.entities.OrderVersion;
|
||||
import org.libreplan.business.workingday.EffortDuration;
|
||||
import org.libreplan.business.workingday.IntraDayDate;
|
||||
import org.libreplan.business.workingday.IntraDayDate.PartialDay;
|
||||
import org.libreplan.business.workingday.ResourcesPerDay;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
|
@ -82,11 +89,15 @@ public class TaskTest {
|
|||
return result;
|
||||
}
|
||||
|
||||
private BaseCalendar taskCalendar;
|
||||
|
||||
private Task task;
|
||||
|
||||
private HoursGroup hoursGroup;
|
||||
|
||||
private BaseCalendar calendar;
|
||||
private ResourceCalendar workerCalendar;
|
||||
|
||||
private Worker worker;
|
||||
|
||||
@Resource
|
||||
private IDataBootstrap defaultAdvanceTypesBootstrapListener;
|
||||
|
|
@ -113,11 +124,13 @@ public class TaskTest {
|
|||
}
|
||||
|
||||
private BaseCalendar stubCalendar() {
|
||||
calendar = createNiceMock(BaseCalendar.class);
|
||||
expect(calendar.getCapacityOn(isA(PartialDay.class)))
|
||||
taskCalendar = createNiceMock(BaseCalendar.class);
|
||||
expect(taskCalendar.getCapacityOn(isA(PartialDay.class)))
|
||||
.andReturn(hours(8)).anyTimes();
|
||||
replay(calendar);
|
||||
return calendar;
|
||||
expect(taskCalendar.getAvailability()).andReturn(
|
||||
AvailabilityTimeLine.allValid()).anyTimes();
|
||||
replay(taskCalendar);
|
||||
return taskCalendar;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -236,8 +249,8 @@ public class TaskTest {
|
|||
public void ifSomeDayIsNotWorkableIsNotCounted() {
|
||||
final LocalDate start = new LocalDate(2010, 1, 13);
|
||||
|
||||
resetToNice(calendar);
|
||||
expect(calendar.getCapacityOn(isA(PartialDay.class))).andAnswer(
|
||||
resetToNice(taskCalendar);
|
||||
expect(taskCalendar.getCapacityOn(isA(PartialDay.class))).andAnswer(
|
||||
new IAnswer<EffortDuration>() {
|
||||
@Override
|
||||
public EffortDuration answer() throws Throwable {
|
||||
|
|
@ -247,7 +260,7 @@ public class TaskTest {
|
|||
: hours(8);
|
||||
}
|
||||
}).anyTimes();
|
||||
replay(calendar);
|
||||
replay(taskCalendar);
|
||||
|
||||
task.setIntraDayStartDate(IntraDayDate.create(start,
|
||||
EffortDuration.hours(3)));
|
||||
|
|
@ -294,4 +307,119 @@ public class TaskTest {
|
|||
assertTrue(task.getNonLimitingResourceAllocations().size() == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalHoursIsZeroIfNoResourcesAreAllocated() {
|
||||
assertThat(task.getTheoreticalCompletedHoursUntilDate(new Date()), equalTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalHoursIsTotalIfDateIsLaterThanEndDate() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
assertThat(task.getTheoreticalCompletedHoursUntilDate(task.getEndDate()), equalTo(task.getTotalHours()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalHoursIsZeroIfDateIsEarlierThanStartDate() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
assertThat(task.getTheoreticalCompletedHoursUntilDate(task.getStartDate()), equalTo(0));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalHoursWithADateWithinStartAndEndDateHead() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
LocalDate limit = task.getStartAsLocalDate().plusDays(1);
|
||||
assertThat(task.getTheoreticalCompletedHoursUntilDate(limit.toDateTimeAtStartOfDay().toDate()), equalTo(8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalHoursWithADateWithinStartAndEndDateTail() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
LocalDate limit = task.getEndAsLocalDate().minusDays(1);
|
||||
assertThat(task.getTheoreticalCompletedHoursUntilDate(limit.toDateTimeAtStartOfDay().toDate()), equalTo(32));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalAdvancePercentageIsZeroIfNoResourcesAreAllocated() {
|
||||
assertThat(task.getTheoreticalAdvancePercentageUntilDate(new Date()), equalTo(new BigDecimal(0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalPercentageIsOneIfDateIsLaterThanEndDate() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
assertThat(task.getTheoreticalAdvancePercentageUntilDate(task.getEndDate()), equalTo(new BigDecimal(1)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theoreticalPercentageWithADateWithinStartAndEndDateHead() {
|
||||
prepareTaskForTheoreticalAdvanceTesting();
|
||||
LocalDate limit = task.getStartAsLocalDate().plusDays(1);
|
||||
assertThat(task.getTheoreticalAdvancePercentageUntilDate(limit.toDateTimeAtStartOfDay().toDate()),
|
||||
equalTo(new BigDecimal("0.2")));
|
||||
}
|
||||
|
||||
private void prepareTaskForTheoreticalAdvanceTesting() {
|
||||
task.getHoursGroup().setWorkingHours(40);
|
||||
assertThat(task.getTotalHours(), equalTo(40));
|
||||
task.setEndDate(task.getStartAsLocalDate().plusDays(5).toDateTimeAtStartOfDay().toDate());
|
||||
|
||||
SpecificResourceAllocation resourceAllocation = SpecificResourceAllocation.create(task);
|
||||
|
||||
givenWorker(8);
|
||||
resourceAllocation.setResource(this.worker);
|
||||
assertTrue(resourceAllocation.getResource() != null);
|
||||
|
||||
resourceAllocation.allocate(ResourcesPerDay.amount(1));
|
||||
assertThat(resourceAllocation.getAssignments().size(), equalTo(5));
|
||||
assertThat(resourceAllocation.getAssignments(), haveHours(8, 8, 8, 8, 8));
|
||||
|
||||
assertThat(task.getAssignedHours(), equalTo(0));
|
||||
task.addResourceAllocation(resourceAllocation);
|
||||
assertTrue(task.getNonLimitingResourceAllocations().size() == 1);
|
||||
assertThat(task.getAssignedHours(), equalTo(40));
|
||||
assertTrue(task.getDayAssignments().size() == 5);
|
||||
}
|
||||
|
||||
private void givenWorker(int hoursPerDay) {
|
||||
this.worker = createNiceMock(Worker.class);
|
||||
givenResourceCalendarAlwaysReturning(hoursPerDay);
|
||||
expect(this.worker.getCalendar()).andReturn(this.workerCalendar).anyTimes();
|
||||
replay(this.worker);
|
||||
}
|
||||
|
||||
/* Taken from: SpecificResourceAllocationTest (TODO: Refactor) */
|
||||
private void givenResourceCalendarAlwaysReturning(final int hours) {
|
||||
this.workerCalendar = createNiceMock(ResourceCalendar.class);
|
||||
expect(this.workerCalendar.getCapacityOn(isA(PartialDay.class)))
|
||||
.andReturn(EffortDuration.hours(hours)).anyTimes();
|
||||
IAnswer<? extends EffortDuration> asDurationAnswer = asDurationOnAnswer(hours(hours));
|
||||
expect(
|
||||
this.workerCalendar.asDurationOn(isA(PartialDay.class),
|
||||
isA(ResourcesPerDay.class)))
|
||||
.andAnswer(asDurationAnswer).anyTimes();
|
||||
expect(this.workerCalendar.getCapacityWithOvertime(isA(LocalDate.class)))
|
||||
.andReturn(
|
||||
Capacity.create(hours(hours))
|
||||
.overAssignableWithoutLimit()).anyTimes();
|
||||
expect(this.workerCalendar.getAvailability()).andReturn(
|
||||
AvailabilityTimeLine.allValid()).anyTimes();
|
||||
replay(this.workerCalendar);
|
||||
}
|
||||
|
||||
/* Taken from: SpecificResourceAllocationTest (TODO: Refactor) */
|
||||
private IAnswer<? extends EffortDuration> asDurationOnAnswer(
|
||||
final EffortDuration duration) {
|
||||
return new IAnswer<EffortDuration>() {
|
||||
|
||||
@Override
|
||||
public EffortDuration answer() throws Throwable {
|
||||
ResourcesPerDay perDay = (ResourcesPerDay) EasyMock
|
||||
.getCurrentArguments()[1];
|
||||
return perDay.asDurationGivenWorkingDayOf(duration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue