Commit fc1a7301 authored by Kevin Robert's avatar Kevin Robert
Browse files

UNOTOPLYS-237 : Mise en place des conditions d'affichage pour les conditions...

UNOTOPLYS-237 : Mise en place des conditions d'affichage pour les conditions "answers", "hasanswer", "hasnoanswer", "value". Modification de la condition avancé, il va falloir mettre des sécurités pour ne pas tout casser.
parent 6e942961
......@@ -31,4 +31,9 @@ class AdvancedCondition extends DisplayCondition {
public String toSpel() {
return condition;
}
@Override
public boolean isValid() {
return StringUtils.isNotBlank(condition);
}
}
package com.unantes.orientactive.condition;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
......@@ -30,4 +32,9 @@ class AnswersCondition extends ReferencedCondition {
public String toSpel() {
return "(" + answers.stream().map(answer -> "answers[" + reference + Expression.VARIABLE_PART_SEPARATOR + answer + "]").collect(Collectors.joining(" or ")) + ")";
}
@Override
public boolean isValid() {
return super.isValid() && CollectionUtils.isNotEmpty(answers);
}
}
package com.unantes.orientactive.condition;
import org.apache.commons.lang3.StringUtils;
/**
* Représentation d'une comparaison simple.
* Compare une référence de variable avec une valeur.
......@@ -34,4 +36,9 @@ class ComparisonCondition extends ReferencedCondition {
public String toSpel() {
return "variables[" + reference + "] " + operator + " " + rightOperand;
}
@Override
public boolean isValid() {
return super.isValid() && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(rightOperand);
}
}
package com.unantes.orientactive.condition;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
......@@ -37,6 +38,14 @@ public abstract class DisplayCondition {
public abstract String toSpel();
/**
* Permet de savoir si la condition est valide, afin de ne pas tenter de la transformer en spel si elle ne l'est pas.
*
* @return Vrai si la condition est valide, faux sinon.
*/
@JsonIgnore
public abstract boolean isValid();
@Override
public boolean equals(final Object o) {
if (this == o) {
......
......@@ -101,7 +101,9 @@ public class DisplayConditions {
}
} else {
AnswersCondition answerCondition = new AnswersCondition();
answerCondition.addAnswers(extractVariableName(astNode.toStringAST()));
String variableName = extractVariableName(astNode.toStringAST());
answerCondition.addAnswers(extractAnswerReference(variableName));
answerCondition.setReference(extractQuestionReference(variableName));
displayConditions.add(answerCondition);
}
}
......@@ -133,6 +135,22 @@ public class DisplayConditions {
return variable[0];
}
private String extractQuestionReference(String variableName) {
String[] infos = variableName.split(Expression.VARIABLE_PART_SEPARATOR);
if (infos.length != 2) {
throw new IllegalStateException(variableName);
}
return infos[0];
}
private String extractAnswerReference(String variableName) {
String[] infos = variableName.split(Expression.VARIABLE_PART_SEPARATOR);
if (infos.length != 2) {
throw new IllegalStateException(variableName);
}
return infos[1];
}
public void joinComparison() {
//@formatter:off
final Map<String, List<ComparisonCondition>> groupedRange = displayConditions.stream()
......@@ -157,8 +175,13 @@ public class DisplayConditions {
}
}
/**
* Traduction en SPEL des expresions valides.
*
* @return L'expression SPEL.
*/
public String toSpEL() {
return displayConditions.stream().map(DisplayCondition::toSpel).collect(Collectors.joining(" and "));
return displayConditions.stream().filter(DisplayCondition::isValid).map(DisplayCondition::toSpel).collect(Collectors.joining(" and "));
}
public List<DisplayCondition> getDisplayConditions() {
......
......@@ -41,14 +41,9 @@ public class Expression implements IExpression {
public static final String VARIABLE_PART_SEPARATOR = "__";
/**
* Le séparateur entre la référence de la question et sa valeur.
* Le séparateur entre la référence d'un écran et la référence d'une question.
*/
private static final String QUESTION_VALUE_SEPARATOR = "__";
/**
* Le séparateur entre la référence du screen et la référence de la question.
*/
private static final String SCREEN_QUESTION_SEPARATOR = "_";
public static final String SCREEN_QUESTION_SEPARATOR = "_";
/**
* Lors du calcul des variables {@link #addVariables(List)}, une boucle infi peut se produire. Cette constante en est le seuil de détection.
......@@ -338,14 +333,14 @@ public class Expression implements IExpression {
}
/**
* Construit la référence à une réponse : referenceEcran_referenceQuestion_valeurReponse
* Construit la référence à une réponse : referenceEcran_referenceQuestion__valeurReponse
* @param screenReference reference de l'écran contenant la question
* @param questionReference reference d'une question de l'écran
* @param value reference d'une réponse de la question
* @return
*/
protected String getAnswerReference(final String screenReference, final String questionReference, final String value) {
return getQuestionReference(screenReference, questionReference) + QUESTION_VALUE_SEPARATOR + value;
return getQuestionReference(screenReference, questionReference) + VARIABLE_PART_SEPARATOR + value;
}
/**
......
package com.unantes.orientactive.condition;
/**
* IMPORTANT : Normalement non supporté par l'application !
* <p>
* Représentation d'une double comparaison.
*/
class RangeCondition extends DisplayCondition {
......@@ -59,4 +61,14 @@ class RangeCondition extends DisplayCondition {
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;
}
}
......@@ -25,4 +25,9 @@ public abstract class ReferencedCondition extends DisplayCondition {
public void setReference(String reference) {
this.reference = reference;
}
@Override
public boolean isValid() {
return StringUtils.isNotBlank(reference);
}
}
......@@ -67,10 +67,9 @@ export default class Autocomplete extends AbstractEditableComponent {
this.results = this.items.filter((item: any) => item.label.toLowerCase().indexOf(this.search.toLowerCase()) > -1);
}
public onChange() {
public onChange(evt) {
this.filterResults();
this.displayList = true;
this.$emit('selected', {});
}
public onArrowDown() {
......
import Vue from 'vue';
import Component from 'vue-class-component';
import { mixins } from 'vue-class-component';
import ConditionMixin from '@/components/screen-item/conditions/condition.mixin';
import OaTextarea from '@/components/forms/textarea/oa-textarea.vue';
import {Prop} from "vue-property-decorator";
@Component({
components: {
OaTextarea,
},
})
export default class AdvancedCondition extends mixins(ConditionMixin) {}
export default class AdvancedCondition extends Vue {
/**
* La condition d'affichage simplifiée de l'item.
*/
@Prop()
public condition;
/**
* Mise à jour de la condition suite à la sélection d'une nouvelle question et sauvegarde.
*
* @param selection La sélection.
*/
public updateCondition(selection: any) {
this.condition.condition = selection;
this.$emit('update');
}
}
......@@ -3,7 +3,7 @@
:label="$t('screen.item.conditions.advanced.condition.label')"
:helpText="$t('screen.item.conditions.advanced.condition.helpText')"
:value="condition.condition"
@update="updateCondition({ condition: $event })"
@update="updateCondition"
/>
</template>
......
......@@ -9,7 +9,7 @@ export default class AnswerConditionMixin extends mixins(QuestionConditionMixin)
this.selectedQuestion = selection;
if (this.selectedQuestion) {
const validCondition = {
questionReference: this.selectedQuestion.value.reference,
reference: this.selectedQuestion.id,
};
this.updateCondition(validCondition);
}
......
import { mixins } from 'vue-class-component';
import { Component, Watch } from 'vue-property-decorator';
import Vue from 'vue';
import {Component, Prop} from 'vue-property-decorator';
import { uuid } from 'vue-uuid';
import Autocomplete from "@/components/forms/autocomplete/autocomplete.vue";
import QuestionConditionMixin from '@/components/screen-item/conditions/question-condition.mixin';
@Component({
components: {
Autocomplete,
},
})
export default class AnswersCondition extends Vue {
@Component
export default class AnswersCondition extends mixins(QuestionConditionMixin) {
public answers: any[];
/**
* La condition d'affichage simplifiée de l'item.
*/
@Prop()
public condition;
public values: string[];
/**
* Les réponses possibles de la question.
*/
public answers: any[] = [];
public uuid: string;
/**
* Les valeurs sélectionnées par l'utilisateur.
*/
public values: string[] = [];
public loadingCompleted: boolean;
/**
* UUID pour générer les identifiants des choix.
*/
public uuid: string = uuid.v1();
constructor() {
super();
this.answers = [];
this.uuid = uuid.v1();
this.loadingCompleted = false;
}
/**
* La question sélectionnée.
*/
public selectedQuestion;
/**
* Sélection d'une question, on va chercher les réponses associées.
*
* @param selection La question sélectionnée.
*/
public selectQuestion(selection: any) {
this.selectedQuestion = selection;
this.answers = this.questions.filter(q => q === this.selectedQuestion).flatMap(q => q.value.choices);
this.values = [];
if (this.condition.answers) {
this.values = this.condition.answers;
} else {
this.values = [];
}
}
/**
* Génération d'un identifiant à partir d'une réponse pour la validité du HTML.
*
* @param value La réponse.
*/
public getChoiceId({ value }) {
return `${this.selectedQuestionReference}_${value}_${this.uuid}`;
return `${value}_${this.uuid}`;
}
/**
* Lors de la sélection d'une réponse on va modifier la condition et sauvegarder les information.
*/
public updateAnswer() {
if (this.selectedQuestion && this.values.length > 0) {
const validCondition = {
questionReference: this.selectedQuestion.id,
answers: this.values,
};
this.updateCondition(validCondition);
this.condition.reference = this.selectedQuestion.id;
this.condition.answers = this.values;
this.$emit('update', this.condition);
}
}
@Watch('questions')
public onQuestionsLoad() {
if (!this.loadingCompleted) {
const questionReference = this.condition.reference;
const selection = this.questions.filter(q => q.id === questionReference);
if (selection && selection.length === 1) {
this.selectQuestion(selection[0]);
}
}
/**
* Récupération de la liste des questions possible.
* Mise en forme des questions pour l'autocomplete.
*/
public get questions(): any {
return this.$store.getters.questions.map((item: any) => {
return {
id: `${item.screenReference}_${item.reference}`,
label: `${item.idxScreen}. ${item.question}`,
value: item,
};
});
}
@Watch('answers')
public onAnswersLoad() {
if (!this.loadingCompleted) {
if (this.condition.answers && this.condition.answers.length > 0) {
this.values = this.condition.answers;
/**
* Récupération du libellé de la question sélectionnée pour l'affichage de l'autocomplete.
*/
public get selectedQuestionLabel() {
if (this.questions && this.questions.length > 0 && this.condition.reference) {
let selectedQuestion = this.questions.find(question => question.id === this.condition.reference);
if (selectedQuestion) {
this.selectQuestion(selectedQuestion);
return selectedQuestion.label;
}
this.loadingCompleted = true;
}
return '';
}
}
......@@ -14,7 +14,6 @@
class="checked:border-blue-700 checked:bg-blue-500 appearance-none border border-gray-400 w-5 h-5 rounded-sm ring-inset ring-4 ring-white cursor-pointer"
type="checkbox"
v-model="values"
:name="selectedQuestionReference"
:value="answer.value"
:id="getChoiceId(answer)"
@change="updateAnswer"
......
import { mixins } from 'vue-class-component';
import { Component } from 'vue-property-decorator';
import Vue from 'vue';
import {Component, Prop} from 'vue-property-decorator';
import Autocomplete from "@/components/forms/autocomplete/autocomplete.vue";
import AnswerConditionMixin from '@/components/screen-item/conditions/answer-condition.mixin';
@Component({
components: {
Autocomplete,
},
})
export default class HasAnswerCondition extends Vue {
/**
* La condition d'affichage simplifiée de l'item.
*/
@Prop()
public condition;
/**
* Récupération de la liste des questions possible.
* Mise en forme des questions pour l'autocomplete.
*/
public get questions(): any {
return this.$store.getters.questions.map((item: any) => {
return {
id: `${item.screenReference}_${item.reference}`,
label: `${item.idxScreen}. ${item.question}`,
value: item,
};
});
}
/**
* Récupération du libellé de la question sélectionnée pour l'affichage de l'autocomplete.
*/
public get selectedQuestionLabel() {
if (this.questions && this.questions.length > 0) {
let selectedQuestion = this.questions.find(question => question.id === this.condition.reference);
if (selectedQuestion) {
return selectedQuestion.label;
}
}
return '';
}
/**
* Mise à jour de la condition suite à la sélection d'une nouvelle question et sauvegarde.
*
* @param selection La sélection.
*/
public selectQuestion(selection: any) {
this.condition.reference = selection.id;
this.$emit('update', this.condition);
}
}
@Component
export default class HasAnswerCondition extends mixins(AnswerConditionMixin) {}
import { mixins } from 'vue-class-component';
import { Component } from 'vue-property-decorator';
import Vue from 'vue';
import {Component, Prop} from 'vue-property-decorator';
import Autocomplete from "@/components/forms/autocomplete/autocomplete.vue";
import AnswerConditionMixin from '@/components/screen-item/conditions/answer-condition.mixin';
@Component({
components: {
Autocomplete,
},
})
export default class HasNoAnswerCondition extends Vue {
@Component
export default class HasNoAnswerCondition extends mixins(AnswerConditionMixin) {}
/**
* La condition d'affichage simplifiée de l'item.
*/
@Prop()
public condition;
/**
* Récupération de la liste des questions possible.
* Mise en forme des questions pour l'autocomplete.
*/
public get questions(): any {
return this.$store.getters.questions.map((item: any) => {
return {
id: `${item.screenReference}_${item.reference}`,
label: `${item.idxScreen}. ${item.question}`,
value: item,
};
});
}
/**
* Récupération du libellé de la question sélectionnée pour l'affichage de l'autocomplete.
*/
public get selectedQuestionLabel() {
if (this.questions && this.questions.length > 0) {
let selectedQuestion = this.questions.find(question => question.id === this.condition.reference);
if (selectedQuestion) {
return selectedQuestion.label;
}
}
return '';
}
/**
* Mise à jour de la condition suite à la sélection d'une nouvelle question et sauvegarde.
*
* @param selection La sélection.
*/
public selectQuestion(selection: any) {
this.condition.reference = selection.id;
this.$emit('update');
}
}
......@@ -21,7 +21,7 @@ export default class QuestionConditionMixin extends mixins(ConditionMixin) {
public get questions(): any {
return this.$store.getters.questions.map((item: any) => {
return {
id: `${item.screenReference}_${item.reference}`,
id: `${item.screenReference}__${item.reference}`,
label: `${item.idxScreen}. ${item.question}`,
value: item,
};
......@@ -31,6 +31,8 @@ export default class QuestionConditionMixin extends mixins(ConditionMixin) {
public get selectedQuestionLabel() {
if (!this.selectedQuestion) {
return null;
} else if (!this.condition) {
this.selectedQuestion = this.questions.find(element => element.id === this.condition.reference);
}
return this.selectedQuestion.label;
}
......@@ -38,6 +40,8 @@ export default class QuestionConditionMixin extends mixins(ConditionMixin) {
public get selectedQuestionReference() {
if (!this.selectedQuestion) {
return null;
}else if (!this.condition) {
this.selectedQuestion = this.questions.find(element => element.id === this.condition.reference);
}
return this.selectedQuestion.id;
}
......
......@@ -83,6 +83,7 @@ export default class ItemConditions extends Vue {
if (conditionIdx > -1) {
this.conditions.splice(conditionIdx, 1);
}
this.saveConditions();
}
private findConditionIndex(condition: any) {
......