From 9b3ab84cf0d737bb629274fc7e35eac85dcc5403 Mon Sep 17 00:00:00 2001 From: Javier Moran Rua Date: Sat, 13 Oct 2012 17:36:36 +0200 Subject: [PATCH] Adds calculator to obtain the ratios of a resource as a spring bean Two load ratios are defined: * Overload = overload / (load + overload) * Availability = min (0,(1 - load/capacity)) FEA: ItEr77S10ResourceAllocationLoadInformation --- .../daos/IResourceLoadRatiosCalculator.java | 80 +++++++ .../daos/ResourceLoadRatiosCalculator.java | 218 ++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 libreplan-business/src/main/java/org/libreplan/business/resources/daos/IResourceLoadRatiosCalculator.java create mode 100644 libreplan-business/src/main/java/org/libreplan/business/resources/daos/ResourceLoadRatiosCalculator.java diff --git a/libreplan-business/src/main/java/org/libreplan/business/resources/daos/IResourceLoadRatiosCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/resources/daos/IResourceLoadRatiosCalculator.java new file mode 100644 index 000000000..d8e349625 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/resources/daos/IResourceLoadRatiosCalculator.java @@ -0,0 +1,80 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 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 . + */ + +package org.libreplan.business.resources.daos; + +import java.math.BigDecimal; + +import org.joda.time.LocalDate; +import org.libreplan.business.resources.entities.Resource; +import org.libreplan.business.scenarios.entities.Scenario; +import org.libreplan.business.workingday.EffortDuration; + +/** + * Specifies the method to calculate the load ratios of a resource and the data + * type which represents the output + * + * @author Javier Moran Rua + * + */ +public interface IResourceLoadRatiosCalculator { + + public interface ILoadRatiosDataType { + + EffortDuration getLoad(); + + EffortDuration getOverload(); + + EffortDuration getCapacity(); + + /** + * Calculates the overtime ratio. The overtime ratio is defined as + * overload / (load+overload). + * + * @return the overtime ratio represented with a {@link BigDecimal} with + * scale of 2. If both load and overload are zero it is returned + * zero. + */ + BigDecimal getOvertimeRatio(); + + /** + * Calculates the availability ratio. The availability ratio is defined + * as 1 - (load/capacity)*100. It is a percentage. + * + * @return the availability ratio represented with a {@link BigDecimal} + * with a scale of 2. In the case that the capacity is zero, a + * 0% of availability is returned. + */ + BigDecimal getAvailiabilityRatio(); + } + + /** + * Calculates the load ratios of a resource between two dates in the + * escenario specified. + * + * @param resource + * @param startDate + * @param endDate + * @return the load ratios calculated. + */ + ILoadRatiosDataType calculateLoadRatios(Resource resource, + LocalDate startDate, + LocalDate endDate, Scenario scenario); + +} diff --git a/libreplan-business/src/main/java/org/libreplan/business/resources/daos/ResourceLoadRatiosCalculator.java b/libreplan-business/src/main/java/org/libreplan/business/resources/daos/ResourceLoadRatiosCalculator.java new file mode 100644 index 000000000..cc34e34c4 --- /dev/null +++ b/libreplan-business/src/main/java/org/libreplan/business/resources/daos/ResourceLoadRatiosCalculator.java @@ -0,0 +1,218 @@ +/* + * This file is part of LibrePlan + * + * Copyright (C) 2012 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 . + */ + +package org.libreplan.business.resources.daos; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.joda.time.LocalDate; +import org.libreplan.business.common.IAdHocTransactionService; +import org.libreplan.business.common.IOnTransaction; +import org.libreplan.business.planner.daos.IDayAssignmentDAO; +import org.libreplan.business.planner.entities.DayAssignment; +import org.libreplan.business.resources.entities.Resource; +import org.libreplan.business.scenarios.entities.Scenario; +import org.libreplan.business.workingday.EffortDuration; +import org.libreplan.business.workingday.IntraDayDate.PartialDay; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +/** + * Class designed to be used as a singleton spring bean implementing the + * {@link IResourceLoadRatiosCalculator} interface. + * + * Spawns a new Hibernate transaction using {@link IAdHocTransactionService} to + * access the database. + * + * @author Javier Moran Rua + * + */ + +@Service +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class ResourceLoadRatiosCalculator implements + IResourceLoadRatiosCalculator { + + @Autowired + private IDayAssignmentDAO dayAssigmentDAO; + + @Autowired + private IResourceDAO resourceDAO; + + @Autowired + private IAdHocTransactionService adHocTransactionService; + + private static class LoadRatiosDataType implements + IResourceLoadRatiosCalculator.ILoadRatiosDataType { + private EffortDuration load; + private EffortDuration overload; + private EffortDuration capacity; + + public LoadRatiosDataType(EffortDuration load, EffortDuration overload, + EffortDuration capacity) { + this.load = load; + this.overload = overload; + this.capacity = capacity; + } + + @Override + public EffortDuration getLoad() { + return this.load; + } + + @Override + public EffortDuration getOverload() { + return this.overload; + } + + @Override + public EffortDuration getCapacity() { + return this.capacity; + } + + @Override + public BigDecimal getOvertimeRatio() { + BigDecimal result; + if (this.load.isZero() && this.overload.isZero()) { + result = BigDecimal.ZERO; + } else { + result = this.overload.dividedByAndResultAsBigDecimal(this.load + .plus(this.capacity)); + } + + return result.setScale(2, BigDecimal.ROUND_HALF_UP); + } + + @Override + public BigDecimal getAvailiabilityRatio() { + BigDecimal result; + if (this.capacity.isZero()) { + result = BigDecimal.ZERO; + } else { + + result = BigDecimal.ONE.subtract(this.load + .dividedByAndResultAsBigDecimal(this.capacity)); + if (result.compareTo(BigDecimal.ZERO) < 0) { + result = BigDecimal.ZERO; + } + } + return result.setScale(2, BigDecimal.ROUND_HALF_UP); + } + } + + @Override + public ILoadRatiosDataType calculateLoadRatios(final Resource resource, + final LocalDate startDate, final LocalDate endDate, + final Scenario scenario) { + + return adHocTransactionService + .runOnReadOnlyTransaction(new IOnTransaction() { + + @Override + @SuppressWarnings("unchecked") + public LoadRatiosDataType execute() { + + resourceDAO.reattach(resource); + + EffortDuration totalLoad = EffortDuration.zero(), + totalOverload = EffortDuration.zero(), + totalCapacity = EffortDuration.zero(); + + for (Map.Entry each : + getAllEffortPerDateFor(scenario, + startDate, endDate, resource) + .entrySet()) { + totalLoad = addLoad(totalLoad, each.getValue()); + totalOverload = addOverload(totalOverload, + resource, each.getValue(), each.getKey()); + + } + + totalCapacity = calculateTotalCapacity(resource, + startDate, endDate); + return new LoadRatiosDataType(totalLoad, + totalOverload, totalCapacity); + } + + private Map + getAllEffortPerDateFor(Scenario scenario,LocalDate startDate, + LocalDate endDate, Resource resource) { + + HashMap result = + new HashMap(); + + List l = dayAssigmentDAO.getAllFor( + scenario, startDate, endDate, + resource); + + EffortDuration newValue = EffortDuration.zero(); + for (DayAssignment each: l) { + if (result.containsKey(each.getDay())) { + newValue = result.get(each.getDay()). + plus(each.getDuration()); + } else { + newValue = each.getDuration(); + } + result.put(each.getDay(), newValue); + } + return result; + } + + private EffortDuration addLoad(EffortDuration currentLoad, + EffortDuration load) { + return currentLoad.plus(load); + } + + private EffortDuration calculateTotalCapacity( + Resource resource, LocalDate startDate, + LocalDate endDate) { + + return resource.getCalendar().getWorkableDuration( + startDate, endDate); + } + + private EffortDuration addOverload( + EffortDuration currentOverload, Resource resource, + EffortDuration loadAtDate, LocalDate date) { + EffortDuration result; + EffortDuration capacityAtDay = getCapacityAtDate( + resource, date); + if (capacityAtDay.compareTo(loadAtDate) < 0) { + result = currentOverload.plus(loadAtDate + .minus(capacityAtDay)); + } else { + result = currentOverload; + } + + return result; + } + + private EffortDuration getCapacityAtDate(Resource resource, + LocalDate date) { + return resource.getCalendar().getCapacityOn( + PartialDay.wholeDay(date)); + } + }); + } +}