package org.sigmah.server.service;
/*
* #%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 com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collection;
import java.util.List;
import javax.persistence.TypedQuery;
import org.sigmah.server.computation.ServerComputations;
import org.sigmah.server.computation.ServerValueResolver;
import org.sigmah.server.dao.base.EntityManagerProvider;
import org.sigmah.server.domain.Project;
import org.sigmah.server.domain.ProjectModel;
import org.sigmah.server.domain.User;
import org.sigmah.server.domain.element.ComputationElement;
import org.sigmah.server.domain.element.FlexibleElement;
import org.sigmah.server.util.EntityLogicalElementTypes;
import org.sigmah.shared.computation.Computation;
import org.sigmah.shared.computation.Computations;
import org.sigmah.shared.computation.instruction.Instructions;
import org.sigmah.shared.computation.value.ComputedValue;
import org.sigmah.shared.computation.value.ComputedValues;
import org.sigmah.shared.util.Future;
import org.sigmah.shared.util.ValueResultUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service handling the update of computations from server-side events.
*
* @author Raphaƫl Calabro (raphael.calabro@netapsys.fr)
*/
@Singleton
public class ComputationService extends EntityManagerProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(ComputationService.class);
/**
* Injection of the service updating the flexible elements values.
*/
@Inject
private ValueService valueService;
/**
* Injection of the resolver of values for computation elements.
*/
@Inject
private ServerValueResolver valueResolver;
/**
* Search for the project model containing the given computation element.
*
* @param computationElement
* Computation element to use for the search.
* @return The parent project model or <code>null</code> if the computation
* element is not attached to a project model.
*/
public ProjectModel getParentProjectModel(final ComputationElement computationElement) {
final TypedQuery<ProjectModel> phaseModelsQuery = em().createQuery("SELECT pm From ProjectModel pm join pm.phaseModels phm join phm.layout l join l.groups g join g.constraints c WHERE :element = c.element", ProjectModel.class);
phaseModelsQuery.setParameter("element", computationElement);
final List<ProjectModel> modelsInPhaseModels = phaseModelsQuery.getResultList();
if (!modelsInPhaseModels.isEmpty()) {
return modelsInPhaseModels.get(0);
}
final TypedQuery<ProjectModel> detailsQuery = em().createQuery("SELECT pm From ProjectModel pm join pm.projectDetails d join d.layout l join l.groups g join g.constraints c WHERE :element = c.element", ProjectModel.class);
detailsQuery.setParameter("element", computationElement);
final List<ProjectModel> modelsInDetails = detailsQuery.getResultList();
if (!modelsInDetails.isEmpty()) {
return modelsInDetails.get(0);
}
return null;
}
public ComputedValue computeValueForProject(final ComputationElement computationElement, final Project project) {
final Computation computation = Computations.parse(computationElement.getRule(), ServerComputations.getAllElementsFromModel(project.getProjectModel()));
final Future<String> computedValue = new Future<>();
computation.computeValueWithResolver(project.getId(), valueResolver, computedValue.defer());
try {
return ComputedValues.from(computedValue.getOrThrow());
} catch (Throwable t) {
throw new IllegalArgumentException("An error occured when computing the formula of the element '" + computationElement.getId() + "' for project '" + project.getId() + "'.", t);
}
}
/**
* Update the value of the given computation element for the given project.
*
* @param computationElement
* Computation element to update.
* @param project
* Project containing the values.
* @param user
* Author of the update.
*/
public void updateComputationValueForProject(final ComputationElement computationElement, final Project project, final User user) {
try {
final ComputedValue value = computeValueForProject(computationElement, project);
valueService.saveValue(value.toString(), computationElement, project.getId(), null, user);
} catch (IllegalArgumentException e) {
LOGGER.error("An error occured when computing the formula of the element '" + computationElement.getId() + "' for project '" + project.getId() + "'.", e);
}
}
/**
* Search every computation element referencing contributions.
*
* @return A collection of every computation element whose formula includes
* a contribution.
*/
public Collection<ComputationElement> getComputationElementsReferencingContributions() {
final TypedQuery<ComputationElement> query = em().createQuery("SELECT ce FROM ComputationElement ce WHERE ce.rule LIKE '%@contribution%'", ComputationElement.class);
return query.getResultList();
}
public Collection<ComputationElement> getComputationElementsReferencingElement(final FlexibleElement element) {
final TypedQuery<ComputationElement> query = em().createQuery("SELECT ce FROM ComputationElement ce WHERE ce.rule LIKE :criteria", ComputationElement.class);
query.setParameter("criteria", "%" + Instructions.ID_PREFIX
+ element.getId()
+ ValueResultUtils.BUDGET_VALUE_SEPARATOR
+ element.getCode()
+ ValueResultUtils.BUDGET_VALUE_SEPARATOR
+ EntityLogicalElementTypes.of(element) + '%');
return query.getResultList();
}
}