Commit 53673d05 authored by Kevin Robert's avatar Kevin Robert
Browse files

Merge branch 'feature/expression_items' into 'develop'

UNOTOPLYS-237 : Correction des conditions de type "Comparison". Mise en place du fonctionne de la condition de type "Range".

See merge request !88
parents 36edc639 3ce7c3f0
package com.unantes.orientactive.condition;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.spel.SpelNode;
import org.springframework.expression.spel.ast.CompoundExpression;
import org.springframework.expression.spel.ast.Literal;
......@@ -17,6 +19,7 @@ import org.springframework.expression.spel.ast.Operator;
import org.springframework.expression.spel.ast.SpelNodeImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
......@@ -25,8 +28,26 @@ import static java.util.stream.Collectors.groupingBy;
public class DisplayConditions {
/**
* Logger.
*/
private final Logger logger = LoggerFactory.getLogger(DisplayConditions.class);
/**
* Les conditions calculées.
*/
private List<DisplayCondition> displayConditions = new ArrayList<>();
/**
* Liste des opérateurs pouvant être dans la comparaison de gauche d'un range.
*/
private List<String> rightOperators = Arrays.asList("<", "<=");
/**
* Liste des opérateurs pouvant être dans la comparaison de droite d'un range.
*/
private List<String> leftOperators = Arrays.asList(">", ">=");
public DisplayConditions() {
super();
}
......@@ -49,7 +70,7 @@ public class DisplayConditions {
SpelNodeImpl leftOperand = or.getLeftOperand();
SpelNodeImpl rightOperand = or.getRightOperand();
if (
(leftOperand instanceof CompoundExpression || leftOperand instanceof Literal) &&
(leftOperand instanceof CompoundExpression || leftOperand instanceof Literal || leftOperand instanceof OpOr) &&
(rightOperand instanceof CompoundExpression || rightOperand instanceof Literal)
) {
AnswersCondition answerCondition = new AnswersCondition();
......@@ -151,32 +172,56 @@ public class DisplayConditions {
return infos[1];
}
/**
* Parsing des comparaisons détectées afin d'essayer de les regrouper en condition de type {@link RangeCondition}.
*/
public void joinComparison() {
//@formatter:off
final Map<String, List<ComparisonCondition>> groupedRange = displayConditions.stream()
.filter(ComparisonCondition.class::isInstance)
.map(ComparisonCondition.class::cast)
.filter(comparisonCondition -> rightOperators.contains(comparisonCondition.getOperator()) || leftOperators.contains(comparisonCondition.getOperator()))
.collect(groupingBy(ComparisonCondition::getReference));
//@formatter:on
for (List<ComparisonCondition> value : groupedRange.values()) {
if (value.size() == 2) {
RangeCondition rangeCondition = new RangeCondition();
ComparisonCondition left = value.get(0);
rangeCondition.setComparedObject(left.getReference());
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);
ComparisonCondition leftComparison = value.get(0);
ComparisonCondition rightComparison = value.get(1);
if (leftOperators.contains(leftComparison.getOperator()) && rightOperators.contains(rightComparison.getOperator())) {
// Les comparaisons sont dans le bon ordre.
createRangeCondition(leftComparison, rightComparison);
} else if (rightOperators.contains(leftComparison.getOperator()) && leftOperators.contains(rightComparison.getOperator())) {
// Les comparaisons sont inversées.
createRangeCondition(rightComparison, leftComparison);
} else {
// Les comparaisons semblent incohérentes. On ne fait pas de range.
logger.warn(String.format("Un item semble avoir des comparaisons incohérentes. Les opérateurs sont '%s' et '%s'.", leftComparison.getOperator(), rightComparison.getOperator()));
}
}
}
}
/**
* Traduction en SPEL des expresions valides.
* Création d'une condition {@link RangeCondition} à partir de deux comparaisons complémentaires.
* Attention, cette méthode ne vérifie pas que les comparaisons soient complémentaires.
*
* @param leftComparison La comparaison correspondant à la partie de gauche.
* @param rightComparison La comparaison correspondant à la partie de droite.
*/
private void createRangeCondition(ComparisonCondition leftComparison, ComparisonCondition rightComparison) {
RangeCondition rangeCondition = new RangeCondition();
rangeCondition.setReference(leftComparison.getReference());
rangeCondition.setLeftBoundary(leftComparison.getRightOperand());
rangeCondition.setLeftOperator(leftComparison.getOperator());
rangeCondition.setRightBoundary(rightComparison.getRightOperand());
rangeCondition.setRightOperator(rightComparison.getOperator());
displayConditions.add(rangeCondition);
displayConditions.remove(leftComparison);
displayConditions.remove(rightComparison);
}
/**
* Traduction en SPEL des expressions valides.
*
* @return L'expression SPEL.
*/
......
package com.unantes.orientactive.condition;
import org.apache.commons.lang3.StringUtils;
/**
* IMPORTANT : Normalement non supporté par l'application !
* <p>
* Représentation d'une double comparaison.
*/
class RangeCondition extends DisplayCondition {
class RangeCondition extends ReferencedCondition {
private String leftOperator;
private String rightOperator;
private String leftBoundary;
private String rightBoundary;
private String comparedObject;
public RangeCondition() {
super("range");
......@@ -19,7 +18,7 @@ class RangeCondition extends DisplayCondition {
@Override
public String toSpel() {
return comparedObject + leftOperator + leftBoundary + " and " + comparedObject + rightOperator + rightBoundary;
return "variables[" + reference + "] " + leftOperator + " " + leftBoundary + " and variables[" + reference + "] " + rightOperator + " " + rightBoundary;
}
public String getLeftOperator() {
......@@ -54,21 +53,8 @@ class RangeCondition extends DisplayCondition {
this.rightBoundary = rightBoundary;
}
public String getComparedObject() {
return comparedObject;
}
public void setComparedObject(final String comparedObject) {
this.comparedObject = comparedObject;
}
/**
* Toujours faux car cette condition n'est pas supportée.
*
* @return faux.
*/
@Override
public boolean isValid() {
return false;
return super.isValid() && StringUtils.isNotBlank(leftOperator) && StringUtils.isNotBlank(rightOperator) && StringUtils.isNotBlank(leftBoundary) && StringUtils.isNotBlank(rightBoundary);
}
}
......@@ -11,7 +11,7 @@ import Autocomplete from '@/components/forms/autocomplete/autocomplete.vue';
Autocomplete,
},
})
export default class ComparaisonCondition extends Vue {
export default class ComparisonCondition extends Vue {
/**
* La condition d'affichage simplifiée de l'item.
......@@ -32,34 +32,34 @@ export default class ComparaisonCondition extends Vue {
/**
* L'opérateur sélectionné.
*/
public selectedRightOperand = {};
public selectedRightOperand;
/**
* Liste des opérateurs possibles.
*/
public operators = [
{
value: 'gte',
value: '>=',
label: '',
},
{
value: 'gt',
value: '>',
label: '>',
},
{
value: 'lte',
value: '<=',
label: '',
},
{
value: 'lt',
value: '<',
label: '<',
},
{
value: 'eq',
value: '==',
label: '=',
},
{
value: 'neq',
value: '!=',
label: '',
},
];
......@@ -126,7 +126,7 @@ export default class ComparaisonCondition extends Vue {
/**
* Action de sélection de l'opérateur.
*
* @param operator L'opérateur.
* @param event La sélection.
*/
public selectRightOperand(event) {
this.condition.rightOperand = event.target.value;
......
......@@ -14,16 +14,16 @@
</div>
<div class="col-span-1">
<select v-model="selectedOperator" @change="selectOperator" class="focus:ring w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none appearance-none ring-inset">
<option v-for="operator in operators" :value="operator.label">{{ operator.label }}</option>
<option v-for="operator in operators" :value="operator.value">{{ operator.label }}</option>
</select>
</div>
<div class="col-span-2">
<input :v-model="selectedRightOperand" :value="selectedRightOperand" @change="selectRightOperand" class="w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none ring-inset focus:ring" type="text" aria-describedby="h0" id="f0" placeholder="1">
<input v-model="selectedRightOperand" @change="selectRightOperand" class="w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none ring-inset focus:ring" type="text" placeholder="1">
</div>
</div>
<p class="mt-1 text-sm italic text-gray-500" id="hundefined">L'élément ne s'affiche que si la valeur de la variable correspond au test ci-dessus</p>
<p class="mt-1 text-sm italic text-gray-500">L'élément ne s'affiche que si la valeur de la variable correspond au test ci-dessus</p>
</div>
</div>
</template>
<script lang="ts" src="./comparaison-condition.component.ts" />
<script lang="ts" src="./comparison-condition.component.ts" />
import Vue from 'vue';
import Component from 'vue-class-component';
import {Prop} from 'vue-property-decorator';
import Autocomplete from '@/components/forms/autocomplete/autocomplete.vue';
/**
* Comparaison de la valeur d'une variable à un nombre. Permet de définir l'opérateur de comparaison.
*/
@Component({
components: {
Autocomplete,
},
})
export default class RangeCondition extends Vue {
/**
* La condition d'affichage simplifiée de l'item.
*/
@Prop()
public condition;
/**
* La variable sélectionnée.
*/
public selectedVariable = {};
/**
* L'opérateur gauche sélectionné.
*/
public selectedLeftOperator = {};
/**
* L'opérateur droit sélectionné.
*/
public selectedRightOperator = {};
/**
* L'opérateur gauche sélectionné.
*/
public selectedLeftBoundary;
/**
* L'opérateur droit sélectionné.
*/
public selectedRightBoundary;
/**
* Liste des opérateurs de gauche possibles.
*/
public leftOperators = [
{
value: '>=',
label: '',
},
{
value: '>',
label: '>',
}
];
/**
* Liste des opérateurs de droite possibles.
*/
public rightOperators = [
{
value: '<=',
label: '',
},
{
value: '<',
label: '<',
}
];
/**
* Initialisation du composant en prenant en compte la condition déjà présente.
*/
constructor() {
super();
if (this.condition.leftOperator) {
this.selectedLeftOperator = this.condition.leftOperator;
}
if (this.condition.rightOperator) {
this.selectedRightOperator = this.condition.rightOperator;
}
if (this.condition.leftBoundary) {
this.selectedLeftBoundary = this.condition.leftBoundary;
}
if (this.condition.rightBoundary) {
this.selectedRightBoundary = this.condition.rightBoundary;
}
}
/**
* Récupération de la liste des variables possibles.
*/
public get variables(): any {
return this.$store.getters.variables.map(variable => {
// On va mettre un valeur au label pour l'affichage de l'autocomplete.
variable.label = variable.name;
return variable;
});
}
/**
* Action de sélection d'une variable.
*
* @param variable La variable sélectionnée.
*/
public selectVariable(variable) {
this.selectedVariable = variable;
this.condition.reference = variable.reference;
this.save();
}
/**
* Récupération du libellé de la question sélectionnée pour l'affichage de l'autocomplete.
*/
public get selectedVariableLabel() {
if (this.variables && this.variables.length > 0) {
let selectedVariable = this.variables.find(variable => variable.reference === this.condition.reference);
if (selectedVariable) {
this.selectedVariable = selectedVariable;
return selectedVariable.label;
}
}
return '';
}
/**
* Action de sélection de l'opérateur de gauche.
*
* @param operator L'opérateur.
*/
public selectLeftOperator(operator) {
this.condition.leftOperator = this.selectedLeftOperator;
this.save();
}
/**
* Action de sélection de l'opérateur de droite.
*
* @param operator L'opérateur.
*/
public selectRightOperator(operator) {
this.condition.rightOperator = this.selectedRightOperator;
this.save();
}
/**
* Action de sélection de la limite basse.
*
* @param event La sélection.
*/
public selectLeftBoundary(event) {
this.condition.leftBoundary = event.target.value;
this.save();
}
/**
* Action de sélection de la limite haute.
*
* @param event La sélection.
*/
public selectRightBoundary(event) {
this.condition.rightBoundary = event.target.value;
this.save();
}
/**
* Sauvegarde de la condition.
*/
public save() {
if (this.condition.reference && this.condition.leftOperator && this.condition.rightOperator && this.condition.leftBoundary && this.condition.rightBoundary) {
this.$emit('update');
}
}
}
<template>
<div>
<autocomplete
:items="variables"
:label="$t('screen.item.conditions.comparison.question.label')"
:value="selectedVariableLabel"
@selected="selectVariable"
/>
<div v-if="selectedVariable.reference" class="pl-6 mb-6">
<div class="grid grid-cols-10 gap-x-2 items-center">
<div class="col-span-3">
<div class="font-bold py-4">La valeur de la variable est…</div>
</div>
<div class="col-span-1">
<select v-model="selectedLeftOperator" @change="selectLeftOperator" class="focus:ring w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none appearance-none ring-inset">
<option v-for="leftOperator in leftOperators" :value="leftOperator.value">{{ leftOperator.label }}</option>
</select>
</div>
<div class="col-span-2">
<input v-model="selectedLeftBoundary" @change="selectLeftBoundary" class="w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none ring-inset focus:ring" type="text" placeholder="1">
</div>
<div class="col-span-1 justify-self-end flex items-center">
<div class="font-bold p-4">et </div>
</div>
<div class="col-span-1">
<select v-model="selectedRightOperator" @change="selectRightOperator" class="focus:ring w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none appearance-none ring-inset">
<option v-for="rightOperator in rightOperators" :value="rightOperator.value">{{ rightOperator.label }}</option>
</select>
</div>
<div class="col-span-2">
<input v-model="selectedRightBoundary" @change="selectRightBoundary" class="w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none ring-inset focus:ring" type="text" placeholder="20">
</div>
</div>
<p class="mt-1 text-sm italic text-gray-500">L'élément ne s'affiche que si la valeur de la variable correspond au test ci-dessus</p>
</div>
</div>
</template>
<script lang="ts" src="./range-condition.component.ts" />
......@@ -12,7 +12,8 @@ import AdvancedCondition from '@/components/screen-item/conditions/advanced-cond
import AnswersCondition from '@/components/screen-item/conditions/answers-condition/answers-condition.vue';
import HasAnswerCondition from '@/components/screen-item/conditions/hasanswer-condition/hasanswer-condition.vue';
import HasNoAnswerCondition from '@/components/screen-item/conditions/hasnoanswer-condition/hasnoanswer-condition.vue';
import ComparaisonCondition from '@/components/screen-item/conditions/comparaison-condition/comparaison-condition.vue';
import ComparisonCondition from '@/components/screen-item/conditions/comparison-condition/comparison-condition.vue';
import RangeCondition from '@/components/screen-item/conditions/range-condition/range-condition.vue';
@Component({
components: {
......@@ -73,7 +74,9 @@ export default class ItemConditions extends Vue {
} else if (type === ConditionTypes.HAS_NO_ANSWER) {
return HasNoAnswerCondition;
} else if (type === ConditionTypes.COMPARISON) {
return ComparaisonCondition;
return ComparisonCondition;
} else if (type === ConditionTypes.RANGE) {
return RangeCondition;
} else {
return null;
}
......
......@@ -24,7 +24,7 @@ export default [
},
{
path: '/admin/forms',
name: 'HomeView',
name: 'HomeViewForms',
component: HomeView,
meta: {
authorities: [Authority.ADMIN_SAAS, Authority.VIEW_FORM, Authority.VIEW_WORKSPACE],
......@@ -33,7 +33,7 @@ export default [
},
{
path: '/admin/workspaces',
name: 'HomeView',
name: 'HomeViewWorkspaces',
component: HomeView,
meta: {
authorities: [Authority.ADMIN_SAAS, Authority.VIEW_FORM, Authority.VIEW_WORKSPACE],
......
......@@ -7,6 +7,7 @@ export const ConditionTypes = {
ANSWERS: 'answers',
COMPARISON: 'comparison',
ADVANCED: 'advanced',
RANGE: 'range',
};
export default class ConditionsService {
......@@ -41,7 +42,6 @@ export default class ConditionsService {
const displayConditions = [];
try {
item.displayConditionSimple.displayConditions.forEach((condition: any) => {
// TODO tester les types de conditions restants
// à voir si ceux qui sont à push dans l'Array directement
// et ceux qui sont à traiter à part, comme c'est fait pour l'advanced condition
if (condition.type === ConditionTypes.ANSWERS) {
......@@ -52,9 +52,11 @@ export default class ConditionsService {
displayConditions.push(condition);
} else if (condition.type === ConditionTypes.COMPARISON) {
displayConditions.push(condition);
} else if (condition.type === ConditionTypes.RANGE) {
displayConditions.push(condition);
} else {
// Si une des conditions n'est pas gérée, on sort de la boucle en erreur
throw 'Unknown condition : ' + JSON.stringify(condition);
throw 'Unknown condition for item "' + item.reference + '" : ' + JSON.stringify(condition);
}
});
} catch (e) {
......
......@@ -69,14 +69,16 @@
"answers": "Condition {{ index }} : Contrôle des réponses à une question",
"hasAnswer": "Condition {{ index }} : Contrôle si question répondue",
"hasNoAnswer": "Condition {{ index }} : Contrôle si question non répondue",
"comparison": "Condition {{ index }} : Comparaison avec la valeur d'une variable"
"comparison": "Condition {{ index }} : Comparaison avec la valeur d'une variable",
"range": "Condition {{ index }} : Valeur d'une variable comprise entre deux valeurs"
},
"types": {
"advanced": "Autre cas (saisie manuelle)",
"answers": "Réponses sélectionnées",
"hasAnswer": "Question répondue",
"hasNoAnswer": "Question non répondue",
"comparison": "Comparaison avec la valeur d’une variable"
"comparison": "Comparaison avec la valeur d’une variable",
"range": "Valeur d'une variable comprise entre deux valeurs"
},
"advanced": {
"condition": {
......@@ -102,6 +104,15 @@
"label": "La valeur de la variable est&mldr;",
"helpText": "L'élément ne s'affiche que si la valeur de la variable correspond au test ci-dessus"
}
},
"range": {
"question": {
"label": "Sélectionnez la variable à contrôler et définissez les seuils à partir desquels l'élément doit s'afficher"