Commit e51c5fe8 authored by Julien BOUYER's avatar Julien BOUYER
Browse files

Merge branch 'feature/spel_expression_handling' into 'develop'

Mise en place de l'API pour les expressions des écrans suivants et pour les...

See merge request !83
parents 966c3162 a045a6f3
......@@ -54,7 +54,7 @@ projectDeployTemplatePipelineV2(
PROJECT_ROOM: 'unantes-staps',
MAVEN_VERSION: 'maven_3.6.3',
MAVEN_JDK_VERSION: 'jdk_11',
MAVEN_GOALS: '-U clean -ntp deploy -P-webpack -Pprod -DskipTests',
MAVEN_GOALS: '-U clean -ntp deploy -P-webpack -Pprod -DskipTests -Dmaven.test.skip=true',
BUILD_STEP_CLOSURE: orientationActiveBuildStep,
DEPLOYMENT_STEP_CLOSURE: orientationActiveDeploymentStep,
DEPLOY_URI_CHECK: '/',
......
This diff is collapsed.
// https://github.com/michael-ciniawsky/postcss-load-config
const tailwindcss = require('tailwindcss');
let plugins;
const isProd = process.env.NODE_ENV === 'production';
const postimport = require('postcss-import');
const autoprefixer = require('autoprefixer');
const csso = require('postcss-csso')({
comments: false,
});
module.exports = {
plugins: {
'postcss-import': {},
'postcss-url': {},
tailwindcss,
autoprefixer,
'postcss-csso': {
comments: false,
},
const tailwindconfig = require('./tailwind.config.js');
tailwindconfig.purge = {
enabled: isProd,
content: ['./src/main/webapp/**.html', './src/main/webapp/app/**/*.{vue,js,ts,jsx,tsx}'],
options: {
safelist: [/^icon/],
},
};
const tailwind = require('tailwindcss')({
config: tailwindconfig,
});
if (isProd) {
plugins = [postimport, tailwind, autoprefixer, csso];
} else {
plugins = [postimport, tailwind];
}
module.exports = {
plugins,
};
package com.unantes.orientactive.condition;
import org.apache.commons.lang3.StringUtils;
/**
* Condition d'affichage complexe.
*/
class AdvancedCondition extends DisplayCondition {
/**
* L'expression SPEL représentant la condition d'affichage.
*/
private String condition = StringUtils.EMPTY;
/**
* Constructeur.
*/
public AdvancedCondition() {
super("advanced");
}
public String getCondition() {
return condition;
}
public void setCondition(final String condition) {
this.condition = condition;
}
@Override
public String toSpel() {
return condition;
}
}
package com.unantes.orientactive.condition;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Représentation d'un test sur la valeur d'une réponse.
*/
class AnswerCondition extends DisplayCondition {
/**
* La liste des réponses de la forme "questionReference_answerReference".
*/
private List<String> answers = new ArrayList<>();
public AnswerCondition() {
super("answers");
}
public List<String> getAnswers() {
return answers;
}
public void addAnswers(String answer) {
answers.add(answer);
}
@Override
public String toSpel() {
return "(" + answers.stream().map(answer -> "answers[" + answer + "]").collect(Collectors.joining(" or ")) + ")";
}
}
package com.unantes.orientactive.condition;
/**
* Représentation d'une comparaison simple.
*/
class ComparisonCondition extends DisplayCondition {
private String operator;
private String leftOperand;
private String rightOperand;
public ComparisonCondition() {
super("comparison");
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getLeftOperand() {
return leftOperand;
}
public void setLeftOperand(String leftOperand) {
this.leftOperand = leftOperand;
}
public String getRightOperand() {
return rightOperand;
}
public void setRightOperand(String rightOperand) {
this.rightOperand = rightOperand;
}
@Override
public String toSpel() {
return leftOperand + " " + operator + " " + rightOperand;
}
}
package com.unantes.orientactive.condition;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* Représentation d'un condition d'affichage d'un écran ou d'un item.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, visible = true, property = "type")
@JsonSubTypes(
{
@JsonSubTypes.Type(value = AnswerCondition.class, name = "answers"), // Réponses sélectionnées.
@JsonSubTypes.Type(value = ComparisonCondition.class, name = "comparison"), // Comparaison simple.
@JsonSubTypes.Type(value = HasAnswerCondition.class, name = "hasAnswer"), // Question répondue.
@JsonSubTypes.Type(value = AdvancedCondition.class, name = "advanced"), // Saisie manuelle.
@JsonSubTypes.Type(value = RangeCondition.class, name = "range"), // Double comparaison.
}
)
public abstract class DisplayCondition {
/**
* Le type de la condition d'affichage.
*/
private String type;
/**
* Constructeur minimal.
*
* @param type Le type de la condition d'affichage.
*/
public DisplayCondition(String type) {
this.type = type;
}
public abstract String toSpel();
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final DisplayCondition that = (DisplayCondition) o;
return new EqualsBuilder().append(type, that.type).append(toSpel(), that.toSpel()).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(type).append(toSpel()).toHashCode();
}
}
package com.unantes.orientactive.condition;
import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.Literal;
import org.springframework.expression.spel.ast.MethodReference;
import org.springframework.expression.spel.ast.OpAnd;
import org.springframework.expression.spel.ast.OpEQ;
import org.springframework.expression.spel.ast.OpGE;
import org.springframework.expression.spel.ast.OpGT;
import org.springframework.expression.spel.ast.OpLE;
import org.springframework.expression.spel.ast.OpLT;
import org.springframework.expression.spel.ast.OpNE;
import org.springframework.expression.spel.ast.OpOr;
import org.springframework.expression.spel.ast.Operator;
import org.springframework.expression.spel.ast.SpelNodeImpl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.stream.Collectors.groupingBy;
public class DisplayConditions {
private List<DisplayCondition> displayConditions = new ArrayList<>();
public DisplayConditions() {
super();
}
public DisplayConditions(List<DisplayCondition> displayConditions) {
super();
this.displayConditions = displayConditions;
}
public void add(SpelNode astNode) {
if (astNode instanceof OpAnd) {
OpAnd and = (OpAnd) astNode;
SpelNodeImpl leftOperand = and.getLeftOperand();
SpelNodeImpl rightOperand = and.getRightOperand();
//if(leftOperand instanceof )
add(leftOperand);
add(rightOperand);
} else if (astNode instanceof OpOr) {
OpOr or = (OpOr) astNode;
SpelNodeImpl leftOperand = or.getLeftOperand();
SpelNodeImpl rightOperand = or.getRightOperand();
if (
(leftOperand instanceof CompoundExpression || leftOperand instanceof Literal) &&
(rightOperand instanceof CompoundExpression || rightOperand instanceof Literal)
) {
AnswerCondition answerCondition = new AnswerCondition();
displayConditions.add(answerCondition);
add(leftOperand, answerCondition);
add(rightOperand, answerCondition);
} else {
AdvancedCondition advancedCondition = new AdvancedCondition();
advancedCondition.setCondition(or.toStringAST());
displayConditions.add(advancedCondition);
}
} else if (
astNode instanceof OpGT ||
astNode instanceof OpGE ||
astNode instanceof OpEQ ||
astNode instanceof OpLT ||
astNode instanceof OpLE ||
astNode instanceof OpNE
) {
Operator relationalOperator = (Operator) astNode;
SpelNodeImpl leftOperand = relationalOperator.getLeftOperand();
SpelNodeImpl rightOperand = relationalOperator.getRightOperand();
if (
(leftOperand instanceof CompoundExpression || leftOperand instanceof Literal) &&
(rightOperand instanceof CompoundExpression || rightOperand instanceof Literal)
) {
ComparisonCondition comparisonCondition = new ComparisonCondition();
displayConditions.add(comparisonCondition);
comparisonCondition.setLeftOperand(leftOperand.toStringAST());
comparisonCondition.setRightOperand(rightOperand.toStringAST());
comparisonCondition.setOperator(relationalOperator.getOperatorName());
} else {
AdvancedCondition advancedCondition = new AdvancedCondition();
advancedCondition.setCondition(relationalOperator.toStringAST());
displayConditions.add(advancedCondition);
}
} else if (astNode instanceof MethodReference) {
final MethodReference hasAnswerMethod = (MethodReference) astNode;
HasAnswerCondition hasAnswerCondition = new HasAnswerCondition();
displayConditions.add(hasAnswerCondition);
hasAnswerCondition.setQuestionReference(hasAnswerMethod.getChild(0).toStringAST());
} else {
AnswerCondition answerCondition = new AnswerCondition();
answerCondition.addAnswers(extractVariableName(astNode.toStringAST()));
displayConditions.add(answerCondition);
}
}
public void add(SpelNode astNode, AnswerCondition answerCondition) {
if (astNode instanceof OpOr) {
OpOr or = (OpOr) astNode;
add(or.getLeftOperand(), answerCondition);
add(or.getRightOperand(), answerCondition);
} else {
System.out.println(astNode.toStringAST());
answerCondition.addAnswers(extractVariableName(astNode.toStringAST()));
}
}
private String extractVariableName(String answer) {
String[] variable = StringUtils.substringsBetween(answer, "[", "]");
if (variable == null || variable.length != 1) {
throw new IllegalStateException(answer);
}
return variable[0];
}
public void joinComparison() {
//@formatter:off
final Map<String, List<ComparisonCondition>> groupedRange = displayConditions.stream()
.filter(ComparisonCondition.class::isInstance)
.map(ComparisonCondition.class::cast)
.collect(groupingBy(ComparisonCondition::getLeftOperand));
//@formatter:on
for (List<ComparisonCondition> value : groupedRange.values()) {
if (value.size() == 2) {
RangeCondition rangeCondition = new RangeCondition();
ComparisonCondition left = value.get(0);
rangeCondition.setComparedObject(left.getLeftOperand());
rangeCondition.setLeftBoundary(left.getRightOperand());
rangeCondition.setLeftOperator(left.getOperator());
ComparisonCondition right = value.get(1);
rangeCondition.setRightBoundary(right.getRightOperand());
rangeCondition.setRightOperator(right.getOperator());
displayConditions.add(rangeCondition);
displayConditions.remove(left);
displayConditions.remove(right);
}
}
}
public String toSpEL() {
return displayConditions.stream().map(DisplayCondition::toSpel).collect(Collectors.joining(" and "));
}
public List<DisplayCondition> getDisplayConditions() {
return displayConditions;
}
}
......@@ -39,7 +39,7 @@ public class Expression implements IExpression {
/**
* Le séparateur entre les différentes parties d'une variable
*/
private static final String VARIABLE_PART_SEPARATOR = "_";
private static final String VARIABLE_PART_SEPARATOR = "__";
/**
* Lors du calcul des variables {@link #addVariables(List)}, une boucle infi peut se produire. Cette constante en est le seuil de détection.
......
package com.unantes.orientactive.condition;
import org.springframework.expression.spel.ast.SpelNodeImpl;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Service;
/**
* Service des expressions.
*/
@Service
public class ExpressionService {
/**
* Parsing d'une condition d'affichage d'un item ou d'un écran pour la convertir dans une expression plus facilement utilisable.
*
* @param expression L'expression.
* @return La représentation simplifiée de l'expression.
*/
public DisplayConditions parseDisplayCondition(String expression) {
SpelExpressionParser spelParser = new SpelExpressionParser();
SpelExpression spelExpression = spelParser.parseRaw(expression);
SpelNodeImpl ast = (SpelNodeImpl) spelExpression.getAST();
DisplayConditions displayConditions = new DisplayConditions();
displayConditions.add(ast);
displayConditions.joinComparison();
return displayConditions;
}
}
package com.unantes.orientactive.condition;
/**
* Représentation d'un test permettant de savoir si une question est répondue.
*/
class HasAnswerCondition extends DisplayCondition {
private String questionReference;
public HasAnswerCondition() {
super("hasAnswer");
}
public String getQuestionReference() {
return questionReference;
}
public void setQuestionReference(String questionReference) {
this.questionReference = questionReference;
}
@Override
public String toSpel() {
return "hasAnswer(" + questionReference + ")";
}
}
package com.unantes.orientactive.condition;
/**
* Représentation d'une double comparaison.
*/
class RangeCondition extends DisplayCondition {
private String leftOperator;
private String rightOperator;
private String leftBoundary;
private String rightBoundary;
private String comparedObject;
public RangeCondition() {
super("range");
}
@Override
public String toSpel() {
return comparedObject + leftOperator + leftBoundary + " and " + comparedObject + rightOperator + rightBoundary;
}
public String getLeftOperator() {
return leftOperator;
}
public void setLeftOperator(final String leftOperator) {
this.leftOperator = leftOperator;
}
public String getRightOperator() {
return rightOperator;
}
public void setRightOperator(final String rightOperator) {
this.rightOperator = rightOperator;
}
public String getLeftBoundary() {
return leftBoundary;
}
public void setLeftBoundary(final String leftBoundary) {
this.leftBoundary = leftBoundary;
}
public String getRightBoundary() {
return rightBoundary;
}
public void setRightBoundary(final String rightBoundary) {
this.rightBoundary = rightBoundary;
}
public String getComparedObject() {
return comparedObject;
}
public void setComparedObject(final String comparedObject) {
this.comparedObject = comparedObject;
}
}
package com.unantes.orientactive.condition.variable;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Représentation d'une expression de moyenne.
*/
public class AverageVariableAPI extends VariableAPI {
/**
* Le nom de la fonction.
*/
private String functionName = "average";
/**