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

UNAPLLYREC-7 : Ajout de la question ouverte.

parent a58061e6
......@@ -20,7 +20,8 @@ import java.util.UUID;
@JsonSubTypes.Type(value = MultipleChoiceItem.class, name = "radio"),
@JsonSubTypes.Type(value = MultipleChoiceItem.class, name = "checkbox"),
@JsonSubTypes.Type(value = PictureItem.class, name = "picture"),
@JsonSubTypes.Type(value = ButtonItem.class, name = "button")
@JsonSubTypes.Type(value = ButtonItem.class, name = "button"),
@JsonSubTypes.Type(value = OpenQuestion.class, name = "openquestion")
})
// @formatter:on
public abstract class Item implements ApiConvertible {
......
......@@ -83,10 +83,10 @@ public class ScreenNavigationService {
final ScreenDTO nextScreen;
if (CollectionUtils.isEmpty(screen.getNextScreenExpressions())) {
nextScreen = getDefaultNextScreen(screen);
LOGGER.debug("Prochain écran (valeur par défaut) : {}", nextScreen.getReference());
LOGGER.debug("Prochain écran (valeur par défaut) : '{}'", nextScreen.getReference());
} else {
nextScreen = getNextScreenFromExpressions(expression, screen);
LOGGER.debug("Prochain écran (à partir des expressions) : {}", nextScreen.getReference());
LOGGER.debug("Prochain écran (à partir des expressions) : '{}'", nextScreen.getReference());
}
filterScreenItems(nextScreen, expression);
return nextScreen;
......
......@@ -33,7 +33,7 @@ public interface FormRepository extends JpaRepository<Form, Long>, RoleRepositor
* @param id id du formulaire
* @return La liste des références des questions.
*/
@Query(value = "select screenitems ->> 'reference' reference from screen sc cross join lateral json_array_elements(sc.items) screenitems where sc.form_id = :formId and screenitems ->> 'type' in ('radio', 'checkbox')", nativeQuery = true)
@Query(value = "select screenitems ->> 'reference' reference from screen sc cross join lateral json_array_elements(sc.items) screenitems where sc.form_id = :formId and screenitems ->> 'type' in ('radio', 'checkbox', 'openquestion')", nativeQuery = true)
List<String> getFormQuestionReferences(@Param("formId") Long id);
/**
......
package com.unantes.orientactive.web.api;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.NativeWebRequest;
import com.unantes.orientactive.converter.ServiceConverter;
import com.unantes.orientactive.converter.bean.AnswerElements;
import com.unantes.orientactive.navigation.NavigationService;
......@@ -22,9 +8,24 @@ import com.unantes.orientactive.service.dto.FormDTO;
import com.unantes.orientactive.service.dto.ScreenDTO;
import com.unantes.orientactive.web.api.model.AnswersAPI;
import com.unantes.orientactive.web.api.model.FormAPI;
import com.unantes.orientactive.web.api.model.ItemAPI;
import com.unantes.orientactive.web.api.model.MultipleChoiceItemAPI;
import com.unantes.orientactive.web.api.model.OpenQuestionItemAPI;
import com.unantes.orientactive.web.api.model.ProgressAPI;
import com.unantes.orientactive.web.api.model.ScreenAPI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Implementation du delegate chargé de répondre aux requêtes via l'API des formulaires.
......@@ -162,37 +163,59 @@ public class FormApiDelegateImpl implements FormApiDelegate {
private void addAnswers(final String sessionId, final Long screenId, final FormAPI formAPI) {
AnswerDTO answer = navigationService.retrieveAnswerForScreen(sessionId, screenId);
List<AnswerElements> answers = answer.getAnswerElements();
valuateSelectedAnswer(formAPI.getScreen(), answers);
valuateAnswers(formAPI.getScreen(), answers);
}
/**
* Value la propriété de sélection des différents items ayant des choix en fonction des réponses saisies par l'utilisateur.
* Value les réponses de l'utilisateur aux différents items de l'écran..
*
* @param screenAPI Le modèle d'API de l'écran.
* @param answers Les réponses.
*/
private void valuateSelectedAnswer(final ScreenAPI screenAPI, final List<AnswerElements> answers) {
// @formatter:off
private void valuateAnswers(final ScreenAPI screenAPI, final List<AnswerElements> answers) {
final Map<String, List<String>> questionsAnswers = answers
.stream()
.filter(answerElements -> answerElements.getAnswer() != null)
.collect(
Collectors.toMap(AnswerElements::getQuestionReference, AnswerElements::getAnswer));
// @formatter:on
//TODO : code déjà présent dans ScreenNavigationService
// @formatter:off
final List<MultipleChoiceItemAPI> questions = screenAPI.getItems()
.stream()
.collect(Collectors.toMap(AnswerElements::getQuestionReference, AnswerElements::getAnswer));
valuateSelectedAnswer(screenAPI.getItems(), questionsAnswers);
valuateOpenQuestionAnswers(screenAPI.getItems(), questionsAnswers);
}
/**
* Sélectionne les cases à cauchées saisies par l'utilisateur.
*
* @param items Les items de l'écran.
* @param answers Les réponses de l'utilisateur.
*/
public void valuateSelectedAnswer(final List<ItemAPI> items, final Map<String, List<String>> answers) {
final List<MultipleChoiceItemAPI> questions = items.stream()
.filter(MultipleChoiceItemAPI.class::isInstance)
.map(MultipleChoiceItemAPI.class::cast)
.collect(Collectors.toList());
// @formatter:on
//TODO : il faut utiliser MultipleChoiceItem; lui ajouter une abstraction Question et une méthode qui permet de traiter une réponse
for (MultipleChoiceItemAPI question : questions) {
if (questionsAnswers.containsKey(question.getReference())) {
List<String> questionAnswers = questionsAnswers.get(question.getReference());
if (answers.containsKey(question.getReference())) {
List<String> questionAnswers = answers.get(question.getReference());
question.getChoices().stream().filter(choice -> questionAnswers.contains(choice.getValue())).forEach(choice -> choice.setSelected(true));
}
}
}
/**
* Ajout des réponses apportées aux questions ouvertes par l'utilisateur.
*
* @param items Les items de l'écran.
* @param answers Les réponses de l'utilisateur.
*/
public void valuateOpenQuestionAnswers(final List<ItemAPI> items, final Map<String, List<String>> answers) {
final List<OpenQuestionItemAPI> openQuestions = items.stream()
.filter(OpenQuestionItemAPI.class::isInstance)
.map(OpenQuestionItemAPI.class::cast)
.collect(Collectors.toList());
for (OpenQuestionItemAPI question : openQuestions) {
if (answers.containsKey(question.getReference())) {
final List<String> questionAnswers = answers.get(question.getReference());
question.setAnswer(questionAnswers.get(0));
}
}
}
}
......@@ -309,6 +309,19 @@ components:
type: string
description: Style du bouton.
example: btn--medium
OpenQuestionItemAPI:
allOf:
- $ref: '#/components/schemas/ItemAPI'
- type: object
properties:
question:
type: string
description: La question ouverte.
example: Est-ce que ce questionnaire vous a été utile ?
answer:
type: string
description: La réponse.
example: Oui.
ChoiceAPI:
properties:
label:
......
......@@ -8,6 +8,7 @@ import Button from '@/components/screen-item/items/button/button-edit.vue';
import Choices from '@/components/screen-item/items/choices/choices-edit.vue';
import Message from '@/components/screen-item/items/message/message-edit.vue';
import Picture from '@/components/screen-item/items/picture/picture-edit.vue';
import OpenQuestion from '@/components/screen-item/items/openquestion/openquestion-edit.vue';
import OaInput from '@/components/forms/input/oa-input.vue';
import OaCheckbox from '@/components/forms/checkbox/oa-checkbox.vue';
......@@ -58,7 +59,7 @@ export default class ItemEdit extends Vue {
let title: string;
if (this.item.type === 'button') {
title = this.item.text;
} else if (this.item.type === 'radio' || this.item.type === 'checkbox') {
} else if (this.item.type === 'radio' || this.item.type === 'checkbox' || this.item.type === 'openquestion') {
title = this.item.question;
} else if (this.item.type === 'message') {
title = this.removeTags(this.item.content);
......@@ -95,6 +96,8 @@ export default class ItemEdit extends Vue {
return Message;
} else if (this.item.type === 'picture') {
return Picture;
} else if (this.item.type === 'openquestion') {
return OpenQuestion;
} else {
return null;
}
......
import { Component } from 'vue-property-decorator';
import { mixins } from 'vue-class-component';
import ItemMixin from '@/components/screen-item/items/item-edit.mixin';
import OaInput from '@/components/forms/input/oa-input.vue';
@Component({
components: {
OaInput,
}
})
export default class OpenQuestionEdit extends mixins(ItemMixin) {
}
<template>
<div>
<oa-input :label="$t('screen.item.openquestion.label')" :value="item.question" required="true" @update="editItem({ question: $event })" />
</div>
</template>
<script lang="ts" src="./openquestion-edit.component.ts"></script>
......@@ -23,6 +23,12 @@
@click="addNewItem('button')"
:title="$t('screen.toolbar.buttons.button')"
></oa-button>
<oa-button
variant="icon"
icon-name="text-box"
@click="addNewItem('openquestion')"
:title="$t('screen.toolbar.buttons.button')"
></oa-button>
<!-- Dropdown-->
<!-- <div class="relative group ml-4">-->
<!-- <button-->
......
import AbstractFrontComponent from '@/front/components/common/abstract-front.component';
import Component from 'vue-class-component';
import {OpenAnswer} from "@/front/model/answer-front.model";
@Component
export default class OpenQuestion extends AbstractFrontComponent {
constructor() {
super();
let initValue = this.item.answer ? this.item.answer : '';
this.$store.commit('addAnswer', new OpenAnswer(this.item.reference, [initValue]));
}
public get question(): any {
return this.item.question;
}
public updateAnswer(event): void {
let value = event.target.value;
if (value) {
this.$store.commit('updateAnswer', { questionReference: this.item.reference, answer: [value] });
}
}
}
<template>
<div>
<fieldset id="question-0" class="form__fieldset">
<legend class="form__legend">{{ item.question }}</legend>
<textarea @change="updateAnswer" :value="item.answer"></textarea>
</fieldset>
</div>
</template>
<script lang="ts" src="./openquestion.component.ts"></script>
......@@ -13,3 +13,11 @@ export class SelectAnswer implements IAnswerFront {
return !this.required || (this.required && this.answer.length > 0);
}
}
export class OpenAnswer implements IAnswerFront {
constructor(public questionReference: string, public answer?: string[]) {}
validate(): boolean {
return true;
}
}
......@@ -20,6 +20,8 @@ export class ItemFront implements IItemFront {
public content?: string,
public text?: string,
public url?: string,
public style?: string
public style?: string,
public question?: string,
public answer?: string
) {}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment