/*
* 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.web.reports;
import static org.libreplan.web.I18nHelper._;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.libreplan.business.labels.daos.ILabelDAO;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.planner.daos.ITaskElementDAO;
import org.libreplan.business.planner.entities.Dependency;
import org.libreplan.business.planner.entities.Dependency.Type;
import org.libreplan.business.planner.entities.Task;
import org.libreplan.business.planner.entities.TaskElement;
import org.libreplan.business.planner.entities.TaskStatusEnum;
import org.libreplan.business.reports.dtos.WorkingArrangementPerOrderDTO;
import org.libreplan.business.reports.dtos.WorkingArrangementPerOrderDTO.DependencyWorkingArrangementDTO;
import org.libreplan.business.resources.daos.ICriterionTypeDAO;
import org.libreplan.business.resources.entities.Criterion;
import org.libreplan.business.resources.entities.CriterionType;
import org.libreplan.business.resources.entities.ResourceEnum;
import org.libreplan.business.scenarios.IScenarioManager;
import org.libreplan.business.scenarios.entities.Scenario;
import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.business.workreports.entities.WorkReportLine;
import org.libreplan.web.security.SecurityUtils;
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;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Diego Pino Garcia <dpino@igalia.com>
* @author Susana Montes Pedreira <smontes@wirelessgalicia.com>
*/
@Service
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class WorkingArrangementsPerOrderModel implements
IWorkingArrangementsPerOrderModel {
@Autowired
IOrderDAO orderDAO;
@Autowired
ITaskElementDAO taskDAO;
@Autowired
IWorkReportLineDAO workReportLineDAO;
@Autowired
private ICommonQueries commonQueries;
@Autowired
private IScenarioManager scenarioManager;
@Autowired
private ILabelDAO labelDAO;
@Autowired
private ICriterionTypeDAO criterionTypeDAO;
private List<Label> selectedLabels = new ArrayList<Label>();
private List<Criterion> selectedCriterions = new ArrayList<Criterion>();
private List<Criterion> allCriterions = new ArrayList<Criterion>();
private List<Label> allLabels = new ArrayList<Label>();
private String selectedCriteria;
private String selectedLabel;
private boolean hasChangeCriteria = false;
private boolean hasChangeLabels = false;
private static List<ResourceEnum> applicableResources = new ArrayList<ResourceEnum>();
static {
applicableResources.add(ResourceEnum.WORKER);
}
@Override
@Transactional(readOnly = true)
public void init() {
selectedCriterions.clear();
selectedLabels.clear();
allLabels.clear();
allCriterions.clear();
loadAllLabels();
loadAllCriterions();
}
@Override
@Transactional(readOnly = true)
public List<Order> getOrders() {
Scenario currentScenario = scenarioManager.getCurrent();
final List<Order> orders = orderDAO
.getOrdersByReadAuthorizationByScenario(
SecurityUtils.getSessionUserLoginName(),
currentScenario);
for (Order each: orders) {
initializeOrderElements(each.getOrderElements());
each.useSchedulingDataFor(currentScenario);
}
Collections.sort(orders);
return orders;
}
private void initializeOrderElements(List<OrderElement> orderElements) {
for (OrderElement orderElement: orderElements) {
orderElement.getName();
}
}
@Override
@Transactional(readOnly = true)
public JRDataSource getWorkingArrangementsPerOrderReportReport(
Order order,
TaskStatusEnum taskStatus, boolean showDependencies,
List<Label> labels, List<Criterion> criterions) {
if (order == null) {
return new JREmptyDataSource();
}
reattachmentOrder(order);
order.useSchedulingDataFor(scenarioManager.getCurrent());
List<WorkingArrangementPerOrderDTO> workingArrangementPerOrderList =
new ArrayList<WorkingArrangementPerOrderDTO>();
reattachLabels();
final List<Task> tasks = commonQueries.filteredTaskElements(order,
labels, criterions);
final List<Task> sortTasks = sortTasks(order, tasks);
final Date deadLineOrder = order.getDeadline();
for (Task task : sortTasks) {
// If taskStatus is ALL, add task and calculate its real status
if (TaskStatusEnum.ALL.equals(taskStatus)) {
workingArrangementPerOrderList
.addAll(createWorkingArrangementPerOrderDTOs(
deadLineOrder, task,
calculateTaskStatus(task), showDependencies));
continue;
}
// Only add task if matches selected taskStatus
if (matchTaskStatus(task, taskStatus)) {
workingArrangementPerOrderList
.addAll(createWorkingArrangementPerOrderDTOs(
deadLineOrder, task, taskStatus,
showDependencies));
}
}
if (!workingArrangementPerOrderList.isEmpty()) {
return new JRBeanCollectionDataSource(
workingArrangementPerOrderList);
} else {
return new JREmptyDataSource();
}
}
private void reattachmentOrder(Order order) {
orderDAO.reattachUnmodifiedEntity(order);
}
private List<Task> sortTasks(Order order, List<Task> tasks) {
List<Task> sortTasks = new ArrayList<Task>();
final List<OrderElement> orderElements = order.getAllChildren();
for (OrderElement orderElement : orderElements) {
Task task = findOrderElementInTasks(orderElement, tasks);
if (task != null) {
sortTasks.add(task);
}
}
return sortTasks;
}
private Task findOrderElementInTasks(OrderElement orderElement,
List<Task> tasks) {
for (Task task : tasks) {
if (task.getOrderElement().getId().equals(orderElement.getId())) {
return task;
}
}
return null;
}
/**
* Create a collection of {@link WorkingArrangementPerOrderDTO}
*
* It's necessary to create one dto for every {@link Dependency} in {@link Task}
*
* @param task
* @param taskStatus
* @return
*/
@Transactional(readOnly = true)
private List<WorkingArrangementPerOrderDTO> createWorkingArrangementPerOrderDTOs(
Date deadLineOrder,
Task task, TaskStatusEnum taskStatus, boolean showDependencies) {
List<WorkingArrangementPerOrderDTO> result = new ArrayList<WorkingArrangementPerOrderDTO>();
// Add current task
final Set<Dependency> dependencies = task
.getDependenciesWithThisDestination();
WorkingArrangementPerOrderDTO workingArrangementPerOrderDTO = new WorkingArrangementPerOrderDTO(
deadLineOrder, task, taskStatus, showDependencies
&& !dependencies.isEmpty());
workingArrangementPerOrderDTO.setStatus(_(workingArrangementPerOrderDTO
.getStatus()));
result.add(workingArrangementPerOrderDTO);
// Add dependencies
if (showDependencies) {
taskDAO.reattach(task);
for (Dependency each : dependencies) {
final OrderElement orderElement = each.getOrigin()
.getOrderElement();
DependencyWorkingArrangementDTO dependencyDTO = new DependencyWorkingArrangementDTO(
orderElement.getName(), orderElement.getCode(), each
.getType().toString(), orderElement
.getAdvancePercentage());
WorkingArrangementPerOrderDTO dto = new WorkingArrangementPerOrderDTO(
task, taskStatus, dependencyDTO);
dto.setStatus(_(dto.getStatus()));
result.add(dto);
}
}
return result;
}
private boolean matchTaskStatus(Task task, TaskStatusEnum taskStatus) {
final TaskStatusEnum _taskStatus = calculateTaskStatus(task);
return _taskStatus != null && _taskStatus.equals(taskStatus);
}
private TaskStatusEnum calculateTaskStatus(Task task) {
if (matchTaskStatusFinished(task)) {
return TaskStatusEnum.FINISHED;
}
if (matchTaskStatusInProgress(task)) {
return TaskStatusEnum.IN_PROGRESS;
}
if (matchTaskStatusPending(task)) {
return TaskStatusEnum.PENDING;
}
if (matchTaskStatusBlocked(task)) {
return TaskStatusEnum.BLOCKED;
}
return null;
}
private boolean matchTaskStatusFinished(Task task) {
final OrderElement order = task.getOrderElement();
BigDecimal measuredProgress = order.getAdvancePercentage();
return isFinished(measuredProgress);
}
private boolean isFinished(BigDecimal measuredProgress) {
measuredProgress = (measuredProgress.multiply(new BigDecimal(100)))
.setScale(0, BigDecimal.ROUND_UP);
return measuredProgress.equals(new BigDecimal(100));
}
private boolean matchTaskStatusInProgress(Task task) {
final OrderElement order = task.getOrderElement();
final BigDecimal measuredProgress = order.getAdvancePercentage();
return isInProgress(measuredProgress)
|| (hasNotYetStarted(measuredProgress) && hasAtLeastOneWorkReportLine(order));
}
private boolean isInProgress(BigDecimal measuredProgress) {
return ((measuredProgress.compareTo(new BigDecimal(1)) < 0) && (measuredProgress
.compareTo(new BigDecimal(0)) > 0));
}
private boolean hasAtLeastOneWorkReportLine(OrderElement order) {
return !getWorkReportLines(order).isEmpty();
}
@Transactional(readOnly = true)
private List<WorkReportLine> getWorkReportLines(OrderElement order) {
return workReportLineDAO.findByOrderElement(order);
}
private boolean matchTaskStatusPending(Task task) {
final OrderElement order = task.getOrderElement();
final BigDecimal measuredProgress = order.getAdvancePercentage();
return hasNotYetStarted(measuredProgress)
&& hasNotWorkReports(order)
&& (!isBlockedByDepedendantTasks(task));
}
private boolean hasNotWorkReports(OrderElement order) {
return !hasAtLeastOneWorkReportLine(order);
}
private boolean hasNotYetStarted(BigDecimal measuredProgress) {
return measuredProgress.setScale(2).equals(
new BigDecimal(0).setScale(2));
}
private boolean matchTaskStatusBlocked(Task task) {
final OrderElement order = task.getOrderElement();
final BigDecimal measuredProgress = order.getAdvancePercentage();
return hasNotYetStarted(measuredProgress)
&& hasNotWorkReports(order)
&& isBlockedByDepedendantTasks(task);
}
private boolean isBlockedByDepedendantTasks(Task task) {
taskDAO.reattach(task);
final Set<Dependency> dependencies = task
.getDependenciesWithThisDestination();
if (dependencies.isEmpty()) {
return false;
}
boolean result = true;
for (Dependency each: dependencies) {
final TaskElement taskElement = each.getOrigin();
final BigDecimal measuredProgress = taskElement.getOrderElement()
.getAdvancePercentage();
final Type dependencyType = each.getType();
if (Type.END_START.equals(dependencyType)) {
result &= (!isFinished(measuredProgress));
}
if (Type.START_START.equals(dependencyType)) {
result &= hasNotYetStarted(measuredProgress);
}
}
return result;
}
private void reattachLabels() {
for (Label label : getAllLabels()) {
labelDAO.reattach(label);
}
}
@Override
public List<Label> getAllLabels() {
return allLabels;
}
@Transactional(readOnly = true)
private void loadAllLabels() {
allLabels = labelDAO.getAll();
// initialize the labels
for (Label label : allLabels) {
label.getType().getName();
}
}
@Override
public void removeSelectedLabel(Label label) {
this.selectedLabels.remove(label);
this.hasChangeLabels = true;
}
@Override
public boolean addSelectedLabel(Label label) {
if (this.selectedLabels.contains(label)) {
return false;
}
this.selectedLabels.add(label);
this.hasChangeLabels = true;
return true;
}
@Override
public List<Label> getSelectedLabels() {
return selectedLabels;
}
@Override
public List<Criterion> getCriterions() {
return this.allCriterions;
}
private void loadAllCriterions() {
List<CriterionType> listTypes = getCriterionTypes();
for (CriterionType criterionType : listTypes) {
if (criterionType.isEnabled()) {
Set<Criterion> listCriterion = getDirectCriterions(criterionType);
addCriterionWithItsType(listCriterion);
}
}
}
private static Set<Criterion> getDirectCriterions(
CriterionType criterionType) {
Set<Criterion> criterions = new HashSet<Criterion>();
for (Criterion criterion : criterionType.getCriterions()) {
if (criterion.getParent() == null) {
criterions.add(criterion);
}
}
return criterions;
}
private void addCriterionWithItsType(Set<Criterion> children) {
for (Criterion criterion : children) {
if (criterion.isActive()) {
// Add to the list
allCriterions.add(criterion);
addCriterionWithItsType(criterion.getChildren());
}
}
}
private List<CriterionType> getCriterionTypes() {
return criterionTypeDAO
.getCriterionTypesByResources(applicableResources);
}
@Override
public void removeSelectedCriterion(Criterion criterion) {
this.selectedCriterions.remove(criterion);
this.hasChangeCriteria = true;
}
@Override
public boolean addSelectedCriterion(Criterion criterion) {
if (this.selectedCriterions.contains(criterion)) {
return false;
}
this.selectedCriterions.add(criterion);
this.hasChangeCriteria = true;
return true;
}
@Override
public List<Criterion> getSelectedCriterions() {
return selectedCriterions;
}
public void setSelectedLabel(String selectedLabel) {
this.selectedLabel = selectedLabel;
}
public String getSelectedLabel() {
if (hasChangeLabels) {
Iterator<Label> iterator = this.selectedLabels.iterator();
this.selectedLabel = null;
if (iterator.hasNext()) {
this.selectedLabel = new String();
this.selectedLabel = this.selectedLabel.concat(iterator.next()
.getName());
}
while (iterator.hasNext()) {
this.selectedLabel = this.selectedLabel.concat(", "
+ iterator.next().getName());
}
hasChangeLabels = false;
}
return selectedLabel;
}
public void setSelectedCriteria(String selectedCriteria) {
this.selectedCriteria = selectedCriteria;
}
public String getSelectedCriteria() {
if (hasChangeCriteria) {
this.selectedCriteria = null;
Iterator<Criterion> iterator = this.selectedCriterions.iterator();
if (iterator.hasNext()) {
this.selectedCriteria = new String();
this.selectedCriteria = this.selectedCriteria.concat(iterator
.next().getName());
}
while (iterator.hasNext()) {
this.selectedCriteria = this.selectedCriteria.concat(", "
+ iterator.next().getName());
}
hasChangeCriteria = false;
}
return selectedCriteria;
}
}