package org.sigmah.server.handler.util; /* * #%L * Sigmah * %% * Copyright (C) 2010 - 2016 URD * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.TypedQuery; import org.apache.commons.lang3.BooleanUtils; import org.sigmah.server.dao.base.EntityManagerProvider; import org.sigmah.server.domain.OrgUnit; import org.sigmah.server.domain.Phase; import org.sigmah.server.domain.Project; import org.sigmah.server.domain.ProjectFunding; import org.sigmah.server.domain.ProjectModelVisibility; import org.sigmah.server.domain.User; import org.sigmah.server.domain.category.CategoryElement; import org.sigmah.server.domain.category.CategoryType; import org.sigmah.server.domain.element.QuestionChoiceElement; import org.sigmah.server.domain.value.Value; import org.sigmah.server.mapper.Mapper; import org.sigmah.shared.dto.ProjectDTO; import org.sigmah.shared.dto.ProjectModelVisibilityDTO; import org.sigmah.shared.dto.UserDTO; import org.sigmah.shared.dto.base.EntityDTO; import org.sigmah.shared.dto.category.CategoryElementDTO; import org.sigmah.shared.dto.category.CategoryTypeDTO; import org.sigmah.shared.dto.country.CountryDTO; import org.sigmah.shared.util.ValueResultUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; import javax.persistence.NoResultException; import org.sigmah.server.domain.element.BudgetRatioElement; import org.sigmah.server.domain.element.FlexibleElement; import org.sigmah.shared.computation.value.ComputedValue; import org.sigmah.shared.computation.value.ComputedValues; /** * Responsible for the mapping of project objects. * * @author tmi * @author Maxime Lombard (mlombard@ideia.fr) * @author Denis Colliot (dcolliot@ideia.fr) */ public class ProjectMapper extends EntityManagerProvider { /** * Injected mapper. */ @Inject private Mapper mapper; /** * Logger. */ private static final Logger LOG = LoggerFactory.getLogger(ProjectMapper.class); /** * <p> * Map a project into a project light DTO. * </p> * <p> * Populates the following fields of the returned {@link ProjectDTO} instance: * <ul> * <li>{@link EntityDTO#ID}</li> * <li>{@link ProjectDTO#NAME}</li> * <li>{@link ProjectDTO#FULL_NAME}</li> * <li>{@link ProjectDTO#START_DATE}</li> * <li>{@link ProjectDTO#END_DATE}</li> * <li>{@link ProjectDTO#CLOSE_DATE}</li> * <li>{@link ProjectDTO#ACTIVITY_ADVANCEMENT}</li> * <li>{@link ProjectDTO#COUNTRY}</li> * <li>{@link ProjectDTO#CURRENT_PHASE_NAME}</li> * <li>{@link ProjectDTO#VISIBILITIES}</li> * <li>{@link ProjectDTO#ORG_UNIT_NAME}</li> * <li>{@link ProjectDTO#CATEGORY_ELEMENTS}</li> * <li>{@link ProjectDTO#RATIO_DIVIDEND_LABEL}</li> * <li>{@link ProjectDTO#RATIO_DIVIDEND_TYPE}</li> * <li>{@link ProjectDTO#RATIO_DIVIDEND_VALUE}</li> * <li>{@link ProjectDTO#RATIO_DIVISOR_LABEL}</li> * <li>{@link ProjectDTO#RATIO_DIVISOR_TYPE}</li> * <li>{@link ProjectDTO#RATIO_DIVISOR_VALUE}</li> * <li>{@link ProjectDTO#PLANNED_BUDGET}</li> * <li>{@link ProjectDTO#RECEIVED_BUDGET}</li> * <li>{@link ProjectDTO#SPEND_BUDGET}</li> * <li>{@link ProjectDTO#CHILDREN_PROJECTS}</li> * <li>{@link ProjectDTO#FAVORITE_USERS}</li> * </ul> * </p> * * @param project * The project. * @param mapChildren * If the children projects must be retrieved. * @return The light DTO. */ public ProjectDTO map(final Project project, final boolean mapChildren) { final StringBuilder sb = new StringBuilder(); sb.append("Project mapping:\n"); final ProjectDTO projectDTO = new ProjectDTO(); // --------------- // -- SIMPLE FIELDS // --------------- long start = new Date().getTime(); projectDTO.setId(project.getId()); projectDTO.setName(project.getName()); projectDTO.setFullName(project.getFullName()); projectDTO.setStartDate(project.getStartDate()); projectDTO.setEndDate(project.getEndDate()); projectDTO.setCloseDate(project.getCloseDate()); projectDTO.setActivityAdvancement(project.getActivityAdvancement()); projectDTO.setCountry(mapper.map(project.getCountry(), new CountryDTO())); sb.append("- SIMPLE FIELDS: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); // --------------- // -- CURRENT PHASE // --------------- start = new Date().getTime(); final Phase currentPhase = project.getCurrentPhase(); if (currentPhase != null) { projectDTO.setCurrentPhaseName(currentPhase.getPhaseModel().getName()); } sb.append("- CURRENT PHASE: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); // --------------- // -- VISIBILITIES // --------------- start = new Date().getTime(); final ArrayList<ProjectModelVisibilityDTO> visibilities = new ArrayList<ProjectModelVisibilityDTO>(); for (final ProjectModelVisibility v : project.getProjectModel().getVisibilities()) { final ProjectModelVisibilityDTO vDTO = new ProjectModelVisibilityDTO(); vDTO.setId(v.getId()); vDTO.setType(v.getType()); vDTO.setOrganizationId(v.getOrganization().getId()); visibilities.add(vDTO); } projectDTO.setVisibilities(visibilities); sb.append("- VISIBILITIES: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); // --------------- // -- ORG UNIT // --------------- start = new Date().getTime(); // Fill the org unit. final TypedQuery<OrgUnit> orgUnitQuery = em().createQuery("SELECT o FROM OrgUnit o WHERE :project MEMBER OF o.databases", OrgUnit.class); orgUnitQuery.setParameter("project", project); for (final OrgUnit orgUnit : orgUnitQuery.getResultList()) { projectDTO.setOrgUnitName(orgUnit.getName() + " - " + orgUnit.getFullName()); break; } sb.append("- ORG UNIT: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); // --------------- // -- CATEGORIES // --------------- start = new Date().getTime(); final TypedQuery<Value> categoriesQuery = em().createQuery( "SELECT v FROM Value v JOIN v.element e WHERE v.containerId = :projectId AND " + "e.id IN (SELECT q.id FROM QuestionElement q WHERE q.categoryType IS NOT NULL)", Value.class); categoriesQuery.setParameter("projectId", project.getId()); final HashSet<CategoryElementDTO> elements = new HashSet<CategoryElementDTO>(); for (final Value value : categoriesQuery.getResultList()) { List<Integer> values = ValueResultUtils.splitValuesAsInteger(value.getValue()); if (!values.isEmpty()) { final TypedQuery<QuestionChoiceElement> choicesQuery = em().createQuery("SELECT c FROM QuestionChoiceElement c WHERE c.id IN (:ids)", QuestionChoiceElement.class); choicesQuery.setParameter("ids", ValueResultUtils.splitValuesAsInteger(value.getValue())); for (final QuestionChoiceElement choice : choicesQuery.getResultList()) { final CategoryType parent = choice.getCategoryElement().getParentType(); final CategoryTypeDTO parentDTO = new CategoryTypeDTO(); parentDTO.setId(parent.getId()); parentDTO.setLabel(parent.getLabel()); parentDTO.setIcon(parent.getIcon()); final CategoryElement element = choice.getCategoryElement(); final CategoryElementDTO elementDTO = new CategoryElementDTO(); elementDTO.setId(element.getId()); elementDTO.setLabel(element.getLabel()); elementDTO.setColor(element.getColor()); elementDTO.setParentCategoryDTO(parentDTO); elements.add(elementDTO); } } } projectDTO.setCategoryElements(elements); fillBudget(project, projectDTO); sb.append("- CATEGORIES: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); // --------------- // -- CHILDREN // --------------- start = new Date().getTime(); final ArrayList<ProjectDTO> children = new ArrayList<ProjectDTO>(); // Maps the funding projects. if (mapChildren && project.getFunding() != null) { for (final ProjectFunding funding : project.getFunding()) { final Project pFunding = funding.getFunding(); if (pFunding != null) { // Recursive call to retrieve the child (without its children). children.add(map(pFunding, false)); } } } // Maps the funded projects. if (mapChildren && project.getFunded() != null) { for (final ProjectFunding funded : project.getFunded()) { final Project pFunded = funded.getFunded(); if (pFunded != null) { // Recursive call to retrieve the child (without its children). children.add(map(pFunded, false)); } } } projectDTO.setChildrenProjects(children); // ------------------ // -- FAVORITE USERS // ------------------ if (project.getFavoriteUsers() != null) { final Set<UserDTO> favoriteUsesSet = new HashSet<UserDTO>(); for (final User u : project.getFavoriteUsers()) { // favoriteUsesSet.add(dozermapper.map(u, new UserDTO())); UserDTO uDTO = new UserDTO(); uDTO.setId(u.getId()); uDTO.setChangePasswordKey(u.getChangePasswordKey()); uDTO.setDateChangePasswordKeyIssued(u.getDateChangePasswordKeyIssued()); uDTO.setEmail(u.getEmail()); uDTO.setFirstName(u.getFirstName()); uDTO.setLocale(u.getLocale()); uDTO.setActive(BooleanUtils.isNotFalse(u.getActive())); favoriteUsesSet.add(uDTO); } projectDTO.setFavoriteUsers(favoriteUsesSet); } else { projectDTO.setFavoriteUsers(null); } // ---END---- sb.append("- CHILDREN: "); sb.append(new Date().getTime() - start); sb.append("ms.\n"); if (LOG.isDebugEnabled()) { LOG.debug(sb.toString()); } return projectDTO; } /** * Fill the budget values of the given project. * * @param project * Project. * @param projectDTO * DTO to fill. */ public void fillBudget(final Project project, final ProjectDTO projectDTO) { final TypedQuery<BudgetRatioElement> budgetRatioQuery = em().createQuery("SELECT b FROM BudgetRatioElement b " + "WHERE EXISTS(SELECT 1 FROM Project p JOIN p.projectModel.projectDetails.layout.groups g join g.constraints c WHERE p = :project AND c.element = b) " + "OR EXISTS(SELECT 1 FROM Project p JOIN p.projectModel.phaseModels phm join phm.layout.groups g join g.constraints c WHERE p = :project AND c.element = b)", BudgetRatioElement.class); budgetRatioQuery.setParameter("project", project); final BudgetRatioElement budgetRatioElement; try { budgetRatioElement = budgetRatioQuery.getSingleResult(); } catch (NoResultException e) { LOG.error("No budget ratio element was found for project #" + project.getId(), e); return; } final TypedQuery<Value> valueQuery = em().createQuery("SELECT v FROM Value v WHERE v.containerId = :projectId AND v.element = :element", Value.class); valueQuery.setParameter("projectId", project.getId()); final FlexibleElement spentBudgetElement = budgetRatioElement.getSpentBudget(); if (spentBudgetElement != null) { final Double spentBudget = getBudgetValue(valueQuery, spentBudgetElement); projectDTO.setSpendBudget(spentBudget); projectDTO.setRatioDividendValue(spentBudget); projectDTO.setRatioDividendLabel(spentBudgetElement.getLabel()); } final FlexibleElement plannedBudgetElement = budgetRatioElement.getPlannedBudget(); if (plannedBudgetElement != null) { final Double plannedBudget = getBudgetValue(valueQuery, plannedBudgetElement); projectDTO.setPlannedBudget(plannedBudget); projectDTO.setRatioDivisorValue(plannedBudget); projectDTO.setRatioDivisorLabel(plannedBudgetElement.getLabel()); } } /** * Get the value of the given flexible element. * * @param valueQuery * Query to use. * @param element * Flexible element to query. * @return The value of the given flexible element as a <code>Double</code> or <code>null</code> if no value was found. */ private Double getBudgetValue(final TypedQuery<Value> valueQuery, final FlexibleElement element) { valueQuery.setParameter("element", element); try { final Value spentValue = valueQuery.getSingleResult(); final ComputedValue computedValue = ComputedValues.from(spentValue.getValue()); return computedValue.get(); } catch (NoResultException e) { LOG.trace("No value was found for element #" + element.getId() + ".", e); } return null; } }