From 8d176bf6d0ff6411dc6edc23ffdc76006eb5853f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 23 May 2024 10:34:18 +0200
Subject: [PATCH 01/30] =?UTF-8?q?Ajout=20du=20formulaire=20d'enqu=C3=AAte.?=
 =?UTF-8?q?=20fixes=20#5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sql/drop.sql                                  |   3 +
 sql/init_data.h2.sql                          |   6 +
 sql/init_data.postgresql.sql                  |  21 +
 sql/questions.csv                             |   4 +
 sql/responses.csv                             |  19 +
 sql/schema.tables.sql                         |  29 ++
 .../java/fr/agrometinfo/www/client/App.java   |   4 +-
 .../www/client/i18n/AppConstants.java         |  28 +-
 .../www/client/presenter/LoginPresenter.java  | 130 +++++++
 .../www/client/view/AbstractBaseView.java     |  41 +-
 .../www/client/view/LayoutView.java           |  59 ++-
 .../www/client/view/LoginView.java            | 179 +++++++++
 .../client/i18n/AppConstants_fr.properties    |  10 +
 .../agrometinfo/www/client/public/style.css   |  18 +
 .../fr/agrometinfo/www/client/style.css       | 208 ++++++++++
 .../www/server/AgroMetInfoConfiguration.java  |   1 -
 .../www/server/dao/DaoHibernate.java          |  25 +-
 .../www/server/dao/QuestionsDao.java          |  32 ++
 .../www/server/dao/QuestionsDaoHibernate.java |  34 ++
 .../www/server/dao/ResponsesDao.java          |  34 ++
 .../www/server/dao/ResponsesDaoHibernate.java |  36 ++
 .../www/server/dao/UserResponsesDao.java      |  48 +++
 .../server/dao/UserResponsesDaoHibernate.java |  64 +++
 .../www/server/model/LoginFormData.java       |  50 +++
 .../www/server/model/Question.java            |  38 ++
 .../www/server/model/Response.java            |  45 +++
 .../www/server/model/UserResponses.java       |  56 +++
 .../www/server/rs/ApplicationConfig.java      |   4 +-
 .../www/server/rs/LoginFormResource.java      | 240 ++++++++++++
 .../www/server/service/MailService.java       | 320 +--------------
 .../www/server/service/MailServiceImpl.java   | 361 +++++++++++++++++
 www-server/src/main/webapp/index.html         |   2 +-
 .../www/server/LoginFormHibernateTest.java    | 192 +++++++++
 .../www/server/exception/ErrorTypeTest.java   |   4 +-
 .../www/server/rs/LoginFormResourceTest.java  | 368 ++++++++++++++++++
 .../test/resources/META-INF/persistence.xml   |   3 +
 .../www/shared/dto/LoginFormDataDTO.java      |  84 ++++
 .../www/shared/dto/QuestionDTO.java           |  85 ++++
 .../www/shared/dto/ResponseDTO.java           |  64 +++
 .../www/shared/service/LoginFormService.java  |  92 +++++
 40 files changed, 2708 insertions(+), 333 deletions(-)
 create mode 100644 sql/questions.csv
 create mode 100644 sql/responses.csv
 create mode 100644 www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
 create mode 100644 www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
 create mode 100644 www-client/src/main/resources/fr/agrometinfo/www/client/style.css
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
 create mode 100644 www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
 create mode 100644 www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
 create mode 100644 www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
 create mode 100644 www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
 create mode 100644 www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
 create mode 100644 www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java

diff --git a/sql/drop.sql b/sql/drop.sql
index ee674b8..324c3c5 100644
--- a/sql/drop.sql
+++ b/sql/drop.sql
@@ -10,3 +10,6 @@ DROP TABLE period;
 DROP TABLE i18n;
 DROP TABLE i18nkey;
 DROP TABLE locale;
+DROP TABLE user2responses;
+DROP TABLE responses;
+DROP TABLE questions;
diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index c898645..6095f2c 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -127,3 +127,9 @@ INSERT INTO simulation (date, simulationid, started, ended) VALUES
 --
 REFRESH MATERIALIZED VIEW v_pra_dailyvalue;
 
+INSERT INTO questions(question_ref, category, fullname)
+	SELECT * FROM CSVREAD('../sql/questions.csv');
+
+INSERT INTO responses(question_ref, fullname)
+	SELECT * FROM CSVREAD('../sql/responses.csv', null, 'fieldSeparator=;');
+
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index 6648baa..056f7c5 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -1,6 +1,19 @@
 -- Initialization script for PostgreSQL database with sample data
 -- also do update.
 
+DELETE FROM normalvalue;
+DELETE FROM dailyvalue;
+DELETE FROM cell;
+DELETE FROM department;
+DELETE FROM region;
+DELETE FROM indicator;
+DELETE FROM period;
+DELETE FROM i18n;
+DELETE FROM i18nkey;
+DELETE FROM locale;
+DELETE FROM questions;
+DELETE FROM responses;
+
 -- translations
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_translation (
     key VARCHAR,
@@ -155,3 +168,11 @@ INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
     JOIN period AS p ON p.id=i.period
     WHERE p.code=t.period
     ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
+
+-- questions
+\COPY questions(question_ref, category, fullname) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
+
+-- responses
+-- WARNING : responses CSV file use semicolon (« ; ») as separator !!
+\COPY responses(question_ref, fullname) FROM sql/responses.csv WITH DELIMITER ';' HEADER CSV;
+
diff --git a/sql/questions.csv b/sql/questions.csv
new file mode 100644
index 0000000..faac491
--- /dev/null
+++ b/sql/questions.csv
@@ -0,0 +1,4 @@
+question_ref,category,fullname
+1,profession,Quelle est votre profession ?
+2,origin,Comment avez-vous connu AgroMetInfo ?
+3,useCase,Dans quel but voulez-vous utiliser cette application ?
diff --git a/sql/responses.csv b/sql/responses.csv
new file mode 100644
index 0000000..a88a2c0
--- /dev/null
+++ b/sql/responses.csv
@@ -0,0 +1,19 @@
+question_ref;fullname
+1;Agriculteur
+1;Conseiller agricole (Chambre d'agriculture, Entreprise de service)
+1;Scientifique
+1;Journaliste
+1;Administration
+1;Enseignant
+1;Citoyen curieux (c'est bien)
+2;Internet
+2;Via le site internet d'AgroClim
+2;Via le site internet INRAE
+2;Média (TV, journaux, radio)
+2;Réseaux sociaux (Linkedin, Facebook, X, ...)
+2;Bouche à oreilles
+3;S'informer
+3;Enseigner
+3;Conseiller
+3;Publier un article dans les médias
+3;Faire une présentation
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index 77eff33..a469a08 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -214,6 +214,35 @@ CREATE TABLE IF NOT EXISTS dailyvisit(
 );
 COMMENT ON TABLE dailyvisit IS 'Number of visits per day.';
 
+-- Tables pour le formulaire d'enquête
+CREATE TABLE IF NOT EXISTS questions (
+	question_ref serial NOT NULL,
+	category varchar(20) NOT NULL,
+	fullname varchar(255) NULL,
+	CONSTRAINT questions_pk PRIMARY KEY (question_ref)
+);
+COMMENT ON TABLE questions IS 'Questions pour le formulaire d''enquête';
+
+CREATE TABLE IF NOT EXISTS responses (
+	response_ref serial NOT NULL,
+	question_ref int4 NOT NULL,
+	fullname varchar NULL,
+	CONSTRAINT responses_pk PRIMARY KEY (response_ref),
+	CONSTRAINT responses_questions_fk FOREIGN KEY (question_ref) REFERENCES questions(question_ref)
+);
+CREATE TABLE IF NOT EXISTS user2responses (
+	user_response_ref serial NOT NULL,
+	datetime timestamp NOT NULL,
+	question_ref int4 NOT NULL,
+	response_ref int4 NULL,
+	other_text varchar NULL,
+	CONSTRAINT user2responses_pk PRIMARY KEY (user_response_ref),
+	CONSTRAINT user2responses_questions_fk FOREIGN KEY (question_ref) REFERENCES questions(question_ref),
+	CONSTRAINT user2responses_responses_fk FOREIGN KEY (response_ref) REFERENCES responses(response_ref)
+);
+COMMENT ON TABLE user2responses IS 'Réponses des utilisateurs';
+
+
 CREATE OR REPLACE VIEW v_i18n
 AS SELECT l.languagetag,
     i.i18nkey,
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/App.java b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
index 920e623..d1895a5 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/App.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
@@ -98,7 +98,9 @@ public class App implements EntryPoint {
 
         // enabling Charba
         Charba.enable();
-
         new LayoutPresenter().start();
+        
+        // formulaire d'enquête
+        new LoginPresenter().start();
     }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index ec07adb..b45c154 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -1,5 +1,7 @@
 package fr.agrometinfo.www.client.i18n;
 
+import java.util.Map;
+
 /**
  * Interface to represent the constants contained in resource bundle:
  * 'fr/agrometinfo/www/client/i18n/AppConstants.properties'.
@@ -338,5 +340,29 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
      */
     @DefaultStringValue("Yes")
     String yes();
-
+    
+    // Formulaire d'enquête - #5
+    @DefaultStringValue("Valider")
+    String validate();
+    
+    @DefaultStringValue("Ignorer")
+    String ignore();
+    
+    @DefaultStringValue("Formulaire d'enquête")
+    String loginFormTitle();
+    
+    @DefaultStringValue("Please complete this survey form to continue on AgroMetInfo.")
+    String loginFormDescription();
+    
+    @DefaultStringValue("Other")
+    String loginFormOtherTextCheckbox();
+    
+    @DefaultStringValue("You must click on checkbox above for activate this input field.")
+    String loginFormOtherTextTooltip();
+    
+    @DefaultStringValue("Your responses have been recorded.")
+    String loginFormSuccess();
+    
+    @DefaultStringValue("Your responses cannot been recorded, but you can use AgroMetInfo application.")
+    String loginFormFail();
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
new file mode 100644
index 0000000..1c7d2f9
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
@@ -0,0 +1,130 @@
+package fr.agrometinfo.www.client.presenter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.dominokit.rest.shared.request.FailedResponseBean;
+
+import com.google.gwt.core.client.GWT;
+
+import fr.agrometinfo.www.client.view.BaseView;
+import fr.agrometinfo.www.client.view.LoginView;
+import fr.agrometinfo.www.shared.dto.QuestionDTO;
+import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.service.LoginFormServiceFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Presenter to login the user with survey form.
+ *
+ * @author Olivier Maury
+ * @author Jérémie Décome
+ */
+public final class LoginPresenter implements Presenter {
+
+    /**
+     * Interface for the related view.
+     */
+    public interface View extends BaseView<LoginPresenter> {
+        /**
+         * Display notification on failure.
+         *
+         * @param failedResponse failure response
+         */
+        void failureNotification(FailedResponseBean failedResponse);
+        /**
+         * Close modal.
+         */
+        void close();
+        /**
+         * @param list list of availables responses.
+         */
+        void setResponses(Map<QuestionDTO, List<ResponseDTO>> map);
+        /**
+         * Showing success login.
+         * @param msg returned message from webservice
+         */
+        void displaySuccessLogin(final String msg);
+    }
+
+    /**
+     * Related view.
+     */
+    private final LoginView view = new LoginView();
+    /** List of responses from webservice. */
+    private List<ResponseDTO> responses;
+    /** List of questions, extracted from responses list. */
+    private List<QuestionDTO> questions;
+    /**
+     * Set responses and extract questions from responses list.
+     * @param list list of responses
+     */
+    private void setResponses(final List<ResponseDTO> list) {
+        GWT.log("setResponses(" + list.size() +") start");
+        this.responses = list;
+        final Map<QuestionDTO, List<ResponseDTO>> responsesMap = new HashMap<>();
+        
+        // extraction des questions, à partir des réponses retournées par le webservice
+        for (final ResponseDTO dto : list) {
+            if (!responsesMap.containsKey(dto.getQuestion())) { // la map ne contient pas la question
+                // on crée l'entrée, avec une liste vide
+                responsesMap.put(dto.getQuestion(), new ArrayList<ResponseDTO>());
+            }
+            responsesMap.get(dto.getQuestion()).add(dto);
+        }
+        this.questions = new ArrayList<>(responsesMap.keySet());
+        view.setResponses(responsesMap);
+        GWT.log("setResponses() end");
+        
+        view.init();
+    }
+    @Override
+    public void start() {
+        view.setPresenter(this);
+        
+        LoginFormServiceFactory.INSTANCE.getResponses()
+        .onSuccess(this::setResponses)
+        .onFailed(view::failureNotification)
+        .send();
+    }
+    public void insertUserResponses(final List<Long> responsesRef, final HashMap<Long, String> otherTextMap) {
+        GWT.log("Insère les réponses de l'utilisateur");
+        
+        // traitement des réponses prédéfinies
+        final List<QuestionDTO> questionsDto = new ArrayList<>();
+        final List<ResponseDTO> responsesDto = new ArrayList<>();
+        
+        for (final Long ref : responsesRef) {
+            // on récupère la réponse correspondante à la référence
+            final ResponseDTO rDto = this.responses.stream().filter(r -> r.getResponseRef() == ref).findFirst().get();
+            if (rDto != null) {
+                if (!responsesDto.contains(rDto)) {
+                    responsesDto.add(rDto);
+                }
+                
+                // on récupère la question correspondantes à la réponse
+                if (!questionsDto.contains(rDto.getQuestion())) {
+                    questionsDto.add(rDto.getQuestion());
+                }
+            }
+        }
+        // ajout de la question, si celle çi n'a qu'une réponse libre
+        this.questions.forEach((q) -> {
+            if (!questionsDto.contains(q) && otherTextMap.containsKey(q.getQuestionRef())) {
+                questionsDto.add(q);
+            }
+        });
+        
+        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDto, responsesDto, otherTextMap);
+        
+        // envoi au webservice
+        LoginFormServiceFactory.INSTANCE.insertAllResponses(data)
+        .onSuccess(view::displaySuccessLogin)
+        .onFailed(view::failureNotification)
+        .send();
+    }
+    
+}
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
index 789fd1c..52236ee 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
@@ -1,5 +1,16 @@
 package fr.agrometinfo.www.client.view;
 
+import java.util.StringJoiner;
+
+import org.dominokit.domino.ui.notifications.Notification;
+import org.dominokit.rest.shared.request.FailedResponseBean;
+
+import com.google.gwt.core.client.GWT;
+
+import elemental2.dom.DomGlobal;
+import fr.agrometinfo.www.client.i18n.AppConstants;
+import fr.agrometinfo.www.client.i18n.AppMessages;
+
 /**
  * Abstract class for all view implementations.
  *
@@ -12,6 +23,30 @@ public abstract class AbstractBaseView<T> implements BaseView<T> {
      * Related presenter.
      */
     private T presenter;
+    /**
+     * I18N constants.
+     */
+    private static final AppConstants CSTS = GWT.create(AppConstants.class);
+    /**
+     * I18N messages.
+     */
+    private static final AppMessages MSGS = GWT.create(AppMessages.class);
+    /**
+    *
+    * @param failure
+    * @return
+    */
+   protected static String getDetails(final FailedResponseBean failure) {
+       final StringJoiner sj = new StringJoiner("<br/>");
+       sj.add(CSTS.failureBody());
+       sj.add(failure.getBody());
+       sj.add(CSTS.failureHeaders());
+       sj.add(failure.getHeaders().toString());
+       sj.add(MSGS.failureStatusCode(failure.getStatusCode()));
+       sj.add(CSTS.failureStatusText());
+       sj.add(failure.getStatusText());
+       return sj.toString();
+   }
 
     /**
      * @return related presenter
@@ -24,5 +59,9 @@ public abstract class AbstractBaseView<T> implements BaseView<T> {
     public final void setPresenter(final T value) {
         this.presenter = value;
     }
-
+    
+   protected Notification notification(final String msg) {
+       DomGlobal.console.info("Notification!");
+       return Notification.create(msg).setPosition(Notification.TOP_LEFT).show();
+   }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
index 5f7e8ba..6006d8a 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
@@ -89,17 +89,6 @@ implements LayoutPresenter.View, LoadingHandler {
         addMenuItem(menu, text, icon, runnable);
     }
 
-    private static String getDetails(final FailedResponseBean failure) {
-        final StringJoiner sj = new StringJoiner("<br/>");
-        sj.add(CSTS.failureBody());
-        sj.add(failure.getBody());
-        sj.add(CSTS.failureHeaders());
-        sj.add(failure.getHeaders().toString());
-        sj.add(MSGS.failureStatusCode(failure.getStatusCode()));
-        sj.add(CSTS.failureStatusText());
-        sj.add(failure.getStatusText());
-        return sj.toString();
-    }
 
     /**
      * @param relativePath documentation path relative to documentation root
@@ -389,11 +378,21 @@ implements LayoutPresenter.View, LoadingHandler {
         return layout.isRightPanelVisible();
     }
 
-    private Notification notification(final String msg) {
-        DomGlobal.console.info("Notification! " + msg);
-        return Notification.create(msg).setPosition(Notification.TOP_LEFT).show();
+    private void onChoiceChange() {
+        if (choice.isValid()) {
+            getPresenter().onChoiceChange(choice);
+        }
+    }
+
+    private void onComparisonChange(final boolean newValue) {
+        choice.setComparison(newValue);
+        onChoiceChange();
     }
 
+    private void onIndicatorChange(final String newValue) {
+        choice.setIndicator(newValue);
+        onChoiceChange();
+    }
     @Override
     public void onLoading(final LoadingEvent event) {
         GWT.log("LayoutView.onLoading() " + event);
@@ -417,7 +416,37 @@ implements LayoutPresenter.View, LoadingHandler {
         }
         GWT.log("LayoutView.onLoading() done");
     }
-
+    private void onPeriodChange(final String newValue) {
+        choice.setPeriod(newValue);
+        choice.setIndicator(null);
+
+        // fill indicators
+        List<IndicatorDTO> list = null;
+        for (final PeriodDTO p : this.periods) {
+            if (p.getCode().equals(newValue)) {
+                list = p.getIndicators();
+                break;
+            }
+        }
+        if (list == null) {
+            return;
+        }
+        DomGlobal.console.info("Indicators : " + list);
+        new HTMLSelectElementBuilder<IndicatorDTO>() //
+        .setSelect(indicatorSelect) //
+        .setTextFunction(IndicatorDTO::getDescription) //
+        .setValueFunction(IndicatorDTO::getCode) //
+        .removeOptions() //
+        .addOptions(list) //
+        .build();
+        // select "meant"
+        if (list.stream().map(IndicatorDTO::getCode).anyMatch(DEFAULT_INDICATOR::equals)) {
+            indicatorSelect.element().value = DEFAULT_INDICATOR;
+            onIndicatorChange(DEFAULT_INDICATOR);
+        } else {
+            onChoiceChange();
+        }
+    }
     /**
      * To display an OpenLayers map, a container with a fixed height CSS property is
      * needed.
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
new file mode 100644
index 0000000..b1f3164
--- /dev/null
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
@@ -0,0 +1,179 @@
+package fr.agrometinfo.www.client.view;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.dominokit.domino.ui.button.Button;
+import org.dominokit.domino.ui.forms.CheckBox;
+import org.dominokit.domino.ui.forms.TextArea;
+import org.dominokit.domino.ui.modals.ModalDialog;
+import org.dominokit.rest.shared.request.FailedResponseBean;
+import org.jboss.elemento.Elements;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.storage.client.Storage;
+
+import fr.agrometinfo.www.client.i18n.AppConstants;
+import fr.agrometinfo.www.client.i18n.AppMessages;
+import fr.agrometinfo.www.client.presenter.LoginPresenter;
+import fr.agrometinfo.www.shared.dto.QuestionDTO;
+import fr.agrometinfo.www.shared.dto.ResponseDTO;
+
+/**
+ * View for survey form.
+ *
+ * @author Olivier Maury
+ * @author Jérémie Décome
+ * 
+ */
+public final class LoginView extends AbstractBaseView<LoginPresenter> implements LoginPresenter.View {
+    /**
+     * I18N constants.
+     */
+    private static final AppConstants CSTS = GWT.create(AppConstants.class);
+    /**
+     * I18N messages.
+     */
+    private static final AppMessages MSGS = GWT.create(AppMessages.class);
+    /**
+     * Is survey was already validated by user ?
+     */
+    private static final String IS_VALIDATED_KEY = "isValidated";
+    /**
+     * List of available responses.
+     */
+    private Map<QuestionDTO, List<ResponseDTO>> availableResponses;
+    /**
+     * The modal used to display the login form.
+     */
+    private ModalDialog modal;
+    /** Validate button of modal. */
+    private Button validate;
+    /** List of all checkbox on modal. */
+    private List<CheckBox> checkBoxList = new ArrayList<>();
+
+    @Override
+    public void close() {
+        modal.close();
+    }
+
+    @Override
+    public void init() {
+    	GWT.log("LoginView.init()");
+    	// on vérifie que le formulaire n'a pas déjà été rempli par l'utilisateur, via le LocalStorage du navigateur
+    	final Storage ls = Storage.getLocalStorageIfSupported();
+    	if (ls != null) {
+    		if (ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
+    			GWT.log("Survey was already validated for this user, don't showing again");
+    			return;
+    		}
+    	}
+        
+    	final Map<Long, TextArea> otherTextArea = new HashMap<>();
+    	
+        this.modal = ModalDialog.create(CSTS.loginFormTitle()).large().setAutoClose(true);
+		this.modal
+		.appendChild(Elements.p().textContent(CSTS.loginFormDescription()).css("login-paragraph"));
+		
+		for (Map.Entry<QuestionDTO, List<ResponseDTO>> entry : this.availableResponses.entrySet()) {
+		    final QuestionDTO k = entry.getKey(); // Pour chaque question
+            
+            // on affiche le libellé
+            this.modal.appendChild(Elements.h(5, "• " + k.getFullName()));
+            
+            // on affiche les réponses possibles
+            entry.getValue().forEach((r) -> {
+                final CheckBox cb = CheckBox.create(r.getFullname()).id(Long.toString(r.getResponseRef()))
+                        .css("login-checkbox");
+                this.modal.appendChild(cb);
+                this.checkBoxList.add(cb);
+            });
+            
+            // on affiche le champ pour la réponse libre
+            final CheckBox otherCb = CheckBox.create(CSTS.loginFormOtherTextCheckbox()).id(k.getCategory() + "_other").css("login-checkbox");
+            final TextArea otherText = TextArea.create().setId(k.getCategory() + "_other_text")
+                    .setDisabled(true)
+                    .setTooltip(CSTS.loginFormOtherTextTooltip())
+                    .setRows(2)
+                    .css("login-textarea");
+            
+            otherCb.addChangeHandler((v) -> {
+                otherText.setDisabled(!otherCb.getValue());
+                if (!otherText.isDisabled()) {
+                    otherText.removeTooltip();
+                } else {
+                    otherText.setTooltip(CSTS.loginFormOtherTextTooltip());
+                }
+            });
+            this.checkBoxList.add(otherCb);
+            this.modal.appendChild(otherCb).appendChild(otherText);
+            otherTextArea.put(k.getQuestionRef(), otherText);
+		}
+		// active le bouton Valider si au moins une case a été cochée
+		this.checkBoxList.forEach((c) -> {
+		    c.addChangeHandler((v) -> this.activateValidateButton());
+		});
+		
+		// E-mail de l'utilisateur
+		
+		this.validate = Button.create(CSTS.validate()).linkify();
+		this.validate.addClickListener((evt) -> {
+		    final HashMap<Long, String> otherTextMap = new HashMap<>();
+		    
+		    // on traite les réponses prédéfinies (récupération des références des réponses), en excluant les cases à cocher pour les réponses libre
+		    final List<Long> selectedResponsesRef = this.checkBoxList.stream()
+		            .filter((v) -> v.getValue() && v.getId().matches("[0-9]+"))
+		            .map(v -> Long.parseLong(v.getId())).collect(Collectors.toList());
+
+		    // traitement des réponses libre de la question
+		    otherTextArea.forEach((k, v) -> {
+		        // si le champ est actif, c'est qu'il y a (potentiellement) une réponse et si le champ n'est pas vide
+		        if (!v.isDisabled() && !v.getValue().equals("")) {
+		            otherTextMap.put(k, v.getValue());
+		        }
+		    });
+		    
+		    this.getPresenter().insertUserResponses(selectedResponsesRef, otherTextMap);
+		    
+			ls.setItem(IS_VALIDATED_KEY, "true");
+			this.modal.close();
+		});
+		this.validate.setDisabled(true);  // le bouton Valider est désactivé par défaut
+		this.modal.appendFooterChild(validate);
+		
+		final Button ignore = Button.create(CSTS.ignore()).linkify();
+		ignore.addClickListener((evt) -> {
+			this.modal.close();
+		});
+		this.modal.appendFooterChild(ignore);
+		
+		this.modal.open();
+		GWT.log("LoginView.init() end");
+    }
+    
+    @Override
+    public void failureNotification(FailedResponseBean failedResponse) {
+        this.notification(CSTS.loginFormFail());
+    }
+
+    @Override
+    public void setResponses(Map<QuestionDTO, List<ResponseDTO>> map) {
+        GWT.log("LoginView.setResponses(" + map.size() + ")");
+        this.availableResponses = map;
+        GWT.log("LoginView.setResponses() end");
+    }
+
+    @Override
+    public void displaySuccessLogin(String msg) {
+        this.notification(CSTS.loginFormSuccess());
+    }
+    /**
+     * Enable or disable validate button depending of checkbox status.
+     */
+    private void activateValidateButton() {
+        this.validate.setDisabled(!this.checkBoxList.stream().anyMatch((c) -> c.getValue() == true));
+    }
+}
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index 3436fb9..685c04a 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -49,3 +49,13 @@ Pour rester au courant des évolutions, vous pouvez nous envoyer un message à \
 <a href="mailto:support-agrometinfo@inrae.fr?subject=AgroMetInfo:%20contact">support-agrometinfo@inrae.fr</a> ou utiliser le formulaire de contact.
 welcomeTitle = Bienvenue sur AgroMetInfo
 yes = Oui
+userProfile = Compte et paramètres
+whyConnectionIsRequired = Vous devez vous identifier pour accéder à AgroMetInfo en raison des accords avec Météo-France relatifs aux échanges de données SAFRAN avec AgroClim.
+validate = Valider
+ignore = Ignorer
+loginFormTitle = Formulaire d'enquête
+loginFormDescription = Merci de compléter ce formulaire d'enquête afin de continuer sur AgroMetInfo
+loginFormOtherTextCheckbox = Autre, à préciser
+loginFormOtherTextTooltip = Vous devez cliquer sur la case à cocher ci-dessus pour activer ce champ de saisie.
+loginFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
+loginFormFail = Vos réponses au formulaire n'ont pas pu être, mais vous pouvez tout de même utiliser AgroMetInfo.
\ No newline at end of file
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
index bdbff67..1cf0b4c 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
@@ -348,3 +348,21 @@ body > .modal-backdrop {
         --rightsidebar-width: 600px;
     }
 }
+
+/* Formulaire d'enquête */
+.login-paragraph {
+	font-size: 18px;
+}
+.login-checkbox {
+	margin: 0 0 2px 5px;
+}
+.login-checkbox .field-cntr {
+	padding: 0 10px !important;
+}
+.login-checkbox label {
+	top: 0px !important;
+	margin-bottom: 0px !important;
+}
+.login-textarea .field-cntr {
+	padding-top: 5px;
+}
\ No newline at end of file
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/style.css b/www-client/src/main/resources/fr/agrometinfo/www/client/style.css
new file mode 100644
index 0000000..bdd54e8
--- /dev/null
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/style.css
@@ -0,0 +1,208 @@
+footer {
+    background-color: #353854;
+}
+footer.footer {
+    max-height: 2em;
+    min-height: 2em;
+    text-align: center;
+    margin-left: auto;
+    margin-right: auto;
+}
+footer, footer a {
+    color: #b5b5c8;
+    font-weight: bold;
+    margin-right: 1em;
+    padding: 0.3em;
+}
+footer a:hover {
+    color: #ffffff;
+}
+select {
+    width: 100%;
+    height: 2.5em;
+    font-size: 1.1em;
+}
+/**
+ * Font awesome icons.
+ */
+/*!
+ * Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
+@font-face {
+    font-family:"Font Awesome 5 Free";
+    font-style:normal;
+    font-weight:900;
+    src:url(vendors/fa/fa-solid-900.eot);
+    src:url(vendors/fa/fa-solid-900.eot?#iefix) format("embedded-opentype"),
+        url(vendors/fa/fa-solid-900.woff2) format("woff2"),
+        url(vendors/fa/fa-solid-900.woff) format("woff"),
+        url(vendors/fa/fa-solid-900.ttf) format("truetype"),
+        url(vendors/fa/fa-solid-900.svg#fontawesome) format("svg")
+}
+.fa, .fab, .fal, .far, .fas {
+    -moz-osx-font-smoothing: grayscale;
+    -webkit-font-smoothing: antialiased;
+    display: inline-block;
+    font-style: normal;
+    font-variant: normal;
+    text-rendering: auto;
+    line-height: 1;
+}
+.fa, .fas {
+    font-family: "Font Awesome 5 Free";
+    font-weight: 900;
+}
+.fa-cloud-showers-heavy::before {
+    content: "\f740";
+}
+.fa-snowflake::before {
+    content: "\f2dc";
+}
+.fa-thermometer-half::before {
+    content: "\f2c9";
+}
+.fa-thermometer-quarter::before {
+    content: "\f2ca";
+}
+.fa-thermometer-three-quarters::before {
+    content: "\f2c8";
+}
+.agroclim-apps img {
+    height: 60px;
+    width: 60px;
+}
+.agroclim-apps ul {
+    box-sizing: content-box;
+    width: 200px;
+}
+.agroclim-apps.dom-ui.menu .simple-menu-item {
+    display: inline-block;
+    padding-bottom: 15px;
+    text-align: center;
+    width: 100px;
+}
+.agrometinfo-navbar .navbar-header {
+    min-height: var(--logo-height);
+    padding-top: 0px;
+}
+.agrometinfo-navbar .navbar-header .menu-toggle .bars {
+    line-height: 15px;
+}
+.agrometinfo-leftsidebar.sidebar,
+.agrometinfo-rightsidebar.right-sidebar {
+	height: calc(100vh - var(--logo-height));
+    top: var(--logo-height);
+}
+.agrometinfo-rightsidebar.right-sidebar {
+	width: var(--rightsidebar-width);
+}
+.agrometinfo-rightsidebar.right-sidebar.slide-out-right {
+	right: calc(-1 * var(--rightsidebar-width));
+}
+.agrometinfo-topbar.navbar {
+    border: 0px;
+}
+.agrometinfo-topbar.navbar-nav > li > a {
+    padding-bottom: 6px;
+    padding-top: 6px;
+}
+.float-right {
+	float: right;
+}
+div.idp {
+    padding: 0.5em;
+}
+.idp img {
+    padding-right: 1em;
+}
+.idp span {
+    font-weight: bold;
+}
+.indicator-categories > i {
+    font-size: 40px;
+    padding-left: 5px;
+}
+.javascript-disabled {
+    width: 22em;
+    position: absolute;
+    left: 50%;
+    margin-left: -11em;
+    color: red;
+    background-color: white;
+    border: 1px solid red;
+    padding: 4px;
+    font-family: sans-serif
+}
+.logo-in img {
+    background-color: white;
+    height: var(--logo-height);
+}
+#mapContainer .ol-attribution.ol-uncollapsible {
+    top: auto;
+    right: auto;
+    bottom: 0.2em;
+    right: .5em;
+}
+#mapContainer .ol-control button {
+    /* same as layer switcher */
+    height: 38px;
+    width: 38px;
+}
+#mapContainer .ol-full-screen {
+    top: .5em;
+    right: .5em;
+    bottom: auto;
+    left: auto;
+}
+#mapContainer .layer-switcher {
+    top: 3.5em;
+    right: .5em;
+    bottom: auto;
+    left: auto;
+}
+#mapContainer .ol-zoom-extent {
+    top: auto;
+    bottom: 8em;
+    right: .5em;
+    left: auto;
+}
+#mapContainer .ol-zoom {
+    top: auto;
+    bottom: 2em;
+    right: .5em;
+    left: auto;
+}
+:root {
+    --logo-height: 50px;
+}
+@media screen and (max-width: 450px) {
+	.navbar-brand {
+		display: none;
+	}
+}
+@media screen and (max-width: 700px) {
+	:root {
+		--rightsidebar-width: 90%;
+	}
+}
+@media screen and (min-width: 700px) {
+	:root {
+		--rightsidebar-width: 400px;
+	}
+}
+
+/* Formulaire d'enquête */
+.login-paragraph {
+	font-size: 18px;
+}
+.login-checkbox {
+	margin: 0 0 2px 5px;
+}
+.login-checkbox .field-cntr {
+	padding: 0 10px !important;
+}
+.login-checkbox label {
+	top: 0px !important;
+	margin-bottom: 0px !important;
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java b/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java
index 9e95b3a..5eb7981 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/AgroMetInfoConfiguration.java
@@ -67,7 +67,6 @@ public class AgroMetInfoConfiguration {
          * E-mail address for support.
          */
         SUPPORT_EMAIL("support.email");
-
         /**
          * Key in config.properties.
          */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
index b9b9c20..f6232d1 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
@@ -86,7 +86,18 @@ public abstract class DaoHibernate<T> {
             return 0L;
         }
     }
-
+    /**
+     * Delete all row in table.
+     */
+    public void deleteAll() {
+        LOGGER.traceEntry();
+        final ScopedEntityManager em = getScopedEntityManager();
+        final Query q = em.createQuery("DELETE FROM " + clazz.getName() + " t");
+        em.getTransaction().begin();
+        q.executeUpdate();
+        em.getTransaction().commit();
+        LOGGER.traceExit();
+    };
     /**
      * Use a consumer to execute JPA operations in a transaction.
      *
@@ -304,4 +315,16 @@ public abstract class DaoHibernate<T> {
     protected final ScopedEntityManager getScopedEntityManager() {
         return PersistenceManager.getInstance().createScopedEntityManager();
     }
+    /**
+     * Save a new entity in database.
+     * @param object
+     *            new entity to save
+     */
+    protected final void save(final Object object) {
+        try (ScopedEntityManager em = getScopedEntityManager()) {
+            em.executeTransaction(() -> em.persist(object));
+        } catch (final Exception e) {
+            LOGGER.catching(e);
+        }
+    }
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
new file mode 100644
index 0000000..e68aaf0
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
@@ -0,0 +1,32 @@
+/**
+ *
+ */
+package fr.agrometinfo.www.server.dao;
+
+import java.util.List;
+
+import fr.agrometinfo.www.server.model.Question;
+
+/**
+ * DAO for {@link Question}.
+ * @author jdecome
+ *
+ */
+public interface QuestionsDao {
+	/**
+	 * @return all of Questions
+	 */
+	List<Question> findAll();
+	/**
+	 * Find question object by reference.
+	 * @param questionRef question_ref
+	 * @return Question object
+	 */
+	Question findByRef(long questionRef);
+	/**
+	 * Find question object by category.
+	 * @param category of question
+	 * @return Question object
+	 */
+	Question findByCategory(final String category);
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
new file mode 100644
index 0000000..9369e17
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
@@ -0,0 +1,34 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import fr.agrometinfo.www.server.model.Question;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public class QuestionsDaoHibernate extends DaoHibernate<Question> implements QuestionsDao {
+
+	public QuestionsDaoHibernate() {
+		super(Question.class);
+	}
+
+	@Override
+	public Question findByRef(long ref) {
+		return super.find(ref);
+	}
+
+	@Override
+	public Question findByCategory(String category) {
+		final String jpql = "SELECT q FROM Question q WHERE q.category = :cat";
+		return super.findOneByJPQL(jpql, Map.of("cat", category), Question.class);
+	}
+
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
new file mode 100644
index 0000000..9715217
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
@@ -0,0 +1,34 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.dao;
+
+import fr.agrometinfo.www.server.model.Response;
+
+import java.util.List;
+
+/**
+ * DAO for {@link Response}.
+ *
+ * @author jdecome
+ *
+ */
+public interface ResponsesDao {
+    /**
+     * Find all responses in database.
+     * @return all responses
+     */
+	List<Response> findAll();
+	/**
+	 * Find all responses by question.
+	 * @param questionRef
+	 * @return list of responses
+	 */
+	List<Response> findAllByQuestion(final long questionRef);
+	/**
+	 * Get response with reference.
+	 * @param responseRef
+	 * @return response
+	 */
+	Response findByRef(final long responseRef);
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
new file mode 100644
index 0000000..5577d8e
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
@@ -0,0 +1,36 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import fr.agrometinfo.www.server.model.Response;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public class ResponsesDaoHibernate extends DaoHibernate<Response> implements ResponsesDao {
+    /**
+     * Constructor.
+     */
+	public ResponsesDaoHibernate() {
+		super(Response.class);
+	}
+
+	@Override
+	public List<Response> findAllByQuestion(final long questionRef) {
+		final String jpql = "SELECT r FROM Response r WHERE r.question.questionRef = :ref";
+		return super.findAllByJPQL(jpql, Map.of("ref", questionRef), Response.class);
+	}
+
+    @Override
+    public Response findByRef(final long responseRef) {
+        return super.find(responseRef);
+    }
+	
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
new file mode 100644
index 0000000..32c8a9d
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -0,0 +1,48 @@
+package fr.agrometinfo.www.server.dao;
+
+import java.util.List;
+
+import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserResponses;
+/**
+ * DAO for {@link UserResponsesDao}.
+ * 
+ * @author jdecome
+ *
+ */
+public interface UserResponsesDao {
+    /**
+     * Get all user responses by question.
+     * @param questionRef
+     * @return list of user's responses
+     */
+	List<UserResponses> findAllByQuestion(final long questionRef);
+	/**
+	 * Insert response or text for specified question.<br>
+     * If r is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code r} must be null. 
+	 * @param q question to answer
+	 * @param r response
+	 * @param otherText text if response is other
+	 */
+	void insertResponse(final Question q, final Response r, final String otherText);
+	/**
+	 * Insert response or text for specified question, identified by reference.<br>
+	 * If responseRef is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code responseRef} must be null. 
+	 * @param questionRef reference of question to answer
+	 * @param responseRef reference of response
+	 * @param otherText text if response is other
+	 */
+	void insertResponse(final Long questionRef, final Long responseRef, final String otherText);
+	/**
+	 * Get all user responses.
+	 * @return number of responses
+	 */
+	int getNbUserResponses();
+	/**
+	 * 
+	 */
+	void deleteAll();
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
new file mode 100644
index 0000000..322dd2e
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -0,0 +1,64 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.dao;
+
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Map;
+
+import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserResponses;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public class UserResponsesDaoHibernate extends DaoHibernate<UserResponses> implements UserResponsesDao {
+    /** DAO for questions. */
+    private QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    /** DAO for responses. */
+    private ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    /** Default constructor. */
+	public UserResponsesDaoHibernate() {
+		super(UserResponses.class);
+	}
+	@Override
+	public List<UserResponses> findAllByQuestion(final long questionRef) {
+	    final String jpql = "SELECT u FROM UserResponses u WHERE u.question.questionRef = :ref";
+		return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponses.class);
+	}
+    @Override
+    public void insertResponse(final Question q, final Response r, final String otherText) {
+        final UserResponses ur = new UserResponses();
+        ur.setDateTime(Timestamp.valueOf(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())));
+        ur.setQuestion(q);
+        ur.setResponse(r);
+        ur.setOtherText(otherText);
+        
+        this.save(ur);
+
+    }
+    @Override
+    public void insertResponse(final Long questionRef, final Long responseRef, final String otherText) {
+        // recherche de la question
+        final Question q = questionsDao.findByRef(questionRef);
+        if (q != null) {
+            if (responseRef != null) {  // insertion d'une réponse prédéfinie, on recherche de la réponse
+                final Response r = responsesDao.findByRef(responseRef);
+                this.insertResponse(q, r, null);
+            } else {                    // insertion d'une réponse libre
+                this.insertResponse(q, null, otherText);
+            }
+        }
+    }
+    @Override
+    public int getNbUserResponses() {
+        return super.findAll().size();
+    }
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java
new file mode 100644
index 0000000..236fbe4
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java
@@ -0,0 +1,50 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.model;
+
+import java.util.HashMap;
+import java.util.List;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Class contains all data of login form.
+ * @author jdecome
+ *
+ */
+@Deprecated
+public class LoginFormData {
+    /**
+     * List of login form questions.<br>
+     * This list questions contains available questions, obtained by {@link QuestionsDao}
+     */
+    @Getter
+    @Setter
+    private List<Question> questions;
+    /**
+     * List of login form responses.<br>
+     * This list responses contains available responses, obtained by {@link ResponsesDao}
+     */
+    @Getter
+    @Setter
+    private List<Response> responses;
+    /**
+     * Storage, for each response, the other text, if specified.
+     */
+    @Getter
+    private HashMap<Long, String> otherTextMap = new HashMap<>();
+    /**
+     * Constructor.
+     * @param qList questions list
+     * @param rList responses list
+     * @param otherText otherText map
+     */
+    public LoginFormData(final List<Question> qList, final List<Response> rList, HashMap<Long, String> otherText) {
+        this.questions = qList;
+        this.responses = rList;
+        this.otherTextMap = otherText;
+    }
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
new file mode 100644
index 0000000..0d2537a
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -0,0 +1,38 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.model;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+/**
+ * Questions for survey login modal.
+ * @author jdecome
+ *
+ */
+@Data
+@Entity
+@Table(name = "questions")
+public class Question {
+	/** PK. */
+	@Id
+	@GeneratedValue(strategy = GenerationType.AUTO)
+	@Column(name = "question_ref")
+	private long questionRef;
+	/**
+	 * Catogory of question.
+	 */
+	@Column(name = "category")
+	private String category;
+	/**
+	 * Label of question.
+	 */
+	@Column(name = "fullname")
+	private String fullName;
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
new file mode 100644
index 0000000..7f3c123
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -0,0 +1,45 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.model;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embedded;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.IdClass;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.OneToOne;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+/**
+ * Available response.
+ * @author jdecome
+ *
+ */
+@Data
+@Entity
+@Table(name = "responses")
+public class Response {
+    /**
+     * Reference of response.
+     */
+	@Id
+	@GeneratedValue(strategy = GenerationType.AUTO)
+	@Column(name = "response_ref")
+	private long responseRef;
+	/**
+	 * Question related to response.
+	 */
+	@OneToOne
+	@JoinColumn(name = "question_ref")
+	private Question question;
+	/**
+	 * Label of the response.
+	 */
+	@Column(name = "fullname")
+	private String fullname;
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
new file mode 100644
index 0000000..e4d6e50
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
@@ -0,0 +1,56 @@
+/**
+ *
+ */
+package fr.agrometinfo.www.server.model;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.IdClass;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.OneToMany;
+import jakarta.persistence.OneToOne;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+/**
+ * User's responses values.
+ * @author jdecome
+ *
+ */
+@Data
+@Entity
+@Table(name = "user2responses")
+public class UserResponses {
+    /** Primary key. */
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "user_response_ref")
+    private long userResponseRef;
+    /** Date time of user responses. */
+	@Column(name = "datetime")
+	private Timestamp dateTime;
+	/** Related question. */
+	@OneToOne
+	@JoinColumn(name = "question_ref")
+	private Question question;
+	/**
+	 * Related response.<br>
+	 * Can be null if it's a other response.
+	 */
+	@OneToOne
+	@JoinColumn(name = "response_ref")
+	private Response response;
+	/**
+	 * Other text response.<br>
+	 * Can be null if it's a related response.
+	 */
+	@Column(name = "other_text")
+	private String otherText;
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
index 26fef07..60623c8 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
@@ -39,11 +39,11 @@ public class ApplicationConfig extends Application {
     public final Set<Class<?>> getClasses() {
         return Set.of(
                 // OpenAPI / Swagger
-                OpenApiResource.class, AcceptHeaderOpenApiResource.class,
+                AcceptHeaderOpenApiResource.class, OpenApiResource.class,
                 // Jackson configuration
                 JacksonConfig.class,
                 // JAX-RS resources
-                ApplicationResource.class, GeometryResource.class, IndicatorResource.class, UserResource.class,
+                ApplicationResource.class, GeometryResource.class, IndicatorResource.class, LoginFormResource.class, UserResource.class, 
                 // POJO
                 // Dependencies of resources
                 LogFilter.class
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
new file mode 100644
index 0000000..432c54c
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
@@ -0,0 +1,240 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.rs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import fr.agrometinfo.www.server.dao.QuestionsDao;
+import fr.agrometinfo.www.server.dao.ResponsesDao;
+import fr.agrometinfo.www.server.dao.UserResponsesDao;
+import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.service.MailService;
+import fr.agrometinfo.www.server.service.MailServiceImpl;
+import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.dto.QuestionDTO;
+import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import fr.agrometinfo.www.shared.service.LoginFormService;
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.WebApplicationException;
+import jakarta.ws.rs.core.MediaType;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * @author jdecome
+ *
+ */
+@Log4j2
+@Path(LoginFormService.PATH)
+@RequestScoped
+public class LoginFormResource implements LoginFormService {
+    /**
+     * Convert {@link Question} entity to dto.
+     * @param question entity
+     * @return dto
+     */
+    public static QuestionDTO toDto(final Question question) {
+        final QuestionDTO dto = new QuestionDTO();
+        dto.setQuestionRef(question.getQuestionRef());
+        dto.setCategory(question.getCategory());
+        dto.setFullName(question.getFullName());
+        return dto;
+    }
+    /**
+     * Convert {@link Response} entity to dto.
+     * @param response entity
+     * @return dto
+     */
+    public static ResponseDTO toDto(final Response response) {
+        final ResponseDTO dto = new ResponseDTO();
+        dto.setResponseRef(response.getResponseRef());
+        dto.setQuestion(toDto(response.getQuestion()));
+        dto.setFullname(response.getFullname());
+        return dto;
+    }
+    /**
+     * Convert {@link QuestionDTO} dto to entity.
+     * @param dto question
+     * @return entity
+     */
+    private static Question toEntity(final QuestionDTO dto) {
+        final Question question = new Question();
+        question.setQuestionRef(dto.getQuestionRef());
+        question.setCategory(dto.getCategory());
+        question.setFullName(dto.getFullName());
+        return question;
+    }
+    /**
+     * Convert {@link ResponseDTO} dto to entity.
+     * @param dto response
+     * @return entity
+     */
+    private static Response toEntity(final ResponseDTO dto) {
+        final Response response = new Response();
+        response.setResponseRef(dto.getResponseRef());
+        response.setQuestion(toEntity(dto.getQuestion()));
+        response.setFullname(dto.getFullname());
+        return response;
+    }
+    /**
+     * DAO for questions ({@link QuestionsDao}).
+     */
+    @Inject
+    private QuestionsDao questionsDao;
+    /**
+     * DAO for responses ({@link ResponsesDao}).
+     */
+    @Inject
+    private ResponsesDao responsesDao;
+    /**
+     * DAO for user responses ({@link UserResponsesDao}
+     */
+    @Inject
+    private UserResponsesDao userResponsesDao;
+    /**
+     * Mail service.
+     */
+    @Inject
+    private MailService mailService;
+    /**
+     * Ensure the value of query parameter is not null and not blanck.
+     * @param value value of query parameter to check
+     * @param queryParamName name of query parameter
+     * @throws WebApplicationException if validation failed
+     */
+    private void checkRequired(final Object value, final String queryParamName) {
+        if (value instanceof final String str && str.isBlank() || value == null) {
+            final jakarta.ws.rs.core.Response.Status badRequest = jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
+            throw new WebApplicationException(
+                    jakarta.ws.rs.core.Response.status(badRequest).entity(ErrorResponseDTO.of(
+                            badRequest.getStatusCode(), 
+                            badRequest.getReasonPhrase(), 
+                            queryParamName + " parameter is mandatory")
+                        ).build());
+        }
+    }
+    @GET
+    @Path(LoginFormService.PATH_QUESTIONS_LIST)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Override
+    public List<QuestionDTO> getQuestions() {
+        final List<Question> list = questionsDao.findAll();
+        final List<QuestionDTO> questions = new ArrayList<>();
+        for (Question q : list) {
+            questions.add(toDto(q));
+        }
+        return questions;
+    }
+    @GET
+    @Path(LoginFormService.PATH_QUESTION_BY_CATEGORY)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Override
+    public QuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category) {
+        this.checkRequired(category, "category");
+        final Question question = questionsDao.findByCategory(category);
+        return toDto(question);
+    }
+    @GET
+    @Path(LoginFormService.PATH_RESPONSES_BY_QUESTION_LIST)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Override
+    public List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef) {
+        this.checkRequired(questionRef, "questionRef");
+        if (this.checkIfQuestionExist(questionRef)) {
+            final List<Response> list = responsesDao.findAllByQuestion(questionRef);
+            final List<ResponseDTO> responses = new ArrayList<>();
+            for (final Response r : list) {
+                responses.add(toDto(r));
+            }
+            return responses;
+        }
+        return null;
+        
+    }
+    @GET
+    @Path(LoginFormService.PATH_RESPONSES_LIST)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Override
+    public List<ResponseDTO> getResponses() {
+        final List<Response> list = responsesDao.findAll();
+        final List<ResponseDTO> responses = new ArrayList<>();
+        for (final Response r : list) {
+            responses.add(toDto(r));
+        }
+        return responses;
+    }
+    @POST
+    @Path(LoginFormService.PATH_INSERT_RESPONSE)
+    @Produces(MediaType.TEXT_PLAIN)
+    @Override
+    public void insertResponse(
+            @FormParam(value = "questionRef") final Long questionRef, 
+            @FormParam(value = "responseRef") final String responseRef, 
+            @FormParam(value = "otherText") final String otherText) {
+        this.checkRequired(questionRef, "questionRef");
+        final boolean responseRefIsNull = responseRef == null || responseRef.equals("null");
+        final boolean otherTextIsNull = otherText == null || otherText.equals("null");
+        if (this.checkIfQuestionExist(questionRef)) {
+            if (!responseRefIsNull && otherTextIsNull) {     // réponse prédéfinie
+                this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null);
+            } else if(responseRefIsNull && !otherTextIsNull) {  // réponse libre
+                this.userResponsesDao.insertResponse(questionRef, null, otherText);
+            } else {
+                // Do nothing
+            }
+        }
+    }
+    
+    private boolean checkIfQuestionExist(final Long questionRef) {
+        if (questionRef != null) {
+            // on cherche dans le DAO
+            final Question q = questionsDao.findByRef(questionRef);
+            return (q != null);
+        }
+        return false;
+    }
+    @POST
+    @Path(LoginFormService.PATH_INSERT_RESPONSE_WITH_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    @Override
+    public String insertAllResponses(final LoginFormDataDTO data) {
+        // traitement des réponses prédéfinies
+        for (final QuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
+            // Insérer les réponses associés
+            final Question q = toEntity(dto);
+            final Long qRef = dto.getQuestionRef();
+            List<ResponseDTO> associatedResponses = data.getResponses().stream().filter(f -> f.getQuestion().getQuestionRef() == qRef).collect(Collectors.toList());
+            // On insère les réponses prédéfinies
+            for (final ResponseDTO rDto : associatedResponses) {
+                final Response r = toEntity(rDto); 
+                this.userResponsesDao.insertResponse(q, r, null);
+            }
+
+            // Si il existe une réponse libre, l'insérer
+            if (data.getOtherTextMap().containsKey(dto.getQuestionRef())
+                    && data.getOtherTextMap().get(dto.getQuestionRef()) != null) {
+                this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getQuestionRef()));
+            }
+        }
+        
+        // une fois que tout est inséré, on envoi un mail au support
+        this.mailService.sendLoginFilled(data);
+        return "0";
+    }
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
index f3f025a..c2524f0 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
@@ -1,319 +1,23 @@
 package fr.agrometinfo.www.server.service;
 
-import java.io.Serializable;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-import java.util.StringJoiner;
-
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.core.Appender;
-import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.Layout;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.config.Configuration;
-import org.apache.logging.log4j.core.config.LoggerConfig;
-import org.apache.logging.log4j.core.layout.PatternLayout;
-
-import fr.agrometinfo.www.server.AgroMetInfoConfiguration;
-import fr.agrometinfo.www.server.AgroMetInfoConfiguration.ConfigurationKey;
-import fr.agrometinfo.www.server.exception.AgroMetInfoErrorCategory;
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
-import fr.agrometinfo.www.server.exception.ErrorCategory;
-import fr.agrometinfo.www.server.exception.ErrorType;
-import jakarta.annotation.PostConstruct;
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
-import jakarta.mail.Address;
-import jakarta.mail.Authenticator;
-import jakarta.mail.Message;
-import jakarta.mail.MessagingException;
-import jakarta.mail.PasswordAuthentication;
-import jakarta.mail.SendFailedException;
-import jakarta.mail.Session;
-import jakarta.mail.Transport;
-import jakarta.mail.internet.InternetAddress;
-import jakarta.mail.internet.MimeMessage;
-import lombok.Data;
-import lombok.Getter;
-import lombok.extern.log4j.Log4j2;
-
-/**
- * Service to send e-mails.
- *
- * @author Olivier Maury
- */
-@ApplicationScoped
-@Log4j2
-public class MailService {
-
-    /**
-     * Text message.
-     */
-    @Data
-    public static class Mail {
-        /**
-         * Body.
-         */
-        private String content;
-        /**
-         * Sender's e-mail address.
-         */
-        private String fromAddress;
-        /**
-         * Subject.
-         */
-        private String subject;
-        /**
-         * Recipients' e-mail addresses.
-         */
-        private List<String> toAddresses;
-    }
+import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
 
+public interface MailService {
     /**
-     * Log4j2 appender to send error logs by e-mail.
+     * Sending e-mail.
+     * @param mail object to send
+     * @throws AgroMetInfoException
      */
-    private class MailAppender extends AbstractAppender {
-        /**
-         * Logger name.
-         */
-        private static final String NAME = "MailAppender";
-
-        /**
-         * Constructor.
-         *
-         * @param filter           The Filter to associate with the Appender.
-         * @param layout           The layout to use to format the event.
-         * @param ignoreExceptions If true, exceptions will be logged and suppressed. If
-         *                         false errors will be logged and then passed to the
-         *                         application.
-         */
-        MailAppender(final Filter filter, final Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
-            super(NAME, filter, layout, ignoreExceptions, null);
-
-        }
-
-        @Override
-        public void append(final LogEvent event) {
-            final Mail mail = new Mail();
-            mail.setSubject("error log");
-            mail.setContent(super.toSerializable(event).toString());
-            mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL)));
-            try {
-                send(mail);
-            } catch (final AgroMetInfoException e) {
-                // do nothing
-                LOGGER.info("Cannot send email: {}", e.getMessage());
-            }
-        }
-    }
-
+    void send(Mail mail) throws AgroMetInfoException;
     /**
-     * Keys from messages.properties used to warn about errors.
+     * Send mail when application started.
      */
-    public enum MailErrorType implements ErrorType {
-        /**
-         * Cannot get message details.
-         */
-        DETAILS("01"),
-        /**
-         * Generic exception.
-         */
-        SEND_FAILED("02"),
-        /**
-         * Some e-mail addresses are not valid.
-         */
-        SEND_FAILED_INVALID("03"),
-        /**
-         * Some messages failed to be sent to valid e-mail addresses.
-         */
-        SEND_FAILED_VALID("04");
-
-        /**
-         * Subcode for the error.
-         */
-        @Getter
-        private final String subCode;
-
-        /**
-         * Constructor.
-         *
-         * @param c Subcode for the error.
-         */
-        MailErrorType(final String c) {
-            this.subCode = c;
-        }
-
-        @Override
-        public ErrorCategory getCategory() {
-            return AgroMetInfoErrorCategory.MAIL;
-        }
-
-        @Override
-        public String getName() {
-            return name();
-        }
-
-    }
-
-    /**
-     * Application configuration.
-     */
-    @Inject
-    private AgroMetInfoConfiguration configuration;
-
-    /**
-     * Session created from parameters in context.xml on Tomcat.
-     */
-    private Session session;
-
-    /**
-     * Initialization of session and log appender.
-     */
-    @PostConstruct
-    public void init() {
-        final Properties props = new Properties();
-        props.put("mail.smtp.auth", "true");
-        props.put("mail.smtp.host", configuration.get(ConfigurationKey.SMTP_HOST));
-        props.put("mail.smtp.port", configuration.get(ConfigurationKey.SMTP_PORT));
-        final Authenticator authenticator = new Authenticator() {
-            @Override
-            protected PasswordAuthentication getPasswordAuthentication() {
-                return new PasswordAuthentication(configuration.get(ConfigurationKey.SMTP_USER),
-                        configuration.get(ConfigurationKey.SMTP_PASSWORD));
-            }
-        };
-        session = Session.getInstance(props, authenticator);
-        // Send e-mails for ERROR level messages
-        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
-        final Configuration config = ctx.getConfiguration();
-        final String pattern = """
-                date : %d
-                level : %-5p
-                class : %c#%M()
-                line : %L
-                msg : %m
-                throwable : %throwable
-                """;
-        final PatternLayout layout = PatternLayout.newBuilder() //
-                .withConfiguration(config) //
-                .withPattern(pattern).build();
-        final Appender appender = new MailAppender(null, layout, true);
-        appender.start();
-        config.addAppender(appender);
-        final LoggerConfig loggerConfig = LoggerConfig.newBuilder() //
-                .withAdditivity(false) //
-                .withConfig(config) //
-                .withLevel(Level.ERROR) //
-                .withLoggerName(MailAppender.NAME) //
-                .build();
-        loggerConfig.addAppender(appender, null, null);
-        config.addLogger("fr.agrometinfo", loggerConfig);
-        ctx.updateLoggers();
-    }
-
-    /**
-     * @param mail text message to send
-     * @throws AgroMetInfoException exception
-     */
-    public void send(final Mail mail) throws AgroMetInfoException {
-        send(toMessage(mail));
-    }
-
-    /**
-     * @param message jakarta message to send
-     * @throws AgroMetInfoException exception
-     */
-    private void send(final Message message) throws AgroMetInfoException {
-        final String subject;
-        final String recipients;
-        try {
-            subject = message.getSubject();
-            recipients = toString(message.getAllRecipients());
-        } catch (final MessagingException e) {
-            throw new AgroMetInfoException(MailErrorType.DETAILS, e.getMessage());
-        }
-        try {
-            Transport.send(message);
-        } catch (final MessagingException e) {
-            if (e instanceof final SendFailedException sfe) {
-                final Address[] invalid = sfe.getInvalidAddresses();
-                if (invalid != null) {
-                    throw new AgroMetInfoException(MailErrorType.SEND_FAILED_INVALID, subject, toString(invalid));
-                }
-                final Address[] validUnsent = sfe.getValidUnsentAddresses();
-                if (validUnsent != null) {
-                    throw new AgroMetInfoException(MailErrorType.SEND_FAILED_VALID, subject, toString(validUnsent));
-                }
-            }
-            throw new AgroMetInfoException(MailErrorType.SEND_FAILED, subject, recipients, e.getMessage());
-        }
-    }
-
-    /**
-     * Send an e-mail to log e-mail address at application start.
-     */
-    public void sendApplicationStarted() {
-        final Mail mail = new Mail();
-        mail.setContent("Démarrage de AgroMetInfo-www : " + LocalDateTime.now());
-        mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
-        mail.setSubject("Démarrage");
-        mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL)));
-        try {
-            send(mail);
-        } catch (final AgroMetInfoException e) {
-            LOGGER.info("failed to send e-mail : {}", e);
-        }
-    }
-
-    /**
-     * @param mail text message to convert
-     * @return jakarta converted message
-     * @throws AgroMetInfoException exception
-     */
-    private Message toMessage(final Mail mail) throws AgroMetInfoException {
-        final String environment = configuration.get(ConfigurationKey.ENVIRONMENT);
-        try {
-            final Message message = new MimeMessage(session);
-            if (mail.getFromAddress() == null) {
-                mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
-            }
-            message.setFrom(new InternetAddress(mail.getFromAddress()));
-            final List<InternetAddress> to = new ArrayList<>();
-            if (mail.getToAddresses() == null || mail.getToAddresses().isEmpty()) {
-                mail.setToAddresses(List.of(configuration.get(ConfigurationKey.SUPPORT_EMAIL)));
-            }
-            for (final String addr : mail.getToAddresses()) {
-                to.add(new InternetAddress(addr));
-            }
-            message.setRecipients(Message.RecipientType.TO, to.toArray(InternetAddress[]::new));
-            if ("prod".equals(environment)) {
-                message.setSubject("AgroMetInfo : " + mail.getSubject());
-            } else {
-                message.setSubject("AgroMetInfo " + environment + " : " + mail.getSubject());
-            }
-            final String content = mail.getContent() + "\n-- \n" + configuration.get(ConfigurationKey.APP_URL);
-            message.setContent(content, "text/plain; charset=UTF-8");
-            message.setHeader("X-Environment", environment);
-            return message;
-        } catch (final MessagingException e) {
-            throw new AgroMetInfoException(MailErrorType.DETAILS, e.getLocalizedMessage());
-        }
-    }
-
+    void sendApplicationStarted();
     /**
-     * @param addresses e-mail addresses
-     * @return string representation of addresses
+     * Send mail when user fill login form, with questions and responses.
+     * @param data
      */
-    private String toString(final Address[] addresses) {
-        final StringJoiner sj = new StringJoiner(", ");
-        for (final Address addr : addresses) {
-            sj.add(addr.toString());
-        }
-        return sj.toString();
-    }
+    void sendLoginFilled(LoginFormDataDTO data);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
new file mode 100644
index 0000000..e752839
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -0,0 +1,361 @@
+package fr.agrometinfo.www.server.service;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.Filter;
+import org.apache.logging.log4j.core.Layout;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+
+import fr.agrometinfo.www.server.AgroMetInfoConfiguration;
+import fr.agrometinfo.www.server.AgroMetInfoConfiguration.ConfigurationKey;
+import fr.agrometinfo.www.server.exception.AgroMetInfoErrorCategory;
+import fr.agrometinfo.www.server.exception.AgroMetInfoException;
+import fr.agrometinfo.www.server.exception.ErrorCategory;
+import fr.agrometinfo.www.server.exception.ErrorType;
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import jakarta.annotation.PostConstruct;
+import jakarta.mail.Address;
+import jakarta.mail.Authenticator;
+import jakarta.mail.Message;
+import jakarta.mail.MessagingException;
+import jakarta.mail.PasswordAuthentication;
+import jakarta.mail.SendFailedException;
+import jakarta.mail.Session;
+import jakarta.mail.Transport;
+import jakarta.mail.internet.InternetAddress;
+import jakarta.mail.internet.MimeMessage;
+import lombok.Data;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+
+/**
+ * Service to send e-mails.
+ *
+ * @author Olivier Maury
+ */
+@ApplicationScoped
+@Log4j2
+public class MailServiceImpl implements MailService {
+
+    /**
+     * Text message.
+     */
+    @Data
+    public static class Mail {
+        /**
+         * Body.
+         */
+        private String content;
+        /**
+         * Sender's e-mail address.
+         */
+        private String fromAddress;
+        /**
+         * Subject.
+         */
+        private String subject;
+        /**
+         * Recipients' e-mail addresses.
+         */
+        private List<String> toAddresses;
+    }
+
+    /**
+     * Log4j2 appender to send error logs by e-mail.
+     */
+    private class MailAppender extends AbstractAppender {
+        /**
+         * Logger name.
+         */
+        private static final String NAME = "MailAppender";
+
+        /**
+         * Constructor.
+         *
+         * @param filter           The Filter to associate with the Appender.
+         * @param layout           The layout to use to format the event.
+         * @param ignoreExceptions If true, exceptions will be logged and suppressed. If
+         *                         false errors will be logged and then passed to the
+         *                         application.
+         */
+        MailAppender(final Filter filter, final Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
+            super(NAME, filter, layout, ignoreExceptions, null);
+
+        }
+
+        @Override
+        public void append(final LogEvent event) {
+            Mail mail = new Mail();
+            mail.setFromAddress("agrometinfo@inrae.fr");
+            mail.setSubject("AgroMetInfo - error log");
+            mail.setContent(super.toSerializable(event).toString());
+            mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL)));
+            try {
+                send(mail);
+            } catch (AgroMetInfoException e) {
+                // do nothing
+                LOGGER.info("Cannot send email: {}", e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Keys from messages.properties used to warn about errors.
+     */
+    public enum MailErrorType implements ErrorType {
+        /**
+         * Cannot get message details.
+         */
+        DETAILS("01"),
+        /**
+         * Generic exception.
+         */
+        SEND_FAILED("02"),
+        /**
+         * Some e-mail addresses are not valid.
+         */
+        SEND_FAILED_INVALID("03"),
+        /**
+         * Some messages failed to be sent to valid e-mail addresses.
+         */
+        SEND_FAILED_VALID("04");
+
+        /**
+         * Subcode for the error.
+         */
+        @Getter
+        private final String subCode;
+
+        /**
+         * Constructor.
+         *
+         * @param c Subcode for the error.
+         */
+        MailErrorType(final String c) {
+            this.subCode = c;
+        }
+
+        @Override
+        public ErrorCategory getCategory() {
+            return AgroMetInfoErrorCategory.MAIL;
+        }
+
+        @Override
+        public String getName() {
+            return name();
+        }
+
+    }
+
+    /**
+     * Application configuration.
+     */
+    @Inject
+    private AgroMetInfoConfiguration configuration;
+
+    /**
+     * Session created from parameters in context.xml on Tomcat.
+     */
+    private Session session;
+
+    /**
+     * Initialization of session and log appender.
+     */
+    @PostConstruct
+    public void init() {
+        Properties props = new Properties();
+        props.put("mail.smtp.auth", "true");
+        props.put("mail.smtp.host", configuration.get(ConfigurationKey.SMTP_HOST));
+        props.put("mail.smtp.port", configuration.get(ConfigurationKey.SMTP_PORT));
+        final Authenticator authenticator = new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                return new PasswordAuthentication(configuration.get(ConfigurationKey.SMTP_USER),
+                        configuration.get(ConfigurationKey.SMTP_PASSWORD));
+            }
+        };
+        session = Session.getInstance(props, authenticator);
+        // Send e-mails for ERROR level messages
+        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+        final Configuration config = ctx.getConfiguration();
+        final String pattern = """
+                date : %d
+                level : %-5p
+                class : %c#%M()
+                line : %L
+                msg : %m
+                throwable : %throwable
+                """;
+        final PatternLayout layout = PatternLayout.newBuilder() //
+                .withConfiguration(config) //
+                .withPattern(pattern).build();
+        final Appender appender = new MailAppender(null, layout, true);
+        appender.start();
+        config.addAppender(appender);
+        LoggerConfig loggerConfig = LoggerConfig.newBuilder() //
+                .withAdditivity(false) //
+                .withConfig(config) //
+                .withLevel(Level.ERROR) //
+                .withLoggerName(MailAppender.NAME) //
+                .build();
+        loggerConfig.addAppender(appender, null, null);
+        config.addLogger("fr.agrometinfo", loggerConfig);
+        ctx.updateLoggers();
+    }
+
+    /**
+     * @param mail text message to send
+     * @throws AgroMetInfoException exception
+     */
+    public void send(final Mail mail) throws AgroMetInfoException {
+        send(toMessage(mail));
+    }
+
+    /**
+     * @param message jakarta message to send
+     * @throws AgroMetInfoException exception
+     */
+    private void send(final Message message) throws AgroMetInfoException {
+        final String subject;
+        final String recipients;
+        try {
+            subject = message.getSubject();
+            recipients = toString(message.getAllRecipients());
+        } catch (MessagingException e) {
+            throw new AgroMetInfoException(MailErrorType.DETAILS, e.getMessage());
+        }
+        try {
+            Transport.send(message);
+        } catch (MessagingException e) {
+            if (e instanceof SendFailedException sfe) {
+                final Address[] invalid = sfe.getInvalidAddresses();
+                if (invalid != null) {
+                    throw new AgroMetInfoException(MailErrorType.SEND_FAILED_INVALID, subject, toString(invalid));
+                }
+                final Address[] validUnsent = sfe.getValidUnsentAddresses();
+                if (validUnsent != null) {
+                    throw new AgroMetInfoException(MailErrorType.SEND_FAILED_VALID, subject, toString(validUnsent));
+                }
+            }
+            throw new AgroMetInfoException(MailErrorType.SEND_FAILED, subject, recipients, e.getMessage());
+        }
+    }
+
+    /**
+     * @param mail text message to convert
+     * @return jakarta converted message
+     * @throws AgroMetInfoException exception
+     */
+    private Message toMessage(final Mail mail) throws AgroMetInfoException {
+        final String environment = configuration.get(ConfigurationKey.ENVIRONMENT);
+        try {
+            final Message message = new MimeMessage(session);
+            if (mail.getFromAddress() == null) {
+                mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
+            }
+            message.setFrom(new InternetAddress(mail.getFromAddress()));
+            List<InternetAddress> to = new ArrayList<>();
+            if (mail.getToAddresses() == null || mail.getToAddresses().isEmpty()) {
+                mail.setToAddresses(List.of(configuration.get(ConfigurationKey.SUPPORT_EMAIL)));
+            }
+            for (String addr : mail.getToAddresses()) {
+                to.add(new InternetAddress(addr));
+            }
+            message.setRecipients(Message.RecipientType.TO, to.toArray(InternetAddress[]::new));
+            if ("prod".equals(environment)) {
+                message.setSubject("AgroMetInfo : " + mail.getSubject());
+            } else {
+                message.setSubject("AgroMetInfo " + environment + " : " + mail.getSubject());
+            }
+            final String content = mail.getContent() + "\n-- \n" + configuration.get(ConfigurationKey.APP_URL);
+            message.setContent(content, "text/plain; charset=UTF-8");
+            message.setHeader("X-Environment", environment);
+            return message;
+        } catch (MessagingException e) {
+            throw new AgroMetInfoException(MailErrorType.DETAILS, e.getLocalizedMessage());
+        }
+    }
+
+    /**
+     * @param addresses e-mail addresses
+     * @return string representation of addresses
+     */
+    private String toString(final Address[] addresses) {
+        final StringJoiner sj = new StringJoiner(", ");
+        for (Address addr : addresses) {
+            sj.add(addr.toString());
+        }
+        return sj.toString();
+    }
+
+    /**
+     * Send an e-mail to log e-mail address at application start.
+     */
+    public void sendApplicationStarted() {
+        if (configuration.get(ConfigurationKey.ENVIRONMENT).equals("dev")) {
+            LOGGER.trace("Environment is dev, e-mail isn't send");
+            return;
+        }
+        Mail mail = new Mail();
+        mail.setContent("Démarrage de AgroMetInfo-www : " + LocalDateTime.now() + "\n-- \n"
+                + configuration.get(ConfigurationKey.APP_URL));
+        mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
+        mail.setSubject("Démarrage");
+        mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL)));
+        try {
+            send(mail);
+        } catch (AgroMetInfoException e) {
+            LOGGER.info("failed to send e-mail : {}", e);
+        }
+    }
+    public void sendLoginFilled(final LoginFormDataDTO data) {
+        final Mail mail = createContentFromData(data);
+        mail.setSubject("Un utilisateur a rempli le formulaire d'enquête");
+        mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
+        mail.setToAddresses(List.of(configuration.get(ConfigurationKey.LOG_EMAIL)));
+        try {
+            send(mail);
+        } catch (AgroMetInfoException e) {
+            // en cas d'erreur, on ne fait rien de plus.
+            LOGGER.trace("failed to send e-mail");
+        }
+        
+    }//*/
+    public static Mail createContentFromData(final LoginFormDataDTO data) {
+        final Mail mail = new Mail();
+        
+        final StringBuilder builder = new StringBuilder();
+        builder.append("Bonjour,\nUn utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\n\n");
+        builder.append("Il a répondu à " + data.getQuestions().size() + " question(s) :\n");
+        
+        data.getQuestions().forEach((q) -> {
+            builder.append("\t• « " + q.getFullName() + " » :\n");
+            data.getResponses().stream().filter(f -> f.getQuestion().getQuestionRef() == q.getQuestionRef())
+                .collect(Collectors.toList())
+                .forEach((r) -> builder.append("\t\t- " + r.getFullname() + "\n"));
+            
+            if (data.getOtherTextMap().containsKey(q.getQuestionRef()) && data.getOtherTextMap().get(q.getQuestionRef()) != null) {
+                builder.append("\t\t- Réponse libre : « " + data.getOtherTextMap().get(q.getQuestionRef()) + " »\n");
+            }
+            builder.append("\n");
+        });
+        mail.setContent(builder.toString());
+        return mail;
+    }
+}
diff --git a/www-server/src/main/webapp/index.html b/www-server/src/main/webapp/index.html
index 5498ba6..bbf735a 100644
--- a/www-server/src/main/webapp/index.html
+++ b/www-server/src/main/webapp/index.html
@@ -24,11 +24,11 @@
         <!--                                                               -->
         <!-- Consider inlining CSS to reduce the number of requested files -->
         <!--                                                               -->
-        <link type="text/css" rel="stylesheet" href="app/style.css">
         <link type="text/css" rel="stylesheet" href="app/vendors/ol/ol.css">
         <link type="text/css" rel="stylesheet" href="app/vendors/ol/ol-layerswitcher.css">
         <link type="text/css" rel="stylesheet" href="app/css/domino-ui.css">
         <link type="text/css" rel="stylesheet" href="app/css/themes/all-themes.min.css">
+        <link type="text/css" rel="stylesheet" href="app/style.css">
 
         <!--                                           -->
         <!-- Any title is fine                         -->
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
new file mode 100644
index 0000000..eba1d30
--- /dev/null
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
@@ -0,0 +1,192 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import org.junit.jupiter.api.Test;
+
+import fr.agrometinfo.www.server.dao.QuestionsDao;
+import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
+import fr.agrometinfo.www.server.dao.ResponsesDao;
+import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.UserResponsesDao;
+import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
+import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserResponses;
+
+/**
+ * Test of UserResponsesDaoHibernate.
+ * @author jdecome
+ *
+ */
+public class LoginFormHibernateTest {
+    /** DAO for user's responses. */
+    private UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
+    /** DAO for questions. */
+    private QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    /** DAO for responses. */
+    private ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    
+    @Test
+    public void getQuestion() {
+        final List<Question> list = questionsDao.findAll();
+        assertNotEquals(list.size(), 0, "La liste de questions est vide");
+        for (final Question q : list) {
+            // recherche de la question par sa référence
+            final Question foundedByRef = questionsDao.findByRef(q.getQuestionRef());
+            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getQuestionRef() + " est null");
+            assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getQuestionRef() + " ne correspond pas");
+            
+            // recherche de la question par sa catégorie
+            final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
+            assertNotNull(foundedByCategory, "La question correspondante à la catégorie « " + q.getCategory() + " » est null");
+            assertEquals(foundedByCategory, q, "La question correspondante à la catégorie « " + q.getCategory() + " » est vide");
+        }
+    }
+    
+    @Test
+    public void getResponses() {
+        final List<Question> questions = questionsDao.findAll();
+        final List<Response> responses = responsesDao.findAll();
+        assertNotEquals(responses.size(), 0, "La liste de réponses est vide");
+        for (final Response r : responses) {
+            assertTrue(questions.contains(r.getQuestion()), "La liste de questions ne contient pas la question " + r.getQuestion().getQuestionRef());
+            
+            // Test à partir de la question correspondante à la réponse
+            final Question q = r.getQuestion();
+            assertNotNull(q, "La question correspondante à la réponse " + r.getResponseRef() + " est null");
+            
+            final Question foundedByRef = questionsDao.findByRef(q.getQuestionRef());
+            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getQuestionRef() + " est null");
+            assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getQuestionRef() + " ne correspond pas");
+            
+            final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
+            assertNotNull(foundedByCategory, "La question correspondante à la catégorie « " + q.getCategory() + " » est null");
+            assertEquals(foundedByCategory, q, "La question correspondante à la catégorie « " + q.getCategory() + " » est vide");
+            
+            // recherche de la réponse par sa référence
+            final Response responseByRef = responsesDao.findByRef(r.getResponseRef());
+            assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getResponseRef() + " est null");
+            assertEquals(responseByRef, r, "La réponse correspondante à la réponse " + r.getResponseRef() + " est vide");
+        }
+    }
+    
+    @Test
+    public void setUserResponses() {
+        final Question profession = questionsDao.findByCategory("profession");
+        assertNotNull(profession, "Aucune question ne correspond à la catégorie « profession »");
+        List<Response> responses = responsesDao.findAllByQuestion(profession.getQuestionRef());
+        assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « profession »");
+        assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « profession » est vide");
+        for (final Response r : responses) {
+            assertEquals(r.getQuestion(), profession, "La question de la réponse ne correspond pas avec la question d'origine");
+        }
+        
+        // Insertion d'une réponse
+        final List<Response> listOfResponses = new ArrayList<>();   // liste des réponses insérées
+        Response r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
+        userResponsesDao.insertResponse(profession, r, null);
+        listOfResponses.add(r);
+        
+        List<UserResponses> list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
+        assertEquals(list.get(0).getQuestion(), profession, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
+        assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
+        assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
+        
+        // Insertion de plusieurs réponses
+        final int nbToInsert = 3;
+        for (final Response res : pickNRandom(responses, nbToInsert)) {
+            userResponsesDao.insertResponse(profession, res, null);
+            listOfResponses.add(res);
+        }
+        list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        assertEquals(list.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + " + nbToInsert + " réponses supplémentaires");
+        for (final UserResponses ur : list) {
+            assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question");
+            assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
+            assertEquals(ur.getResponse().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+            assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+        }
+
+        final int newNbResponses = 1 + nbToInsert;
+        
+        // Insertion d'une réponse à choix libre
+        String other = "Ceci est une réponse libre";
+        userResponsesDao.insertResponse(profession, null, other);
+        
+        list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        assertEquals(list.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les " + newNbResponses + " + la réponse libre");
+        for (final UserResponses ur : list) {
+            assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
+            if (ur.getOtherText() != null) {    // on est sur une réponse libre
+                assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
+                assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
+            } else {
+                assertEquals(ur.getResponse().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            }
+        }
+        
+        // Essai de l'insertion par les références, avec une autre question
+        final Question useCase = questionsDao.findByCategory("useCase");
+        assertNotNull(useCase, "Aucune question ne correspond à la catégorie « useCase »");
+        responses = responsesDao.findAllByQuestion(useCase.getQuestionRef());
+        assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « useCase »");
+        assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « useCase » est vide");
+        
+        r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
+        userResponsesDao.insertResponse(useCase.getQuestionRef(), r.getResponseRef(), null);
+        
+        list = userResponsesDao.findAllByQuestion(useCase.getQuestionRef());
+        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir un élément");
+        assertEquals(list.get(0).getQuestion(), useCase, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
+        assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
+        assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
+        
+        other = other.concat(" pour la question « useCase »");
+        userResponsesDao.insertResponse(useCase.getQuestionRef(), null, other);
+        
+        list = userResponsesDao.findAllByQuestion(useCase.getQuestionRef());
+        assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir deux éléments");
+        for (final UserResponses ur : list) {
+            assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
+            if (ur.getOtherText() != null) {    // on est sur une réponse libre
+                assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
+                assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
+            } else {
+                assertEquals(ur.getResponse().getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            }
+        }
+        
+        
+    }
+    /**
+     * Return randomly responses list
+     * @param list original list
+     * @param nbElements to pick
+     * @return
+     */
+    private List<Response> pickNRandom(final List<Response> list, final int nbElements) {
+        final List<Response> copy = new ArrayList<Response>(list);
+        Collections.shuffle(copy);
+        if (nbElements > copy.size()) {
+            return copy.subList(0, copy.size());
+        } else {
+            return copy.subList(0, nbElements);
+        }
+    }
+}
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/exception/ErrorTypeTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/exception/ErrorTypeTest.java
index abbdc2a..678149f 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/exception/ErrorTypeTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/exception/ErrorTypeTest.java
@@ -15,7 +15,7 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 
 import fr.agrometinfo.www.server.I18n;
-import fr.agrometinfo.www.server.service.MailService;
+import fr.agrometinfo.www.server.service.MailServiceImpl;
 
 /**
  * Ensure all implementations of {@link ErrorType} are well defined.
@@ -33,7 +33,7 @@ public class ErrorTypeTest {
      * @return classes to test.
      */
     public static List<Class<? extends Enum<?>>> data() {
-        return Arrays.asList(MailService.MailErrorType.class);
+        return Arrays.asList(MailServiceImpl.MailErrorType.class);
     }
 
     /**
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
new file mode 100644
index 0000000..e0d1d75
--- /dev/null
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
@@ -0,0 +1,368 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.server.rs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Test;
+
+
+import fr.agrometinfo.www.server.dao.QuestionsDao;
+import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
+import fr.agrometinfo.www.server.dao.ResponsesDao;
+import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.UserResponsesDao;
+import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
+import fr.agrometinfo.www.server.exception.AgroMetInfoException;
+import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserResponses;
+import fr.agrometinfo.www.server.service.MailService;
+import fr.agrometinfo.www.server.service.MailServiceImpl;
+import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.dto.QuestionDTO;
+import fr.agrometinfo.www.shared.dto.ResponseDTO;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Form;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * Test login form webservice.
+ * @author jdecome
+ *
+ */
+@Log4j2
+public class LoginFormResourceTest extends JerseyTest {
+    /**
+     * Mock class for mail service.
+     * @author jdecome
+     *
+     */
+    public class MailServiceTest implements MailService {
+        /** Mail object. */
+        @Getter
+        private Mail mail = null;
+        @Override
+        public void sendLoginFilled(LoginFormDataDTO data) {
+            this.mail = MailServiceImpl.createContentFromData(data);
+        }
+        
+        @Override
+        public void sendApplicationStarted() {
+            // do nothing
+        }
+        
+        @Override
+        public void send(Mail mail) throws AgroMetInfoException {
+            // do nothing
+        }
+        
+    };
+    /** Path separator. */
+    private static final String SEP = "/";
+    /** Parsing JSON to object. */
+    private final ObjectMapper objectMapper = new ObjectMapper();
+    /** DAO for Questions. */
+    private final QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    /** DAO for Responses. */
+    private final ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    /** DAO for UserResponses. */
+    private final UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
+    /** Mail service. */
+    private final MailService mailService = new MailServiceTest();
+    
+    @Override
+    protected final Application configure() {
+        return new ResourceConfig(LoginFormResource.class).register(new AbstractBinder() {
+            @Override
+            protected void configure() {
+                bind(questionsDao).to(QuestionsDao.class);
+                bind(responsesDao).to(ResponsesDao.class);
+                bind(userResponsesDao).to(UserResponsesDao.class);
+                bind(mailService).to(MailService.class);
+            }
+        });
+    }
+    /** Test de la récupération des réponses. */
+    @Test
+    public void getResponses() {
+        // On récupère les questions via le webservice
+        final String jsonQuestions = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTIONS_LIST).request().get(String.class);
+        assertNotNull(jsonQuestions, "Impossible de récupérer la ressource (objet null) pour les questions");
+        assertFalse(jsonQuestions.isEmpty(), "La chaine de caractère JSON des questions est vide");
+        try {
+            final List<Question> questions = objectMapper.readValue(jsonQuestions, new TypeReference<List<Question>>(){});
+            assertNotNull(questions, "La conversion du JSON des questions en liste a échoué");
+            assertNotEquals(questions.size(), 0, "La liste de questions est vide");
+            
+            // On récupère l'ensemble des réponses
+            final String jsonAllResponses = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_LIST).request().get(String.class);
+            assertNotNull(jsonAllResponses, "Impossible de récupérer la ressource (objet null) des réponses");
+            assertFalse(jsonAllResponses.isEmpty(), "La chaine de caractère JSON des réponses est vide");
+            
+            final List<Response> allResponses = objectMapper.readValue(jsonAllResponses, new TypeReference<List<Response>>() {});
+            assertNotNull(allResponses, "La conversion du JSON des réponses en liste a échoué");
+            assertNotEquals(allResponses.size(), 0, "La liste de réponses est vide");
+            
+            for (final Response r : allResponses) {
+                assertTrue(questions.contains(r.getQuestion()), "La question de la réponse « " + r.getFullname() + " » n'est pas contenue dans la liste des questions");
+            }
+            
+            // Pour chaque question, 
+            for (final Question q : questions) {
+                // on vérifie que la question est retrouvable par la catégorie, via le webservice
+                final String jsonCategory = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTION_BY_CATEGORY)
+                                                .queryParam("category", q.getCategory())
+                                                .request().get(String.class);
+                
+                assertNotNull(jsonCategory, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question n°" + q.getQuestionRef());
+                assertFalse(jsonCategory.isEmpty(), "La chaine de caractère JSON de la catégorie pour la question n° " + q.getQuestionRef() + " est vide");
+                
+                final Question questionByCategory = objectMapper.readValue(jsonCategory, new TypeReference<Question>() {});
+                assertNotNull(questionByCategory, "La conversion du JSON de la catégorie pour la question n° " + q.getQuestionRef() + " n'a pas fonctionnée");
+                assertEquals(questionByCategory, q, "La question lue par la catégorie ne correspond pas avec la question n° " + q.getQuestionRef() + "d'origine");
+                
+                // On vérifie, via le DAO
+                final Question questionByDao = questionsDao.findByCategory(q.getCategory());
+                assertNotNull(questionByDao, "La lecture de la question n° " + q.getQuestionRef() + " via le DAO n'a pas fonctionnée");
+                assertEquals(questionByDao, q, "La question lue par le DAO ne correspond pas avec la question n° " + q.getQuestionRef() + "d'origine");
+                
+                // on récupère les réponses associées via le webservice
+                final String jsonResponses = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
+                                                .queryParam("questionRef", String.valueOf(q.getQuestionRef()))
+                                                .request()
+                                                .get(String.class);
+                
+                assertNotNull(jsonResponses, "Impossible de récupérer la ressource (objet null)  pour la question n° " + q.getQuestionRef() + " est vide");
+                assertFalse(jsonResponses.isEmpty(), "La chaine de caractère JSON pour la question n° " + q.getQuestionRef() + " est vide");
+                
+                final List<Response> responses = objectMapper.readValue(jsonResponses, new TypeReference<List<Response>>() {});
+                assertNotNull(responses, "La conversion du JSON des réponses de la question n° " + q.getQuestionRef() + " n'a pas fonctionnée");
+                assertNotEquals(responses.size(), 0, " La liste de réponses pour la question n° " + q.getQuestionRef() + " est vide");
+                
+                final List<Response> responsesByDao = responsesDao.findAllByQuestion(q.getQuestionRef());
+                assertNotNull(responsesByDao, " La lecture des réponses pour la question n° " + q.getQuestionRef() + " via le DAO n'a pas fonctionné");
+                assertNotEquals(responsesByDao.size(), 0, " La liste de réponses via le DAO pour la question n° " + q.getQuestionRef() + " est vide");
+                assertEquals(responses, responsesByDao, "La liste de réponses via le webservice n'est pas identique avec la liste via le DAO");
+                
+                for (final Response r : responses) {
+                    assertNotNull(r.getQuestion(), "La question de la réponse n° " + r.getResponseRef() + " est null");
+                    assertEquals(r.getQuestion(), q, "La question de la réponse n° " + r.getResponseRef() + " ne correspond pas à la question");
+                    assertTrue(responsesByDao.contains(r), "La liste via le DAO ne contient pas la réponse n° " + r.getResponseRef());
+                }
+                
+            }
+        } catch (final JsonProcessingException e) {
+            log.info(e.getMessage());
+        }
+    }
+    /** Test de l'écriture en base. */
+    @Test
+    public void setUserResponses() {
+         this.resetUserResponses();
+         final String category = "origin";
+         // Question
+         final String jsonOrigins = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTION_BY_CATEGORY)
+                                         .queryParam("category", category)
+                                        .request().get(String.class);
+         assertNotNull(jsonOrigins, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question de l'origine");
+         assertFalse(jsonOrigins.isEmpty(), "La chaine de caractère JSON pour la question de catégorie « " + category + " » est vide");
+         try {
+            final Question origin = objectMapper.readValue(jsonOrigins, new TypeReference<Question>() {});
+            assertEquals(origin, questionsDao.findByCategory(category), "La question lue dans le webservice ne correspond pas avec la question via le DAO");
+            
+            // on récupère les réponses de la question
+            final List<Response> responses = objectMapper.readValue(target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
+                    .queryParam("questionRef", String.valueOf(origin.getQuestionRef()))
+                    .request()
+                    .get(String.class), new TypeReference<List<Response>>() {});
+            
+            assertNotNull(responses, "La conversion du JSON des réponses de la question « " + origin.getCategory() + " » n'a pas fonctionnée");
+            assertNotEquals(responses.size(), " La liste de réponses pour la question " + origin.getCategory() + " est vide");
+            for (final Response r : responses) {
+                assertEquals(r.getQuestion(), origin, "La question de la réponse ne correspond pas avec la question d'origine");
+            }
+            
+            final List<Response> listOfResponses = new ArrayList<>();   // liste des réponses insérées
+            // Insertion d'une réponse prédéfénie
+            Response r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
+            final Form form = new Form();
+            form.param("questionRef", String.valueOf(origin.getQuestionRef()));
+            form.param("responseRef", String.valueOf(r.getResponseRef()));
+            form.param("otherText", "null");
+            
+            target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+                .request(MediaType.TEXT_PLAIN)
+                .post(Entity.form(form));
+            listOfResponses.add(r);
+            
+            // On lit la réponse insérée, via le DAO
+            List<UserResponses> userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            assertEquals(userResponsesList.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
+            assertEquals(userResponsesList.get(0).getQuestion(), origin, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
+            assertEquals(userResponsesList.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
+            assertNull(userResponsesList.get(0).getOtherText(), "Le texte libre doit être null");
+            
+            // insertion de plusieurs réponses
+            final int nbToInsert = 3;
+            final List<Response> randoms = this.pickNRandom(responses, nbToInsert);
+            for (final Response res : randoms) {
+                final Form f = new Form();
+                f.param("questionRef", String.valueOf(origin.getQuestionRef()));
+                f.param("responseRef", String.valueOf(res.getResponseRef()));
+                f.param("otherText", "null");
+                
+                target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+                .request(MediaType.TEXT_PLAIN)
+                .post(Entity.form(f));
+                
+                listOfResponses.add(res);
+            }
+            
+            // Vérification via le DAO
+            userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            assertEquals(userResponsesList.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + "
+                                                                        + nbToInsert + " réponses supplémentaires");
+            for (final UserResponses ur : userResponsesList) {
+                assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question");
+                assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
+                assertEquals(ur.getResponse().getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            }
+            
+            final int newNbResponses = 1 + nbToInsert;
+            
+            // insertion d'une réponse à choix libre
+            final String other = "Ceci est une réponse libre";
+            final Form formOther = new Form();
+            formOther.param("questionRef", String.valueOf(origin.getQuestionRef()));
+            formOther.param("responseRef", "null");
+            formOther.param("otherText", other);
+            
+            target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+            .request(MediaType.TEXT_PLAIN)
+            .post(Entity.form(formOther));
+            
+            userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            assertEquals(userResponsesList.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les "
+                                    + newNbResponses + " réponses + la réponse libre");
+            for (final UserResponses ur : userResponsesList) {
+                assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
+                if (ur.getOtherText() != null) {    // on est sur une réponse libre
+                    assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
+                    assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
+                } else {
+                    assertEquals(ur.getResponse().getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                    assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+                }
+            }
+        } catch (JsonProcessingException e) {
+            log.info(e.getMessage());
+        }
+    }
+    /** Test en définissant les réponses pour les questions. */
+    @Test
+    public void testWithJsonResponses() {
+        this.resetUserResponses();
+        final List<Question> questionsList = questionsDao.findAll();
+        final List<QuestionDTO> questionsDTO = questionsList.stream().map(LoginFormResource::toDto).toList();
+        
+        final List<Response> responsesList = new ArrayList<>();
+        final HashMap<Long, String> otherTextMap = new HashMap<>();
+        
+        for (final Question q : questionsList) {
+            final List<Response> responsesListForQuestion = responsesDao.findAllByQuestion(q.getQuestionRef());
+            
+            // on récupère un nombre aléatoire de réponse
+            final List<Response> randomList = this.pickNRandom(responsesListForQuestion, ThreadLocalRandom.current().nextInt(0, responsesListForQuestion.size() - 1));
+           
+            // si il y a au moins une réponse récupérée aléatoirement
+            if (randomList.size() > 0) {
+                responsesList.addAll(randomList);
+            }
+            
+            otherTextMap.put(q.getQuestionRef(), "Réponse libre pour la question « " + q.getFullName() + " »");
+        }
+        final List<ResponseDTO> responsesDTO = responsesList.stream().map(LoginFormResource::toDto).toList();
+        
+        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDTO, responsesDTO, otherTextMap);
+        assertEquals(data.getQuestions(), questionsDTO, "La liste de questions dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
+        assertEquals(data.getResponses(), responsesDTO, "La liste de réponses dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
+        assertEquals(data.getOtherTextMap(), otherTextMap, "La liste de réponses libre dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
+        
+        
+        jakarta.ws.rs.core.Response ret = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE_WITH_JSON)
+                .request(MediaType.APPLICATION_JSON).post(Entity.entity(data, MediaType.APPLICATION_JSON));
+        
+        assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "Le point d'entrée d'insertion doit retourner le code status 200 (OK)");
+        
+        // Pour le test, vérifier, pour chaque question, les réponses via le DAO
+        for (final Question q : questionsDao.findAll()) {
+            final List<UserResponses> list = userResponsesDao.findAllByQuestion(q.getQuestionRef());
+            assertNotNull(list, "La liste de réponses de l'utilisateur ne doit pas être null");
+            for (final UserResponses ur : list) {
+                assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
+                assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getFullName() + " »");
+                assertTrue(ur.getUserResponseRef() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
+                if (ur.getResponse() != null) {
+                    assertNotNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+                    assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
+                } else {
+                    assertNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
+                    assertNotNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+                }
+            }
+        }
+        
+        // on vérifie le mail
+        final Mail mail = ((MailServiceTest) this.mailService).getMail();
+        assertNotNull(mail, "L'objet mail ne doit pas être null");
+        assertNotNull(mail.getContent(), "Le corps du message n'est pas défini (= null)");
+        assertFalse(mail.getContent().isEmpty(), "Le corps du message est vide");
+    }
+    /**
+     * 
+     * @param list origin list
+     * @param nbElements to pick in list
+     * @return randomized list with nbElements
+     */
+    private List<Response> pickNRandom(final List<Response> list, final int nbElements) {
+        final List<Response> copy = new ArrayList<Response>(list);
+        Collections.shuffle(copy);
+        if (nbElements > copy.size()) {
+            return copy.subList(0, copy.size());
+        } else {
+            return copy.subList(0, nbElements);
+        }
+    }
+    /**
+     * Reset user2responses table.
+     */
+    private void resetUserResponses() {
+        this.userResponsesDao.deleteAll();
+    }
+}
diff --git a/www-server/src/test/resources/META-INF/persistence.xml b/www-server/src/test/resources/META-INF/persistence.xml
index 55e45d5..e0622b5 100644
--- a/www-server/src/test/resources/META-INF/persistence.xml
+++ b/www-server/src/test/resources/META-INF/persistence.xml
@@ -18,6 +18,9 @@
     <class>fr.agrometinfo.www.server.model.PraDailyValue</class>
     <class>fr.agrometinfo.www.server.model.Region</class>
     <class>fr.agrometinfo.www.server.model.Simulation</class>
+    <class>fr.agrometinfo.www.server.model.Question</class>
+    <class>fr.agrometinfo.www.server.model.Response</class>
+    <class>fr.agrometinfo.www.server.model.UserResponses</class>
     <properties>
       <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:agrometinfo;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM '../sql/schema.types.h2.sql'\;RUNSCRIPT FROM '../sql/schema.tables.sql'\;RUNSCRIPT FROM '../sql/init_data.h2.sql';" />
       <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
new file mode 100644
index 0000000..0d2cefc
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
@@ -0,0 +1,84 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.shared.dto;
+
+import java.util.HashMap;
+import java.util.List;
+
+
+/**
+ * Class contains all data of login form.
+ * @author jdecome
+ *
+ */
+public class LoginFormDataDTO {
+    /**
+     * List of login form questions.<br>
+     * This list questions contains available questions, obtained by {@link QuestionsDao}
+     */
+    private List<QuestionDTO> questions;
+    /**
+     * List of login form responses.<br>
+     * This list responses contains available responses, obtained by {@link ResponsesDao}
+     */
+    private List<ResponseDTO> responses;
+    /**
+     * Storage, for each response, the other text, if specified.
+     */
+    private HashMap<Long, String> otherTextMap = new HashMap<>();
+    /**
+     * Default constructor.
+     */
+    public LoginFormDataDTO() {
+        
+    }
+    /**
+     * Constructor.
+     * @param qList questions list
+     * @param rList responses list
+     * @param otherText otherText map
+     */
+    public LoginFormDataDTO(final List<QuestionDTO> qList, final List<ResponseDTO> rList, HashMap<Long, String> otherText) {
+        this.questions = qList;
+        this.responses = rList;
+        this.otherTextMap = otherText;
+    }
+    /**
+     * @return the questions
+     */
+    public List<QuestionDTO> getQuestions() {
+        return questions;
+    }
+    /**
+     * @return the responses
+     */
+    public List<ResponseDTO> getResponses() {
+        return responses;
+    }
+    /**
+     * @return the otherTextMap
+     */
+    public HashMap<Long, String> getOtherTextMap() {
+        return otherTextMap;
+    }
+    /**
+     * @param questions the questions to set
+     */
+    public void setQuestions(List<QuestionDTO> questions) {
+        this.questions = questions;
+    }
+    /**
+     * @param responses the responses to set
+     */
+    public void setResponses(List<ResponseDTO> responses) {
+        this.responses = responses;
+    }
+    /**
+     * @param otherTextMap the otherTextMap to set
+     */
+    public void setOtherTextMap(HashMap<Long, String> otherTextMap) {
+        this.otherTextMap = otherTextMap;
+    }
+    
+}
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
new file mode 100644
index 0000000..3028802
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
@@ -0,0 +1,85 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.shared.dto;
+
+import java.util.Objects;
+
+import org.dominokit.jackson.annotation.JSONMapper;
+
+/**
+ * DTO for questions of login form.
+ * @author jdecome
+ *
+ */
+@JSONMapper
+public class QuestionDTO {
+    /**
+     * Ref of question.
+     */
+    private long questionRef;
+    /**
+     * Category of question.
+     */
+    private String category;
+    /**
+     * Label of question.
+     */
+    private String fullName;
+    /**
+     * @return the questionRef
+     */
+    public long getQuestionRef() {
+        return questionRef;
+    }
+    /**
+     * @return the category
+     */
+    public String getCategory() {
+        return category;
+    }
+    /**
+     * @return the fullName
+     */
+    public String getFullName() {
+        return fullName;
+    }
+    /**
+     * @param questionRef the questionRef to set
+     */
+    public void setQuestionRef(final long questionRef) {
+        this.questionRef = questionRef;
+    }
+    /**
+     * @param category the category to set
+     */
+    public void setCategory(final String category) {
+        this.category = category;
+    }
+    /**
+     * @param fullName the fullName to set
+     */
+    public void setFullName(final String fullName) {
+        this.fullName = fullName;
+    }
+    @Override
+    public int hashCode() {
+        return Objects.hash(category, fullName, questionRef);
+    }
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        QuestionDTO other = (QuestionDTO) obj;
+        return Objects.equals(category, other.category) && Objects.equals(fullName, other.fullName)
+                && questionRef == other.questionRef;
+    }
+    
+}
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
new file mode 100644
index 0000000..f52837f
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
@@ -0,0 +1,64 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.shared.dto;
+
+import org.dominokit.jackson.annotation.JSONMapper;
+
+/**
+ *  DTO for responses of login form.
+ * @author jdecome
+ *
+ */
+@JSONMapper
+public class ResponseDTO {
+    /**
+     * Ref of this response.
+     */
+    private long responseRef;
+    /**
+     * DTO of question associated to response.
+     */
+    private QuestionDTO question;
+    /**
+     * Label of response.
+     */
+    private String fullname;
+    /**
+     * @return the responseRef
+     */
+    public long getResponseRef() {
+        return responseRef;
+    }
+    /**
+     * @return the question
+     */
+    public QuestionDTO getQuestion() {
+        return question;
+    }
+    /**
+     * @return the fullname
+     */
+    public String getFullname() {
+        return fullname;
+    }
+    /**
+     * @param responseRef the responseRef to set
+     */
+    public void setResponseRef(final long responseRef) {
+        this.responseRef = responseRef;
+    }
+    /**
+     * @param question the question to set
+     */
+    public void setQuestion(final QuestionDTO question) {
+        this.question = question;
+    }
+    /**
+     * @param fullname the fullname to set
+     */
+    public void setFullname(final String fullname) {
+        this.fullname = fullname;
+    }
+    
+}
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
new file mode 100644
index 0000000..d226069
--- /dev/null
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
@@ -0,0 +1,92 @@
+/**
+ * 
+ */
+package fr.agrometinfo.www.shared.service;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.dominokit.rest.shared.request.service.annotations.RequestFactory;
+import org.dominokit.rest.shared.request.service.annotations.RequestBody;
+
+import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.dto.QuestionDTO;
+import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import jakarta.ws.rs.DefaultValue;
+import jakarta.ws.rs.FormParam;
+import jakarta.ws.rs.QueryParam;
+
+/**
+ * @author jdecome
+ *
+ */
+@RequestFactory
+@Path(LoginFormService.PATH)
+public interface LoginFormService {
+    /** Service base path. */
+    public static final String PATH = "login";
+    /** Path for {@link LoginFormService#getQuestions()}. */
+    public static final String PATH_QUESTIONS_LIST = "questions";
+    /**Path for {@link LoginFormService#getQuestionByCategory(String)}. */
+    public static final String PATH_QUESTION_BY_CATEGORY = "question";
+    /** Path for {@link LoginFormService#getResponses}. */
+    public static final String PATH_RESPONSES_LIST = "responses";
+    /** Path for {@link LoginFormService#getResponsesByQuestion(long)}. */
+    public static final String PATH_RESPONSES_BY_QUESTION_LIST = "responsesByQuestion";
+    /** Path for {@link LoginFormService#getResponses()}. */
+    public static final String PATH_INSERT_RESPONSE = "insert";
+    /** Path for {@link LoginFormService#insertResponse(String)}. */
+    public static final String PATH_INSERT_RESPONSE_WITH_JSON = "insertMultipleValues";
+    /**
+     * @return list of questions.
+     */
+    @GET
+    @Path(PATH_QUESTIONS_LIST)
+    List<QuestionDTO> getQuestions();
+    @GET
+    @Path(PATH_RESPONSES_LIST)
+    List<ResponseDTO> getResponses();
+    /**
+     * Get question by his category.
+     * @param category of the question
+     * @return question
+     */
+    @GET
+    @Path(PATH_QUESTION_BY_CATEGORY)
+    QuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category);
+    /**
+     * Returning the list of possible response for one question.
+     * @param questionRef ref of question
+     * @return list of question's responses
+     */
+    @GET
+    @Path(PATH_RESPONSES_BY_QUESTION_LIST)
+    List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef);
+    /**
+     * Insert response or text for specified question.<br>
+     * If responseRef is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code responseRef} must be null. 
+     * @param questionRef ref of question
+     * @param responseRef ref of response or « null » String for null value
+     * @param otherText text if response is other
+     * 
+     * @return
+     */
+    @POST
+    @Path(PATH_INSERT_RESPONSE)
+    void insertResponse(
+            @FormParam(value = "questionRef") final Long questionRef, 
+            @FormParam(value = "responseRef") @DefaultValue("null") final String responseRef, 
+            @FormParam(value = "otherText") @DefaultValue("null") final String otherText
+    );
+    /**
+     * Insert all responses of login form.
+     * @param responsesList
+     */
+    @POST
+    @Path(PATH_INSERT_RESPONSE_WITH_JSON)
+    String insertAllResponses(@RequestBody final LoginFormDataDTO data);
+}
-- 
GitLab


From 6451a7662f3c67e6aa2a245f6fc693f09b4acc02 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 23 May 2024 12:10:41 +0200
Subject: [PATCH 02/30] Corrections erreurs PMD. fixes #5

---
 .../java/fr/agrometinfo/www/client/App.java   |  1 -
 .../www/client/i18n/AppConstants.java         |  2 -
 .../www/client/view/LayoutView.java           |  2 +
 .../www/client/view/LoginView.java            | 13 ++---
 .../www/server/dao/DaoHibernate.java          |  2 +-
 .../www/server/dao/QuestionsDaoHibernate.java |  1 -
 .../www/server/model/LoginFormData.java       | 50 -------------------
 .../www/server/model/Response.java            |  2 -
 .../www/server/model/UserResponses.java       |  4 --
 .../www/server/rs/LoginFormResource.java      | 10 +---
 .../www/shared/service/LoginFormService.java  | 14 +++---
 11 files changed, 15 insertions(+), 86 deletions(-)
 delete mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/App.java b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
index d1895a5..37dd573 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/App.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
@@ -82,7 +82,6 @@ public class App implements EntryPoint {
     public static EventBus getEventBus() {
         return EVENT_BUS;
     }
-
     /**
      * This is the entry point method.
      */
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index b45c154..7d58fa5 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -1,7 +1,5 @@
 package fr.agrometinfo.www.client.i18n;
 
-import java.util.Map;
-
 /**
  * Interface to represent the constants contained in resource bundle:
  * 'fr/agrometinfo/www/client/i18n/AppConstants.properties'.
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
index 6006d8a..4dcdfe4 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
@@ -17,6 +17,8 @@ import org.dominokit.domino.ui.loaders.LoaderEffect;
 import org.dominokit.domino.ui.menu.Menu;
 import org.dominokit.domino.ui.menu.MenuItem;
 import org.dominokit.domino.ui.notifications.Notification;
+import org.dominokit.domino.ui.popover.PopupPosition;
+import org.dominokit.domino.ui.popover.Tooltip;
 import org.dominokit.domino.ui.style.Styles;
 import org.dominokit.domino.ui.utils.DominoElement;
 import org.dominokit.domino.ui.utils.DominoUIConfig;
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
index b1f3164..57dc2eb 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
@@ -17,7 +17,6 @@ import com.google.gwt.core.client.GWT;
 import com.google.gwt.storage.client.Storage;
 
 import fr.agrometinfo.www.client.i18n.AppConstants;
-import fr.agrometinfo.www.client.i18n.AppMessages;
 import fr.agrometinfo.www.client.presenter.LoginPresenter;
 import fr.agrometinfo.www.shared.dto.QuestionDTO;
 import fr.agrometinfo.www.shared.dto.ResponseDTO;
@@ -34,10 +33,6 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
      * I18N constants.
      */
     private static final AppConstants CSTS = GWT.create(AppConstants.class);
-    /**
-     * I18N messages.
-     */
-    private static final AppMessages MSGS = GWT.create(AppMessages.class);
     /**
      * Is survey was already validated by user ?
      */
@@ -65,11 +60,9 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
     	GWT.log("LoginView.init()");
     	// on vérifie que le formulaire n'a pas déjà été rempli par l'utilisateur, via le LocalStorage du navigateur
     	final Storage ls = Storage.getLocalStorageIfSupported();
-    	if (ls != null) {
-    		if (ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
-    			GWT.log("Survey was already validated for this user, don't showing again");
-    			return;
-    		}
+    	if (ls != null && ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
+			GWT.log("Survey was already validated for this user, don't showing again");
+			return;
     	}
         
     	final Map<Long, TextArea> otherTextArea = new HashMap<>();
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
index f6232d1..5045a5d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
@@ -97,7 +97,7 @@ public abstract class DaoHibernate<T> {
         q.executeUpdate();
         em.getTransaction().commit();
         LOGGER.traceExit();
-    };
+    }
     /**
      * Use a consumer to execute JPA operations in a transaction.
      *
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
index 9369e17..147ccea 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
@@ -3,7 +3,6 @@
  */
 package fr.agrometinfo.www.server.dao;
 
-import java.util.List;
 import java.util.Map;
 
 import fr.agrometinfo.www.server.model.Question;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java
deleted file mode 100644
index 236fbe4..0000000
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/LoginFormData.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * 
- */
-package fr.agrometinfo.www.server.model;
-
-import java.util.HashMap;
-import java.util.List;
-
-import lombok.Data;
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * Class contains all data of login form.
- * @author jdecome
- *
- */
-@Deprecated
-public class LoginFormData {
-    /**
-     * List of login form questions.<br>
-     * This list questions contains available questions, obtained by {@link QuestionsDao}
-     */
-    @Getter
-    @Setter
-    private List<Question> questions;
-    /**
-     * List of login form responses.<br>
-     * This list responses contains available responses, obtained by {@link ResponsesDao}
-     */
-    @Getter
-    @Setter
-    private List<Response> responses;
-    /**
-     * Storage, for each response, the other text, if specified.
-     */
-    @Getter
-    private HashMap<Long, String> otherTextMap = new HashMap<>();
-    /**
-     * Constructor.
-     * @param qList questions list
-     * @param rList responses list
-     * @param otherText otherText map
-     */
-    public LoginFormData(final List<Question> qList, final List<Response> rList, HashMap<Long, String> otherText) {
-        this.questions = qList;
-        this.responses = rList;
-        this.otherTextMap = otherText;
-    }
-}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
index 7f3c123..b87ec07 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -4,12 +4,10 @@
 package fr.agrometinfo.www.server.model;
 
 import jakarta.persistence.Column;
-import jakarta.persistence.Embedded;
 import jakarta.persistence.Entity;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.IdClass;
 import jakarta.persistence.JoinColumn;
 import jakarta.persistence.OneToOne;
 import jakarta.persistence.Table;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
index e4d6e50..5c5371b 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
@@ -4,17 +4,13 @@
 package fr.agrometinfo.www.server.model;
 
 import java.sql.Timestamp;
-import java.util.List;
 
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
-import jakarta.persistence.FetchType;
 import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
-import jakarta.persistence.IdClass;
 import jakarta.persistence.JoinColumn;
-import jakarta.persistence.OneToMany;
 import jakarta.persistence.OneToOne;
 import jakarta.persistence.Table;
 import lombok.Data;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
index 432c54c..1a34f79 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
@@ -5,10 +5,8 @@ package fr.agrometinfo.www.server.rs;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 import java.util.stream.Collectors;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 
 import fr.agrometinfo.www.server.dao.QuestionsDao;
 import fr.agrometinfo.www.server.dao.ResponsesDao;
@@ -16,7 +14,6 @@ import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.service.MailService;
-import fr.agrometinfo.www.server.service.MailServiceImpl;
 import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
 import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
 import fr.agrometinfo.www.shared.dto.QuestionDTO;
@@ -33,13 +30,12 @@ import jakarta.ws.rs.QueryParam;
 import jakarta.ws.rs.FormParam;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.MediaType;
-import lombok.extern.log4j.Log4j2;
 
 /**
+ * Endpoint for WS login form.
  * @author jdecome
  *
  */
-@Log4j2
 @Path(LoginFormService.PATH)
 @RequestScoped
 public class LoginFormResource implements LoginFormService {
@@ -194,8 +190,6 @@ public class LoginFormResource implements LoginFormService {
                 this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null);
             } else if(responseRefIsNull && !otherTextIsNull) {  // réponse libre
                 this.userResponsesDao.insertResponse(questionRef, null, otherText);
-            } else {
-                // Do nothing
             }
         }
     }
@@ -204,7 +198,7 @@ public class LoginFormResource implements LoginFormService {
         if (questionRef != null) {
             // on cherche dans le DAO
             final Question q = questionsDao.findByRef(questionRef);
-            return (q != null);
+            return q != null;
         }
         return false;
     }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
index d226069..4c04cc0 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
@@ -27,19 +27,19 @@ import jakarta.ws.rs.QueryParam;
 @Path(LoginFormService.PATH)
 public interface LoginFormService {
     /** Service base path. */
-    public static final String PATH = "login";
+    String PATH = "login";
     /** Path for {@link LoginFormService#getQuestions()}. */
-    public static final String PATH_QUESTIONS_LIST = "questions";
+    String PATH_QUESTIONS_LIST = "questions";
     /**Path for {@link LoginFormService#getQuestionByCategory(String)}. */
-    public static final String PATH_QUESTION_BY_CATEGORY = "question";
+    String PATH_QUESTION_BY_CATEGORY = "question";
     /** Path for {@link LoginFormService#getResponses}. */
-    public static final String PATH_RESPONSES_LIST = "responses";
+    String PATH_RESPONSES_LIST = "responses";
     /** Path for {@link LoginFormService#getResponsesByQuestion(long)}. */
-    public static final String PATH_RESPONSES_BY_QUESTION_LIST = "responsesByQuestion";
+    String PATH_RESPONSES_BY_QUESTION_LIST = "responsesByQuestion";
     /** Path for {@link LoginFormService#getResponses()}. */
-    public static final String PATH_INSERT_RESPONSE = "insert";
+    String PATH_INSERT_RESPONSE = "insert";
     /** Path for {@link LoginFormService#insertResponse(String)}. */
-    public static final String PATH_INSERT_RESPONSE_WITH_JSON = "insertMultipleValues";
+    String PATH_INSERT_RESPONSE_WITH_JSON = "insertMultipleValues";
     /**
      * @return list of questions.
      */
-- 
GitLab


From d0866a937cb96b8d018e6974c97ede0494ca8ddf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 23 May 2024 15:13:04 +0200
Subject: [PATCH 03/30] Corrections PMD 2. fixes #5

---
 config/pmd-suppressions.properties | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/config/pmd-suppressions.properties b/config/pmd-suppressions.properties
index 11439c6..89c8232 100644
--- a/config/pmd-suppressions.properties
+++ b/config/pmd-suppressions.properties
@@ -8,9 +8,17 @@ fr.agrometinfo.www.shared.dto.IndicatorDTOBeanJsonDeserializerImpl=UnnecessaryIm
 fr.agrometinfo.www.shared.dto.IndicatorDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.IndicatorDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.MessageDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.LoginFormDataDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTOBeanJsonDeserializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.QuestionDTOBeanJsonDeserializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.QuestionDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.QuestionDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.ResponseDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.ResponseDTOBeanJsonDeserializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.ResponseDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.service.ResponseDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SimpleFeatureBeanJsonDeserializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SimpleFeatureBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonDeserializerImpl=UnnecessaryImport
@@ -18,6 +26,9 @@ fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SummaryDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.service.ApplicationServiceFactory=UnnecessaryImport
 fr.agrometinfo.www.shared.service.IndicatorServiceFactory=UnnecessaryImport
+fr.agrometinfo.www.shared.service.LoginFormServiceFactory=UnnecessaryImport
+fr.agrometinfo.www.shared.service.QuestionDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.service.ResponseDTOBeanJsonDeserializerImpl=UnnecessaryImport
 org.geojson.FeatureBeanJsonDeserializerImpl=UnnecessaryImport
 org.geojson.FeatureBeanJsonSerializerImpl=UnnecessaryImport
 org.geojson.FeatureCollectionBeanJsonDeserializerImpl=UnnecessaryImport
-- 
GitLab


From ca0c0725aeaf23e1f9dbbd337c5cffb911adb399 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 24 May 2024 08:30:36 +0200
Subject: [PATCH 04/30] Corrections checkstyle. fixes #5

---
 .../java/fr/agrometinfo/www/client/App.java   |   3 +-
 .../www/client/i18n/AppConstants.java         |  33 +++-
 .../www/client/presenter/LoginPresenter.java  |  26 ++--
 .../www/client/view/AbstractBaseView.java     |  18 ++-
 .../www/client/view/LoginView.java            | 147 +++++++++---------
 .../www/server/dao/QuestionsDao.java          |  32 ++--
 .../www/server/dao/QuestionsDaoHibernate.java |  34 ++--
 .../www/server/dao/ResponsesDao.java          |  30 ++--
 .../www/server/dao/ResponsesDaoHibernate.java |  23 ++-
 .../www/server/dao/UserResponsesDao.java      |  56 ++++---
 .../server/dao/UserResponsesDaoHibernate.java |  27 ++--
 .../www/server/model/Question.java            |  32 ++--
 .../www/server/model/Response.java            |  32 ++--
 .../www/server/model/UserResponses.java       |  38 ++---
 .../www/server/rs/LoginFormResource.java      |  30 ++--
 .../www/server/service/MailServiceImpl.java   |  29 ++--
 .../www/server/rs/LoginFormResourceTest.java  |   2 +-
 .../www/shared/dto/LoginFormDataDTO.java      |  25 ++-
 .../www/shared/dto/QuestionDTO.java           |  25 +--
 .../www/shared/dto/ResponseDTO.java           |  21 ++-
 .../www/shared/service/LoginFormService.java  |  21 ++-
 21 files changed, 363 insertions(+), 321 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/App.java b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
index 37dd573..6134041 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/App.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
@@ -97,8 +97,9 @@ public class App implements EntryPoint {
 
         // enabling Charba
         Charba.enable();
+
         new LayoutPresenter().start();
-        
+
         // formulaire d'enquête
         new LoginPresenter().start();
     }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index 7d58fa5..474fe81 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -338,29 +338,46 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
      */
     @DefaultStringValue("Yes")
     String yes();
-    
+
     // Formulaire d'enquête - #5
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Valider")
     String validate();
-    
+    /**
+    * @return translation
+    */
     @DefaultStringValue("Ignorer")
     String ignore();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Formulaire d'enquête")
     String loginFormTitle();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Please complete this survey form to continue on AgroMetInfo.")
     String loginFormDescription();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Other")
     String loginFormOtherTextCheckbox();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("You must click on checkbox above for activate this input field.")
     String loginFormOtherTextTooltip();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Your responses have been recorded.")
     String loginFormSuccess();
-    
+    /**
+     * @return translation
+     */
     @DefaultStringValue("Your responses cannot been recorded, but you can use AgroMetInfo application.")
     String loginFormFail();
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
index 1c7d2f9..a949503 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
@@ -40,14 +40,14 @@ public final class LoginPresenter implements Presenter {
          */
         void close();
         /**
-         * @param list list of availables responses.
+         * @param map list of availables responses.
          */
         void setResponses(Map<QuestionDTO, List<ResponseDTO>> map);
         /**
          * Showing success login.
          * @param msg returned message from webservice
          */
-        void displaySuccessLogin(final String msg);
+        void displaySuccessLogin(String msg);
     }
 
     /**
@@ -63,10 +63,9 @@ public final class LoginPresenter implements Presenter {
      * @param list list of responses
      */
     private void setResponses(final List<ResponseDTO> list) {
-        GWT.log("setResponses(" + list.size() +") start");
         this.responses = list;
         final Map<QuestionDTO, List<ResponseDTO>> responsesMap = new HashMap<>();
-        
+
         // extraction des questions, à partir des réponses retournées par le webservice
         for (final ResponseDTO dto : list) {
             if (!responsesMap.containsKey(dto.getQuestion())) { // la map ne contient pas la question
@@ -77,14 +76,13 @@ public final class LoginPresenter implements Presenter {
         }
         this.questions = new ArrayList<>(responsesMap.keySet());
         view.setResponses(responsesMap);
-        GWT.log("setResponses() end");
-        
+
         view.init();
     }
     @Override
     public void start() {
         view.setPresenter(this);
-        
+
         LoginFormServiceFactory.INSTANCE.getResponses()
         .onSuccess(this::setResponses)
         .onFailed(view::failureNotification)
@@ -92,11 +90,11 @@ public final class LoginPresenter implements Presenter {
     }
     public void insertUserResponses(final List<Long> responsesRef, final HashMap<Long, String> otherTextMap) {
         GWT.log("Insère les réponses de l'utilisateur");
-        
+
         // traitement des réponses prédéfinies
         final List<QuestionDTO> questionsDto = new ArrayList<>();
         final List<ResponseDTO> responsesDto = new ArrayList<>();
-        
+
         for (final Long ref : responsesRef) {
             // on récupère la réponse correspondante à la référence
             final ResponseDTO rDto = this.responses.stream().filter(r -> r.getResponseRef() == ref).findFirst().get();
@@ -104,7 +102,7 @@ public final class LoginPresenter implements Presenter {
                 if (!responsesDto.contains(rDto)) {
                     responsesDto.add(rDto);
                 }
-                
+
                 // on récupère la question correspondantes à la réponse
                 if (!questionsDto.contains(rDto.getQuestion())) {
                     questionsDto.add(rDto.getQuestion());
@@ -117,14 +115,14 @@ public final class LoginPresenter implements Presenter {
                 questionsDto.add(q);
             }
         });
-        
-        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDto, responsesDto, otherTextMap);
-        
+
+
         // envoi au webservice
+        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDto, responsesDto, otherTextMap);
         LoginFormServiceFactory.INSTANCE.insertAllResponses(data)
         .onSuccess(view::displaySuccessLogin)
         .onFailed(view::failureNotification)
         .send();
     }
-    
+
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
index 52236ee..5521df9 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/AbstractBaseView.java
@@ -34,7 +34,7 @@ public abstract class AbstractBaseView<T> implements BaseView<T> {
     /**
     *
     * @param failure
-    * @return
+    * @return details of failure object
     */
    protected static String getDetails(final FailedResponseBean failure) {
        final StringJoiner sj = new StringJoiner("<br/>");
@@ -54,14 +54,18 @@ public abstract class AbstractBaseView<T> implements BaseView<T> {
     protected final T getPresenter() {
         return presenter;
     }
-
+    /** */
     @Override
     public final void setPresenter(final T value) {
         this.presenter = value;
     }
-    
-   protected Notification notification(final String msg) {
-       DomGlobal.console.info("Notification!");
-       return Notification.create(msg).setPosition(Notification.TOP_LEFT).show();
-   }
+    /**
+     * Show notification with message.
+     * @param msg message to display
+     * @return notification object
+     */
+    protected Notification notification(final String msg) {
+        DomGlobal.console.info("Notification!");
+        return Notification.create(msg).setPosition(Notification.TOP_LEFT).show();
+    }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
index 57dc2eb..ab0502c 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
@@ -26,7 +26,6 @@ import fr.agrometinfo.www.shared.dto.ResponseDTO;
  *
  * @author Olivier Maury
  * @author Jérémie Décome
- * 
  */
 public final class LoginView extends AbstractBaseView<LoginPresenter> implements LoginPresenter.View {
     /**
@@ -34,9 +33,13 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
      */
     private static final AppConstants CSTS = GWT.create(AppConstants.class);
     /**
-     * Is survey was already validated by user ?
+     * Is survey was already validated by user ? Key for LocalStorage.
      */
     private static final String IS_VALIDATED_KEY = "isValidated";
+    /**
+     * Level for questions.
+     */
+    private static final int SECTION_TITLE_LEVEL = 5;
     /**
      * List of available responses.
      */
@@ -57,26 +60,26 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
 
     @Override
     public void init() {
-    	GWT.log("LoginView.init()");
-    	// on vérifie que le formulaire n'a pas déjà été rempli par l'utilisateur, via le LocalStorage du navigateur
-    	final Storage ls = Storage.getLocalStorageIfSupported();
-    	if (ls != null && ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
-			GWT.log("Survey was already validated for this user, don't showing again");
-			return;
-    	}
-        
-    	final Map<Long, TextArea> otherTextArea = new HashMap<>();
-    	
+        GWT.log("LoginView.init()");
+        // on vérifie que le formulaire n'a pas déjà été rempli par l'utilisateur, via le LocalStorage du navigateur
+        final Storage ls = Storage.getLocalStorageIfSupported();
+        if (ls != null && ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
+            GWT.log("Survey was already validated for this user, don't showing again");
+            return;
+        }
+
+        final Map<Long, TextArea> otherTextArea = new HashMap<>();
+
         this.modal = ModalDialog.create(CSTS.loginFormTitle()).large().setAutoClose(true);
-		this.modal
-		.appendChild(Elements.p().textContent(CSTS.loginFormDescription()).css("login-paragraph"));
-		
-		for (Map.Entry<QuestionDTO, List<ResponseDTO>> entry : this.availableResponses.entrySet()) {
-		    final QuestionDTO k = entry.getKey(); // Pour chaque question
-            
+        this.modal
+        .appendChild(Elements.p().textContent(CSTS.loginFormDescription()).css("login-paragraph"));
+
+        for (Map.Entry<QuestionDTO, List<ResponseDTO>> entry : this.availableResponses.entrySet()) {
+            final QuestionDTO k = entry.getKey(); // Pour chaque question
+
             // on affiche le libellé
-            this.modal.appendChild(Elements.h(5, "• " + k.getFullName()));
-            
+            this.modal.appendChild(Elements.h(SECTION_TITLE_LEVEL, "• " + k.getFullName()));
+
             // on affiche les réponses possibles
             entry.getValue().forEach((r) -> {
                 final CheckBox cb = CheckBox.create(r.getFullname()).id(Long.toString(r.getResponseRef()))
@@ -84,15 +87,17 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
                 this.modal.appendChild(cb);
                 this.checkBoxList.add(cb);
             });
-            
+
             // on affiche le champ pour la réponse libre
-            final CheckBox otherCb = CheckBox.create(CSTS.loginFormOtherTextCheckbox()).id(k.getCategory() + "_other").css("login-checkbox");
+            final CheckBox otherCb = CheckBox.create(CSTS.loginFormOtherTextCheckbox())
+                    .id(k.getCategory() + "_other")
+                    .css("login-checkbox");
             final TextArea otherText = TextArea.create().setId(k.getCategory() + "_other_text")
                     .setDisabled(true)
                     .setTooltip(CSTS.loginFormOtherTextTooltip())
                     .setRows(2)
                     .css("login-textarea");
-            
+
             otherCb.addChangeHandler((v) -> {
                 otherText.setDisabled(!otherCb.getValue());
                 if (!otherText.isDisabled()) {
@@ -104,69 +109,69 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
             this.checkBoxList.add(otherCb);
             this.modal.appendChild(otherCb).appendChild(otherText);
             otherTextArea.put(k.getQuestionRef(), otherText);
-		}
-		// active le bouton Valider si au moins une case a été cochée
-		this.checkBoxList.forEach((c) -> {
-		    c.addChangeHandler((v) -> this.activateValidateButton());
-		});
-		
-		// E-mail de l'utilisateur
-		
-		this.validate = Button.create(CSTS.validate()).linkify();
-		this.validate.addClickListener((evt) -> {
-		    final HashMap<Long, String> otherTextMap = new HashMap<>();
-		    
-		    // on traite les réponses prédéfinies (récupération des références des réponses), en excluant les cases à cocher pour les réponses libre
-		    final List<Long> selectedResponsesRef = this.checkBoxList.stream()
-		            .filter((v) -> v.getValue() && v.getId().matches("[0-9]+"))
-		            .map(v -> Long.parseLong(v.getId())).collect(Collectors.toList());
-
-		    // traitement des réponses libre de la question
-		    otherTextArea.forEach((k, v) -> {
-		        // si le champ est actif, c'est qu'il y a (potentiellement) une réponse et si le champ n'est pas vide
-		        if (!v.isDisabled() && !v.getValue().equals("")) {
-		            otherTextMap.put(k, v.getValue());
-		        }
-		    });
-		    
-		    this.getPresenter().insertUserResponses(selectedResponsesRef, otherTextMap);
-		    
-			ls.setItem(IS_VALIDATED_KEY, "true");
-			this.modal.close();
-		});
-		this.validate.setDisabled(true);  // le bouton Valider est désactivé par défaut
-		this.modal.appendFooterChild(validate);
-		
-		final Button ignore = Button.create(CSTS.ignore()).linkify();
-		ignore.addClickListener((evt) -> {
-			this.modal.close();
-		});
-		this.modal.appendFooterChild(ignore);
-		
-		this.modal.open();
-		GWT.log("LoginView.init() end");
+        }
+        // active le bouton Valider si au moins une case a été cochée
+        this.checkBoxList.forEach((c) -> {
+            c.addChangeHandler((v) -> this.activateValidateButton());
+        });
+
+        // E-mail de l'utilisateur
+
+        // Boutons de la fenêtre modale
+        this.validate = Button.create(CSTS.validate()).linkify();
+        this.validate.addClickListener((evt) -> {
+            final HashMap<Long, String> otherTextMap = new HashMap<>();
+
+            // on traite les réponses prédéfinies (récupération des références des réponses),
+            // en excluant les cases à cocher pour les réponses libre
+            final List<Long> selectedResponsesRef = this.checkBoxList.stream()
+                    .filter((v) -> v.getValue() && v.getId().matches("[0-9]+"))
+                    .map(v -> Long.parseLong(v.getId())).collect(Collectors.toList());
+
+            // traitement des réponses libre de la question
+            otherTextArea.forEach((k, v) -> {
+                // si le champ est actif, c'est qu'il y a (potentiellement) une réponse et si le champ n'est pas vide
+                if (!v.isDisabled() && !v.getValue().equals("")) {
+                    otherTextMap.put(k, v.getValue());
+                }
+            });
+
+            this.getPresenter().insertUserResponses(selectedResponsesRef, otherTextMap);
+
+            ls.setItem(IS_VALIDATED_KEY, "true");
+            this.modal.close();
+        });
+        this.validate.setDisabled(true);  // le bouton Valider est désactivé par défaut
+        this.modal.appendFooterChild(validate);
+
+        final Button ignore = Button.create(CSTS.ignore()).linkify();
+        ignore.addClickListener((evt) -> {
+            this.modal.close();
+        });
+        this.modal.appendFooterChild(ignore);
+
+        this.modal.open();
+        GWT.log("LoginView.init() end");
     }
-    
+
     @Override
-    public void failureNotification(FailedResponseBean failedResponse) {
+    public void failureNotification(final FailedResponseBean failedResponse) {
         this.notification(CSTS.loginFormFail());
     }
 
     @Override
-    public void setResponses(Map<QuestionDTO, List<ResponseDTO>> map) {
-        GWT.log("LoginView.setResponses(" + map.size() + ")");
+    public void setResponses(final Map<QuestionDTO, List<ResponseDTO>> map) {
         this.availableResponses = map;
-        GWT.log("LoginView.setResponses() end");
     }
 
     @Override
-    public void displaySuccessLogin(String msg) {
+    public void displaySuccessLogin(final String msg) {
         this.notification(CSTS.loginFormSuccess());
     }
     /**
      * Enable or disable validate button depending of checkbox status.
      */
     private void activateValidateButton() {
-        this.validate.setDisabled(!this.checkBoxList.stream().anyMatch((c) -> c.getValue() == true));
+        this.validate.setDisabled(!this.checkBoxList.stream().anyMatch((c) -> c.getValue()));
     }
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
index e68aaf0..7e65a6d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
@@ -13,20 +13,20 @@ import fr.agrometinfo.www.server.model.Question;
  *
  */
 public interface QuestionsDao {
-	/**
-	 * @return all of Questions
-	 */
-	List<Question> findAll();
-	/**
-	 * Find question object by reference.
-	 * @param questionRef question_ref
-	 * @return Question object
-	 */
-	Question findByRef(long questionRef);
-	/**
-	 * Find question object by category.
-	 * @param category of question
-	 * @return Question object
-	 */
-	Question findByCategory(final String category);
+    /**
+     * @return all of Questions
+     */
+    List<Question> findAll();
+    /**
+     * Find question object by reference.
+     * @param questionRef question_ref
+     * @return Question object
+     */
+    Question findByRef(long questionRef);
+    /**
+     * Find question object by category.
+     * @param category of question
+     * @return Question object
+     */
+    Question findByCategory(String category);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
index 147ccea..e243e48 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.dao;
 
@@ -14,20 +14,22 @@ import jakarta.enterprise.context.ApplicationScoped;
  */
 @ApplicationScoped
 public class QuestionsDaoHibernate extends DaoHibernate<Question> implements QuestionsDao {
-
-	public QuestionsDaoHibernate() {
-		super(Question.class);
-	}
-
-	@Override
-	public Question findByRef(long ref) {
-		return super.find(ref);
-	}
-
-	@Override
-	public Question findByCategory(String category) {
-		final String jpql = "SELECT q FROM Question q WHERE q.category = :cat";
-		return super.findOneByJPQL(jpql, Map.of("cat", category), Question.class);
-	}
+    /**
+     * Constructor.
+     */
+    public QuestionsDaoHibernate() {
+        super(Question.class);
+    }
+    /** */
+    @Override
+    public Question findByRef(final long ref) {
+        return super.find(ref);
+    }
+    /** */
+    @Override
+    public Question findByCategory(final String category) {
+        final String jpql = "SELECT q FROM Question q WHERE q.category = :cat";
+        return super.findOneByJPQL(jpql, Map.of("cat", category), Question.class);
+    }
 
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
index 9715217..acd4cb9 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.dao;
 
@@ -9,26 +9,24 @@ import java.util.List;
 
 /**
  * DAO for {@link Response}.
- *
  * @author jdecome
- *
  */
 public interface ResponsesDao {
     /**
      * Find all responses in database.
      * @return all responses
      */
-	List<Response> findAll();
-	/**
-	 * Find all responses by question.
-	 * @param questionRef
-	 * @return list of responses
-	 */
-	List<Response> findAllByQuestion(final long questionRef);
-	/**
-	 * Get response with reference.
-	 * @param responseRef
-	 * @return response
-	 */
-	Response findByRef(final long responseRef);
+    List<Response> findAll();
+    /**
+     * Find all responses by question.
+     * @param questionRef
+     * @return list of responses
+     */
+    List<Response> findAllByQuestion(long questionRef);
+    /**
+     * Get response with reference.
+     * @param responseRef
+     * @return response
+     */
+    Response findByRef(long responseRef);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
index 5577d8e..ecaebf4 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.dao;
 
@@ -18,19 +18,18 @@ public class ResponsesDaoHibernate extends DaoHibernate<Response> implements Res
     /**
      * Constructor.
      */
-	public ResponsesDaoHibernate() {
-		super(Response.class);
-	}
-
-	@Override
-	public List<Response> findAllByQuestion(final long questionRef) {
-		final String jpql = "SELECT r FROM Response r WHERE r.question.questionRef = :ref";
-		return super.findAllByJPQL(jpql, Map.of("ref", questionRef), Response.class);
-	}
-
+    public ResponsesDaoHibernate() {
+        super(Response.class);
+    }
+    /** */
+    @Override
+    public List<Response> findAllByQuestion(final long questionRef) {
+        final String jpql = "SELECT r FROM Response r WHERE r.question.questionRef = :ref";
+        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), Response.class);
+    }
+    /** */
     @Override
     public Response findByRef(final long responseRef) {
         return super.find(responseRef);
     }
-	
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
index 32c8a9d..d45e51d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -7,9 +7,7 @@ import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.model.UserResponses;
 /**
  * DAO for {@link UserResponsesDao}.
- * 
  * @author jdecome
- *
  */
 public interface UserResponsesDao {
     /**
@@ -17,32 +15,32 @@ public interface UserResponsesDao {
      * @param questionRef
      * @return list of user's responses
      */
-	List<UserResponses> findAllByQuestion(final long questionRef);
-	/**
-	 * Insert response or text for specified question.<br>
+    List<UserResponses> findAllByQuestion(long questionRef);
+    /**
+     * Insert response or text for specified question.<br>
      * If r is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code r} must be null. 
-	 * @param q question to answer
-	 * @param r response
-	 * @param otherText text if response is other
-	 */
-	void insertResponse(final Question q, final Response r, final String otherText);
-	/**
-	 * Insert response or text for specified question, identified by reference.<br>
-	 * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null. 
-	 * @param questionRef reference of question to answer
-	 * @param responseRef reference of response
-	 * @param otherText text if response is other
-	 */
-	void insertResponse(final Long questionRef, final Long responseRef, final String otherText);
-	/**
-	 * Get all user responses.
-	 * @return number of responses
-	 */
-	int getNbUserResponses();
-	/**
-	 * 
-	 */
-	void deleteAll();
+     * If otherText is provided, {@code r} must be null.
+     * @param q question to answer
+     * @param r response
+     * @param otherText text if response is other
+     */
+    void insertResponse(Question q, Response r, String otherText);
+    /**
+     * Insert response or text for specified question, identified by reference.<br>
+     * If responseRef is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code responseRef} must be null.
+     * @param questionRef reference of question to answer
+     * @param responseRef reference of response
+     * @param otherText text if response is other
+     */
+    void insertResponse(Long questionRef, Long responseRef, String otherText);
+    /**
+     * Get all user responses.
+     * @return number of responses
+     */
+    int getNbUserResponses();
+    /**
+     * Delete all rows in table.
+     */
+    void deleteAll();
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 322dd2e..8c9f128 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.dao;
 
@@ -25,25 +25,29 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponses> imple
     /** DAO for responses. */
     private ResponsesDao responsesDao = new ResponsesDaoHibernate();
     /** Default constructor. */
-	public UserResponsesDaoHibernate() {
-		super(UserResponses.class);
-	}
-	@Override
-	public List<UserResponses> findAllByQuestion(final long questionRef) {
-	    final String jpql = "SELECT u FROM UserResponses u WHERE u.question.questionRef = :ref";
-		return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponses.class);
-	}
+    public UserResponsesDaoHibernate() {
+        super(UserResponses.class);
+    }
+    /** */
+    @Override
+    public List<UserResponses> findAllByQuestion(final long questionRef) {
+        final String jpql = "SELECT u FROM UserResponses u WHERE u.question.questionRef = :ref";
+        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponses.class);
+    }
+    /** */
     @Override
     public void insertResponse(final Question q, final Response r, final String otherText) {
         final UserResponses ur = new UserResponses();
-        ur.setDateTime(Timestamp.valueOf(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.now())));
+        ur.setDateTime(Timestamp.valueOf(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
+                .format(LocalDateTime.now())));
         ur.setQuestion(q);
         ur.setResponse(r);
         ur.setOtherText(otherText);
-        
+
         this.save(ur);
 
     }
+    /** */
     @Override
     public void insertResponse(final Long questionRef, final Long responseRef, final String otherText) {
         // recherche de la question
@@ -57,6 +61,7 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponses> imple
             }
         }
     }
+    /** */
     @Override
     public int getNbUserResponses() {
         return super.findAll().size();
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
index 0d2537a..fcbea50 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.model;
 
@@ -20,19 +20,19 @@ import lombok.Data;
 @Entity
 @Table(name = "questions")
 public class Question {
-	/** PK. */
-	@Id
-	@GeneratedValue(strategy = GenerationType.AUTO)
-	@Column(name = "question_ref")
-	private long questionRef;
-	/**
-	 * Catogory of question.
-	 */
-	@Column(name = "category")
-	private String category;
-	/**
-	 * Label of question.
-	 */
-	@Column(name = "fullname")
-	private String fullName;
+    /** PK. */
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "question_ref")
+    private long questionRef;
+    /**
+     * Catogory of question.
+     */
+    @Column(name = "category")
+    private String category;
+    /**
+     * Label of question.
+     */
+    @Column(name = "fullname")
+    private String fullName;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
index b87ec07..5e044c9 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.model;
 
@@ -25,19 +25,19 @@ public class Response {
     /**
      * Reference of response.
      */
-	@Id
-	@GeneratedValue(strategy = GenerationType.AUTO)
-	@Column(name = "response_ref")
-	private long responseRef;
-	/**
-	 * Question related to response.
-	 */
-	@OneToOne
-	@JoinColumn(name = "question_ref")
-	private Question question;
-	/**
-	 * Label of the response.
-	 */
-	@Column(name = "fullname")
-	private String fullname;
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @Column(name = "response_ref")
+    private long responseRef;
+    /**
+     * Question related to response.
+     */
+    @OneToOne
+    @JoinColumn(name = "question_ref")
+    private Question question;
+    /**
+     * Label of the response.
+     */
+    @Column(name = "fullname")
+    private String fullname;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
index 5c5371b..4938f83 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
@@ -30,23 +30,23 @@ public class UserResponses {
     @Column(name = "user_response_ref")
     private long userResponseRef;
     /** Date time of user responses. */
-	@Column(name = "datetime")
-	private Timestamp dateTime;
-	/** Related question. */
-	@OneToOne
-	@JoinColumn(name = "question_ref")
-	private Question question;
-	/**
-	 * Related response.<br>
-	 * Can be null if it's a other response.
-	 */
-	@OneToOne
-	@JoinColumn(name = "response_ref")
-	private Response response;
-	/**
-	 * Other text response.<br>
-	 * Can be null if it's a related response.
-	 */
-	@Column(name = "other_text")
-	private String otherText;
+    @Column(name = "datetime")
+    private Timestamp dateTime;
+    /** Related question. */
+    @OneToOne
+    @JoinColumn(name = "question_ref")
+    private Question question;
+    /**
+     * Related response.<br>
+     * Can be null if it's a other response.
+     */
+    @OneToOne
+    @JoinColumn(name = "response_ref")
+    private Response response;
+    /**
+     * Other text response.<br>
+     * Can be null if it's a related response.
+     */
+    @Column(name = "other_text")
+    private String otherText;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
index 1a34f79..b5454cd 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.server.rs;
 
@@ -98,7 +98,7 @@ public class LoginFormResource implements LoginFormService {
     @Inject
     private ResponsesDao responsesDao;
     /**
-     * DAO for user responses ({@link UserResponsesDao}
+     * DAO for user responses ({@link UserResponsesDao}.
      */
     @Inject
     private UserResponsesDao userResponsesDao;
@@ -118,8 +118,8 @@ public class LoginFormResource implements LoginFormService {
             final jakarta.ws.rs.core.Response.Status badRequest = jakarta.ws.rs.core.Response.Status.BAD_REQUEST;
             throw new WebApplicationException(
                     jakarta.ws.rs.core.Response.status(badRequest).entity(ErrorResponseDTO.of(
-                            badRequest.getStatusCode(), 
-                            badRequest.getReasonPhrase(), 
+                            badRequest.getStatusCode(),
+                            badRequest.getReasonPhrase(),
                             queryParamName + " parameter is mandatory")
                         ).build());
         }
@@ -160,7 +160,6 @@ public class LoginFormResource implements LoginFormService {
             return responses;
         }
         return null;
-        
     }
     @GET
     @Path(LoginFormService.PATH_RESPONSES_LIST)
@@ -179,8 +178,8 @@ public class LoginFormResource implements LoginFormService {
     @Produces(MediaType.TEXT_PLAIN)
     @Override
     public void insertResponse(
-            @FormParam(value = "questionRef") final Long questionRef, 
-            @FormParam(value = "responseRef") final String responseRef, 
+            @FormParam(value = "questionRef") final Long questionRef,
+            @FormParam(value = "responseRef") final String responseRef,
             @FormParam(value = "otherText") final String otherText) {
         this.checkRequired(questionRef, "questionRef");
         final boolean responseRefIsNull = responseRef == null || responseRef.equals("null");
@@ -188,12 +187,16 @@ public class LoginFormResource implements LoginFormService {
         if (this.checkIfQuestionExist(questionRef)) {
             if (!responseRefIsNull && otherTextIsNull) {     // réponse prédéfinie
                 this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null);
-            } else if(responseRefIsNull && !otherTextIsNull) {  // réponse libre
+            } else if (responseRefIsNull && !otherTextIsNull) {  // réponse libre
                 this.userResponsesDao.insertResponse(questionRef, null, otherText);
             }
         }
     }
-    
+    /**
+     * Check on database if reference of question exists.
+     * @param questionRef
+     * @return {@code true} if question exists or {@code false} otherwise
+     */
     private boolean checkIfQuestionExist(final Long questionRef) {
         if (questionRef != null) {
             // on cherche dans le DAO
@@ -213,10 +216,13 @@ public class LoginFormResource implements LoginFormService {
             // Insérer les réponses associés
             final Question q = toEntity(dto);
             final Long qRef = dto.getQuestionRef();
-            List<ResponseDTO> associatedResponses = data.getResponses().stream().filter(f -> f.getQuestion().getQuestionRef() == qRef).collect(Collectors.toList());
+            List<ResponseDTO> associatedResponses = data.getResponses()
+                    .stream().filter(f -> f.getQuestion().getQuestionRef() == qRef)
+                    .collect(Collectors.toList());
+
             // On insère les réponses prédéfinies
             for (final ResponseDTO rDto : associatedResponses) {
-                final Response r = toEntity(rDto); 
+                final Response r = toEntity(rDto);
                 this.userResponsesDao.insertResponse(q, r, null);
             }
 
@@ -226,7 +232,7 @@ public class LoginFormResource implements LoginFormService {
                 this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getQuestionRef()));
             }
         }
-        
+
         // une fois que tout est inséré, on envoi un mail au support
         this.mailService.sendLoginFilled(data);
         return "0";
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
index e752839..ce6cc68 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -10,6 +10,7 @@ import java.util.stream.Collectors;
 
 import org.apache.logging.log4j.Level;
 import org.apache.logging.log4j.LogManager;
+
 import org.apache.logging.log4j.core.Appender;
 import org.apache.logging.log4j.core.Filter;
 import org.apache.logging.log4j.core.Layout;
@@ -324,6 +325,10 @@ public class MailServiceImpl implements MailService {
             LOGGER.info("failed to send e-mail : {}", e);
         }
     }
+    /**
+     * Send an e-mail when user filled survey form.
+     * @param data data of survey
+     */
     public void sendLoginFilled(final LoginFormDataDTO data) {
         final Mail mail = createContentFromData(data);
         mail.setSubject("Un utilisateur a rempli le formulaire d'enquête");
@@ -332,25 +337,31 @@ public class MailServiceImpl implements MailService {
         try {
             send(mail);
         } catch (AgroMetInfoException e) {
-            // en cas d'erreur, on ne fait rien de plus.
-            LOGGER.trace("failed to send e-mail");
+            LOGGER.info("failed to send e-mail : {}", e);
         }
-        
-    }//*/
+    }
+    /**
+     * Generate body e-mail with survey data.<br>
+     * Mail object returned is just a mail with content.
+     * @param data data of survey
+     * @return mail object
+     */
     public static Mail createContentFromData(final LoginFormDataDTO data) {
         final Mail mail = new Mail();
-        
+
         final StringBuilder builder = new StringBuilder();
-        builder.append("Bonjour,\nUn utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\n\n");
+        builder.append("Bonjour,\n");
+        builder.append("Un utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\\n\\n");
         builder.append("Il a répondu à " + data.getQuestions().size() + " question(s) :\n");
-        
+
         data.getQuestions().forEach((q) -> {
             builder.append("\t• « " + q.getFullName() + " » :\n");
             data.getResponses().stream().filter(f -> f.getQuestion().getQuestionRef() == q.getQuestionRef())
                 .collect(Collectors.toList())
                 .forEach((r) -> builder.append("\t\t- " + r.getFullname() + "\n"));
-            
-            if (data.getOtherTextMap().containsKey(q.getQuestionRef()) && data.getOtherTextMap().get(q.getQuestionRef()) != null) {
+
+            if (data.getOtherTextMap().containsKey(q.getQuestionRef())
+                    && data.getOtherTextMap().get(q.getQuestionRef()) != null) {
                 builder.append("\t\t- Réponse libre : « " + data.getOtherTextMap().get(q.getQuestionRef()) + " »\n");
             }
             builder.append("\n");
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
index e0d1d75..4bf7ab3 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
@@ -81,7 +81,7 @@ public class LoginFormResourceTest extends JerseyTest {
             // do nothing
         }
         
-    };
+    }
     /** Path separator. */
     private static final String SEP = "/";
     /** Parsing JSON to object. */
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
index 0d2cefc..30493a8 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.shared.dto;
 
@@ -31,7 +31,6 @@ public class LoginFormDataDTO {
      * Default constructor.
      */
     public LoginFormDataDTO() {
-        
     }
     /**
      * Constructor.
@@ -39,7 +38,8 @@ public class LoginFormDataDTO {
      * @param rList responses list
      * @param otherText otherText map
      */
-    public LoginFormDataDTO(final List<QuestionDTO> qList, final List<ResponseDTO> rList, HashMap<Long, String> otherText) {
+    public LoginFormDataDTO(final List<QuestionDTO> qList,
+            final List<ResponseDTO> rList, final HashMap<Long, String> otherText) {
         this.questions = qList;
         this.responses = rList;
         this.otherTextMap = otherText;
@@ -63,22 +63,21 @@ public class LoginFormDataDTO {
         return otherTextMap;
     }
     /**
-     * @param questions the questions to set
+     * @param questionsList the questions to set
      */
-    public void setQuestions(List<QuestionDTO> questions) {
-        this.questions = questions;
+    public void setQuestions(final List<QuestionDTO> questionsList) {
+        this.questions = questionsList;
     }
     /**
-     * @param responses the responses to set
+     * @param responsesList the responses to set
      */
-    public void setResponses(List<ResponseDTO> responses) {
-        this.responses = responses;
+    public void setResponses(final List<ResponseDTO> responsesList) {
+        this.responses = responsesList;
     }
     /**
-     * @param otherTextMap the otherTextMap to set
+     * @param map the otherTextMap to set
      */
-    public void setOtherTextMap(HashMap<Long, String> otherTextMap) {
-        this.otherTextMap = otherTextMap;
+    public void setOtherTextMap(final HashMap<Long, String> map) {
+        this.otherTextMap = map;
     }
-    
 }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
index 3028802..60de95d 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.shared.dto;
 
@@ -45,29 +45,31 @@ public class QuestionDTO {
         return fullName;
     }
     /**
-     * @param questionRef the questionRef to set
+     * @param value the questionRef to set
      */
-    public void setQuestionRef(final long questionRef) {
-        this.questionRef = questionRef;
+    public void setQuestionRef(final long value) {
+        this.questionRef = value;
     }
     /**
-     * @param category the category to set
+     * @param value the category to set
      */
-    public void setCategory(final String category) {
-        this.category = category;
+    public void setCategory(final String value) {
+        this.category = value;
     }
     /**
-     * @param fullName the fullName to set
+     * @param value the fullName to set
      */
-    public void setFullName(final String fullName) {
-        this.fullName = fullName;
+    public void setFullName(final String value) {
+        this.fullName = value;
     }
+    /** */
     @Override
     public int hashCode() {
         return Objects.hash(category, fullName, questionRef);
     }
+    /** */
     @Override
-    public boolean equals(Object obj) {
+    public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -81,5 +83,4 @@ public class QuestionDTO {
         return Objects.equals(category, other.category) && Objects.equals(fullName, other.fullName)
                 && questionRef == other.questionRef;
     }
-    
 }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
index f52837f..f1e86aa 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.shared.dto;
 
@@ -43,22 +43,21 @@ public class ResponseDTO {
         return fullname;
     }
     /**
-     * @param responseRef the responseRef to set
+     * @param value the responseRef to set
      */
-    public void setResponseRef(final long responseRef) {
-        this.responseRef = responseRef;
+    public void setResponseRef(final long value) {
+        this.responseRef = value;
     }
     /**
-     * @param question the question to set
+     * @param dto the question to set
      */
-    public void setQuestion(final QuestionDTO question) {
-        this.question = question;
+    public void setQuestion(final QuestionDTO dto) {
+        this.question = dto;
     }
     /**
-     * @param fullname the fullname to set
+     * @param value the fullname to set
      */
-    public void setFullname(final String fullname) {
-        this.fullname = fullname;
+    public void setFullname(final String value) {
+        this.fullname = value;
     }
-    
 }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
index 4c04cc0..94083e2 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
@@ -1,5 +1,5 @@
 /**
- * 
+ *
  */
 package fr.agrometinfo.www.shared.service;
 
@@ -56,7 +56,7 @@ public interface LoginFormService {
      */
     @GET
     @Path(PATH_QUESTION_BY_CATEGORY)
-    QuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category);
+    QuestionDTO getQuestionByCategory(@QueryParam(value = "category") String category);
     /**
      * Returning the list of possible response for one question.
      * @param questionRef ref of question
@@ -64,29 +64,28 @@ public interface LoginFormService {
      */
     @GET
     @Path(PATH_RESPONSES_BY_QUESTION_LIST)
-    List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef);
+    List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") Long questionRef);
     /**
      * Insert response or text for specified question.<br>
      * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null. 
+     * If otherText is provided, {@code responseRef} must be null.
      * @param questionRef ref of question
      * @param responseRef ref of response or « null » String for null value
      * @param otherText text if response is other
-     * 
-     * @return
      */
     @POST
     @Path(PATH_INSERT_RESPONSE)
     void insertResponse(
-            @FormParam(value = "questionRef") final Long questionRef, 
-            @FormParam(value = "responseRef") @DefaultValue("null") final String responseRef, 
-            @FormParam(value = "otherText") @DefaultValue("null") final String otherText
+            @FormParam(value = "questionRef") Long questionRef,
+            @FormParam(value = "responseRef") @DefaultValue("null") String responseRef,
+            @FormParam(value = "otherText") @DefaultValue("null") String otherText
     );
     /**
      * Insert all responses of login form.
-     * @param responsesList
+     * @param data object to insert
+     * @return return code
      */
     @POST
     @Path(PATH_INSERT_RESPONSE_WITH_JSON)
-    String insertAllResponses(@RequestBody final LoginFormDataDTO data);
+    String insertAllResponses(@RequestBody LoginFormDataDTO data);
 }
-- 
GitLab


From eab03e1e27cefae59e49cd6c96ce81e858178222 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 24 May 2024 14:03:50 +0200
Subject: [PATCH 05/30] =?UTF-8?q?Ajout=20du=20texte=20de=20pr=C3=A9sentati?=
 =?UTF-8?q?on.=20Corrections=20suite=20revue=20de=20code=20d'Olivier=20fix?=
 =?UTF-8?q?es=20#5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../fr/agrometinfo/www/client/style.css       | 208 ------------------
 1 file changed, 208 deletions(-)
 delete mode 100644 www-client/src/main/resources/fr/agrometinfo/www/client/style.css

diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/style.css b/www-client/src/main/resources/fr/agrometinfo/www/client/style.css
deleted file mode 100644
index bdd54e8..0000000
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/style.css
+++ /dev/null
@@ -1,208 +0,0 @@
-footer {
-    background-color: #353854;
-}
-footer.footer {
-    max-height: 2em;
-    min-height: 2em;
-    text-align: center;
-    margin-left: auto;
-    margin-right: auto;
-}
-footer, footer a {
-    color: #b5b5c8;
-    font-weight: bold;
-    margin-right: 1em;
-    padding: 0.3em;
-}
-footer a:hover {
-    color: #ffffff;
-}
-select {
-    width: 100%;
-    height: 2.5em;
-    font-size: 1.1em;
-}
-/**
- * Font awesome icons.
- */
-/*!
- * Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
-@font-face {
-    font-family:"Font Awesome 5 Free";
-    font-style:normal;
-    font-weight:900;
-    src:url(vendors/fa/fa-solid-900.eot);
-    src:url(vendors/fa/fa-solid-900.eot?#iefix) format("embedded-opentype"),
-        url(vendors/fa/fa-solid-900.woff2) format("woff2"),
-        url(vendors/fa/fa-solid-900.woff) format("woff"),
-        url(vendors/fa/fa-solid-900.ttf) format("truetype"),
-        url(vendors/fa/fa-solid-900.svg#fontawesome) format("svg")
-}
-.fa, .fab, .fal, .far, .fas {
-    -moz-osx-font-smoothing: grayscale;
-    -webkit-font-smoothing: antialiased;
-    display: inline-block;
-    font-style: normal;
-    font-variant: normal;
-    text-rendering: auto;
-    line-height: 1;
-}
-.fa, .fas {
-    font-family: "Font Awesome 5 Free";
-    font-weight: 900;
-}
-.fa-cloud-showers-heavy::before {
-    content: "\f740";
-}
-.fa-snowflake::before {
-    content: "\f2dc";
-}
-.fa-thermometer-half::before {
-    content: "\f2c9";
-}
-.fa-thermometer-quarter::before {
-    content: "\f2ca";
-}
-.fa-thermometer-three-quarters::before {
-    content: "\f2c8";
-}
-.agroclim-apps img {
-    height: 60px;
-    width: 60px;
-}
-.agroclim-apps ul {
-    box-sizing: content-box;
-    width: 200px;
-}
-.agroclim-apps.dom-ui.menu .simple-menu-item {
-    display: inline-block;
-    padding-bottom: 15px;
-    text-align: center;
-    width: 100px;
-}
-.agrometinfo-navbar .navbar-header {
-    min-height: var(--logo-height);
-    padding-top: 0px;
-}
-.agrometinfo-navbar .navbar-header .menu-toggle .bars {
-    line-height: 15px;
-}
-.agrometinfo-leftsidebar.sidebar,
-.agrometinfo-rightsidebar.right-sidebar {
-	height: calc(100vh - var(--logo-height));
-    top: var(--logo-height);
-}
-.agrometinfo-rightsidebar.right-sidebar {
-	width: var(--rightsidebar-width);
-}
-.agrometinfo-rightsidebar.right-sidebar.slide-out-right {
-	right: calc(-1 * var(--rightsidebar-width));
-}
-.agrometinfo-topbar.navbar {
-    border: 0px;
-}
-.agrometinfo-topbar.navbar-nav > li > a {
-    padding-bottom: 6px;
-    padding-top: 6px;
-}
-.float-right {
-	float: right;
-}
-div.idp {
-    padding: 0.5em;
-}
-.idp img {
-    padding-right: 1em;
-}
-.idp span {
-    font-weight: bold;
-}
-.indicator-categories > i {
-    font-size: 40px;
-    padding-left: 5px;
-}
-.javascript-disabled {
-    width: 22em;
-    position: absolute;
-    left: 50%;
-    margin-left: -11em;
-    color: red;
-    background-color: white;
-    border: 1px solid red;
-    padding: 4px;
-    font-family: sans-serif
-}
-.logo-in img {
-    background-color: white;
-    height: var(--logo-height);
-}
-#mapContainer .ol-attribution.ol-uncollapsible {
-    top: auto;
-    right: auto;
-    bottom: 0.2em;
-    right: .5em;
-}
-#mapContainer .ol-control button {
-    /* same as layer switcher */
-    height: 38px;
-    width: 38px;
-}
-#mapContainer .ol-full-screen {
-    top: .5em;
-    right: .5em;
-    bottom: auto;
-    left: auto;
-}
-#mapContainer .layer-switcher {
-    top: 3.5em;
-    right: .5em;
-    bottom: auto;
-    left: auto;
-}
-#mapContainer .ol-zoom-extent {
-    top: auto;
-    bottom: 8em;
-    right: .5em;
-    left: auto;
-}
-#mapContainer .ol-zoom {
-    top: auto;
-    bottom: 2em;
-    right: .5em;
-    left: auto;
-}
-:root {
-    --logo-height: 50px;
-}
-@media screen and (max-width: 450px) {
-	.navbar-brand {
-		display: none;
-	}
-}
-@media screen and (max-width: 700px) {
-	:root {
-		--rightsidebar-width: 90%;
-	}
-}
-@media screen and (min-width: 700px) {
-	:root {
-		--rightsidebar-width: 400px;
-	}
-}
-
-/* Formulaire d'enquête */
-.login-paragraph {
-	font-size: 18px;
-}
-.login-checkbox {
-	margin: 0 0 2px 5px;
-}
-.login-checkbox .field-cntr {
-	padding: 0 10px !important;
-}
-.login-checkbox label {
-	top: 0px !important;
-	margin-bottom: 0px !important;
-}
-- 
GitLab


From 07a4f73a13c5530048b67f8cc78db0e03a933744 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 24 May 2024 14:08:13 +0200
Subject: [PATCH 06/30] Checkstyle

---
 pom.xml                                                     | 1 +
 .../main/java/fr/agrometinfo/www/client/view/LoginView.java | 5 ++++-
 .../agrometinfo/www/client/i18n/AppConstants_fr.properties  | 6 +++---
 .../resources/fr/agrometinfo/www/client/public/style.css    | 2 +-
 .../fr/agrometinfo/www/server/rs/LoginFormResource.java     | 4 ++--
 5 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/pom.xml b/pom.xml
index 5cfaed5..ee1f2a4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -433,6 +433,7 @@
             <failOnViolation>true</failOnViolation>
             <propertyExpansion>basedir=${project.basedir}</propertyExpansion>
             <violationSeverity>warning</violationSeverity>
+            <consoleOutput>true</consoleOutput>
           </configuration>
         </plugin>
         <plugin>
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
index ab0502c..a4b9431 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
@@ -11,6 +11,7 @@ import org.dominokit.domino.ui.forms.CheckBox;
 import org.dominokit.domino.ui.forms.TextArea;
 import org.dominokit.domino.ui.modals.ModalDialog;
 import org.dominokit.rest.shared.request.FailedResponseBean;
+import org.gwtproject.safehtml.shared.SafeHtmlBuilder;
 import org.jboss.elemento.Elements;
 
 import com.google.gwt.core.client.GWT;
@@ -72,7 +73,9 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
 
         this.modal = ModalDialog.create(CSTS.loginFormTitle()).large().setAutoClose(true);
         this.modal
-        .appendChild(Elements.p().textContent(CSTS.loginFormDescription()).css("login-paragraph"));
+        .appendChild(Elements.p()
+                .innerHtml(new SafeHtmlBuilder().appendHtmlConstant(CSTS.loginFormDescription()).toSafeHtml())
+                );
 
         for (Map.Entry<QuestionDTO, List<ResponseDTO>> entry : this.availableResponses.entrySet()) {
             final QuestionDTO k = entry.getKey(); // Pour chaque question
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index 685c04a..e68475e 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -54,8 +54,8 @@ whyConnectionIsRequired = Vous devez vous identifier pour accéder à AgroMetInf
 validate = Valider
 ignore = Ignorer
 loginFormTitle = Formulaire d'enquête
-loginFormDescription = Merci de compléter ce formulaire d'enquête afin de continuer sur AgroMetInfo
+loginFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider àla faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous. L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
 loginFormOtherTextCheckbox = Autre, à préciser
-loginFormOtherTextTooltip = Vous devez cliquer sur la case à cocher ci-dessus pour activer ce champ de saisie.
+loginFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
 loginFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
-loginFormFail = Vos réponses au formulaire n'ont pas pu être, mais vous pouvez tout de même utiliser AgroMetInfo.
\ No newline at end of file
+loginFormFail = Vos réponses au formulaire n'ont pas pu être enregistrées, mais vous pouvez tout de même utiliser AgroMetInfo.
\ No newline at end of file
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
index 1cf0b4c..4dac15b 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/public/style.css
@@ -351,7 +351,7 @@ body > .modal-backdrop {
 
 /* Formulaire d'enquête */
 .login-paragraph {
-	font-size: 18px;
+	font-size: 15px;
 }
 .login-checkbox {
 	margin: 0 0 2px 5px;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
index b5454cd..ef3dac4 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
@@ -44,7 +44,7 @@ public class LoginFormResource implements LoginFormService {
      * @param question entity
      * @return dto
      */
-    public static QuestionDTO toDto(final Question question) {
+    static QuestionDTO toDto(final Question question) {
         final QuestionDTO dto = new QuestionDTO();
         dto.setQuestionRef(question.getQuestionRef());
         dto.setCategory(question.getCategory());
@@ -56,7 +56,7 @@ public class LoginFormResource implements LoginFormService {
      * @param response entity
      * @return dto
      */
-    public static ResponseDTO toDto(final Response response) {
+    static ResponseDTO toDto(final Response response) {
         final ResponseDTO dto = new ResponseDTO();
         dto.setResponseRef(response.getResponseRef());
         dto.setQuestion(toDto(response.getQuestion()));
-- 
GitLab


From 7b37f5d65985be947a7816a7f92eb1ac87ed1999 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 24 May 2024 15:48:29 +0200
Subject: [PATCH 07/30] =?UTF-8?q?Texte=20de=20pr=C3=A9sentation=20en=20ang?=
 =?UTF-8?q?lais.=20fixes=20#5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/fr/agrometinfo/www/client/i18n/AppConstants.java | 8 +++++++-
 .../www/client/i18n/AppConstants_fr.properties            | 2 +-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index 474fe81..0c09cec 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -358,7 +358,13 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     /**
      * @return translation
      */
-    @DefaultStringValue("Please complete this survey form to continue on AgroMetInfo.")
+    @DefaultStringValue("Welcome to the new version of AgroMetInfo !<br/>"
+            + "To help us improve the application and make it as responsive as possible to your needs, "
+            + "<b>please fill in the short survey below</b> (it will take 2 minutes maximum).<br/>"
+            + "All information is anonymous, but will help us to better understand how you use the application."
+            + "<br/><b>Don't hesitate to give us your email address</b> "
+            + "so that we can keep you informed of all developments in the coming months and years."
+            + "<br/><br/>The AgroMetInfo development team")
     String loginFormDescription();
     /**
      * @return translation
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index e68475e..fe21225 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -54,7 +54,7 @@ whyConnectionIsRequired = Vous devez vous identifier pour accéder à AgroMetInf
 validate = Valider
 ignore = Ignorer
 loginFormTitle = Formulaire d'enquête
-loginFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider àla faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous. L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
+loginFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider à la faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous (cela prendra 2 minutes maximum). L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
 loginFormOtherTextCheckbox = Autre, à préciser
 loginFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
 loginFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
-- 
GitLab


From 3b7b610a8a8dbee44fcb237f888b6e40c61a572b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 24 May 2024 16:24:17 +0200
Subject: [PATCH 08/30] Suite revue de code d'Olivier (23/05/2024) : -
 Commentaires SQL en anglais - Nom des tables au singulier

---
 .../www/server/model/{UserResponses.java => UserResponse.java}    | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename www-server/src/main/java/fr/agrometinfo/www/server/model/{UserResponses.java => UserResponse.java} (100%)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
similarity index 100%
rename from www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponses.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
-- 
GitLab


From 8e2fdf6d82c7fd5aad4045473629036936b702cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Mon, 27 May 2024 09:26:30 +0200
Subject: [PATCH 09/30] Renommer les tables au singulier Commentaires en
 anglais

---
 sql/init_data.h2.sql                          |  4 +--
 sql/init_data.postgresql.sql                  |  8 +++---
 sql/schema.tables.sql                         | 26 ++++++++++---------
 .../www/server/dao/UserResponsesDao.java      |  4 +--
 .../server/dao/UserResponsesDaoHibernate.java | 14 +++++-----
 .../www/server/model/Question.java            |  2 +-
 .../www/server/model/Response.java            |  2 +-
 .../www/server/model/UserResponse.java        |  5 ++--
 .../www/server/LoginFormHibernateTest.java    | 10 +++----
 .../www/server/rs/LoginFormResourceTest.java  | 12 ++++-----
 .../test/resources/META-INF/persistence.xml   |  2 +-
 11 files changed, 45 insertions(+), 44 deletions(-)

diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index 6095f2c..f994c42 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -127,9 +127,9 @@ INSERT INTO simulation (date, simulationid, started, ended) VALUES
 --
 REFRESH MATERIALIZED VIEW v_pra_dailyvalue;
 
-INSERT INTO questions(question_ref, category, fullname)
+INSERT INTO question(question_ref, category, fullname)
 	SELECT * FROM CSVREAD('../sql/questions.csv');
 
-INSERT INTO responses(question_ref, fullname)
+INSERT INTO response(question_ref, fullname)
 	SELECT * FROM CSVREAD('../sql/responses.csv', null, 'fieldSeparator=;');
 
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index 056f7c5..b63281d 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -11,8 +11,8 @@ DELETE FROM period;
 DELETE FROM i18n;
 DELETE FROM i18nkey;
 DELETE FROM locale;
-DELETE FROM questions;
-DELETE FROM responses;
+DELETE FROM question;
+DELETE FROM response;
 
 -- translations
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_translation (
@@ -170,9 +170,9 @@ INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
     ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
 
 -- questions
-\COPY questions(question_ref, category, fullname) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
+\COPY question(question_ref, category, fullname) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
 
 -- responses
 -- WARNING : responses CSV file use semicolon (« ; ») as separator !!
-\COPY responses(question_ref, fullname) FROM sql/responses.csv WITH DELIMITER ';' HEADER CSV;
+\COPY response(question_ref, fullname) FROM responses.csv WITH DELIMITER ';' HEADER CSV;
 
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index a469a08..fdeb30b 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -214,33 +214,35 @@ CREATE TABLE IF NOT EXISTS dailyvisit(
 );
 COMMENT ON TABLE dailyvisit IS 'Number of visits per day.';
 
--- Tables pour le formulaire d'enquête
-CREATE TABLE IF NOT EXISTS questions (
+-- Tables for survey form - #5
+CREATE TABLE IF NOT EXISTS question (
 	question_ref serial NOT NULL,
 	category varchar(20) NOT NULL,
 	fullname varchar(255) NULL,
-	CONSTRAINT questions_pk PRIMARY KEY (question_ref)
+	CONSTRAINT question_pk PRIMARY KEY (question_ref)
 );
-COMMENT ON TABLE questions IS 'Questions pour le formulaire d''enquête';
+COMMENT ON TABLE question IS 'Questions for survey form';
 
-CREATE TABLE IF NOT EXISTS responses (
+CREATE TABLE IF NOT EXISTS response (
 	response_ref serial NOT NULL,
 	question_ref int4 NOT NULL,
 	fullname varchar NULL,
-	CONSTRAINT responses_pk PRIMARY KEY (response_ref),
-	CONSTRAINT responses_questions_fk FOREIGN KEY (question_ref) REFERENCES questions(question_ref)
+	CONSTRAINT response_pk PRIMARY KEY (response_ref),
+	CONSTRAINT response_question_fk FOREIGN KEY (question_ref) REFERENCES question(question_ref)
 );
-CREATE TABLE IF NOT EXISTS user2responses (
+COMMENT ON TABLE response IS 'Options responses for questions.';
+
+CREATE TABLE IF NOT EXISTS user2response (
 	user_response_ref serial NOT NULL,
 	datetime timestamp NOT NULL,
 	question_ref int4 NOT NULL,
 	response_ref int4 NULL,
 	other_text varchar NULL,
-	CONSTRAINT user2responses_pk PRIMARY KEY (user_response_ref),
-	CONSTRAINT user2responses_questions_fk FOREIGN KEY (question_ref) REFERENCES questions(question_ref),
-	CONSTRAINT user2responses_responses_fk FOREIGN KEY (response_ref) REFERENCES responses(response_ref)
+	CONSTRAINT user2response_pk PRIMARY KEY (user_response_ref),
+	CONSTRAINT user2response_question_fk FOREIGN KEY (question_ref) REFERENCES question(question_ref),
+	CONSTRAINT user2response_response_fk FOREIGN KEY (response_ref) REFERENCES response(response_ref)
 );
-COMMENT ON TABLE user2responses IS 'Réponses des utilisateurs';
+COMMENT ON TABLE user2response IS 'Responses of user to questions, and other text if present';
 
 
 CREATE OR REPLACE VIEW v_i18n
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
index d45e51d..06260f1 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -4,7 +4,7 @@ import java.util.List;
 
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
-import fr.agrometinfo.www.server.model.UserResponses;
+import fr.agrometinfo.www.server.model.UserResponse;
 /**
  * DAO for {@link UserResponsesDao}.
  * @author jdecome
@@ -15,7 +15,7 @@ public interface UserResponsesDao {
      * @param questionRef
      * @return list of user's responses
      */
-    List<UserResponses> findAllByQuestion(long questionRef);
+    List<UserResponse> findAllByQuestion(long questionRef);
     /**
      * Insert response or text for specified question.<br>
      * If r is provided, {@code otherText} must be null.<br>
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 8c9f128..9baf80b 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -11,7 +11,7 @@ import java.util.Map;
 
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
-import fr.agrometinfo.www.server.model.UserResponses;
+import fr.agrometinfo.www.server.model.UserResponse;
 import jakarta.enterprise.context.ApplicationScoped;
 
 /**
@@ -19,25 +19,25 @@ import jakarta.enterprise.context.ApplicationScoped;
  *
  */
 @ApplicationScoped
-public class UserResponsesDaoHibernate extends DaoHibernate<UserResponses> implements UserResponsesDao {
+public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implements UserResponsesDao {
     /** DAO for questions. */
     private QuestionsDao questionsDao = new QuestionsDaoHibernate();
     /** DAO for responses. */
     private ResponsesDao responsesDao = new ResponsesDaoHibernate();
     /** Default constructor. */
     public UserResponsesDaoHibernate() {
-        super(UserResponses.class);
+        super(UserResponse.class);
     }
     /** */
     @Override
-    public List<UserResponses> findAllByQuestion(final long questionRef) {
-        final String jpql = "SELECT u FROM UserResponses u WHERE u.question.questionRef = :ref";
-        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponses.class);
+    public List<UserResponse> findAllByQuestion(final long questionRef) {
+        final String jpql = "SELECT u FROM UserResponse u WHERE u.question.questionRef = :ref";
+        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponse.class);
     }
     /** */
     @Override
     public void insertResponse(final Question q, final Response r, final String otherText) {
-        final UserResponses ur = new UserResponses();
+        final UserResponse ur = new UserResponse();
         ur.setDateTime(Timestamp.valueOf(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
                 .format(LocalDateTime.now())));
         ur.setQuestion(q);
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
index fcbea50..57da32f 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -18,7 +18,7 @@ import lombok.Data;
  */
 @Data
 @Entity
-@Table(name = "questions")
+@Table(name = "question")
 public class Question {
     /** PK. */
     @Id
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
index 5e044c9..c48ef5c 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -20,7 +20,7 @@ import lombok.Data;
  */
 @Data
 @Entity
-@Table(name = "responses")
+@Table(name = "response")
 public class Response {
     /**
      * Reference of response.
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index 4938f83..1193939 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -18,12 +18,11 @@ import lombok.Data;
 /**
  * User's responses values.
  * @author jdecome
- *
  */
 @Data
 @Entity
-@Table(name = "user2responses")
-public class UserResponses {
+@Table(name = "user2response")
+public class UserResponse {
     /** Primary key. */
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
index eba1d30..33d3395 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
@@ -24,7 +24,7 @@ import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
-import fr.agrometinfo.www.server.model.UserResponses;
+import fr.agrometinfo.www.server.model.UserResponse;
 
 /**
  * Test of UserResponsesDaoHibernate.
@@ -100,7 +100,7 @@ public class LoginFormHibernateTest {
         userResponsesDao.insertResponse(profession, r, null);
         listOfResponses.add(r);
         
-        List<UserResponses> list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), profession, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
         assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
@@ -114,7 +114,7 @@ public class LoginFormHibernateTest {
         }
         list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
         assertEquals(list.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + " + nbToInsert + " réponses supplémentaires");
-        for (final UserResponses ur : list) {
+        for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question");
             assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
             assertEquals(ur.getResponse().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
@@ -129,7 +129,7 @@ public class LoginFormHibernateTest {
         
         list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
         assertEquals(list.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les " + newNbResponses + " + la réponse libre");
-        for (final UserResponses ur : list) {
+        for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
             if (ur.getOtherText() != null) {    // on est sur une réponse libre
                 assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
@@ -161,7 +161,7 @@ public class LoginFormHibernateTest {
         
         list = userResponsesDao.findAllByQuestion(useCase.getQuestionRef());
         assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir deux éléments");
-        for (final UserResponses ur : list) {
+        for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
             if (ur.getOtherText() != null) {    // on est sur une réponse libre
                 assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
index 4bf7ab3..3ec6678 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
@@ -35,7 +35,7 @@ import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
-import fr.agrometinfo.www.server.model.UserResponses;
+import fr.agrometinfo.www.server.model.UserResponse;
 import fr.agrometinfo.www.server.service.MailService;
 import fr.agrometinfo.www.server.service.MailServiceImpl;
 import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
@@ -221,7 +221,7 @@ public class LoginFormResourceTest extends JerseyTest {
             listOfResponses.add(r);
             
             // On lit la réponse insérée, via le DAO
-            List<UserResponses> userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            List<UserResponse> userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
             assertEquals(userResponsesList.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
             assertEquals(userResponsesList.get(0).getQuestion(), origin, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
             assertEquals(userResponsesList.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
@@ -247,7 +247,7 @@ public class LoginFormResourceTest extends JerseyTest {
             userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
             assertEquals(userResponsesList.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + "
                                                                         + nbToInsert + " réponses supplémentaires");
-            for (final UserResponses ur : userResponsesList) {
+            for (final UserResponse ur : userResponsesList) {
                 assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question");
                 assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
                 assertEquals(ur.getResponse().getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
@@ -270,7 +270,7 @@ public class LoginFormResourceTest extends JerseyTest {
             userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
             assertEquals(userResponsesList.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les "
                                     + newNbResponses + " réponses + la réponse libre");
-            for (final UserResponses ur : userResponsesList) {
+            for (final UserResponse ur : userResponsesList) {
                 assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
                 if (ur.getOtherText() != null) {    // on est sur une réponse libre
                     assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
@@ -322,9 +322,9 @@ public class LoginFormResourceTest extends JerseyTest {
         
         // Pour le test, vérifier, pour chaque question, les réponses via le DAO
         for (final Question q : questionsDao.findAll()) {
-            final List<UserResponses> list = userResponsesDao.findAllByQuestion(q.getQuestionRef());
+            final List<UserResponse> list = userResponsesDao.findAllByQuestion(q.getQuestionRef());
             assertNotNull(list, "La liste de réponses de l'utilisateur ne doit pas être null");
-            for (final UserResponses ur : list) {
+            for (final UserResponse ur : list) {
                 assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
                 assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getFullName() + " »");
                 assertTrue(ur.getUserResponseRef() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
diff --git a/www-server/src/test/resources/META-INF/persistence.xml b/www-server/src/test/resources/META-INF/persistence.xml
index e0622b5..d790ccb 100644
--- a/www-server/src/test/resources/META-INF/persistence.xml
+++ b/www-server/src/test/resources/META-INF/persistence.xml
@@ -20,7 +20,7 @@
     <class>fr.agrometinfo.www.server.model.Simulation</class>
     <class>fr.agrometinfo.www.server.model.Question</class>
     <class>fr.agrometinfo.www.server.model.Response</class>
-    <class>fr.agrometinfo.www.server.model.UserResponses</class>
+    <class>fr.agrometinfo.www.server.model.UserResponse</class>
     <properties>
       <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:agrometinfo;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM '../sql/schema.types.h2.sql'\;RUNSCRIPT FROM '../sql/schema.tables.sql'\;RUNSCRIPT FROM '../sql/init_data.h2.sql';" />
       <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
-- 
GitLab


From 571049a604c175f0ad1cfe47e9829e603928273d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Mon, 27 May 2024 11:50:59 +0200
Subject: [PATCH 10/30] Renommer les colonnes des tables

---
 sql/init_data.h2.sql                          |  4 +-
 sql/init_data.postgresql.sql                  |  4 +-
 sql/questions.csv                             |  2 +-
 sql/responses.csv                             |  2 +-
 sql/schema.tables.sql                         | 34 +++++-----
 .../www/client/presenter/LoginPresenter.java  |  4 +-
 .../www/client/view/LoginView.java            |  6 +-
 .../www/server/dao/ResponsesDaoHibernate.java |  2 +-
 .../server/dao/UserResponsesDaoHibernate.java |  2 +-
 .../www/server/model/Question.java            | 13 ++--
 .../www/server/model/Response.java            | 13 ++--
 .../www/server/model/UserResponse.java        |  8 +--
 .../www/server/rs/LoginFormResource.java      | 26 ++++----
 .../www/server/service/MailServiceImpl.java   | 12 ++--
 .../www/server/LoginFormHibernateTest.java    | 40 ++++++------
 .../www/server/rs/LoginFormResourceTest.java  | 64 +++++++++----------
 .../www/shared/dto/QuestionDTO.java           | 26 ++++----
 .../www/shared/dto/ResponseDTO.java           | 20 +++---
 18 files changed, 143 insertions(+), 139 deletions(-)

diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index f994c42..74219fe 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -127,9 +127,9 @@ INSERT INTO simulation (date, simulationid, started, ended) VALUES
 --
 REFRESH MATERIALIZED VIEW v_pra_dailyvalue;
 
-INSERT INTO question(question_ref, category, fullname)
+INSERT INTO question(id, category, description)
 	SELECT * FROM CSVREAD('../sql/questions.csv');
 
-INSERT INTO response(question_ref, fullname)
+INSERT INTO response(question, description)
 	SELECT * FROM CSVREAD('../sql/responses.csv', null, 'fieldSeparator=;');
 
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index b63281d..d53ac68 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -170,9 +170,9 @@ INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
     ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
 
 -- questions
-\COPY question(question_ref, category, fullname) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
+\COPY question(id, category, description) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
 
 -- responses
 -- WARNING : responses CSV file use semicolon (« ; ») as separator !!
-\COPY response(question_ref, fullname) FROM responses.csv WITH DELIMITER ';' HEADER CSV;
+\COPY response(question, description) FROM responses.csv WITH DELIMITER ';' HEADER CSV;
 
diff --git a/sql/questions.csv b/sql/questions.csv
index faac491..927f34e 100644
--- a/sql/questions.csv
+++ b/sql/questions.csv
@@ -1,4 +1,4 @@
-question_ref,category,fullname
+id,category,description
 1,profession,Quelle est votre profession ?
 2,origin,Comment avez-vous connu AgroMetInfo ?
 3,useCase,Dans quel but voulez-vous utiliser cette application ?
diff --git a/sql/responses.csv b/sql/responses.csv
index a88a2c0..930e6f7 100644
--- a/sql/responses.csv
+++ b/sql/responses.csv
@@ -1,4 +1,4 @@
-question_ref;fullname
+question;description
 1;Agriculteur
 1;Conseiller agricole (Chambre d'agriculture, Entreprise de service)
 1;Scientifique
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index fdeb30b..c4a57a7 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -216,31 +216,31 @@ COMMENT ON TABLE dailyvisit IS 'Number of visits per day.';
 
 -- Tables for survey form - #5
 CREATE TABLE IF NOT EXISTS question (
-	question_ref serial NOT NULL,
-	category varchar(20) NOT NULL,
-	fullname varchar(255) NULL,
-	CONSTRAINT question_pk PRIMARY KEY (question_ref)
+	id SERIAL NOT NULL,
+	category VARCHAR(20) NOT NULL,
+	description VARCHAR(255) NULL,
+	CONSTRAINT PK_question PRIMARY KEY (id)
 );
 COMMENT ON TABLE question IS 'Questions for survey form';
 
 CREATE TABLE IF NOT EXISTS response (
-	response_ref serial NOT NULL,
-	question_ref int4 NOT NULL,
-	fullname varchar NULL,
-	CONSTRAINT response_pk PRIMARY KEY (response_ref),
-	CONSTRAINT response_question_fk FOREIGN KEY (question_ref) REFERENCES question(question_ref)
+	id SERIAL NOT NULL,
+	question INT4 NOT NULL,
+	description VARCHAR NULL,
+	CONSTRAINT PK_response PRIMARY KEY (id),
+	CONSTRAINT FK_response_question FOREIGN KEY (question) REFERENCES question(id)
 );
 COMMENT ON TABLE response IS 'Options responses for questions.';
 
 CREATE TABLE IF NOT EXISTS user2response (
-	user_response_ref serial NOT NULL,
-	datetime timestamp NOT NULL,
-	question_ref int4 NOT NULL,
-	response_ref int4 NULL,
-	other_text varchar NULL,
-	CONSTRAINT user2response_pk PRIMARY KEY (user_response_ref),
-	CONSTRAINT user2response_question_fk FOREIGN KEY (question_ref) REFERENCES question(question_ref),
-	CONSTRAINT user2response_response_fk FOREIGN KEY (response_ref) REFERENCES response(response_ref)
+	id SERIAL NOT NULL,
+	datetime TIMESTAMP NOT NULL,
+	question INT4 NOT NULL,
+	response INT4 NULL,
+	other_text VARCHAR NULL,
+	CONSTRAINT PK_user2response PRIMARY KEY (id),
+	CONSTRAINT FK_user2response_question FOREIGN KEY (question) REFERENCES question(id),
+	CONSTRAINT FK_user2response_response FOREIGN KEY (response) REFERENCES response(id)
 );
 COMMENT ON TABLE user2response IS 'Responses of user to questions, and other text if present';
 
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
index a949503..336eba3 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
@@ -97,7 +97,7 @@ public final class LoginPresenter implements Presenter {
 
         for (final Long ref : responsesRef) {
             // on récupère la réponse correspondante à la référence
-            final ResponseDTO rDto = this.responses.stream().filter(r -> r.getResponseRef() == ref).findFirst().get();
+            final ResponseDTO rDto = this.responses.stream().filter(r -> r.getId() == ref).findFirst().get();
             if (rDto != null) {
                 if (!responsesDto.contains(rDto)) {
                     responsesDto.add(rDto);
@@ -111,7 +111,7 @@ public final class LoginPresenter implements Presenter {
         }
         // ajout de la question, si celle çi n'a qu'une réponse libre
         this.questions.forEach((q) -> {
-            if (!questionsDto.contains(q) && otherTextMap.containsKey(q.getQuestionRef())) {
+            if (!questionsDto.contains(q) && otherTextMap.containsKey(q.getId())) {
                 questionsDto.add(q);
             }
         });
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
index a4b9431..3e175a4 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
@@ -81,11 +81,11 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
             final QuestionDTO k = entry.getKey(); // Pour chaque question
 
             // on affiche le libellé
-            this.modal.appendChild(Elements.h(SECTION_TITLE_LEVEL, "• " + k.getFullName()));
+            this.modal.appendChild(Elements.h(SECTION_TITLE_LEVEL, "• " + k.getDescription()));
 
             // on affiche les réponses possibles
             entry.getValue().forEach((r) -> {
-                final CheckBox cb = CheckBox.create(r.getFullname()).id(Long.toString(r.getResponseRef()))
+                final CheckBox cb = CheckBox.create(r.getDescription()).id(Long.toString(r.getId()))
                         .css("login-checkbox");
                 this.modal.appendChild(cb);
                 this.checkBoxList.add(cb);
@@ -111,7 +111,7 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
             });
             this.checkBoxList.add(otherCb);
             this.modal.appendChild(otherCb).appendChild(otherText);
-            otherTextArea.put(k.getQuestionRef(), otherText);
+            otherTextArea.put(k.getId(), otherText);
         }
         // active le bouton Valider si au moins une case a été cochée
         this.checkBoxList.forEach((c) -> {
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
index ecaebf4..ed2ad39 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
@@ -24,7 +24,7 @@ public class ResponsesDaoHibernate extends DaoHibernate<Response> implements Res
     /** */
     @Override
     public List<Response> findAllByQuestion(final long questionRef) {
-        final String jpql = "SELECT r FROM Response r WHERE r.question.questionRef = :ref";
+        final String jpql = "SELECT r FROM Response r WHERE r.question.id = :ref";
         return super.findAllByJPQL(jpql, Map.of("ref", questionRef), Response.class);
     }
     /** */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 9baf80b..8d147b9 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -31,7 +31,7 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
     /** */
     @Override
     public List<UserResponse> findAllByQuestion(final long questionRef) {
-        final String jpql = "SELECT u FROM UserResponse u WHERE u.question.questionRef = :ref";
+        final String jpql = "SELECT u FROM UserResponse u WHERE u.question.id = :ref";
         return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponse.class);
     }
     /** */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
index 57da32f..8f26872 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -20,11 +20,14 @@ import lombok.Data;
 @Entity
 @Table(name = "question")
 public class Question {
-    /** PK. */
+    /**
+     * Reference of question.<br>
+     * Primary key.
+     */
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
-    @Column(name = "question_ref")
-    private long questionRef;
+    @Column(name = "id")
+    private long id;
     /**
      * Catogory of question.
      */
@@ -33,6 +36,6 @@ public class Question {
     /**
      * Label of question.
      */
-    @Column(name = "fullname")
-    private String fullName;
+    @Column(name = "description")
+    private String description;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
index c48ef5c..8e132f9 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -23,21 +23,22 @@ import lombok.Data;
 @Table(name = "response")
 public class Response {
     /**
-     * Reference of response.
+     * Reference of response.<br>
+     * Primary key.
      */
     @Id
     @GeneratedValue(strategy = GenerationType.AUTO)
-    @Column(name = "response_ref")
-    private long responseRef;
+    @Column(name = "id")
+    private long id;
     /**
      * Question related to response.
      */
     @OneToOne
-    @JoinColumn(name = "question_ref")
+    @JoinColumn(name = "question")
     private Question question;
     /**
      * Label of the response.
      */
-    @Column(name = "fullname")
-    private String fullname;
+    @Column(name = "description")
+    private String description;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index 1193939..a143f06 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -26,21 +26,21 @@ public class UserResponse {
     /** Primary key. */
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
-    @Column(name = "user_response_ref")
-    private long userResponseRef;
+    @Column(name = "id")
+    private long id;
     /** Date time of user responses. */
     @Column(name = "datetime")
     private Timestamp dateTime;
     /** Related question. */
     @OneToOne
-    @JoinColumn(name = "question_ref")
+    @JoinColumn(name = "question")
     private Question question;
     /**
      * Related response.<br>
      * Can be null if it's a other response.
      */
     @OneToOne
-    @JoinColumn(name = "response_ref")
+    @JoinColumn(name = "response")
     private Response response;
     /**
      * Other text response.<br>
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
index ef3dac4..88f81e7 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
@@ -46,9 +46,9 @@ public class LoginFormResource implements LoginFormService {
      */
     static QuestionDTO toDto(final Question question) {
         final QuestionDTO dto = new QuestionDTO();
-        dto.setQuestionRef(question.getQuestionRef());
+        dto.setId(question.getId());
         dto.setCategory(question.getCategory());
-        dto.setFullName(question.getFullName());
+        dto.setDescription(question.getDescription());
         return dto;
     }
     /**
@@ -58,9 +58,9 @@ public class LoginFormResource implements LoginFormService {
      */
     static ResponseDTO toDto(final Response response) {
         final ResponseDTO dto = new ResponseDTO();
-        dto.setResponseRef(response.getResponseRef());
+        dto.setId(response.getId());
         dto.setQuestion(toDto(response.getQuestion()));
-        dto.setFullname(response.getFullname());
+        dto.setDescription(response.getDescription());
         return dto;
     }
     /**
@@ -70,9 +70,9 @@ public class LoginFormResource implements LoginFormService {
      */
     private static Question toEntity(final QuestionDTO dto) {
         final Question question = new Question();
-        question.setQuestionRef(dto.getQuestionRef());
+        question.setId(dto.getId());
         question.setCategory(dto.getCategory());
-        question.setFullName(dto.getFullName());
+        question.setDescription(dto.getDescription());
         return question;
     }
     /**
@@ -82,9 +82,9 @@ public class LoginFormResource implements LoginFormService {
      */
     private static Response toEntity(final ResponseDTO dto) {
         final Response response = new Response();
-        response.setResponseRef(dto.getResponseRef());
+        response.setId(dto.getId());
         response.setQuestion(toEntity(dto.getQuestion()));
-        response.setFullname(dto.getFullname());
+        response.setDescription(dto.getDescription());
         return response;
     }
     /**
@@ -215,9 +215,9 @@ public class LoginFormResource implements LoginFormService {
         for (final QuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
             // Insérer les réponses associés
             final Question q = toEntity(dto);
-            final Long qRef = dto.getQuestionRef();
+            final Long qRef = dto.getId();
             List<ResponseDTO> associatedResponses = data.getResponses()
-                    .stream().filter(f -> f.getQuestion().getQuestionRef() == qRef)
+                    .stream().filter(f -> f.getQuestion().getId() == qRef)
                     .collect(Collectors.toList());
 
             // On insère les réponses prédéfinies
@@ -227,9 +227,9 @@ public class LoginFormResource implements LoginFormService {
             }
 
             // Si il existe une réponse libre, l'insérer
-            if (data.getOtherTextMap().containsKey(dto.getQuestionRef())
-                    && data.getOtherTextMap().get(dto.getQuestionRef()) != null) {
-                this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getQuestionRef()));
+            if (data.getOtherTextMap().containsKey(dto.getId())
+                    && data.getOtherTextMap().get(dto.getId()) != null) {
+                this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getId()));
             }
         }
 
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
index ce6cc68..c4dcaa4 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -355,14 +355,14 @@ public class MailServiceImpl implements MailService {
         builder.append("Il a répondu à " + data.getQuestions().size() + " question(s) :\n");
 
         data.getQuestions().forEach((q) -> {
-            builder.append("\t• « " + q.getFullName() + " » :\n");
-            data.getResponses().stream().filter(f -> f.getQuestion().getQuestionRef() == q.getQuestionRef())
+            builder.append("\t• « " + q.getDescription() + " » :\n");
+            data.getResponses().stream().filter(f -> f.getQuestion().getId() == q.getId())
                 .collect(Collectors.toList())
-                .forEach((r) -> builder.append("\t\t- " + r.getFullname() + "\n"));
+                .forEach((r) -> builder.append("\t\t- " + r.getDescription() + "\n"));
 
-            if (data.getOtherTextMap().containsKey(q.getQuestionRef())
-                    && data.getOtherTextMap().get(q.getQuestionRef()) != null) {
-                builder.append("\t\t- Réponse libre : « " + data.getOtherTextMap().get(q.getQuestionRef()) + " »\n");
+            if (data.getOtherTextMap().containsKey(q.getId())
+                    && data.getOtherTextMap().get(q.getId()) != null) {
+                builder.append("\t\t- Réponse libre : « " + data.getOtherTextMap().get(q.getId()) + " »\n");
             }
             builder.append("\n");
         });
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
index 33d3395..65f4d27 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
@@ -45,9 +45,9 @@ public class LoginFormHibernateTest {
         assertNotEquals(list.size(), 0, "La liste de questions est vide");
         for (final Question q : list) {
             // recherche de la question par sa référence
-            final Question foundedByRef = questionsDao.findByRef(q.getQuestionRef());
-            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getQuestionRef() + " est null");
-            assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getQuestionRef() + " ne correspond pas");
+            final Question foundedByRef = questionsDao.findByRef(q.getId());
+            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
+            assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
             
             // recherche de la question par sa catégorie
             final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
@@ -62,24 +62,24 @@ public class LoginFormHibernateTest {
         final List<Response> responses = responsesDao.findAll();
         assertNotEquals(responses.size(), 0, "La liste de réponses est vide");
         for (final Response r : responses) {
-            assertTrue(questions.contains(r.getQuestion()), "La liste de questions ne contient pas la question " + r.getQuestion().getQuestionRef());
+            assertTrue(questions.contains(r.getQuestion()), "La liste de questions ne contient pas la question " + r.getQuestion().getId());
             
             // Test à partir de la question correspondante à la réponse
             final Question q = r.getQuestion();
-            assertNotNull(q, "La question correspondante à la réponse " + r.getResponseRef() + " est null");
+            assertNotNull(q, "La question correspondante à la réponse " + r.getId() + " est null");
             
-            final Question foundedByRef = questionsDao.findByRef(q.getQuestionRef());
-            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getQuestionRef() + " est null");
-            assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getQuestionRef() + " ne correspond pas");
+            final Question foundedByRef = questionsDao.findByRef(q.getId());
+            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
+            assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
             
             final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
             assertNotNull(foundedByCategory, "La question correspondante à la catégorie « " + q.getCategory() + " » est null");
             assertEquals(foundedByCategory, q, "La question correspondante à la catégorie « " + q.getCategory() + " » est vide");
             
             // recherche de la réponse par sa référence
-            final Response responseByRef = responsesDao.findByRef(r.getResponseRef());
-            assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getResponseRef() + " est null");
-            assertEquals(responseByRef, r, "La réponse correspondante à la réponse " + r.getResponseRef() + " est vide");
+            final Response responseByRef = responsesDao.findByRef(r.getId());
+            assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getId() + " est null");
+            assertEquals(responseByRef, r, "La réponse correspondante à la réponse " + r.getId() + " est vide");
         }
     }
     
@@ -87,7 +87,7 @@ public class LoginFormHibernateTest {
     public void setUserResponses() {
         final Question profession = questionsDao.findByCategory("profession");
         assertNotNull(profession, "Aucune question ne correspond à la catégorie « profession »");
-        List<Response> responses = responsesDao.findAllByQuestion(profession.getQuestionRef());
+        List<Response> responses = responsesDao.findAllByQuestion(profession.getId());
         assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « profession »");
         assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « profession » est vide");
         for (final Response r : responses) {
@@ -100,7 +100,7 @@ public class LoginFormHibernateTest {
         userResponsesDao.insertResponse(profession, r, null);
         listOfResponses.add(r);
         
-        List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getId());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), profession, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
         assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
@@ -112,7 +112,7 @@ public class LoginFormHibernateTest {
             userResponsesDao.insertResponse(profession, res, null);
             listOfResponses.add(res);
         }
-        list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        list = userResponsesDao.findAllByQuestion(profession.getId());
         assertEquals(list.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + " + nbToInsert + " réponses supplémentaires");
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question");
@@ -127,7 +127,7 @@ public class LoginFormHibernateTest {
         String other = "Ceci est une réponse libre";
         userResponsesDao.insertResponse(profession, null, other);
         
-        list = userResponsesDao.findAllByQuestion(profession.getQuestionRef());
+        list = userResponsesDao.findAllByQuestion(profession.getId());
         assertEquals(list.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les " + newNbResponses + " + la réponse libre");
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
@@ -143,23 +143,23 @@ public class LoginFormHibernateTest {
         // Essai de l'insertion par les références, avec une autre question
         final Question useCase = questionsDao.findByCategory("useCase");
         assertNotNull(useCase, "Aucune question ne correspond à la catégorie « useCase »");
-        responses = responsesDao.findAllByQuestion(useCase.getQuestionRef());
+        responses = responsesDao.findAllByQuestion(useCase.getId());
         assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « useCase »");
         assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « useCase » est vide");
         
         r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
-        userResponsesDao.insertResponse(useCase.getQuestionRef(), r.getResponseRef(), null);
+        userResponsesDao.insertResponse(useCase.getId(), r.getId(), null);
         
-        list = userResponsesDao.findAllByQuestion(useCase.getQuestionRef());
+        list = userResponsesDao.findAllByQuestion(useCase.getId());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), useCase, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
         assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
         assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
         
         other = other.concat(" pour la question « useCase »");
-        userResponsesDao.insertResponse(useCase.getQuestionRef(), null, other);
+        userResponsesDao.insertResponse(useCase.getId(), null, other);
         
-        list = userResponsesDao.findAllByQuestion(useCase.getQuestionRef());
+        list = userResponsesDao.findAllByQuestion(useCase.getId());
         assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir deux éléments");
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
index 3ec6678..e36a2bb 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
@@ -129,7 +129,7 @@ public class LoginFormResourceTest extends JerseyTest {
             assertNotEquals(allResponses.size(), 0, "La liste de réponses est vide");
             
             for (final Response r : allResponses) {
-                assertTrue(questions.contains(r.getQuestion()), "La question de la réponse « " + r.getFullname() + " » n'est pas contenue dans la liste des questions");
+                assertTrue(questions.contains(r.getQuestion()), "La question de la réponse « " + r.getDescription() + " » n'est pas contenue dans la liste des questions");
             }
             
             // Pour chaque question, 
@@ -139,40 +139,40 @@ public class LoginFormResourceTest extends JerseyTest {
                                                 .queryParam("category", q.getCategory())
                                                 .request().get(String.class);
                 
-                assertNotNull(jsonCategory, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question n°" + q.getQuestionRef());
-                assertFalse(jsonCategory.isEmpty(), "La chaine de caractère JSON de la catégorie pour la question n° " + q.getQuestionRef() + " est vide");
+                assertNotNull(jsonCategory, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question n°" + q.getId());
+                assertFalse(jsonCategory.isEmpty(), "La chaine de caractère JSON de la catégorie pour la question n° " + q.getId() + " est vide");
                 
                 final Question questionByCategory = objectMapper.readValue(jsonCategory, new TypeReference<Question>() {});
-                assertNotNull(questionByCategory, "La conversion du JSON de la catégorie pour la question n° " + q.getQuestionRef() + " n'a pas fonctionnée");
-                assertEquals(questionByCategory, q, "La question lue par la catégorie ne correspond pas avec la question n° " + q.getQuestionRef() + "d'origine");
+                assertNotNull(questionByCategory, "La conversion du JSON de la catégorie pour la question n° " + q.getId() + " n'a pas fonctionnée");
+                assertEquals(questionByCategory, q, "La question lue par la catégorie ne correspond pas avec la question n° " + q.getId() + "d'origine");
                 
                 // On vérifie, via le DAO
                 final Question questionByDao = questionsDao.findByCategory(q.getCategory());
-                assertNotNull(questionByDao, "La lecture de la question n° " + q.getQuestionRef() + " via le DAO n'a pas fonctionnée");
-                assertEquals(questionByDao, q, "La question lue par le DAO ne correspond pas avec la question n° " + q.getQuestionRef() + "d'origine");
+                assertNotNull(questionByDao, "La lecture de la question n° " + q.getId() + " via le DAO n'a pas fonctionnée");
+                assertEquals(questionByDao, q, "La question lue par le DAO ne correspond pas avec la question n° " + q.getId() + "d'origine");
                 
                 // on récupère les réponses associées via le webservice
                 final String jsonResponses = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
-                                                .queryParam("questionRef", String.valueOf(q.getQuestionRef()))
+                                                .queryParam("questionRef", String.valueOf(q.getId()))
                                                 .request()
                                                 .get(String.class);
                 
-                assertNotNull(jsonResponses, "Impossible de récupérer la ressource (objet null)  pour la question n° " + q.getQuestionRef() + " est vide");
-                assertFalse(jsonResponses.isEmpty(), "La chaine de caractère JSON pour la question n° " + q.getQuestionRef() + " est vide");
+                assertNotNull(jsonResponses, "Impossible de récupérer la ressource (objet null)  pour la question n° " + q.getId() + " est vide");
+                assertFalse(jsonResponses.isEmpty(), "La chaine de caractère JSON pour la question n° " + q.getId() + " est vide");
                 
                 final List<Response> responses = objectMapper.readValue(jsonResponses, new TypeReference<List<Response>>() {});
-                assertNotNull(responses, "La conversion du JSON des réponses de la question n° " + q.getQuestionRef() + " n'a pas fonctionnée");
-                assertNotEquals(responses.size(), 0, " La liste de réponses pour la question n° " + q.getQuestionRef() + " est vide");
+                assertNotNull(responses, "La conversion du JSON des réponses de la question n° " + q.getId() + " n'a pas fonctionnée");
+                assertNotEquals(responses.size(), 0, " La liste de réponses pour la question n° " + q.getId() + " est vide");
                 
-                final List<Response> responsesByDao = responsesDao.findAllByQuestion(q.getQuestionRef());
-                assertNotNull(responsesByDao, " La lecture des réponses pour la question n° " + q.getQuestionRef() + " via le DAO n'a pas fonctionné");
-                assertNotEquals(responsesByDao.size(), 0, " La liste de réponses via le DAO pour la question n° " + q.getQuestionRef() + " est vide");
+                final List<Response> responsesByDao = responsesDao.findAllByQuestion(q.getId());
+                assertNotNull(responsesByDao, " La lecture des réponses pour la question n° " + q.getId() + " via le DAO n'a pas fonctionné");
+                assertNotEquals(responsesByDao.size(), 0, " La liste de réponses via le DAO pour la question n° " + q.getId() + " est vide");
                 assertEquals(responses, responsesByDao, "La liste de réponses via le webservice n'est pas identique avec la liste via le DAO");
                 
                 for (final Response r : responses) {
-                    assertNotNull(r.getQuestion(), "La question de la réponse n° " + r.getResponseRef() + " est null");
-                    assertEquals(r.getQuestion(), q, "La question de la réponse n° " + r.getResponseRef() + " ne correspond pas à la question");
-                    assertTrue(responsesByDao.contains(r), "La liste via le DAO ne contient pas la réponse n° " + r.getResponseRef());
+                    assertNotNull(r.getQuestion(), "La question de la réponse n° " + r.getId() + " est null");
+                    assertEquals(r.getQuestion(), q, "La question de la réponse n° " + r.getId() + " ne correspond pas à la question");
+                    assertTrue(responsesByDao.contains(r), "La liste via le DAO ne contient pas la réponse n° " + r.getId());
                 }
                 
             }
@@ -197,7 +197,7 @@ public class LoginFormResourceTest extends JerseyTest {
             
             // on récupère les réponses de la question
             final List<Response> responses = objectMapper.readValue(target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
-                    .queryParam("questionRef", String.valueOf(origin.getQuestionRef()))
+                    .queryParam("questionRef", String.valueOf(origin.getId()))
                     .request()
                     .get(String.class), new TypeReference<List<Response>>() {});
             
@@ -211,8 +211,8 @@ public class LoginFormResourceTest extends JerseyTest {
             // Insertion d'une réponse prédéfénie
             Response r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
             final Form form = new Form();
-            form.param("questionRef", String.valueOf(origin.getQuestionRef()));
-            form.param("responseRef", String.valueOf(r.getResponseRef()));
+            form.param("questionRef", String.valueOf(origin.getId()));
+            form.param("responseRef", String.valueOf(r.getId()));
             form.param("otherText", "null");
             
             target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
@@ -221,7 +221,7 @@ public class LoginFormResourceTest extends JerseyTest {
             listOfResponses.add(r);
             
             // On lit la réponse insérée, via le DAO
-            List<UserResponse> userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            List<UserResponse> userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
             assertEquals(userResponsesList.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
             assertEquals(userResponsesList.get(0).getQuestion(), origin, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
             assertEquals(userResponsesList.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
@@ -232,8 +232,8 @@ public class LoginFormResourceTest extends JerseyTest {
             final List<Response> randoms = this.pickNRandom(responses, nbToInsert);
             for (final Response res : randoms) {
                 final Form f = new Form();
-                f.param("questionRef", String.valueOf(origin.getQuestionRef()));
-                f.param("responseRef", String.valueOf(res.getResponseRef()));
+                f.param("questionRef", String.valueOf(origin.getId()));
+                f.param("responseRef", String.valueOf(res.getId()));
                 f.param("otherText", "null");
                 
                 target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
@@ -244,7 +244,7 @@ public class LoginFormResourceTest extends JerseyTest {
             }
             
             // Vérification via le DAO
-            userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
             assertEquals(userResponsesList.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + "
                                                                         + nbToInsert + " réponses supplémentaires");
             for (final UserResponse ur : userResponsesList) {
@@ -259,7 +259,7 @@ public class LoginFormResourceTest extends JerseyTest {
             // insertion d'une réponse à choix libre
             final String other = "Ceci est une réponse libre";
             final Form formOther = new Form();
-            formOther.param("questionRef", String.valueOf(origin.getQuestionRef()));
+            formOther.param("questionRef", String.valueOf(origin.getId()));
             formOther.param("responseRef", "null");
             formOther.param("otherText", other);
             
@@ -267,7 +267,7 @@ public class LoginFormResourceTest extends JerseyTest {
             .request(MediaType.TEXT_PLAIN)
             .post(Entity.form(formOther));
             
-            userResponsesList = userResponsesDao.findAllByQuestion(origin.getQuestionRef());
+            userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
             assertEquals(userResponsesList.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les "
                                     + newNbResponses + " réponses + la réponse libre");
             for (final UserResponse ur : userResponsesList) {
@@ -295,7 +295,7 @@ public class LoginFormResourceTest extends JerseyTest {
         final HashMap<Long, String> otherTextMap = new HashMap<>();
         
         for (final Question q : questionsList) {
-            final List<Response> responsesListForQuestion = responsesDao.findAllByQuestion(q.getQuestionRef());
+            final List<Response> responsesListForQuestion = responsesDao.findAllByQuestion(q.getId());
             
             // on récupère un nombre aléatoire de réponse
             final List<Response> randomList = this.pickNRandom(responsesListForQuestion, ThreadLocalRandom.current().nextInt(0, responsesListForQuestion.size() - 1));
@@ -305,7 +305,7 @@ public class LoginFormResourceTest extends JerseyTest {
                 responsesList.addAll(randomList);
             }
             
-            otherTextMap.put(q.getQuestionRef(), "Réponse libre pour la question « " + q.getFullName() + " »");
+            otherTextMap.put(q.getId(), "Réponse libre pour la question « " + q.getDescription() + " »");
         }
         final List<ResponseDTO> responsesDTO = responsesList.stream().map(LoginFormResource::toDto).toList();
         
@@ -322,12 +322,12 @@ public class LoginFormResourceTest extends JerseyTest {
         
         // Pour le test, vérifier, pour chaque question, les réponses via le DAO
         for (final Question q : questionsDao.findAll()) {
-            final List<UserResponse> list = userResponsesDao.findAllByQuestion(q.getQuestionRef());
+            final List<UserResponse> list = userResponsesDao.findAllByQuestion(q.getId());
             assertNotNull(list, "La liste de réponses de l'utilisateur ne doit pas être null");
             for (final UserResponse ur : list) {
                 assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
-                assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getFullName() + " »");
-                assertTrue(ur.getUserResponseRef() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
+                assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getDescription() + " »");
+                assertTrue(ur.getId() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
                 if (ur.getResponse() != null) {
                     assertNotNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
                     assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
index 60de95d..cab54a7 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
@@ -17,7 +17,7 @@ public class QuestionDTO {
     /**
      * Ref of question.
      */
-    private long questionRef;
+    private long id;
     /**
      * Category of question.
      */
@@ -25,12 +25,12 @@ public class QuestionDTO {
     /**
      * Label of question.
      */
-    private String fullName;
+    private String description;
     /**
      * @return the questionRef
      */
-    public long getQuestionRef() {
-        return questionRef;
+    public long getId() {
+        return id;
     }
     /**
      * @return the category
@@ -41,14 +41,14 @@ public class QuestionDTO {
     /**
      * @return the fullName
      */
-    public String getFullName() {
-        return fullName;
+    public String getDescription() {
+        return description;
     }
     /**
      * @param value the questionRef to set
      */
-    public void setQuestionRef(final long value) {
-        this.questionRef = value;
+    public void setId(final long value) {
+        this.id = value;
     }
     /**
      * @param value the category to set
@@ -59,13 +59,13 @@ public class QuestionDTO {
     /**
      * @param value the fullName to set
      */
-    public void setFullName(final String value) {
-        this.fullName = value;
+    public void setDescription(final String value) {
+        this.description = value;
     }
     /** */
     @Override
     public int hashCode() {
-        return Objects.hash(category, fullName, questionRef);
+        return Objects.hash(category, description, id);
     }
     /** */
     @Override
@@ -80,7 +80,7 @@ public class QuestionDTO {
             return false;
         }
         QuestionDTO other = (QuestionDTO) obj;
-        return Objects.equals(category, other.category) && Objects.equals(fullName, other.fullName)
-                && questionRef == other.questionRef;
+        return Objects.equals(category, other.category) && Objects.equals(description, other.description)
+                && id == other.id;
     }
 }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
index f1e86aa..849f10b 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
@@ -15,7 +15,7 @@ public class ResponseDTO {
     /**
      * Ref of this response.
      */
-    private long responseRef;
+    private long id;
     /**
      * DTO of question associated to response.
      */
@@ -23,12 +23,12 @@ public class ResponseDTO {
     /**
      * Label of response.
      */
-    private String fullname;
+    private String description;
     /**
      * @return the responseRef
      */
-    public long getResponseRef() {
-        return responseRef;
+    public long getId() {
+        return id;
     }
     /**
      * @return the question
@@ -39,14 +39,14 @@ public class ResponseDTO {
     /**
      * @return the fullname
      */
-    public String getFullname() {
-        return fullname;
+    public String getDescription() {
+        return description;
     }
     /**
      * @param value the responseRef to set
      */
-    public void setResponseRef(final long value) {
-        this.responseRef = value;
+    public void setId(final long value) {
+        this.id = value;
     }
     /**
      * @param dto the question to set
@@ -57,7 +57,7 @@ public class ResponseDTO {
     /**
      * @param value the fullname to set
      */
-    public void setFullname(final String value) {
-        this.fullname = value;
+    public void setDescription(final String value) {
+        this.description = value;
     }
 }
-- 
GitLab


From 7abca50bee6145e516a9c974ea34ae2585e3cd0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Mon, 27 May 2024 12:53:58 +0200
Subject: [PATCH 11/30] =?UTF-8?q?Modifier=20les=20noms=20de=20classe=20?=
 =?UTF-8?q?=C2=AB=20Login=20=C2=BB=20en=20=C2=AB=20Survey=20=C2=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/fr/agrometinfo/www/client/App.java   |  3 +-
 ...ginPresenter.java => SurveyPresenter.java} | 44 ++++++-------
 .../view/{LoginView.java => SurveyView.java}  | 16 ++---
 .../www/server/rs/ApplicationConfig.java      |  2 +-
 ...mResource.java => SurveyFormResource.java} | 62 +++++++++----------
 .../www/server/service/MailService.java       |  4 +-
 .../www/server/service/MailServiceImpl.java   |  6 +-
 ...Test.java => SurveyFormHibernateTest.java} |  2 +-
 ...eTest.java => SurveyFormResourceTest.java} | 38 ++++++------
 ...{ResponseDTO.java => SurveyOptionDTO.java} | 10 +--
 ...uestionDTO.java => SurveyQuestionDTO.java} |  4 +-
 ...ormDataDTO.java => SurveyResponseDTO.java} | 20 +++---
 ...ormService.java => SurveyFormService.java} | 32 +++++-----
 13 files changed, 122 insertions(+), 121 deletions(-)
 rename www-client/src/main/java/fr/agrometinfo/www/client/presenter/{LoginPresenter.java => SurveyPresenter.java} (69%)
 rename www-client/src/main/java/fr/agrometinfo/www/client/view/{LoginView.java => SurveyView.java} (90%)
 rename www-server/src/main/java/fr/agrometinfo/www/server/rs/{LoginFormResource.java => SurveyFormResource.java} (78%)
 rename www-server/src/test/java/fr/agrometinfo/www/server/{LoginFormHibernateTest.java => SurveyFormHibernateTest.java} (99%)
 rename www-server/src/test/java/fr/agrometinfo/www/server/rs/{LoginFormResourceTest.java => SurveyFormResourceTest.java} (91%)
 rename www-shared/src/main/java/fr/agrometinfo/www/shared/dto/{ResponseDTO.java => SurveyOptionDTO.java} (81%)
 rename www-shared/src/main/java/fr/agrometinfo/www/shared/dto/{QuestionDTO.java => SurveyQuestionDTO.java} (94%)
 rename www-shared/src/main/java/fr/agrometinfo/www/shared/dto/{LoginFormDataDTO.java => SurveyResponseDTO.java} (73%)
 rename www-shared/src/main/java/fr/agrometinfo/www/shared/service/{LoginFormService.java => SurveyFormService.java} (69%)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/App.java b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
index 6134041..2de0783 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/App.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
@@ -22,6 +22,7 @@ import fr.agrometinfo.www.client.i18n.AppMessages;
 import fr.agrometinfo.www.client.presenter.LayoutPresenter;
 import fr.agrometinfo.www.client.util.ApplicationUtils;
 import fr.agrometinfo.www.shared.service.ApplicationServiceFactory;
+import fr.agrometinfo.www.client.presenter.SurveyPresenter;
 
 /**
  * Entry point classes define <code>onModuleLoad()</code>.
@@ -101,6 +102,6 @@ public class App implements EntryPoint {
         new LayoutPresenter().start();
 
         // formulaire d'enquête
-        new LoginPresenter().start();
+        new SurveyPresenter().start();
     }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
similarity index 69%
rename from www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
rename to www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
index 336eba3..13d349d 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/LoginPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
@@ -8,27 +8,27 @@ import org.dominokit.rest.shared.request.FailedResponseBean;
 import com.google.gwt.core.client.GWT;
 
 import fr.agrometinfo.www.client.view.BaseView;
-import fr.agrometinfo.www.client.view.LoginView;
-import fr.agrometinfo.www.shared.dto.QuestionDTO;
-import fr.agrometinfo.www.shared.dto.ResponseDTO;
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
-import fr.agrometinfo.www.shared.service.LoginFormServiceFactory;
+import fr.agrometinfo.www.client.view.SurveyView;
+import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
+import fr.agrometinfo.www.shared.service.SurveyFormServiceFactory;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Presenter to login the user with survey form.
+ * Presenter for survey form.
  *
  * @author Olivier Maury
  * @author Jérémie Décome
  */
-public final class LoginPresenter implements Presenter {
+public final class SurveyPresenter implements Presenter {
 
     /**
      * Interface for the related view.
      */
-    public interface View extends BaseView<LoginPresenter> {
+    public interface View extends BaseView<SurveyPresenter> {
         /**
          * Display notification on failure.
          *
@@ -42,7 +42,7 @@ public final class LoginPresenter implements Presenter {
         /**
          * @param map list of availables responses.
          */
-        void setResponses(Map<QuestionDTO, List<ResponseDTO>> map);
+        void setResponses(Map<SurveyQuestionDTO, List<SurveyOptionDTO>> map);
         /**
          * Showing success login.
          * @param msg returned message from webservice
@@ -53,24 +53,24 @@ public final class LoginPresenter implements Presenter {
     /**
      * Related view.
      */
-    private final LoginView view = new LoginView();
+    private final SurveyView view = new SurveyView();
     /** List of responses from webservice. */
-    private List<ResponseDTO> responses;
+    private List<SurveyOptionDTO> responses;
     /** List of questions, extracted from responses list. */
-    private List<QuestionDTO> questions;
+    private List<SurveyQuestionDTO> questions;
     /**
      * Set responses and extract questions from responses list.
      * @param list list of responses
      */
-    private void setResponses(final List<ResponseDTO> list) {
+    private void setResponses(final List<SurveyOptionDTO> list) {
         this.responses = list;
-        final Map<QuestionDTO, List<ResponseDTO>> responsesMap = new HashMap<>();
+        final Map<SurveyQuestionDTO, List<SurveyOptionDTO>> responsesMap = new HashMap<>();
 
         // extraction des questions, à partir des réponses retournées par le webservice
-        for (final ResponseDTO dto : list) {
+        for (final SurveyOptionDTO dto : list) {
             if (!responsesMap.containsKey(dto.getQuestion())) { // la map ne contient pas la question
                 // on crée l'entrée, avec une liste vide
-                responsesMap.put(dto.getQuestion(), new ArrayList<ResponseDTO>());
+                responsesMap.put(dto.getQuestion(), new ArrayList<SurveyOptionDTO>());
             }
             responsesMap.get(dto.getQuestion()).add(dto);
         }
@@ -83,7 +83,7 @@ public final class LoginPresenter implements Presenter {
     public void start() {
         view.setPresenter(this);
 
-        LoginFormServiceFactory.INSTANCE.getResponses()
+        SurveyFormServiceFactory.INSTANCE.getResponses()
         .onSuccess(this::setResponses)
         .onFailed(view::failureNotification)
         .send();
@@ -92,12 +92,12 @@ public final class LoginPresenter implements Presenter {
         GWT.log("Insère les réponses de l'utilisateur");
 
         // traitement des réponses prédéfinies
-        final List<QuestionDTO> questionsDto = new ArrayList<>();
-        final List<ResponseDTO> responsesDto = new ArrayList<>();
+        final List<SurveyQuestionDTO> questionsDto = new ArrayList<>();
+        final List<SurveyOptionDTO> responsesDto = new ArrayList<>();
 
         for (final Long ref : responsesRef) {
             // on récupère la réponse correspondante à la référence
-            final ResponseDTO rDto = this.responses.stream().filter(r -> r.getId() == ref).findFirst().get();
+            final SurveyOptionDTO rDto = this.responses.stream().filter(r -> r.getId() == ref).findFirst().get();
             if (rDto != null) {
                 if (!responsesDto.contains(rDto)) {
                     responsesDto.add(rDto);
@@ -118,8 +118,8 @@ public final class LoginPresenter implements Presenter {
 
 
         // envoi au webservice
-        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDto, responsesDto, otherTextMap);
-        LoginFormServiceFactory.INSTANCE.insertAllResponses(data)
+        final SurveyResponseDTO data = new SurveyResponseDTO(questionsDto, responsesDto, otherTextMap);
+        SurveyFormServiceFactory.INSTANCE.insertAllResponses(data)
         .onSuccess(view::displaySuccessLogin)
         .onFailed(view::failureNotification)
         .send();
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
similarity index 90%
rename from www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
rename to www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
index 3e175a4..adc2e38 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LoginView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
@@ -18,9 +18,9 @@ import com.google.gwt.core.client.GWT;
 import com.google.gwt.storage.client.Storage;
 
 import fr.agrometinfo.www.client.i18n.AppConstants;
-import fr.agrometinfo.www.client.presenter.LoginPresenter;
-import fr.agrometinfo.www.shared.dto.QuestionDTO;
-import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import fr.agrometinfo.www.client.presenter.SurveyPresenter;
+import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
 
 /**
  * View for survey form.
@@ -28,7 +28,7 @@ import fr.agrometinfo.www.shared.dto.ResponseDTO;
  * @author Olivier Maury
  * @author Jérémie Décome
  */
-public final class LoginView extends AbstractBaseView<LoginPresenter> implements LoginPresenter.View {
+public final class SurveyView extends AbstractBaseView<SurveyPresenter> implements SurveyPresenter.View {
     /**
      * I18N constants.
      */
@@ -44,7 +44,7 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
     /**
      * List of available responses.
      */
-    private Map<QuestionDTO, List<ResponseDTO>> availableResponses;
+    private Map<SurveyQuestionDTO, List<SurveyOptionDTO>> availableResponses;
     /**
      * The modal used to display the login form.
      */
@@ -77,8 +77,8 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
                 .innerHtml(new SafeHtmlBuilder().appendHtmlConstant(CSTS.loginFormDescription()).toSafeHtml())
                 );
 
-        for (Map.Entry<QuestionDTO, List<ResponseDTO>> entry : this.availableResponses.entrySet()) {
-            final QuestionDTO k = entry.getKey(); // Pour chaque question
+        for (Map.Entry<SurveyQuestionDTO, List<SurveyOptionDTO>> entry : this.availableResponses.entrySet()) {
+            final SurveyQuestionDTO k = entry.getKey(); // Pour chaque question
 
             // on affiche le libellé
             this.modal.appendChild(Elements.h(SECTION_TITLE_LEVEL, "• " + k.getDescription()));
@@ -163,7 +163,7 @@ public final class LoginView extends AbstractBaseView<LoginPresenter> implements
     }
 
     @Override
-    public void setResponses(final Map<QuestionDTO, List<ResponseDTO>> map) {
+    public void setResponses(final Map<SurveyQuestionDTO, List<SurveyOptionDTO>> map) {
         this.availableResponses = map;
     }
 
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
index 60623c8..de1362f 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
@@ -43,7 +43,7 @@ public class ApplicationConfig extends Application {
                 // Jackson configuration
                 JacksonConfig.class,
                 // JAX-RS resources
-                ApplicationResource.class, GeometryResource.class, IndicatorResource.class, LoginFormResource.class, UserResource.class, 
+                ApplicationResource.class, GeometryResource.class, IndicatorResource.class, SurveyFormResource.class, UserResource.class,
                 // POJO
                 // Dependencies of resources
                 LogFilter.class
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
similarity index 78%
rename from www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index 88f81e7..67e57b1 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/LoginFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -15,10 +15,10 @@ import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.service.MailService;
 import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
-import fr.agrometinfo.www.shared.dto.QuestionDTO;
-import fr.agrometinfo.www.shared.dto.ResponseDTO;
-import fr.agrometinfo.www.shared.service.LoginFormService;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
+import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
+import fr.agrometinfo.www.shared.service.SurveyFormService;
 import jakarta.enterprise.context.RequestScoped;
 import jakarta.inject.Inject;
 import jakarta.ws.rs.Consumes;
@@ -36,16 +36,16 @@ import jakarta.ws.rs.core.MediaType;
  * @author jdecome
  *
  */
-@Path(LoginFormService.PATH)
+@Path(SurveyFormService.PATH)
 @RequestScoped
-public class LoginFormResource implements LoginFormService {
+public class SurveyFormResource implements SurveyFormService {
     /**
      * Convert {@link Question} entity to dto.
      * @param question entity
      * @return dto
      */
-    static QuestionDTO toDto(final Question question) {
-        final QuestionDTO dto = new QuestionDTO();
+    static SurveyQuestionDTO toDto(final Question question) {
+        final SurveyQuestionDTO dto = new SurveyQuestionDTO();
         dto.setId(question.getId());
         dto.setCategory(question.getCategory());
         dto.setDescription(question.getDescription());
@@ -56,19 +56,19 @@ public class LoginFormResource implements LoginFormService {
      * @param response entity
      * @return dto
      */
-    static ResponseDTO toDto(final Response response) {
-        final ResponseDTO dto = new ResponseDTO();
+    static SurveyOptionDTO toDto(final Response response) {
+        final SurveyOptionDTO dto = new SurveyOptionDTO();
         dto.setId(response.getId());
         dto.setQuestion(toDto(response.getQuestion()));
         dto.setDescription(response.getDescription());
         return dto;
     }
     /**
-     * Convert {@link QuestionDTO} dto to entity.
+     * Convert {@link SurveyQuestionDTO} dto to entity.
      * @param dto question
      * @return entity
      */
-    private static Question toEntity(final QuestionDTO dto) {
+    private static Question toEntity(final SurveyQuestionDTO dto) {
         final Question question = new Question();
         question.setId(dto.getId());
         question.setCategory(dto.getCategory());
@@ -76,11 +76,11 @@ public class LoginFormResource implements LoginFormService {
         return question;
     }
     /**
-     * Convert {@link ResponseDTO} dto to entity.
+     * Convert {@link SurveyOptionDTO} dto to entity.
      * @param dto response
      * @return entity
      */
-    private static Response toEntity(final ResponseDTO dto) {
+    private static Response toEntity(final SurveyOptionDTO dto) {
         final Response response = new Response();
         response.setId(dto.getId());
         response.setQuestion(toEntity(dto.getQuestion()));
@@ -125,35 +125,35 @@ public class LoginFormResource implements LoginFormService {
         }
     }
     @GET
-    @Path(LoginFormService.PATH_QUESTIONS_LIST)
+    @Path(SurveyFormService.PATH_QUESTIONS_LIST)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
-    public List<QuestionDTO> getQuestions() {
+    public List<SurveyQuestionDTO> getQuestions() {
         final List<Question> list = questionsDao.findAll();
-        final List<QuestionDTO> questions = new ArrayList<>();
+        final List<SurveyQuestionDTO> questions = new ArrayList<>();
         for (Question q : list) {
             questions.add(toDto(q));
         }
         return questions;
     }
     @GET
-    @Path(LoginFormService.PATH_QUESTION_BY_CATEGORY)
+    @Path(SurveyFormService.PATH_QUESTION_BY_CATEGORY)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
-    public QuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category) {
+    public SurveyQuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category) {
         this.checkRequired(category, "category");
         final Question question = questionsDao.findByCategory(category);
         return toDto(question);
     }
     @GET
-    @Path(LoginFormService.PATH_RESPONSES_BY_QUESTION_LIST)
+    @Path(SurveyFormService.PATH_RESPONSES_BY_QUESTION_LIST)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
-    public List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef) {
+    public List<SurveyOptionDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef) {
         this.checkRequired(questionRef, "questionRef");
         if (this.checkIfQuestionExist(questionRef)) {
             final List<Response> list = responsesDao.findAllByQuestion(questionRef);
-            final List<ResponseDTO> responses = new ArrayList<>();
+            final List<SurveyOptionDTO> responses = new ArrayList<>();
             for (final Response r : list) {
                 responses.add(toDto(r));
             }
@@ -162,19 +162,19 @@ public class LoginFormResource implements LoginFormService {
         return null;
     }
     @GET
-    @Path(LoginFormService.PATH_RESPONSES_LIST)
+    @Path(SurveyFormService.PATH_RESPONSES_LIST)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
-    public List<ResponseDTO> getResponses() {
+    public List<SurveyOptionDTO> getResponses() {
         final List<Response> list = responsesDao.findAll();
-        final List<ResponseDTO> responses = new ArrayList<>();
+        final List<SurveyOptionDTO> responses = new ArrayList<>();
         for (final Response r : list) {
             responses.add(toDto(r));
         }
         return responses;
     }
     @POST
-    @Path(LoginFormService.PATH_INSERT_RESPONSE)
+    @Path(SurveyFormService.PATH_INSERT_RESPONSE)
     @Produces(MediaType.TEXT_PLAIN)
     @Override
     public void insertResponse(
@@ -206,22 +206,22 @@ public class LoginFormResource implements LoginFormService {
         return false;
     }
     @POST
-    @Path(LoginFormService.PATH_INSERT_RESPONSE_WITH_JSON)
+    @Path(SurveyFormService.PATH_INSERT_RESPONSE_WITH_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
-    public String insertAllResponses(final LoginFormDataDTO data) {
+    public String insertAllResponses(final SurveyResponseDTO data) {
         // traitement des réponses prédéfinies
-        for (final QuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
+        for (final SurveyQuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
             // Insérer les réponses associés
             final Question q = toEntity(dto);
             final Long qRef = dto.getId();
-            List<ResponseDTO> associatedResponses = data.getResponses()
+            List<SurveyOptionDTO> associatedResponses = data.getResponses()
                     .stream().filter(f -> f.getQuestion().getId() == qRef)
                     .collect(Collectors.toList());
 
             // On insère les réponses prédéfinies
-            for (final ResponseDTO rDto : associatedResponses) {
+            for (final SurveyOptionDTO rDto : associatedResponses) {
                 final Response r = toEntity(rDto);
                 this.userResponsesDao.insertResponse(q, r, null);
             }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
index c2524f0..a472586 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
@@ -2,7 +2,7 @@ package fr.agrometinfo.www.server.service;
 
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
 import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
 
 public interface MailService {
     /**
@@ -19,5 +19,5 @@ public interface MailService {
      * Send mail when user fill login form, with questions and responses.
      * @param data
      */
-    void sendLoginFilled(LoginFormDataDTO data);
+    void sendLoginFilled(SurveyResponseDTO data);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
index c4dcaa4..2a2933f 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -27,7 +27,7 @@ import fr.agrometinfo.www.server.exception.AgroMetInfoErrorCategory;
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
 import fr.agrometinfo.www.server.exception.ErrorCategory;
 import fr.agrometinfo.www.server.exception.ErrorType;
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
 import jakarta.annotation.PostConstruct;
 import jakarta.mail.Address;
 import jakarta.mail.Authenticator;
@@ -329,7 +329,7 @@ public class MailServiceImpl implements MailService {
      * Send an e-mail when user filled survey form.
      * @param data data of survey
      */
-    public void sendLoginFilled(final LoginFormDataDTO data) {
+    public void sendLoginFilled(final SurveyResponseDTO data) {
         final Mail mail = createContentFromData(data);
         mail.setSubject("Un utilisateur a rempli le formulaire d'enquête");
         mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
@@ -346,7 +346,7 @@ public class MailServiceImpl implements MailService {
      * @param data data of survey
      * @return mail object
      */
-    public static Mail createContentFromData(final LoginFormDataDTO data) {
+    public static Mail createContentFromData(final SurveyResponseDTO data) {
         final Mail mail = new Mail();
 
         final StringBuilder builder = new StringBuilder();
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
similarity index 99%
rename from www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
rename to www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 65f4d27..93a8c1b 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/LoginFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -31,7 +31,7 @@ import fr.agrometinfo.www.server.model.UserResponse;
  * @author jdecome
  *
  */
-public class LoginFormHibernateTest {
+public class SurveyFormHibernateTest {
     /** DAO for user's responses. */
     private UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
     /** DAO for questions. */
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
similarity index 91%
rename from www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
rename to www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index e36a2bb..5ffca0c 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/LoginFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -39,9 +39,9 @@ import fr.agrometinfo.www.server.model.UserResponse;
 import fr.agrometinfo.www.server.service.MailService;
 import fr.agrometinfo.www.server.service.MailServiceImpl;
 import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
-import fr.agrometinfo.www.shared.dto.QuestionDTO;
-import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
+import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
 
 import jakarta.ws.rs.client.Entity;
 import jakarta.ws.rs.core.Application;
@@ -56,7 +56,7 @@ import lombok.extern.log4j.Log4j2;
  *
  */
 @Log4j2
-public class LoginFormResourceTest extends JerseyTest {
+public class SurveyFormResourceTest extends JerseyTest {
     /**
      * Mock class for mail service.
      * @author jdecome
@@ -67,7 +67,7 @@ public class LoginFormResourceTest extends JerseyTest {
         @Getter
         private Mail mail = null;
         @Override
-        public void sendLoginFilled(LoginFormDataDTO data) {
+        public void sendLoginFilled(SurveyResponseDTO data) {
             this.mail = MailServiceImpl.createContentFromData(data);
         }
         
@@ -97,7 +97,7 @@ public class LoginFormResourceTest extends JerseyTest {
     
     @Override
     protected final Application configure() {
-        return new ResourceConfig(LoginFormResource.class).register(new AbstractBinder() {
+        return new ResourceConfig(SurveyFormResource.class).register(new AbstractBinder() {
             @Override
             protected void configure() {
                 bind(questionsDao).to(QuestionsDao.class);
@@ -111,7 +111,7 @@ public class LoginFormResourceTest extends JerseyTest {
     @Test
     public void getResponses() {
         // On récupère les questions via le webservice
-        final String jsonQuestions = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTIONS_LIST).request().get(String.class);
+        final String jsonQuestions = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTIONS_LIST).request().get(String.class);
         assertNotNull(jsonQuestions, "Impossible de récupérer la ressource (objet null) pour les questions");
         assertFalse(jsonQuestions.isEmpty(), "La chaine de caractère JSON des questions est vide");
         try {
@@ -120,7 +120,7 @@ public class LoginFormResourceTest extends JerseyTest {
             assertNotEquals(questions.size(), 0, "La liste de questions est vide");
             
             // On récupère l'ensemble des réponses
-            final String jsonAllResponses = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_LIST).request().get(String.class);
+            final String jsonAllResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_LIST).request().get(String.class);
             assertNotNull(jsonAllResponses, "Impossible de récupérer la ressource (objet null) des réponses");
             assertFalse(jsonAllResponses.isEmpty(), "La chaine de caractère JSON des réponses est vide");
             
@@ -135,7 +135,7 @@ public class LoginFormResourceTest extends JerseyTest {
             // Pour chaque question, 
             for (final Question q : questions) {
                 // on vérifie que la question est retrouvable par la catégorie, via le webservice
-                final String jsonCategory = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTION_BY_CATEGORY)
+                final String jsonCategory = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTION_BY_CATEGORY)
                                                 .queryParam("category", q.getCategory())
                                                 .request().get(String.class);
                 
@@ -152,7 +152,7 @@ public class LoginFormResourceTest extends JerseyTest {
                 assertEquals(questionByDao, q, "La question lue par le DAO ne correspond pas avec la question n° " + q.getId() + "d'origine");
                 
                 // on récupère les réponses associées via le webservice
-                final String jsonResponses = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
+                final String jsonResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
                                                 .queryParam("questionRef", String.valueOf(q.getId()))
                                                 .request()
                                                 .get(String.class);
@@ -186,7 +186,7 @@ public class LoginFormResourceTest extends JerseyTest {
          this.resetUserResponses();
          final String category = "origin";
          // Question
-         final String jsonOrigins = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_QUESTION_BY_CATEGORY)
+         final String jsonOrigins = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTION_BY_CATEGORY)
                                          .queryParam("category", category)
                                         .request().get(String.class);
          assertNotNull(jsonOrigins, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question de l'origine");
@@ -196,7 +196,7 @@ public class LoginFormResourceTest extends JerseyTest {
             assertEquals(origin, questionsDao.findByCategory(category), "La question lue dans le webservice ne correspond pas avec la question via le DAO");
             
             // on récupère les réponses de la question
-            final List<Response> responses = objectMapper.readValue(target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
+            final List<Response> responses = objectMapper.readValue(target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
                     .queryParam("questionRef", String.valueOf(origin.getId()))
                     .request()
                     .get(String.class), new TypeReference<List<Response>>() {});
@@ -215,7 +215,7 @@ public class LoginFormResourceTest extends JerseyTest {
             form.param("responseRef", String.valueOf(r.getId()));
             form.param("otherText", "null");
             
-            target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+            target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
                 .request(MediaType.TEXT_PLAIN)
                 .post(Entity.form(form));
             listOfResponses.add(r);
@@ -236,7 +236,7 @@ public class LoginFormResourceTest extends JerseyTest {
                 f.param("responseRef", String.valueOf(res.getId()));
                 f.param("otherText", "null");
                 
-                target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+                target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
                 .request(MediaType.TEXT_PLAIN)
                 .post(Entity.form(f));
                 
@@ -263,7 +263,7 @@ public class LoginFormResourceTest extends JerseyTest {
             formOther.param("responseRef", "null");
             formOther.param("otherText", other);
             
-            target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE)
+            target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
             .request(MediaType.TEXT_PLAIN)
             .post(Entity.form(formOther));
             
@@ -289,7 +289,7 @@ public class LoginFormResourceTest extends JerseyTest {
     public void testWithJsonResponses() {
         this.resetUserResponses();
         final List<Question> questionsList = questionsDao.findAll();
-        final List<QuestionDTO> questionsDTO = questionsList.stream().map(LoginFormResource::toDto).toList();
+        final List<SurveyQuestionDTO> questionsDTO = questionsList.stream().map(SurveyFormResource::toDto).toList();
         
         final List<Response> responsesList = new ArrayList<>();
         final HashMap<Long, String> otherTextMap = new HashMap<>();
@@ -307,15 +307,15 @@ public class LoginFormResourceTest extends JerseyTest {
             
             otherTextMap.put(q.getId(), "Réponse libre pour la question « " + q.getDescription() + " »");
         }
-        final List<ResponseDTO> responsesDTO = responsesList.stream().map(LoginFormResource::toDto).toList();
+        final List<SurveyOptionDTO> responsesDTO = responsesList.stream().map(SurveyFormResource::toDto).toList();
         
-        final LoginFormDataDTO data = new LoginFormDataDTO(questionsDTO, responsesDTO, otherTextMap);
+        final SurveyResponseDTO data = new SurveyResponseDTO(questionsDTO, responsesDTO, otherTextMap);
         assertEquals(data.getQuestions(), questionsDTO, "La liste de questions dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         assertEquals(data.getResponses(), responsesDTO, "La liste de réponses dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         assertEquals(data.getOtherTextMap(), otherTextMap, "La liste de réponses libre dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         
         
-        jakarta.ws.rs.core.Response ret = target(LoginFormResource.PATH + SEP + LoginFormResource.PATH_INSERT_RESPONSE_WITH_JSON)
+        jakarta.ws.rs.core.Response ret = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE_WITH_JSON)
                 .request(MediaType.APPLICATION_JSON).post(Entity.entity(data, MediaType.APPLICATION_JSON));
         
         assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "Le point d'entrée d'insertion doit retourner le code status 200 (OK)");
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
similarity index 81%
rename from www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
rename to www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
index 849f10b..438217d 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/ResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
@@ -6,12 +6,12 @@ package fr.agrometinfo.www.shared.dto;
 import org.dominokit.jackson.annotation.JSONMapper;
 
 /**
- *  DTO for responses of login form.
+ *  DTO for responses (as options) for survey form.
  * @author jdecome
  *
  */
 @JSONMapper
-public class ResponseDTO {
+public class SurveyOptionDTO {
     /**
      * Ref of this response.
      */
@@ -19,7 +19,7 @@ public class ResponseDTO {
     /**
      * DTO of question associated to response.
      */
-    private QuestionDTO question;
+    private SurveyQuestionDTO question;
     /**
      * Label of response.
      */
@@ -33,7 +33,7 @@ public class ResponseDTO {
     /**
      * @return the question
      */
-    public QuestionDTO getQuestion() {
+    public SurveyQuestionDTO getQuestion() {
         return question;
     }
     /**
@@ -51,7 +51,7 @@ public class ResponseDTO {
     /**
      * @param dto the question to set
      */
-    public void setQuestion(final QuestionDTO dto) {
+    public void setQuestion(final SurveyQuestionDTO dto) {
         this.question = dto;
     }
     /**
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
similarity index 94%
rename from www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
rename to www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
index cab54a7..78ceb88 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/QuestionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
@@ -13,7 +13,7 @@ import org.dominokit.jackson.annotation.JSONMapper;
  *
  */
 @JSONMapper
-public class QuestionDTO {
+public class SurveyQuestionDTO {
     /**
      * Ref of question.
      */
@@ -79,7 +79,7 @@ public class QuestionDTO {
         if (getClass() != obj.getClass()) {
             return false;
         }
-        QuestionDTO other = (QuestionDTO) obj;
+        SurveyQuestionDTO other = (SurveyQuestionDTO) obj;
         return Objects.equals(category, other.category) && Objects.equals(description, other.description)
                 && id == other.id;
     }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
similarity index 73%
rename from www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
rename to www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
index 30493a8..8982cea 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/LoginFormDataDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
@@ -12,17 +12,17 @@ import java.util.List;
  * @author jdecome
  *
  */
-public class LoginFormDataDTO {
+public class SurveyResponseDTO {
     /**
      * List of login form questions.<br>
      * This list questions contains available questions, obtained by {@link QuestionsDao}
      */
-    private List<QuestionDTO> questions;
+    private List<SurveyQuestionDTO> questions;
     /**
      * List of login form responses.<br>
      * This list responses contains available responses, obtained by {@link ResponsesDao}
      */
-    private List<ResponseDTO> responses;
+    private List<SurveyOptionDTO> responses;
     /**
      * Storage, for each response, the other text, if specified.
      */
@@ -30,7 +30,7 @@ public class LoginFormDataDTO {
     /**
      * Default constructor.
      */
-    public LoginFormDataDTO() {
+    public SurveyResponseDTO() {
     }
     /**
      * Constructor.
@@ -38,8 +38,8 @@ public class LoginFormDataDTO {
      * @param rList responses list
      * @param otherText otherText map
      */
-    public LoginFormDataDTO(final List<QuestionDTO> qList,
-            final List<ResponseDTO> rList, final HashMap<Long, String> otherText) {
+    public SurveyResponseDTO(final List<SurveyQuestionDTO> qList,
+            final List<SurveyOptionDTO> rList, final HashMap<Long, String> otherText) {
         this.questions = qList;
         this.responses = rList;
         this.otherTextMap = otherText;
@@ -47,13 +47,13 @@ public class LoginFormDataDTO {
     /**
      * @return the questions
      */
-    public List<QuestionDTO> getQuestions() {
+    public List<SurveyQuestionDTO> getQuestions() {
         return questions;
     }
     /**
      * @return the responses
      */
-    public List<ResponseDTO> getResponses() {
+    public List<SurveyOptionDTO> getResponses() {
         return responses;
     }
     /**
@@ -65,13 +65,13 @@ public class LoginFormDataDTO {
     /**
      * @param questionsList the questions to set
      */
-    public void setQuestions(final List<QuestionDTO> questionsList) {
+    public void setQuestions(final List<SurveyQuestionDTO> questionsList) {
         this.questions = questionsList;
     }
     /**
      * @param responsesList the responses to set
      */
-    public void setResponses(final List<ResponseDTO> responsesList) {
+    public void setResponses(final List<SurveyOptionDTO> responsesList) {
         this.responses = responsesList;
     }
     /**
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
similarity index 69%
rename from www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
rename to www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
index 94083e2..a253ead 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/LoginFormService.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
@@ -12,9 +12,9 @@ import javax.ws.rs.Path;
 import org.dominokit.rest.shared.request.service.annotations.RequestFactory;
 import org.dominokit.rest.shared.request.service.annotations.RequestBody;
 
-import fr.agrometinfo.www.shared.dto.LoginFormDataDTO;
-import fr.agrometinfo.www.shared.dto.QuestionDTO;
-import fr.agrometinfo.www.shared.dto.ResponseDTO;
+import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
+import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
+import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
 import jakarta.ws.rs.DefaultValue;
 import jakarta.ws.rs.FormParam;
 import jakarta.ws.rs.QueryParam;
@@ -24,31 +24,31 @@ import jakarta.ws.rs.QueryParam;
  *
  */
 @RequestFactory
-@Path(LoginFormService.PATH)
-public interface LoginFormService {
+@Path(SurveyFormService.PATH)
+public interface SurveyFormService {
     /** Service base path. */
     String PATH = "login";
-    /** Path for {@link LoginFormService#getQuestions()}. */
+    /** Path for {@link SurveyFormService#getQuestions()}. */
     String PATH_QUESTIONS_LIST = "questions";
-    /**Path for {@link LoginFormService#getQuestionByCategory(String)}. */
+    /**Path for {@link SurveyFormService#getQuestionByCategory(String)}. */
     String PATH_QUESTION_BY_CATEGORY = "question";
-    /** Path for {@link LoginFormService#getResponses}. */
+    /** Path for {@link SurveyFormService#getResponses}. */
     String PATH_RESPONSES_LIST = "responses";
-    /** Path for {@link LoginFormService#getResponsesByQuestion(long)}. */
+    /** Path for {@link SurveyFormService#getResponsesByQuestion(long)}. */
     String PATH_RESPONSES_BY_QUESTION_LIST = "responsesByQuestion";
-    /** Path for {@link LoginFormService#getResponses()}. */
+    /** Path for {@link SurveyFormService#getResponses()}. */
     String PATH_INSERT_RESPONSE = "insert";
-    /** Path for {@link LoginFormService#insertResponse(String)}. */
+    /** Path for {@link SurveyFormService#insertResponse(String)}. */
     String PATH_INSERT_RESPONSE_WITH_JSON = "insertMultipleValues";
     /**
      * @return list of questions.
      */
     @GET
     @Path(PATH_QUESTIONS_LIST)
-    List<QuestionDTO> getQuestions();
+    List<SurveyQuestionDTO> getQuestions();
     @GET
     @Path(PATH_RESPONSES_LIST)
-    List<ResponseDTO> getResponses();
+    List<SurveyOptionDTO> getResponses();
     /**
      * Get question by his category.
      * @param category of the question
@@ -56,7 +56,7 @@ public interface LoginFormService {
      */
     @GET
     @Path(PATH_QUESTION_BY_CATEGORY)
-    QuestionDTO getQuestionByCategory(@QueryParam(value = "category") String category);
+    SurveyQuestionDTO getQuestionByCategory(@QueryParam(value = "category") String category);
     /**
      * Returning the list of possible response for one question.
      * @param questionRef ref of question
@@ -64,7 +64,7 @@ public interface LoginFormService {
      */
     @GET
     @Path(PATH_RESPONSES_BY_QUESTION_LIST)
-    List<ResponseDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") Long questionRef);
+    List<SurveyOptionDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") Long questionRef);
     /**
      * Insert response or text for specified question.<br>
      * If responseRef is provided, {@code otherText} must be null.<br>
@@ -87,5 +87,5 @@ public interface LoginFormService {
      */
     @POST
     @Path(PATH_INSERT_RESPONSE_WITH_JSON)
-    String insertAllResponses(@RequestBody LoginFormDataDTO data);
+    String insertAllResponses(@RequestBody SurveyResponseDTO data);
 }
-- 
GitLab


From 56824e83f532def732e1f80a46682ebef5d41656 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Mon, 27 May 2024 15:35:11 +0200
Subject: [PATCH 12/30] =?UTF-8?q?Modification=20de=20la=20transaction=20po?=
 =?UTF-8?q?ur=20la=20m=C3=A9thode=20deleteAll()?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../agrometinfo/www/server/dao/DaoHibernate.java   | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
index 5045a5d..14fa4db 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
@@ -91,11 +91,15 @@ public abstract class DaoHibernate<T> {
      */
     public void deleteAll() {
         LOGGER.traceEntry();
-        final ScopedEntityManager em = getScopedEntityManager();
-        final Query q = em.createQuery("DELETE FROM " + clazz.getName() + " t");
-        em.getTransaction().begin();
-        q.executeUpdate();
-        em.getTransaction().commit();
+        try (ScopedEntityManager em = getScopedEntityManager()) {
+            final Query q = em.createQuery("DELETE FROM " + clazz.getName() + " t");
+            em.getTransaction().begin();
+            q.executeUpdate();
+            em.getTransaction().commit();
+        } catch (final Exception e) {
+            LOGGER.catching(e);
+        }
+        
         LOGGER.traceExit();
     }
     /**
-- 
GitLab


From 3639cb42d3550ca13736db623625a779d7ca8c9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Mon, 27 May 2024 16:17:16 +0200
Subject: [PATCH 13/30] corrections pmd

---
 config/pmd-suppressions.properties | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/config/pmd-suppressions.properties b/config/pmd-suppressions.properties
index 89c8232..a22f226 100644
--- a/config/pmd-suppressions.properties
+++ b/config/pmd-suppressions.properties
@@ -8,17 +8,17 @@ fr.agrometinfo.www.shared.dto.IndicatorDTOBeanJsonDeserializerImpl=UnnecessaryIm
 fr.agrometinfo.www.shared.dto.IndicatorDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.IndicatorDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.MessageDTOBeanJsonSerializerImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.LoginFormDataDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyResponseDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTOBeanJsonDeserializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.PeriodDTO_MapperImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.QuestionDTOBeanJsonDeserializerImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.QuestionDTOBeanJsonSerializerImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.QuestionDTO_MapperImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.ResponseDTOBeanJsonSerializerImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.ResponseDTOBeanJsonDeserializerImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.dto.ResponseDTO_MapperImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.service.ResponseDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyQuestionDTOBeanJsonDeserializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyQuestionDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyQuestionDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyOptionDTOBeanJsonSerializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyOptionDTOBeanJsonDeserializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.dto.SurveyOptionDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.service.SurveyOptionDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SimpleFeatureBeanJsonDeserializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SimpleFeatureBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonDeserializerImpl=UnnecessaryImport
@@ -26,9 +26,9 @@ fr.agrometinfo.www.shared.dto.SummaryDTOBeanJsonSerializerImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.dto.SummaryDTO_MapperImpl=UnnecessaryImport
 fr.agrometinfo.www.shared.service.ApplicationServiceFactory=UnnecessaryImport
 fr.agrometinfo.www.shared.service.IndicatorServiceFactory=UnnecessaryImport
-fr.agrometinfo.www.shared.service.LoginFormServiceFactory=UnnecessaryImport
-fr.agrometinfo.www.shared.service.QuestionDTO_MapperImpl=UnnecessaryImport
-fr.agrometinfo.www.shared.service.ResponseDTOBeanJsonDeserializerImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.service.SurveyFormServiceFactory=UnnecessaryImport
+fr.agrometinfo.www.shared.service.SurveyQuestionDTO_MapperImpl=UnnecessaryImport
+fr.agrometinfo.www.shared.service.SurveyOptionDTOBeanJsonDeserializerImpl=UnnecessaryImport
 org.geojson.FeatureBeanJsonDeserializerImpl=UnnecessaryImport
 org.geojson.FeatureBeanJsonSerializerImpl=UnnecessaryImport
 org.geojson.FeatureCollectionBeanJsonDeserializerImpl=UnnecessaryImport
-- 
GitLab


From 4d49b7a383e0c6bdadca6fd5ecd64760bf2d8041 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 28 May 2024 07:59:16 +0200
Subject: [PATCH 14/30] corrections checkstyle commit 2f4ce26e

---
 .../main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java    | 1 -
 1 file changed, 1 deletion(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
index 14fa4db..48899a6 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/DaoHibernate.java
@@ -99,7 +99,6 @@ public abstract class DaoHibernate<T> {
         } catch (final Exception e) {
             LOGGER.catching(e);
         }
-        
         LOGGER.traceExit();
     }
     /**
-- 
GitLab


From d8d76f5e9204fdca9faa92fc00bd42d2fffefb4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 28 May 2024 09:41:13 +0200
Subject: [PATCH 15/30] Utiliser des LinkedHashSet pour les listes. fixes #5

---
 .../www/client/presenter/SurveyPresenter.java | 25 +++++++++++--------
 .../www/server/rs/SurveyFormResource.java     | 16 +++---------
 .../www/shared/dto/SurveyResponseDTO.java     |  7 +++---
 3 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
index 13d349d..eb295e7 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
@@ -17,6 +17,9 @@ import fr.agrometinfo.www.shared.service.SurveyFormServiceFactory;
 import java.util.HashMap;
 import java.util.Map;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+
 /**
  * Presenter for survey form.
  *
@@ -88,37 +91,37 @@ public final class SurveyPresenter implements Presenter {
         .onFailed(view::failureNotification)
         .send();
     }
-    public void insertUserResponses(final List<Long> responsesRef, final HashMap<Long, String> otherTextMap) {
+    public void insertUserResponses(final List<Long> responsesRef, final Map<Long, String> otherTextMap) {
         GWT.log("Insère les réponses de l'utilisateur");
 
         // traitement des réponses prédéfinies
-        final List<SurveyQuestionDTO> questionsDto = new ArrayList<>();
-        final List<SurveyOptionDTO> responsesDto = new ArrayList<>();
+        final Set<SurveyQuestionDTO> questionsDto = new LinkedHashSet<>();
+        final Set<SurveyOptionDTO> responsesDto = new LinkedHashSet<>();
 
         for (final Long ref : responsesRef) {
             // on récupère la réponse correspondante à la référence
             final SurveyOptionDTO rDto = this.responses.stream().filter(r -> r.getId() == ref).findFirst().get();
             if (rDto != null) {
-                if (!responsesDto.contains(rDto)) {
-                    responsesDto.add(rDto);
-                }
+                responsesDto.add(rDto);
 
                 // on récupère la question correspondantes à la réponse
-                if (!questionsDto.contains(rDto.getQuestion())) {
-                    questionsDto.add(rDto.getQuestion());
-                }
+                questionsDto.add(rDto.getQuestion());
             }
         }
         // ajout de la question, si celle çi n'a qu'une réponse libre
         this.questions.forEach((q) -> {
-            if (!questionsDto.contains(q) && otherTextMap.containsKey(q.getId())) {
+            if (otherTextMap.containsKey(q.getId())) {
                 questionsDto.add(q);
             }
         });
 
 
         // envoi au webservice
-        final SurveyResponseDTO data = new SurveyResponseDTO(questionsDto, responsesDto, otherTextMap);
+        final SurveyResponseDTO data = new SurveyResponseDTO(
+                new ArrayList<SurveyQuestionDTO>(questionsDto),
+                new ArrayList<SurveyOptionDTO>(responsesDto),
+                otherTextMap);
+
         SurveyFormServiceFactory.INSTANCE.insertAllResponses(data)
         .onSuccess(view::displaySuccessLogin)
         .onFailed(view::failureNotification)
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index 67e57b1..d8f86ec 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -129,12 +129,8 @@ public class SurveyFormResource implements SurveyFormService {
     @Produces(MediaType.APPLICATION_JSON)
     @Override
     public List<SurveyQuestionDTO> getQuestions() {
-        final List<Question> list = questionsDao.findAll();
-        final List<SurveyQuestionDTO> questions = new ArrayList<>();
-        for (Question q : list) {
-            questions.add(toDto(q));
-        }
-        return questions;
+        return questionsDao.findAll().stream()
+                .map(SurveyFormResource::toDto).collect(Collectors.toList());
     }
     @GET
     @Path(SurveyFormService.PATH_QUESTION_BY_CATEGORY)
@@ -166,12 +162,8 @@ public class SurveyFormResource implements SurveyFormService {
     @Produces(MediaType.APPLICATION_JSON)
     @Override
     public List<SurveyOptionDTO> getResponses() {
-        final List<Response> list = responsesDao.findAll();
-        final List<SurveyOptionDTO> responses = new ArrayList<>();
-        for (final Response r : list) {
-            responses.add(toDto(r));
-        }
-        return responses;
+        return responsesDao.findAll().stream()
+                .map(SurveyFormResource::toDto).collect(Collectors.toList());
     }
     @POST
     @Path(SurveyFormService.PATH_INSERT_RESPONSE)
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
index 8982cea..57cc022 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
@@ -5,6 +5,7 @@ package fr.agrometinfo.www.shared.dto;
 
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 
 /**
@@ -26,7 +27,7 @@ public class SurveyResponseDTO {
     /**
      * Storage, for each response, the other text, if specified.
      */
-    private HashMap<Long, String> otherTextMap = new HashMap<>();
+    private Map<Long, String> otherTextMap = new HashMap<>();
     /**
      * Default constructor.
      */
@@ -39,7 +40,7 @@ public class SurveyResponseDTO {
      * @param otherText otherText map
      */
     public SurveyResponseDTO(final List<SurveyQuestionDTO> qList,
-            final List<SurveyOptionDTO> rList, final HashMap<Long, String> otherText) {
+            final List<SurveyOptionDTO> rList, final Map<Long, String> otherText) {
         this.questions = qList;
         this.responses = rList;
         this.otherTextMap = otherText;
@@ -59,7 +60,7 @@ public class SurveyResponseDTO {
     /**
      * @return the otherTextMap
      */
-    public HashMap<Long, String> getOtherTextMap() {
+    public Map<Long, String> getOtherTextMap() {
         return otherTextMap;
     }
     /**
-- 
GitLab


From a6be7ad9ef80b1293424d1fc7f0e0065cebf0a4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 28 May 2024 11:14:50 +0200
Subject: [PATCH 16/30] =?UTF-8?q?Modifier=20la=20fa=C3=A7on=20d'=C3=A9crir?=
 =?UTF-8?q?e=20la=20date=20et=20l'heure=20pour=20la=20colonne=20user2respo?=
 =?UTF-8?q?nses.datetime?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../www/server/dao/UserResponsesDaoHibernate.java           | 5 -----
 .../java/fr/agrometinfo/www/server/model/UserResponse.java  | 6 +++---
 2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 8d147b9..3ebd4c7 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -3,9 +3,6 @@
  */
 package fr.agrometinfo.www.server.dao;
 
-import java.sql.Timestamp;
-import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map;
 
@@ -38,8 +35,6 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
     @Override
     public void insertResponse(final Question q, final Response r, final String otherText) {
         final UserResponse ur = new UserResponse();
-        ur.setDateTime(Timestamp.valueOf(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
-                .format(LocalDateTime.now())));
         ur.setQuestion(q);
         ur.setResponse(r);
         ur.setOtherText(otherText);
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index a143f06..1666da5 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -3,7 +3,7 @@
  */
 package fr.agrometinfo.www.server.model;
 
-import java.sql.Timestamp;
+import java.time.LocalDateTime;
 
 import jakarta.persistence.Column;
 import jakarta.persistence.Entity;
@@ -28,9 +28,9 @@ public class UserResponse {
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "id")
     private long id;
-    /** Date time of user responses. */
+    /** Date time of user responses (creation date). */
     @Column(name = "datetime")
-    private Timestamp dateTime;
+    private LocalDateTime dateTime = LocalDateTime.now();
     /** Related question. */
     @OneToOne
     @JoinColumn(name = "question")
-- 
GitLab


From dfe04f2ae9dc45dc60ec61a9ab7e4dbee8b4fb15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 28 May 2024 11:21:38 +0200
Subject: [PATCH 17/30] correction contenu mail

---
 .../java/fr/agrometinfo/www/server/service/MailServiceImpl.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
index 2a2933f..4f614ef 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -351,7 +351,7 @@ public class MailServiceImpl implements MailService {
 
         final StringBuilder builder = new StringBuilder();
         builder.append("Bonjour,\n");
-        builder.append("Un utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\\n\\n");
+        builder.append("Un utilisateur vient de remplir le formulaire d'enquête d'AgroMetInfo à l'instant.\n\n");
         builder.append("Il a répondu à " + data.getQuestions().size() + " question(s) :\n");
 
         data.getQuestions().forEach((q) -> {
-- 
GitLab


From ddcbb285f08452fb8a36a490045cb1df03634b0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 28 May 2024 11:37:07 +0200
Subject: [PATCH 18/30] =?UTF-8?q?Tri=20des=20cl=C3=A9s=20i18n=20par=20ordr?=
 =?UTF-8?q?e=20alphab=C3=A9tique=20et=20modification=20des=20cl=C3=A9s=20p?=
 =?UTF-8?q?our=20le=20formulaire?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../www/client/i18n/AppConstants.java         | 79 ++++++++++++-------
 .../www/client/view/SurveyView.java           | 14 ++--
 .../client/i18n/AppConstants_fr.properties    | 21 +++--
 3 files changed, 69 insertions(+), 45 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index 0c09cec..5c89b9a 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -183,6 +183,12 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     @DefaultStringValue("HTTP status text:")
     String failureStatusText();
 
+    /**
+    * @return translation
+    */
+    @DefaultStringValue("Ignorer")
+    String ignore();
+
     /**
      * @return translation
      */
@@ -314,8 +320,8 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     /**
      * @return translation
      */
-    @DefaultStringValue("Toggle right panel")
-    String toggleRightPanel();
+    @DefaultStringValue("Formulaire d'enquête")
+    String surveyFormTitle();
 
     /**
      * @return translation
@@ -327,6 +333,18 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
             + "or use the contact form.")
     String welcomeBody();
 
+    /**
+     * @return
+     */
+    @DefaultStringValue("Welcome to the new version of AgroMetInfo !<br/>"
+            + "To help us improve the application and make it as responsive as possible to your needs, "
+            + "<b>please fill in the short survey below</b> (it will take 2 minutes maximum).<br/>"
+            + "All information is anonymous, but will help us to better understand how you use the application."
+            + "<br/><b>Don't hesitate to give us your email address</b> "
+            + "so that we can keep you informed of all developments in the coming months and years."
+            + "<br/><br/>The AgroMetInfo development team")
+    String surveyFormDescription();
+
     /**
      * @return translation
      */
@@ -334,56 +352,57 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     String welcomeTitle();
 
     /**
-     * @return translation
+     * @return
      */
-    @DefaultStringValue("Yes")
-    String yes();
+    @DefaultStringValue("Other")
+    String surveyFormOtherTextCheckbox();
 
-    // Formulaire d'enquête - #5
     /**
      * @return translation
      */
-    @DefaultStringValue("Valider")
-    String validate();
+    @DefaultStringValue("You must click on checkbox above for activate this input field.")
+    String surveyFormOtherTextTooltip();
+
     /**
-    * @return translation
-    */
-    @DefaultStringValue("Ignorer")
-    String ignore();
+     * @return translation
+     */
+    @DefaultStringValue("Your responses have been recorded.")
+    String surveyFormSuccess();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("Formulaire d'enquête")
-    String loginFormTitle();
+    @DefaultStringValue("Your responses cannot been recorded, but you can use AgroMetInfo application.")
+    String surveyFormFail();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("Welcome to the new version of AgroMetInfo !<br/>"
-            + "To help us improve the application and make it as responsive as possible to your needs, "
-            + "<b>please fill in the short survey below</b> (it will take 2 minutes maximum).<br/>"
-            + "All information is anonymous, but will help us to better understand how you use the application."
-            + "<br/><b>Don't hesitate to give us your email address</b> "
-            + "so that we can keep you informed of all developments in the coming months and years."
-            + "<br/><br/>The AgroMetInfo development team")
-    String loginFormDescription();
+    @DefaultStringValue("Toggle right panel")
+    String toggleRightPanel();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("Other")
-    String loginFormOtherTextCheckbox();
+    @DefaultStringValue("Profile and settings")
+    String userProfile();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("You must click on checkbox above for activate this input field.")
-    String loginFormOtherTextTooltip();
+    @DefaultStringValue("Valider")
+    String validate();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("Your responses have been recorded.")
-    String loginFormSuccess();
+    @DefaultStringValue("You must be identified to access AgroMetInfo due to Meteo-France agreements related to "
+            + "SAFRAN data exchanges with AgroClim.")
+    String whyConnectionIsRequired();
+
     /**
      * @return translation
      */
-    @DefaultStringValue("Your responses cannot been recorded, but you can use AgroMetInfo application.")
-    String loginFormFail();
+    @DefaultStringValue("Yes")
+    String yes();
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
index adc2e38..5580d64 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
@@ -71,10 +71,10 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
 
         final Map<Long, TextArea> otherTextArea = new HashMap<>();
 
-        this.modal = ModalDialog.create(CSTS.loginFormTitle()).large().setAutoClose(true);
+        this.modal = ModalDialog.create(CSTS.surveyFormTitle()).large().setAutoClose(true);
         this.modal
         .appendChild(Elements.p()
-                .innerHtml(new SafeHtmlBuilder().appendHtmlConstant(CSTS.loginFormDescription()).toSafeHtml())
+                .innerHtml(new SafeHtmlBuilder().appendHtmlConstant(CSTS.surveyFormDescription()).toSafeHtml())
                 );
 
         for (Map.Entry<SurveyQuestionDTO, List<SurveyOptionDTO>> entry : this.availableResponses.entrySet()) {
@@ -92,12 +92,12 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
             });
 
             // on affiche le champ pour la réponse libre
-            final CheckBox otherCb = CheckBox.create(CSTS.loginFormOtherTextCheckbox())
+            final CheckBox otherCb = CheckBox.create(CSTS.surveyFormOtherTextCheckbox())
                     .id(k.getCategory() + "_other")
                     .css("login-checkbox");
             final TextArea otherText = TextArea.create().setId(k.getCategory() + "_other_text")
                     .setDisabled(true)
-                    .setTooltip(CSTS.loginFormOtherTextTooltip())
+                    .setTooltip(CSTS.surveyFormOtherTextTooltip())
                     .setRows(2)
                     .css("login-textarea");
 
@@ -106,7 +106,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
                 if (!otherText.isDisabled()) {
                     otherText.removeTooltip();
                 } else {
-                    otherText.setTooltip(CSTS.loginFormOtherTextTooltip());
+                    otherText.setTooltip(CSTS.surveyFormOtherTextTooltip());
                 }
             });
             this.checkBoxList.add(otherCb);
@@ -159,7 +159,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
 
     @Override
     public void failureNotification(final FailedResponseBean failedResponse) {
-        this.notification(CSTS.loginFormFail());
+        this.notification(CSTS.surveyFormFail());
     }
 
     @Override
@@ -169,7 +169,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
 
     @Override
     public void displaySuccessLogin(final String msg) {
-        this.notification(CSTS.loginFormSuccess());
+        this.notification(CSTS.surveyFormSuccess());
     }
     /**
      * Enable or disable validate button depending of checkbox status.
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index fe21225..917d271 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -33,6 +33,10 @@ invalidEmailAddress = Adresse courriel invalide
 legalNotice = Mentions légales
 legalNoticePath = legal-notice.html
 messageSent = Votre message a bien été envoyé à l’équipe d’AgroMetInfo.
+ignore = Ignorer
+login = Se connecter
+loginOrSignIn = ou s’inscrire avec
+logout = Se déconnecter
 metropolitanFrance = France métropolitaine
 no = Non
 normalComparison= Comparaison à la normale
@@ -43,6 +47,13 @@ releaseNotes = Notes de version
 releaseNotesPath = release-notes.html
 reloadingApplication = Rechargement de l'application pour une nouvelle version\u2026
 seePrivacyPolicy = Consultez le paragraphe « Données personnelles » dans les mentions légales.
+selectPrompt = -- sélectionner --
+surveyFormTitle = Formulaire d'enquête
+surveyFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider à la faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous (cela prendra 2 minutes maximum). L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
+surveyFormOtherTextCheckbox = Autre, à préciser
+surveyFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
+surveyFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
+surveyFormFail = Vos réponses au formulaire n'ont pas pu être enregistrées, mais vous pouvez tout de même utiliser AgroMetInfo.
 toggleRightPanel = Afficher / masquer le panneau de droite
 welcomeBody = AgroMetInfo est en cours de refonte.<br/>\
 Pour rester au courant des évolutions, vous pouvez nous envoyer un message à \
@@ -50,12 +61,6 @@ Pour rester au courant des évolutions, vous pouvez nous envoyer un message à \
 welcomeTitle = Bienvenue sur AgroMetInfo
 yes = Oui
 userProfile = Compte et paramètres
-whyConnectionIsRequired = Vous devez vous identifier pour accéder à AgroMetInfo en raison des accords avec Météo-France relatifs aux échanges de données SAFRAN avec AgroClim.
 validate = Valider
-ignore = Ignorer
-loginFormTitle = Formulaire d'enquête
-loginFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider à la faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous (cela prendra 2 minutes maximum). L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
-loginFormOtherTextCheckbox = Autre, à préciser
-loginFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
-loginFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
-loginFormFail = Vos réponses au formulaire n'ont pas pu être enregistrées, mais vous pouvez tout de même utiliser AgroMetInfo.
\ No newline at end of file
+whyConnectionIsRequired = Vous devez vous identifier pour accéder à AgroMetInfo en raison des accords avec Météo-France relatifs aux échanges de données SAFRAN avec AgroClim.
+yes= Oui
\ No newline at end of file
-- 
GitLab


From 2b14753463bba92dfc90fab008b2574e8a5acd8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 30 May 2024 14:22:22 +0200
Subject: [PATCH 19/30] =?UTF-8?q?Corrections=20suite=20revue=20de=20code?=
 =?UTF-8?q?=20(libell=C3=A9s=20des=20options=20aux=20questions,=20commenta?=
 =?UTF-8?q?ires=20code)=20Ajout=20de=20l'adresse=20mail=20dans=20le=20form?=
 =?UTF-8?q?ulaire=20d'enqu=C3=AAte?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sql/responses.csv                             |  2 +-
 sql/schema.tables.sql                         |  7 ++
 .../www/client/i18n/AppConstants.java         |  6 ++
 .../www/client/presenter/SurveyPresenter.java | 11 +++-
 .../www/client/view/SurveyView.java           | 10 ++-
 .../client/i18n/AppConstants_fr.properties    |  1 +
 .../www/server/dao/UserEmailDao.java          | 25 +++++++
 .../www/server/dao/UserEmailDaoHibernate.java | 31 +++++++++
 .../www/server/dao/UserResponsesDao.java      | 29 +++++++-
 .../server/dao/UserResponsesDaoHibernate.java | 25 +++++--
 .../www/server/model/Question.java            |  2 +-
 .../www/server/model/Response.java            |  2 +-
 .../www/server/model/UserEmail.java           | 39 +++++++++++
 .../www/server/model/UserResponse.java        |  2 +-
 .../www/server/rs/SurveyFormResource.java     | 25 +++++--
 .../www/server/util/EmailUtils.java           | 25 +++++++
 .../www/server/SurveyFormHibernateTest.java   | 19 +++++-
 .../www/server/rs/SurveyFormResourceTest.java | 66 ++++++++++++++-----
 .../test/resources/META-INF/persistence.xml   |  1 +
 .../www/shared/dto/SurveyResponseDTO.java     | 19 +++++-
 20 files changed, 309 insertions(+), 38 deletions(-)
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java

diff --git a/sql/responses.csv b/sql/responses.csv
index 930e6f7..97e9914 100644
--- a/sql/responses.csv
+++ b/sql/responses.csv
@@ -10,7 +10,7 @@ question;description
 2;Via le site internet d'AgroClim
 2;Via le site internet INRAE
 2;Média (TV, journaux, radio)
-2;Réseaux sociaux (Linkedin, Facebook, X, ...)
+2;Réseaux sociaux (Linkedin, Facebook, X…)
 2;Bouche à oreilles
 3;S'informer
 3;Enseigner
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index c4a57a7..79f133e 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -244,6 +244,13 @@ CREATE TABLE IF NOT EXISTS user2response (
 );
 COMMENT ON TABLE user2response IS 'Responses of user to questions, and other text if present';
 
+CREATE TABLE IF NOT EXISTS user_email (
+	id SERIAL NOT NULL,
+	datetime TIMESTAMP NOT NULL,
+	email VARCHAR NOT NULL,
+	CONSTRAINT PK_user_email PRIMARY KEY (id)
+);
+COMMENT ON TABLE user_email IS 'Simple table for register email of user, when he fills out survey';
 
 CREATE OR REPLACE VIEW v_i18n
 AS SELECT l.languagetag,
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index 5c89b9a..8d5c483 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -345,6 +345,12 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
             + "<br/><br/>The AgroMetInfo development team")
     String surveyFormDescription();
 
+    /**
+     * @return translation
+     */
+    @DefaultStringValue("Your e-mail address (optional) :")
+    String surveyFromEmailDescription();
+
     /**
      * @return translation
      */
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
index eb295e7..85ebcdd 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
@@ -91,7 +91,10 @@ public final class SurveyPresenter implements Presenter {
         .onFailed(view::failureNotification)
         .send();
     }
-    public void insertUserResponses(final List<Long> responsesRef, final Map<Long, String> otherTextMap) {
+    public void insertUserResponses(
+            final List<Long> responsesRef,
+            final Map<Long, String> otherTextMap,
+            final String email) {
         GWT.log("Insère les réponses de l'utilisateur");
 
         // traitement des réponses prédéfinies
@@ -115,13 +118,17 @@ public final class SurveyPresenter implements Presenter {
             }
         });
 
-
         // envoi au webservice
         final SurveyResponseDTO data = new SurveyResponseDTO(
                 new ArrayList<SurveyQuestionDTO>(questionsDto),
                 new ArrayList<SurveyOptionDTO>(responsesDto),
                 otherTextMap);
 
+        // traitement de l'adresse e-mail
+        if (!email.isEmpty()) {
+            data.setEmail(email);
+        }
+
         SurveyFormServiceFactory.INSTANCE.insertAllResponses(data)
         .onSuccess(view::displaySuccessLogin)
         .onFailed(view::failureNotification)
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
index 5580d64..2dfc9c9 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
@@ -9,6 +9,7 @@ import java.util.HashMap;
 import org.dominokit.domino.ui.button.Button;
 import org.dominokit.domino.ui.forms.CheckBox;
 import org.dominokit.domino.ui.forms.TextArea;
+import org.dominokit.domino.ui.forms.TextBox;
 import org.dominokit.domino.ui.modals.ModalDialog;
 import org.dominokit.rest.shared.request.FailedResponseBean;
 import org.gwtproject.safehtml.shared.SafeHtmlBuilder;
@@ -53,6 +54,10 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
     private Button validate;
     /** List of all checkbox on modal. */
     private List<CheckBox> checkBoxList = new ArrayList<>();
+    /**
+     * E-mail field.
+     */
+    private TextBox email;
 
     @Override
     public void close() {
@@ -119,6 +124,9 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
         });
 
         // E-mail de l'utilisateur
+        this.modal.appendChild(Elements.p().textContent(CSTS.surveyFromEmailDescription()));
+        this.email = TextBox.create().setType("email");
+        this.modal.appendChild(this.email);
 
         // Boutons de la fenêtre modale
         this.validate = Button.create(CSTS.validate()).linkify();
@@ -139,7 +147,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
                 }
             });
 
-            this.getPresenter().insertUserResponses(selectedResponsesRef, otherTextMap);
+            this.getPresenter().insertUserResponses(selectedResponsesRef, otherTextMap, this.email.getValue());
 
             ls.setItem(IS_VALIDATED_KEY, "true");
             this.modal.close();
diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index 917d271..fc13d99 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -50,6 +50,7 @@ seePrivacyPolicy = Consultez le paragraphe « Données personnelles » dans les
 selectPrompt = -- sélectionner --
 surveyFormTitle = Formulaire d'enquête
 surveyFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider à la faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous (cela prendra 2 minutes maximum). L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
+surveyFromEmailDescription = Votre adresse courriel (facultatif) :
 surveyFormOtherTextCheckbox = Autre, à préciser
 surveyFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
 surveyFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
new file mode 100644
index 0000000..1c3503a
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
@@ -0,0 +1,25 @@
+package fr.agrometinfo.www.server.dao;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import fr.agrometinfo.www.server.model.UserEmail;
+
+/**
+ * DAO for {@link UserEmail}.
+ * @author jdecome
+ *
+ */
+public interface UserEmailDao {
+    /**
+     * Insert e-mail address in table.
+     * @param email
+     * @param datetime provided localdatetime
+     */
+    void insertEmailUserAddress(String email, LocalDateTime datetime);
+    /**
+     * Getting all email address in database.
+     * @return list of email address
+     */
+    List<UserEmail> getEmailAddressList();
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
new file mode 100644
index 0000000..80388d2
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
@@ -0,0 +1,31 @@
+package fr.agrometinfo.www.server.dao;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import fr.agrometinfo.www.server.model.UserEmail;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public final class UserEmailDaoHibernate extends DaoHibernate<UserEmail> implements UserEmailDao {
+    /** Default constructor. */
+    public UserEmailDaoHibernate() {
+        super(UserEmail.class);
+    }
+    @Override
+    public void insertEmailUserAddress(final String email, final LocalDateTime datetime) {
+        final UserEmail um = new UserEmail();
+        um.setEmail(email);
+        um.setDatetime(datetime);
+        this.save(um);
+    }
+    @Override
+    public List<UserEmail> getEmailAddressList() {
+        return super.findAll();
+    }
+
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
index 06260f1..2531e58 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -1,12 +1,13 @@
 package fr.agrometinfo.www.server.dao;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.model.UserResponse;
 /**
- * DAO for {@link UserResponsesDao}.
+ * DAO for {@link UserResponse}.
  * @author jdecome
  */
 public interface UserResponsesDao {
@@ -19,21 +20,43 @@ public interface UserResponsesDao {
     /**
      * Insert response or text for specified question.<br>
      * If r is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code r} must be null.
+     * If otherText is provided, {@code r} must be null.<br>
+     * datetime is defined by {@link LocalDateTime#now} in this method.
      * @param q question to answer
      * @param r response
      * @param otherText text if response is other
      */
     void insertResponse(Question q, Response r, String otherText);
+    /**
+     * Insert response or text for specified question.<br>
+     * If r is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code r} must be null.
+     * @param q question to answer
+     * @param r response
+     * @param otherText text if response is other
+     * @param datetime provided localdatetime
+     */
+    void insertResponse(Question q, Response r, String otherText, LocalDateTime datetime);
     /**
      * Insert response or text for specified question, identified by reference.<br>
      * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null.
+     * If otherText is provided, {@code responseRef} must be null.<br>
+     * datetime is defined by {@link LocalDateTime#now} in this method.
      * @param questionRef reference of question to answer
      * @param responseRef reference of response
      * @param otherText text if response is other
      */
     void insertResponse(Long questionRef, Long responseRef, String otherText);
+    /**
+     * Insert response or text for specified question, identified by reference.<br>
+     * If responseRef is provided, {@code otherText} must be null.<br>
+     * If otherText is provided, {@code responseRef} must be null.
+     * @param questionRef reference of question to answer
+     * @param responseRef reference of response
+     * @param otherText text if response is other
+     * @param datetime provided datetime
+     */
+    void insertResponse(Long questionRef, Long responseRef, String otherText, LocalDateTime datetime);
     /**
      * Get all user responses.
      * @return number of responses
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 3ebd4c7..5f9aea3 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -3,6 +3,7 @@
  */
 package fr.agrometinfo.www.server.dao;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
 
@@ -33,31 +34,45 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
     }
     /** */
     @Override
-    public void insertResponse(final Question q, final Response r, final String otherText) {
+    public void insertResponse(
+            final Question q, final Response r,
+            final String otherText, final LocalDateTime datetime) {
         final UserResponse ur = new UserResponse();
+        ur.setDateTime(datetime);
         ur.setQuestion(q);
         ur.setResponse(r);
         ur.setOtherText(otherText);
 
         this.save(ur);
-
     }
     /** */
     @Override
-    public void insertResponse(final Long questionRef, final Long responseRef, final String otherText) {
+    public void insertResponse(final Question q, final Response r, final String otherText) {
+       this.insertResponse(q, r, otherText, LocalDateTime.now());
+    }
+    /** */
+    @Override
+    public void insertResponse(
+            final Long questionRef, final Long responseRef,
+            final String otherText, final LocalDateTime datetime) {
         // recherche de la question
         final Question q = questionsDao.findByRef(questionRef);
         if (q != null) {
             if (responseRef != null) {  // insertion d'une réponse prédéfinie, on recherche de la réponse
                 final Response r = responsesDao.findByRef(responseRef);
-                this.insertResponse(q, r, null);
+                this.insertResponse(q, r, null, datetime);
             } else {                    // insertion d'une réponse libre
-                this.insertResponse(q, null, otherText);
+                this.insertResponse(q, null, otherText, datetime);
             }
         }
     }
     /** */
     @Override
+    public void insertResponse(final Long questionRef, final Long responseRef, final String otherText) {
+        this.insertResponse(questionRef, responseRef, otherText, LocalDateTime.now());
+    }
+    /** */
+    @Override
     public int getNbUserResponses() {
         return super.findAll().size();
     }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
index 8f26872..3621392 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -12,7 +12,7 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * Questions for survey login modal.
+ * Questions for survey modal.
  * @author jdecome
  *
  */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
index 8e132f9..9ed8eba 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
@@ -14,7 +14,7 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * Available response.
+ * Options for questions.
  * @author jdecome
  *
  */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
new file mode 100644
index 0000000..015174c
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
@@ -0,0 +1,39 @@
+package fr.agrometinfo.www.server.model;
+
+import java.time.LocalDateTime;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import lombok.Data;
+
+/**
+ * Table for storage email adress.<br>
+ * @author jdecome
+ *
+ */
+@Data
+@Entity
+@Table(name = "user_email")
+public class UserEmail {
+    /**
+     * Primary key.
+     */
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private long id;
+    /**
+     * Date time of user response (creation date).
+     */
+    @Column(name = "datetime")
+    private LocalDateTime datetime;
+    /**
+     * E-mail address.
+     */
+    @Column(name = "email")
+    private String email;
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index 1666da5..2b68c16 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -30,7 +30,7 @@ public class UserResponse {
     private long id;
     /** Date time of user responses (creation date). */
     @Column(name = "datetime")
-    private LocalDateTime dateTime = LocalDateTime.now();
+    private LocalDateTime dateTime;
     /** Related question. */
     @OneToOne
     @JoinColumn(name = "question")
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index d8f86ec..e964f86 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -3,17 +3,19 @@
  */
 package fr.agrometinfo.www.server.rs;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-
 import fr.agrometinfo.www.server.dao.QuestionsDao;
 import fr.agrometinfo.www.server.dao.ResponsesDao;
+import fr.agrometinfo.www.server.dao.UserEmailDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.service.MailService;
+import fr.agrometinfo.www.server.util.EmailUtils;
 import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
 import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
 import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
@@ -102,6 +104,11 @@ public class SurveyFormResource implements SurveyFormService {
      */
     @Inject
     private UserResponsesDao userResponsesDao;
+    /**
+     * DAO for user email {@link UserEmailDao}.
+     */
+    @Inject
+    private UserEmailDao userEmailDao;
     /**
      * Mail service.
      */
@@ -178,9 +185,9 @@ public class SurveyFormResource implements SurveyFormService {
         final boolean otherTextIsNull = otherText == null || otherText.equals("null");
         if (this.checkIfQuestionExist(questionRef)) {
             if (!responseRefIsNull && otherTextIsNull) {     // réponse prédéfinie
-                this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null);
+                this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null, LocalDateTime.now());
             } else if (responseRefIsNull && !otherTextIsNull) {  // réponse libre
-                this.userResponsesDao.insertResponse(questionRef, null, otherText);
+                this.userResponsesDao.insertResponse(questionRef, null, otherText, LocalDateTime.now());
             }
         }
     }
@@ -203,6 +210,9 @@ public class SurveyFormResource implements SurveyFormService {
     @Produces(MediaType.APPLICATION_JSON)
     @Override
     public String insertAllResponses(final SurveyResponseDTO data) {
+        // datetime commun à tous les enregistrements (des réponses et de l'email)
+        final LocalDateTime datetime = LocalDateTime.now();
+
         // traitement des réponses prédéfinies
         for (final SurveyQuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
             // Insérer les réponses associés
@@ -215,16 +225,21 @@ public class SurveyFormResource implements SurveyFormService {
             // On insère les réponses prédéfinies
             for (final SurveyOptionDTO rDto : associatedResponses) {
                 final Response r = toEntity(rDto);
-                this.userResponsesDao.insertResponse(q, r, null);
+                this.userResponsesDao.insertResponse(q, r, null, datetime);
             }
 
             // Si il existe une réponse libre, l'insérer
             if (data.getOtherTextMap().containsKey(dto.getId())
                     && data.getOtherTextMap().get(dto.getId()) != null) {
-                this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getId()));
+                this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getId()), datetime);
             }
         }
 
+        // Si l'utilisateur a fourni son adresse e-mail
+        if (EmailUtils.isEmailAddressValid(data.getEmail())) {
+            this.userEmailDao.insertEmailUserAddress(data.getEmail(), datetime);
+        }
+
         // une fois que tout est inséré, on envoi un mail au support
         this.mailService.sendLoginFilled(data);
         return "0";
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java b/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java
new file mode 100644
index 0000000..855de26
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java
@@ -0,0 +1,25 @@
+package fr.agrometinfo.www.server.util;
+
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author jdecome
+ *
+ */
+public class EmailUtils extends StringUtils {
+    /**
+     * Check if email address provided is a valid address.<br>
+     * Verification by not null and regex
+     * @param email address
+     * @return {@code true} or {@code false}
+     */
+    public static final boolean isEmailAddressValid(final String email) {
+        if (email == null) {
+            return false;
+        }
+        final String emailPattern = "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}";
+        return Pattern.matches(emailPattern, email);
+    }
+}
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 93a8c1b..c4ecae8 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -20,10 +21,13 @@ import fr.agrometinfo.www.server.dao.QuestionsDao;
 import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
 import fr.agrometinfo.www.server.dao.ResponsesDao;
 import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.UserEmailDao;
+import fr.agrometinfo.www.server.dao.UserEmailDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
 
 /**
@@ -38,6 +42,8 @@ public class SurveyFormHibernateTest {
     private QuestionsDao questionsDao = new QuestionsDaoHibernate();
     /** DAO for responses. */
     private ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    /** DAO for email address of user. */
+    private UserEmailDao userEmailDao = new UserEmailDaoHibernate();
     
     @Test
     public void getQuestion() {
@@ -171,8 +177,19 @@ public class SurveyFormHibernateTest {
                 assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
             }
         }
+    }
+    @Test
+    public void setUserResponsesWithEmail() {
+        // just testing insertion of email user, without insertion of responses (already tested)
+        final String email = "john.doe@inrae.fr";
+        List<UserEmail> list = this.userEmailDao.getEmailAddressList();
+        assertNotNull(list, "La liste des e-mail n'est pas initialisée");
+        assertEquals(list.size(), 0, "La liste des e-mail est vide");
+        this.userEmailDao.insertEmailUserAddress(email, LocalDateTime.now());
         
-        
+        list = this.userEmailDao.getEmailAddressList();
+        assertEquals(list.size(), 1, "La liste des e-mail doit contenir au moins un élément");
+        assertEquals(list.get(0).getEmail(), email, "L'élément enregistré ne correspond pas avec le mail inséré « " + email + " »");
     }
     /**
      * Return randomly responses list
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 5ffca0c..362b8d4 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -30,11 +31,14 @@ import fr.agrometinfo.www.server.dao.QuestionsDao;
 import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
 import fr.agrometinfo.www.server.dao.ResponsesDao;
 import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.UserEmailDao;
+import fr.agrometinfo.www.server.dao.UserEmailDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
 import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
 import fr.agrometinfo.www.server.service.MailService;
 import fr.agrometinfo.www.server.service.MailServiceImpl;
@@ -92,6 +96,8 @@ public class SurveyFormResourceTest extends JerseyTest {
     private final ResponsesDao responsesDao = new ResponsesDaoHibernate();
     /** DAO for UserResponses. */
     private final UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
+    /** DAO for UserMail */
+    private final UserEmailDao userEmailDao = new UserEmailDaoHibernate();
     /** Mail service. */
     private final MailService mailService = new MailServiceTest();
     
@@ -103,6 +109,7 @@ public class SurveyFormResourceTest extends JerseyTest {
                 bind(questionsDao).to(QuestionsDao.class);
                 bind(responsesDao).to(ResponsesDao.class);
                 bind(userResponsesDao).to(UserResponsesDao.class);
+                bind(userEmailDao).to(UserEmailDao.class);
                 bind(mailService).to(MailService.class);
             }
         });
@@ -310,35 +317,62 @@ public class SurveyFormResourceTest extends JerseyTest {
         final List<SurveyOptionDTO> responsesDTO = responsesList.stream().map(SurveyFormResource::toDto).toList();
         
         final SurveyResponseDTO data = new SurveyResponseDTO(questionsDTO, responsesDTO, otherTextMap);
+        data.setEmail("john.doe@inrae.fr");
         assertEquals(data.getQuestions(), questionsDTO, "La liste de questions dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         assertEquals(data.getResponses(), responsesDTO, "La liste de réponses dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         assertEquals(data.getOtherTextMap(), otherTextMap, "La liste de réponses libre dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
         
-        
         jakarta.ws.rs.core.Response ret = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE_WITH_JSON)
                 .request(MediaType.APPLICATION_JSON).post(Entity.entity(data, MediaType.APPLICATION_JSON));
         
         assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "Le point d'entrée d'insertion doit retourner le code status 200 (OK)");
         
-        // Pour le test, vérifier, pour chaque question, les réponses via le DAO
+        // Récupère toutes les réponses pour toutes les questions
+        List<UserResponse> list = new ArrayList<>();
         for (final Question q : questionsDao.findAll()) {
-            final List<UserResponse> list = userResponsesDao.findAllByQuestion(q.getId());
-            assertNotNull(list, "La liste de réponses de l'utilisateur ne doit pas être null");
-            for (final UserResponse ur : list) {
-                assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
+            final List<UserResponse> responsesByQuestion = userResponsesDao.findAllByQuestion(q.getId());
+            assertNotNull(responsesByQuestion, "La liste de réponses de l'utilisateur pour la question « " + q.getId() + " » ne doit pas être null");
+            responsesByQuestion.forEach((ur) -> {
                 assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getDescription() + " »");
-                assertTrue(ur.getId() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
-                if (ur.getResponse() != null) {
-                    assertNotNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
-                    assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
-                } else {
-                    assertNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
-                    assertNotNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
-                }
-            }
+            });
+            list.addAll(responsesByQuestion);
         }
         
-        // on vérifie le mail
+        // Test sur toutes les réponses
+        list.forEach((ur) -> {
+            assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
+            assertTrue(ur.getId() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
+            if (ur.getResponse() != null) {
+                assertNotNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+                assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
+            } else {
+                assertNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
+                assertNotNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+            }
+        });
+
+        // on vérifie que tous les enregistrements ont le même datetime
+        assertEquals(list.stream().map((u) -> u.getDateTime()).distinct().count(), 1, "Toutes les réponses n'ont pas le même datetime");
+        final LocalDateTime datetimeResponse = list.stream().map((u) -> u.getDateTime()).distinct().findFirst().get();
+        assertNotNull(datetimeResponse, "Le datetime lue dans les réponses est null");
+        
+        // dans ce cas de test, on a inséré l'adresse mail de l'utilisateur
+        final List<UserEmail> emails = this.userEmailDao.getEmailAddressList();
+        assertNotNull(emails, "La liste des emails n'est pas initialisée");
+        assertNotEquals(emails.size(), 0, "La liste des emails est vide");
+        
+        emails.forEach((e) -> {
+            assertTrue((e.getId() > 0), "L'id de l'objet doit être supérieur à 0");
+            assertNotNull(e.getDatetime(), "Le timestamp de la question ne doit pas être null");
+            assertNotNull(e.getEmail(), "L'adresse mail contenue dans l'objet ne doit pas être null");
+        });
+        assertEquals(emails.stream().map((e) -> e.getDatetime()).distinct().count(), 1, "Toutes les adresses mails n'ont pas le même datetime");
+        final LocalDateTime datetimeEmail = emails.stream().map((e) -> e.getDatetime()).distinct().findFirst().get();
+        assertNotNull(datetimeEmail, "Le datetime lue dans les emails est null");
+        
+        assertEquals(datetimeResponse, datetimeEmail, "Les deux datetime ne sont pas identique");
+        
+        // on vérifie le mail envoyé au support, lorsqu'un utilisateur a rempli le formulaire
         final Mail mail = ((MailServiceTest) this.mailService).getMail();
         assertNotNull(mail, "L'objet mail ne doit pas être null");
         assertNotNull(mail.getContent(), "Le corps du message n'est pas défini (= null)");
diff --git a/www-server/src/test/resources/META-INF/persistence.xml b/www-server/src/test/resources/META-INF/persistence.xml
index d790ccb..9f6b455 100644
--- a/www-server/src/test/resources/META-INF/persistence.xml
+++ b/www-server/src/test/resources/META-INF/persistence.xml
@@ -21,6 +21,7 @@
     <class>fr.agrometinfo.www.server.model.Question</class>
     <class>fr.agrometinfo.www.server.model.Response</class>
     <class>fr.agrometinfo.www.server.model.UserResponse</class>
+    <class>fr.agrometinfo.www.server.model.UserEmail</class>
     <properties>
       <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:agrometinfo;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM '../sql/schema.types.h2.sql'\;RUNSCRIPT FROM '../sql/schema.tables.sql'\;RUNSCRIPT FROM '../sql/init_data.h2.sql';" />
       <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
index 57cc022..f8ad80b 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
@@ -9,7 +9,7 @@ import java.util.Map;
 
 
 /**
- * Class contains all data of login form.
+ * Class contains all data of survey form.
  * @author jdecome
  *
  */
@@ -28,6 +28,11 @@ public class SurveyResponseDTO {
      * Storage, for each response, the other text, if specified.
      */
     private Map<Long, String> otherTextMap = new HashMap<>();
+    /**
+     * Email user.<br>
+     * Can be null if user don't provide it (by called {@link SurveyResponseDTO#setEmail(String)} method only).
+     */
+    private String email = null;
     /**
      * Default constructor.
      */
@@ -81,4 +86,16 @@ public class SurveyResponseDTO {
     public void setOtherTextMap(final HashMap<Long, String> map) {
         this.otherTextMap = map;
     }
+    /**
+     * @return the email
+     */
+    public String getEmail() {
+        return email;
+    }
+    /**
+     * @param value the email to set
+     */
+    public void setEmail(final String value) {
+        this.email = value;
+    }
 }
-- 
GitLab


From 292d3652008aee62c1672e0133e48d07e57024f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 30 May 2024 15:07:47 +0200
Subject: [PATCH 20/30] corrections test 7f5f8cdc

---
 .../main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java | 4 ++++
 .../fr/agrometinfo/www/server/SurveyFormHibernateTest.java    | 2 +-
 .../fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java  | 4 +++-
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
index 1c3503a..4ab9254 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
@@ -22,4 +22,8 @@ public interface UserEmailDao {
      * @return list of email address
      */
     List<UserEmail> getEmailAddressList();
+    /**
+     *
+     */
+    void deleteAll();
 }
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index c4ecae8..9986f3c 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -181,7 +181,7 @@ public class SurveyFormHibernateTest {
     @Test
     public void setUserResponsesWithEmail() {
         // just testing insertion of email user, without insertion of responses (already tested)
-        final String email = "john.doe@inrae.fr";
+        final String email = "jane.doe@inrae.fr";
         List<UserEmail> list = this.userEmailDao.getEmailAddressList();
         assertNotNull(list, "La liste des e-mail n'est pas initialisée");
         assertEquals(list.size(), 0, "La liste des e-mail est vide");
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 362b8d4..0f13c86 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -366,6 +366,7 @@ public class SurveyFormResourceTest extends JerseyTest {
             assertNotNull(e.getDatetime(), "Le timestamp de la question ne doit pas être null");
             assertNotNull(e.getEmail(), "L'adresse mail contenue dans l'objet ne doit pas être null");
         });
+
         assertEquals(emails.stream().map((e) -> e.getDatetime()).distinct().count(), 1, "Toutes les adresses mails n'ont pas le même datetime");
         final LocalDateTime datetimeEmail = emails.stream().map((e) -> e.getDatetime()).distinct().findFirst().get();
         assertNotNull(datetimeEmail, "Le datetime lue dans les emails est null");
@@ -394,9 +395,10 @@ public class SurveyFormResourceTest extends JerseyTest {
         }
     }
     /**
-     * Reset user2responses table.
+     * Reset tables.
      */
     private void resetUserResponses() {
         this.userResponsesDao.deleteAll();
+        this.userEmailDao.deleteAll();
     }
 }
-- 
GitLab


From 5acdab10f726252ace70596f90aaf92519617a1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Thu, 30 May 2024 16:05:45 +0200
Subject: [PATCH 21/30] Suppression de la colonne question.category car inutile

---
 sql/init_data.h2.sql                          |   2 +-
 sql/init_data.postgresql.sql                  |   2 +-
 sql/questions.csv                             |   8 +-
 sql/schema.tables.sql                         |   1 -
 .../www/client/view/SurveyView.java           |   3 +-
 .../www/server/dao/QuestionsDao.java          |   6 -
 .../www/server/dao/QuestionsDaoHibernate.java |   9 --
 .../www/server/model/Question.java            |   5 -
 .../www/server/rs/SurveyFormResource.java     |  11 --
 .../www/server/SurveyFormHibernateTest.java   |  32 ++---
 .../www/server/rs/SurveyFormResourceTest.java | 121 ------------------
 .../www/shared/dto/SurveyQuestionDTO.java     |  20 +--
 .../www/shared/service/SurveyFormService.java |  10 --
 13 files changed, 21 insertions(+), 209 deletions(-)

diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index 74219fe..4de57ed 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -127,7 +127,7 @@ INSERT INTO simulation (date, simulationid, started, ended) VALUES
 --
 REFRESH MATERIALIZED VIEW v_pra_dailyvalue;
 
-INSERT INTO question(id, category, description)
+INSERT INTO question(id, description)
 	SELECT * FROM CSVREAD('../sql/questions.csv');
 
 INSERT INTO response(question, description)
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index d53ac68..bf1b5d5 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -170,7 +170,7 @@ INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
     ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
 
 -- questions
-\COPY question(id, category, description) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
+\COPY question(id, description) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
 
 -- responses
 -- WARNING : responses CSV file use semicolon (« ; ») as separator !!
diff --git a/sql/questions.csv b/sql/questions.csv
index 927f34e..f851766 100644
--- a/sql/questions.csv
+++ b/sql/questions.csv
@@ -1,4 +1,4 @@
-id,category,description
-1,profession,Quelle est votre profession ?
-2,origin,Comment avez-vous connu AgroMetInfo ?
-3,useCase,Dans quel but voulez-vous utiliser cette application ?
+id,description
+1,Quelle est votre profession ?
+2,Comment avez-vous connu AgroMetInfo ?
+3,Dans quel but voulez-vous utiliser cette application ?
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index 79f133e..681b22d 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -217,7 +217,6 @@ COMMENT ON TABLE dailyvisit IS 'Number of visits per day.';
 -- Tables for survey form - #5
 CREATE TABLE IF NOT EXISTS question (
 	id SERIAL NOT NULL,
-	category VARCHAR(20) NOT NULL,
 	description VARCHAR(255) NULL,
 	CONSTRAINT PK_question PRIMARY KEY (id)
 );
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
index 2dfc9c9..63b0c02 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
@@ -98,9 +98,8 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
 
             // on affiche le champ pour la réponse libre
             final CheckBox otherCb = CheckBox.create(CSTS.surveyFormOtherTextCheckbox())
-                    .id(k.getCategory() + "_other")
                     .css("login-checkbox");
-            final TextArea otherText = TextArea.create().setId(k.getCategory() + "_other_text")
+            final TextArea otherText = TextArea.create()
                     .setDisabled(true)
                     .setTooltip(CSTS.surveyFormOtherTextTooltip())
                     .setRows(2)
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
index 7e65a6d..b0893c9 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
@@ -23,10 +23,4 @@ public interface QuestionsDao {
      * @return Question object
      */
     Question findByRef(long questionRef);
-    /**
-     * Find question object by category.
-     * @param category of question
-     * @return Question object
-     */
-    Question findByCategory(String category);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
index e243e48..3803ea4 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
@@ -3,8 +3,6 @@
  */
 package fr.agrometinfo.www.server.dao;
 
-import java.util.Map;
-
 import fr.agrometinfo.www.server.model.Question;
 import jakarta.enterprise.context.ApplicationScoped;
 
@@ -25,11 +23,4 @@ public class QuestionsDaoHibernate extends DaoHibernate<Question> implements Que
     public Question findByRef(final long ref) {
         return super.find(ref);
     }
-    /** */
-    @Override
-    public Question findByCategory(final String category) {
-        final String jpql = "SELECT q FROM Question q WHERE q.category = :cat";
-        return super.findOneByJPQL(jpql, Map.of("cat", category), Question.class);
-    }
-
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
index 3621392..f618b1e 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
@@ -28,11 +28,6 @@ public class Question {
     @GeneratedValue(strategy = GenerationType.AUTO)
     @Column(name = "id")
     private long id;
-    /**
-     * Catogory of question.
-     */
-    @Column(name = "category")
-    private String category;
     /**
      * Label of question.
      */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index e964f86..37e6b19 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -49,7 +49,6 @@ public class SurveyFormResource implements SurveyFormService {
     static SurveyQuestionDTO toDto(final Question question) {
         final SurveyQuestionDTO dto = new SurveyQuestionDTO();
         dto.setId(question.getId());
-        dto.setCategory(question.getCategory());
         dto.setDescription(question.getDescription());
         return dto;
     }
@@ -73,7 +72,6 @@ public class SurveyFormResource implements SurveyFormService {
     private static Question toEntity(final SurveyQuestionDTO dto) {
         final Question question = new Question();
         question.setId(dto.getId());
-        question.setCategory(dto.getCategory());
         question.setDescription(dto.getDescription());
         return question;
     }
@@ -140,15 +138,6 @@ public class SurveyFormResource implements SurveyFormService {
                 .map(SurveyFormResource::toDto).collect(Collectors.toList());
     }
     @GET
-    @Path(SurveyFormService.PATH_QUESTION_BY_CATEGORY)
-    @Produces(MediaType.APPLICATION_JSON)
-    @Override
-    public SurveyQuestionDTO getQuestionByCategory(@QueryParam(value = "category") final String category) {
-        this.checkRequired(category, "category");
-        final Question question = questionsDao.findByCategory(category);
-        return toDto(question);
-    }
-    @GET
     @Path(SurveyFormService.PATH_RESPONSES_BY_QUESTION_LIST)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 9986f3c..4f85011 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -54,11 +54,6 @@ public class SurveyFormHibernateTest {
             final Question foundedByRef = questionsDao.findByRef(q.getId());
             assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
             assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
-            
-            // recherche de la question par sa catégorie
-            final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
-            assertNotNull(foundedByCategory, "La question correspondante à la catégorie « " + q.getCategory() + " » est null");
-            assertEquals(foundedByCategory, q, "La question correspondante à la catégorie « " + q.getCategory() + " » est vide");
         }
     }
     
@@ -78,10 +73,6 @@ public class SurveyFormHibernateTest {
             assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
             assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
             
-            final Question foundedByCategory = questionsDao.findByCategory(q.getCategory());
-            assertNotNull(foundedByCategory, "La question correspondante à la catégorie « " + q.getCategory() + " » est null");
-            assertEquals(foundedByCategory, q, "La question correspondante à la catégorie « " + q.getCategory() + " » est vide");
-            
             // recherche de la réponse par sa référence
             final Response responseByRef = responsesDao.findByRef(r.getId());
             assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getId() + " est null");
@@ -91,11 +82,12 @@ public class SurveyFormHibernateTest {
     
     @Test
     public void setUserResponses() {
-        final Question profession = questionsDao.findByCategory("profession");
-        assertNotNull(profession, "Aucune question ne correspond à la catégorie « profession »");
+        final List<Question> questions = questionsDao.findAll();
+        final Question profession = questions.stream().filter((q) -> q.getDescription().contains("profession")).findFirst().get();
+        assertNotNull(profession, "Aucune question ne correspond à la profession");
         List<Response> responses = responsesDao.findAllByQuestion(profession.getId());
-        assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « profession »");
-        assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « profession » est vide");
+        assertNotNull(responses, "Aucune réponse ne correspond à la profession");
+        assertNotEquals(responses.size(), 0, "La liste de réponses pour la profession est vide");
         for (final Response r : responses) {
             assertEquals(r.getQuestion(), profession, "La question de la réponse ne correspond pas avec la question d'origine");
         }
@@ -147,26 +139,26 @@ public class SurveyFormHibernateTest {
         }
         
         // Essai de l'insertion par les références, avec une autre question
-        final Question useCase = questionsDao.findByCategory("useCase");
-        assertNotNull(useCase, "Aucune question ne correspond à la catégorie « useCase »");
+        final Question useCase = questions.stream().filter((q) -> q.getId() == 3).findFirst().get();
+        assertNotNull(useCase, "Aucune question ne correspond aux cas d'utilisation");
         responses = responsesDao.findAllByQuestion(useCase.getId());
-        assertNotNull(responses, "Aucune réponse ne correspond à la catégorie « useCase »");
-        assertNotEquals(responses.size(), 0, "La liste de réponses pour la catégorie « useCase » est vide");
+        assertNotNull(responses, "Aucune réponse ne correspond à la question des cas d'utilisation");
+        assertNotEquals(responses.size(), 0, "La liste de réponses pour la la question des cas d'utilisation est vide");
         
         r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
         userResponsesDao.insertResponse(useCase.getId(), r.getId(), null);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
-        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir un élément");
+        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), useCase, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
         assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
         assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
         
-        other = other.concat(" pour la question « useCase »");
+        other = other.concat(" pour la question des cas d'utilisation");
         userResponsesDao.insertResponse(useCase.getId(), null, other);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
-        assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question « useCase » doit contenir deux éléments");
+        assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir deux éléments");
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
             if (ur.getOtherText() != null) {    // on est sur une réponse libre
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 0f13c86..12d1bc2 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -141,23 +141,6 @@ public class SurveyFormResourceTest extends JerseyTest {
             
             // Pour chaque question, 
             for (final Question q : questions) {
-                // on vérifie que la question est retrouvable par la catégorie, via le webservice
-                final String jsonCategory = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTION_BY_CATEGORY)
-                                                .queryParam("category", q.getCategory())
-                                                .request().get(String.class);
-                
-                assertNotNull(jsonCategory, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question n°" + q.getId());
-                assertFalse(jsonCategory.isEmpty(), "La chaine de caractère JSON de la catégorie pour la question n° " + q.getId() + " est vide");
-                
-                final Question questionByCategory = objectMapper.readValue(jsonCategory, new TypeReference<Question>() {});
-                assertNotNull(questionByCategory, "La conversion du JSON de la catégorie pour la question n° " + q.getId() + " n'a pas fonctionnée");
-                assertEquals(questionByCategory, q, "La question lue par la catégorie ne correspond pas avec la question n° " + q.getId() + "d'origine");
-                
-                // On vérifie, via le DAO
-                final Question questionByDao = questionsDao.findByCategory(q.getCategory());
-                assertNotNull(questionByDao, "La lecture de la question n° " + q.getId() + " via le DAO n'a pas fonctionnée");
-                assertEquals(questionByDao, q, "La question lue par le DAO ne correspond pas avec la question n° " + q.getId() + "d'origine");
-                
                 // on récupère les réponses associées via le webservice
                 final String jsonResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
                                                 .queryParam("questionRef", String.valueOf(q.getId()))
@@ -187,110 +170,6 @@ public class SurveyFormResourceTest extends JerseyTest {
             log.info(e.getMessage());
         }
     }
-    /** Test de l'écriture en base. */
-    @Test
-    public void setUserResponses() {
-         this.resetUserResponses();
-         final String category = "origin";
-         // Question
-         final String jsonOrigins = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTION_BY_CATEGORY)
-                                         .queryParam("category", category)
-                                        .request().get(String.class);
-         assertNotNull(jsonOrigins, "Impossible de récupérer la ressource (objet null) de la catégorie pour la question de l'origine");
-         assertFalse(jsonOrigins.isEmpty(), "La chaine de caractère JSON pour la question de catégorie « " + category + " » est vide");
-         try {
-            final Question origin = objectMapper.readValue(jsonOrigins, new TypeReference<Question>() {});
-            assertEquals(origin, questionsDao.findByCategory(category), "La question lue dans le webservice ne correspond pas avec la question via le DAO");
-            
-            // on récupère les réponses de la question
-            final List<Response> responses = objectMapper.readValue(target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
-                    .queryParam("questionRef", String.valueOf(origin.getId()))
-                    .request()
-                    .get(String.class), new TypeReference<List<Response>>() {});
-            
-            assertNotNull(responses, "La conversion du JSON des réponses de la question « " + origin.getCategory() + " » n'a pas fonctionnée");
-            assertNotEquals(responses.size(), " La liste de réponses pour la question " + origin.getCategory() + " est vide");
-            for (final Response r : responses) {
-                assertEquals(r.getQuestion(), origin, "La question de la réponse ne correspond pas avec la question d'origine");
-            }
-            
-            final List<Response> listOfResponses = new ArrayList<>();   // liste des réponses insérées
-            // Insertion d'une réponse prédéfénie
-            Response r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
-            final Form form = new Form();
-            form.param("questionRef", String.valueOf(origin.getId()));
-            form.param("responseRef", String.valueOf(r.getId()));
-            form.param("otherText", "null");
-            
-            target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
-                .request(MediaType.TEXT_PLAIN)
-                .post(Entity.form(form));
-            listOfResponses.add(r);
-            
-            // On lit la réponse insérée, via le DAO
-            List<UserResponse> userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
-            assertEquals(userResponsesList.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
-            assertEquals(userResponsesList.get(0).getQuestion(), origin, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
-            assertEquals(userResponsesList.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
-            assertNull(userResponsesList.get(0).getOtherText(), "Le texte libre doit être null");
-            
-            // insertion de plusieurs réponses
-            final int nbToInsert = 3;
-            final List<Response> randoms = this.pickNRandom(responses, nbToInsert);
-            for (final Response res : randoms) {
-                final Form f = new Form();
-                f.param("questionRef", String.valueOf(origin.getId()));
-                f.param("responseRef", String.valueOf(res.getId()));
-                f.param("otherText", "null");
-                
-                target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
-                .request(MediaType.TEXT_PLAIN)
-                .post(Entity.form(f));
-                
-                listOfResponses.add(res);
-            }
-            
-            // Vérification via le DAO
-            userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
-            assertEquals(userResponsesList.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + "
-                                                                        + nbToInsert + " réponses supplémentaires");
-            for (final UserResponse ur : userResponsesList) {
-                assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question");
-                assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
-                assertEquals(ur.getResponse().getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
-                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
-            }
-            
-            final int newNbResponses = 1 + nbToInsert;
-            
-            // insertion d'une réponse à choix libre
-            final String other = "Ceci est une réponse libre";
-            final Form formOther = new Form();
-            formOther.param("questionRef", String.valueOf(origin.getId()));
-            formOther.param("responseRef", "null");
-            formOther.param("otherText", other);
-            
-            target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
-            .request(MediaType.TEXT_PLAIN)
-            .post(Entity.form(formOther));
-            
-            userResponsesList = userResponsesDao.findAllByQuestion(origin.getId());
-            assertEquals(userResponsesList.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les "
-                                    + newNbResponses + " réponses + la réponse libre");
-            for (final UserResponse ur : userResponsesList) {
-                assertEquals(ur.getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
-                if (ur.getOtherText() != null) {    // on est sur une réponse libre
-                    assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
-                    assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
-                } else {
-                    assertEquals(ur.getResponse().getQuestion(), origin, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
-                    assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
-                }
-            }
-        } catch (JsonProcessingException e) {
-            log.info(e.getMessage());
-        }
-    }
     /** Test en définissant les réponses pour les questions. */
     @Test
     public void testWithJsonResponses() {
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
index 78ceb88..49b5545 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
@@ -18,10 +18,6 @@ public class SurveyQuestionDTO {
      * Ref of question.
      */
     private long id;
-    /**
-     * Category of question.
-     */
-    private String category;
     /**
      * Label of question.
      */
@@ -32,12 +28,6 @@ public class SurveyQuestionDTO {
     public long getId() {
         return id;
     }
-    /**
-     * @return the category
-     */
-    public String getCategory() {
-        return category;
-    }
     /**
      * @return the fullName
      */
@@ -50,12 +40,6 @@ public class SurveyQuestionDTO {
     public void setId(final long value) {
         this.id = value;
     }
-    /**
-     * @param value the category to set
-     */
-    public void setCategory(final String value) {
-        this.category = value;
-    }
     /**
      * @param value the fullName to set
      */
@@ -65,7 +49,7 @@ public class SurveyQuestionDTO {
     /** */
     @Override
     public int hashCode() {
-        return Objects.hash(category, description, id);
+        return Objects.hash(description, id);
     }
     /** */
     @Override
@@ -80,7 +64,7 @@ public class SurveyQuestionDTO {
             return false;
         }
         SurveyQuestionDTO other = (SurveyQuestionDTO) obj;
-        return Objects.equals(category, other.category) && Objects.equals(description, other.description)
+        return Objects.equals(description, other.description)
                 && id == other.id;
     }
 }
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
index a253ead..4475dc5 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
@@ -30,8 +30,6 @@ public interface SurveyFormService {
     String PATH = "login";
     /** Path for {@link SurveyFormService#getQuestions()}. */
     String PATH_QUESTIONS_LIST = "questions";
-    /**Path for {@link SurveyFormService#getQuestionByCategory(String)}. */
-    String PATH_QUESTION_BY_CATEGORY = "question";
     /** Path for {@link SurveyFormService#getResponses}. */
     String PATH_RESPONSES_LIST = "responses";
     /** Path for {@link SurveyFormService#getResponsesByQuestion(long)}. */
@@ -49,14 +47,6 @@ public interface SurveyFormService {
     @GET
     @Path(PATH_RESPONSES_LIST)
     List<SurveyOptionDTO> getResponses();
-    /**
-     * Get question by his category.
-     * @param category of the question
-     * @return question
-     */
-    @GET
-    @Path(PATH_QUESTION_BY_CATEGORY)
-    SurveyQuestionDTO getQuestionByCategory(@QueryParam(value = "category") String category);
     /**
      * Returning the list of possible response for one question.
      * @param questionRef ref of question
-- 
GitLab


From 28e47a9a4f30b744824e121f5c3fc5a5d8530caa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 31 May 2024 08:36:18 +0200
Subject: [PATCH 22/30] correction test commit 03208350

---
 .../fr/agrometinfo/www/server/SurveyFormHibernateTest.java | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 4f85011..6b1c3cd 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -29,12 +29,14 @@ import fr.agrometinfo.www.server.model.Question;
 import fr.agrometinfo.www.server.model.Response;
 import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
+import lombok.extern.log4j.Log4j2;
 
 /**
  * Test of UserResponsesDaoHibernate.
  * @author jdecome
  *
  */
+@Log4j2
 public class SurveyFormHibernateTest {
     /** DAO for user's responses. */
     private UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
@@ -82,6 +84,7 @@ public class SurveyFormHibernateTest {
     
     @Test
     public void setUserResponses() {
+        this.resetUserResponses();
         final List<Question> questions = questionsDao.findAll();
         final Question profession = questions.stream().filter((q) -> q.getDescription().contains("profession")).findFirst().get();
         assertNotNull(profession, "Aucune question ne correspond à la profession");
@@ -198,4 +201,8 @@ public class SurveyFormHibernateTest {
             return copy.subList(0, nbElements);
         }
     }
+    private void resetUserResponses() {
+        this.userResponsesDao.deleteAll();
+        this.userEmailDao.deleteAll();
+    }
 }
-- 
GitLab


From 844bcc8e835b29c16a5de050a990e1ed481be491 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 31 May 2024 14:47:05 +0200
Subject: [PATCH 23/30] Modification des noms des tables SQL

---
 sql/drop.sql                                  |  7 +-
 sql/init_data.h2.sql                          |  4 +-
 sql/init_data.postgresql.sql                  |  8 +--
 sql/responses.csv                             |  2 +-
 sql/schema.tables.sql                         | 38 +++++-----
 .../www/client/presenter/SurveyPresenter.java |  2 +
 .../www/server/dao/QuestionsDaoHibernate.java | 26 -------
 .../www/server/dao/ResponsesDaoHibernate.java | 35 ---------
 ...ResponsesDao.java => SurveyOptionDao.java} | 12 ++--
 .../server/dao/SurveyOptionDaoHibernate.java  | 35 +++++++++
 ...estionsDao.java => SurveyQuestionDao.java} | 12 ++--
 .../dao/SurveyQuestionDaoHibernate.java       | 26 +++++++
 .../www/server/dao/UserResponsesDao.java      |  8 +--
 .../server/dao/UserResponsesDaoHibernate.java | 18 ++---
 .../{Response.java => SurveyOption.java}      | 16 ++---
 .../{Question.java => SurveyQuestion.java}    |  4 +-
 .../www/server/model/UserEmail.java           |  2 +-
 .../www/server/model/UserResponse.java        | 14 ++--
 .../www/server/rs/SurveyFormResource.java     | 42 +++++------
 .../www/server/SurveyFormHibernateTest.java   | 72 +++++++++----------
 .../www/server/rs/SurveyFormResourceTest.java | 56 +++++++--------
 .../test/resources/META-INF/persistence.xml   |  4 +-
 22 files changed, 223 insertions(+), 220 deletions(-)
 delete mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
 delete mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
 rename www-server/src/main/java/fr/agrometinfo/www/server/dao/{ResponsesDao.java => SurveyOptionDao.java} (60%)
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
 rename www-server/src/main/java/fr/agrometinfo/www/server/dao/{QuestionsDao.java => SurveyQuestionDao.java} (50%)
 create mode 100644 www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
 rename www-server/src/main/java/fr/agrometinfo/www/server/model/{Response.java => SurveyOption.java} (71%)
 rename www-server/src/main/java/fr/agrometinfo/www/server/model/{Question.java => SurveyQuestion.java} (91%)

diff --git a/sql/drop.sql b/sql/drop.sql
index 324c3c5..24a5942 100644
--- a/sql/drop.sql
+++ b/sql/drop.sql
@@ -10,6 +10,7 @@ DROP TABLE period;
 DROP TABLE i18n;
 DROP TABLE i18nkey;
 DROP TABLE locale;
-DROP TABLE user2responses;
-DROP TABLE responses;
-DROP TABLE questions;
+DROP TABLE userresponse;
+DROP TABLE surveyoption;
+DROP TABLE surveyquestion;
+DROP TABLE useremail;
diff --git a/sql/init_data.h2.sql b/sql/init_data.h2.sql
index 4de57ed..c40a7c0 100644
--- a/sql/init_data.h2.sql
+++ b/sql/init_data.h2.sql
@@ -127,9 +127,9 @@ INSERT INTO simulation (date, simulationid, started, ended) VALUES
 --
 REFRESH MATERIALIZED VIEW v_pra_dailyvalue;
 
-INSERT INTO question(id, description)
+INSERT INTO surveyquestion(id, description)
 	SELECT * FROM CSVREAD('../sql/questions.csv');
 
-INSERT INTO response(question, description)
+INSERT INTO surveyoption(surveyquestion, description)
 	SELECT * FROM CSVREAD('../sql/responses.csv', null, 'fieldSeparator=;');
 
diff --git a/sql/init_data.postgresql.sql b/sql/init_data.postgresql.sql
index bf1b5d5..4a60713 100644
--- a/sql/init_data.postgresql.sql
+++ b/sql/init_data.postgresql.sql
@@ -11,8 +11,8 @@ DELETE FROM period;
 DELETE FROM i18n;
 DELETE FROM i18nkey;
 DELETE FROM locale;
-DELETE FROM question;
-DELETE FROM response;
+DELETE FROM surveyquestion;
+DELETE FROM surveyoption;
 
 -- translations
 CREATE TEMPORARY TABLE IF NOT EXISTS tmp_translation (
@@ -170,9 +170,9 @@ INSERT INTO normalvalue (indicator, cell, doy, medianvalue, q5, q95)
     ON CONFLICT ON CONSTRAINT "UK_normalvalue" DO NOTHING;
 
 -- questions
-\COPY question(id, description) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
+\COPY surveyquestion(id, description) FROM questions.csv WITH DELIMITER ',' HEADER CSV;
 
 -- responses
 -- WARNING : responses CSV file use semicolon (« ; ») as separator !!
-\COPY response(question, description) FROM responses.csv WITH DELIMITER ';' HEADER CSV;
+\COPY surveyoption(surveyquestion, description) FROM responses.csv WITH DELIMITER ';' HEADER CSV;
 
diff --git a/sql/responses.csv b/sql/responses.csv
index 97e9914..173c16e 100644
--- a/sql/responses.csv
+++ b/sql/responses.csv
@@ -1,4 +1,4 @@
-question;description
+surveyquestion;description
 1;Agriculteur
 1;Conseiller agricole (Chambre d'agriculture, Entreprise de service)
 1;Scientifique
diff --git a/sql/schema.tables.sql b/sql/schema.tables.sql
index 681b22d..47eab0f 100644
--- a/sql/schema.tables.sql
+++ b/sql/schema.tables.sql
@@ -215,41 +215,41 @@ CREATE TABLE IF NOT EXISTS dailyvisit(
 COMMENT ON TABLE dailyvisit IS 'Number of visits per day.';
 
 -- Tables for survey form - #5
-CREATE TABLE IF NOT EXISTS question (
+CREATE TABLE IF NOT EXISTS surveyquestion (
 	id SERIAL NOT NULL,
 	description VARCHAR(255) NULL,
-	CONSTRAINT PK_question PRIMARY KEY (id)
+	CONSTRAINT PK_surveyquestion PRIMARY KEY (id)
 );
-COMMENT ON TABLE question IS 'Questions for survey form';
+COMMENT ON TABLE surveyquestion IS 'Questions for survey form';
 
-CREATE TABLE IF NOT EXISTS response (
+CREATE TABLE IF NOT EXISTS surveyoption (
 	id SERIAL NOT NULL,
-	question INT4 NOT NULL,
+	surveyquestion INT4 NOT NULL,
 	description VARCHAR NULL,
-	CONSTRAINT PK_response PRIMARY KEY (id),
-	CONSTRAINT FK_response_question FOREIGN KEY (question) REFERENCES question(id)
+	CONSTRAINT PK_surveyoption PRIMARY KEY (id),
+	CONSTRAINT FK_surveyoption_surveyquestion FOREIGN KEY (surveyquestion) REFERENCES surveyquestion(id)
 );
-COMMENT ON TABLE response IS 'Options responses for questions.';
+COMMENT ON TABLE surveyoption IS 'Options responses for questions.';
 
-CREATE TABLE IF NOT EXISTS user2response (
+CREATE TABLE IF NOT EXISTS userresponse (
 	id SERIAL NOT NULL,
 	datetime TIMESTAMP NOT NULL,
-	question INT4 NOT NULL,
-	response INT4 NULL,
-	other_text VARCHAR NULL,
-	CONSTRAINT PK_user2response PRIMARY KEY (id),
-	CONSTRAINT FK_user2response_question FOREIGN KEY (question) REFERENCES question(id),
-	CONSTRAINT FK_user2response_response FOREIGN KEY (response) REFERENCES response(id)
+	surveyquestion INT4 NOT NULL,
+	surveyoption INT4 NULL,
+	othertext VARCHAR NULL,
+	CONSTRAINT PK_userresponse PRIMARY KEY (id),
+	CONSTRAINT FK_userresponse_surveyquestion FOREIGN KEY (surveyquestion) REFERENCES surveyquestion(id),
+	CONSTRAINT FK_userresponse_surveyoption FOREIGN KEY (surveyoption) REFERENCES surveyoption(id)
 );
-COMMENT ON TABLE user2response IS 'Responses of user to questions, and other text if present';
+COMMENT ON TABLE userresponse IS 'Responses of user to questions, and other text if present';
 
-CREATE TABLE IF NOT EXISTS user_email (
+CREATE TABLE IF NOT EXISTS useremail (
 	id SERIAL NOT NULL,
 	datetime TIMESTAMP NOT NULL,
 	email VARCHAR NOT NULL,
-	CONSTRAINT PK_user_email PRIMARY KEY (id)
+	CONSTRAINT PK_useremail PRIMARY KEY (id)
 );
-COMMENT ON TABLE user_email IS 'Simple table for register email of user, when he fills out survey';
+COMMENT ON TABLE useremail IS 'Simple table for register email of user, when he fills out survey';
 
 CREATE OR REPLACE VIEW v_i18n
 AS SELECT l.languagetag,
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
index 85ebcdd..b40371f 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
@@ -66,6 +66,7 @@ public final class SurveyPresenter implements Presenter {
      * @param list list of responses
      */
     private void setResponses(final List<SurveyOptionDTO> list) {
+        GWT.log("SurveyPresenter.setResponses(" + list.size() + ") enter");
         this.responses = list;
         final Map<SurveyQuestionDTO, List<SurveyOptionDTO>> responsesMap = new HashMap<>();
 
@@ -81,6 +82,7 @@ public final class SurveyPresenter implements Presenter {
         view.setResponses(responsesMap);
 
         view.init();
+        GWT.log("SurveyPresenter.setResponses() end");
     }
     @Override
     public void start() {
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
deleted file mode 100644
index 3803ea4..0000000
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDaoHibernate.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- *
- */
-package fr.agrometinfo.www.server.dao;
-
-import fr.agrometinfo.www.server.model.Question;
-import jakarta.enterprise.context.ApplicationScoped;
-
-/**
- * @author jdecome
- *
- */
-@ApplicationScoped
-public class QuestionsDaoHibernate extends DaoHibernate<Question> implements QuestionsDao {
-    /**
-     * Constructor.
-     */
-    public QuestionsDaoHibernate() {
-        super(Question.class);
-    }
-    /** */
-    @Override
-    public Question findByRef(final long ref) {
-        return super.find(ref);
-    }
-}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
deleted file mode 100644
index ed2ad39..0000000
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDaoHibernate.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- *
- */
-package fr.agrometinfo.www.server.dao;
-
-import java.util.List;
-import java.util.Map;
-
-import fr.agrometinfo.www.server.model.Response;
-import jakarta.enterprise.context.ApplicationScoped;
-
-/**
- * @author jdecome
- *
- */
-@ApplicationScoped
-public class ResponsesDaoHibernate extends DaoHibernate<Response> implements ResponsesDao {
-    /**
-     * Constructor.
-     */
-    public ResponsesDaoHibernate() {
-        super(Response.class);
-    }
-    /** */
-    @Override
-    public List<Response> findAllByQuestion(final long questionRef) {
-        final String jpql = "SELECT r FROM Response r WHERE r.question.id = :ref";
-        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), Response.class);
-    }
-    /** */
-    @Override
-    public Response findByRef(final long responseRef) {
-        return super.find(responseRef);
-    }
-}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
similarity index 60%
rename from www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
index acd4cb9..52dab0b 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/ResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
@@ -3,30 +3,30 @@
  */
 package fr.agrometinfo.www.server.dao;
 
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyOption;
 
 import java.util.List;
 
 /**
- * DAO for {@link Response}.
+ * DAO for {@link SurveyOption}.
  * @author jdecome
  */
-public interface ResponsesDao {
+public interface SurveyOptionDao {
     /**
      * Find all responses in database.
      * @return all responses
      */
-    List<Response> findAll();
+    List<SurveyOption> findAll();
     /**
      * Find all responses by question.
      * @param questionRef
      * @return list of responses
      */
-    List<Response> findAllByQuestion(long questionRef);
+    List<SurveyOption> findAllByQuestion(long questionRef);
     /**
      * Get response with reference.
      * @param responseRef
      * @return response
      */
-    Response findByRef(long responseRef);
+    SurveyOption findByRef(long responseRef);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
new file mode 100644
index 0000000..d107e16
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
@@ -0,0 +1,35 @@
+/**
+ *
+ */
+package fr.agrometinfo.www.server.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import fr.agrometinfo.www.server.model.SurveyOption;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public class SurveyOptionDaoHibernate extends DaoHibernate<SurveyOption> implements SurveyOptionDao {
+    /**
+     * Constructor.
+     */
+    public SurveyOptionDaoHibernate() {
+        super(SurveyOption.class);
+    }
+    /** */
+    @Override
+    public List<SurveyOption> findAllByQuestion(final long questionRef) {
+        final String jpql = "SELECT r FROM SurveyOption r WHERE r.question.id = :ref";
+        return super.findAllByJPQL(jpql, Map.of("ref", questionRef), SurveyOption.class);
+    }
+    /** */
+    @Override
+    public SurveyOption findByRef(final long responseRef) {
+        return super.find(responseRef);
+    }
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
similarity index 50%
rename from www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
index b0893c9..ef2d5e2 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/QuestionsDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
@@ -5,22 +5,22 @@ package fr.agrometinfo.www.server.dao;
 
 import java.util.List;
 
-import fr.agrometinfo.www.server.model.Question;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
 
 /**
- * DAO for {@link Question}.
+ * DAO for {@link SurveyQuestion}.
  * @author jdecome
  *
  */
-public interface QuestionsDao {
+public interface SurveyQuestionDao {
     /**
      * @return all of Questions
      */
-    List<Question> findAll();
+    List<SurveyQuestion> findAll();
     /**
      * Find question object by reference.
      * @param questionRef question_ref
-     * @return Question object
+     * @return SurveyQuestion object
      */
-    Question findByRef(long questionRef);
+    SurveyQuestion findByRef(long questionRef);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
new file mode 100644
index 0000000..cddc768
--- /dev/null
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
@@ -0,0 +1,26 @@
+/**
+ *
+ */
+package fr.agrometinfo.www.server.dao;
+
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import jakarta.enterprise.context.ApplicationScoped;
+
+/**
+ * @author jdecome
+ *
+ */
+@ApplicationScoped
+public class SurveyQuestionDaoHibernate extends DaoHibernate<SurveyQuestion> implements SurveyQuestionDao {
+    /**
+     * Constructor.
+     */
+    public SurveyQuestionDaoHibernate() {
+        super(SurveyQuestion.class);
+    }
+    /** */
+    @Override
+    public SurveyQuestion findByRef(final long ref) {
+        return super.find(ref);
+    }
+}
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
index 2531e58..9880b59 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -3,8 +3,8 @@ package fr.agrometinfo.www.server.dao;
 import java.time.LocalDateTime;
 import java.util.List;
 
-import fr.agrometinfo.www.server.model.Question;
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.model.UserResponse;
 /**
  * DAO for {@link UserResponse}.
@@ -26,7 +26,7 @@ public interface UserResponsesDao {
      * @param r response
      * @param otherText text if response is other
      */
-    void insertResponse(Question q, Response r, String otherText);
+    void insertResponse(SurveyQuestion q, SurveyOption r, String otherText);
     /**
      * Insert response or text for specified question.<br>
      * If r is provided, {@code otherText} must be null.<br>
@@ -36,7 +36,7 @@ public interface UserResponsesDao {
      * @param otherText text if response is other
      * @param datetime provided localdatetime
      */
-    void insertResponse(Question q, Response r, String otherText, LocalDateTime datetime);
+    void insertResponse(SurveyQuestion q, SurveyOption r, String otherText, LocalDateTime datetime);
     /**
      * Insert response or text for specified question, identified by reference.<br>
      * If responseRef is provided, {@code otherText} must be null.<br>
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 5f9aea3..0e17273 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -7,8 +7,8 @@ import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
 
-import fr.agrometinfo.www.server.model.Question;
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.model.UserResponse;
 import jakarta.enterprise.context.ApplicationScoped;
 
@@ -19,9 +19,9 @@ import jakarta.enterprise.context.ApplicationScoped;
 @ApplicationScoped
 public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implements UserResponsesDao {
     /** DAO for questions. */
-    private QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    private SurveyQuestionDao questionsDao = new SurveyQuestionDaoHibernate();
     /** DAO for responses. */
-    private ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    private SurveyOptionDao responsesDao = new SurveyOptionDaoHibernate();
     /** Default constructor. */
     public UserResponsesDaoHibernate() {
         super(UserResponse.class);
@@ -35,19 +35,19 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
     /** */
     @Override
     public void insertResponse(
-            final Question q, final Response r,
+            final SurveyQuestion q, final SurveyOption r,
             final String otherText, final LocalDateTime datetime) {
         final UserResponse ur = new UserResponse();
         ur.setDateTime(datetime);
         ur.setQuestion(q);
-        ur.setResponse(r);
+        ur.setOption(r);
         ur.setOtherText(otherText);
 
         this.save(ur);
     }
     /** */
     @Override
-    public void insertResponse(final Question q, final Response r, final String otherText) {
+    public void insertResponse(final SurveyQuestion q, final SurveyOption r, final String otherText) {
        this.insertResponse(q, r, otherText, LocalDateTime.now());
     }
     /** */
@@ -56,10 +56,10 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
             final Long questionRef, final Long responseRef,
             final String otherText, final LocalDateTime datetime) {
         // recherche de la question
-        final Question q = questionsDao.findByRef(questionRef);
+        final SurveyQuestion q = questionsDao.findByRef(questionRef);
         if (q != null) {
             if (responseRef != null) {  // insertion d'une réponse prédéfinie, on recherche de la réponse
-                final Response r = responsesDao.findByRef(responseRef);
+                final SurveyOption r = responsesDao.findByRef(responseRef);
                 this.insertResponse(q, r, null, datetime);
             } else {                    // insertion d'une réponse libre
                 this.insertResponse(q, null, otherText, datetime);
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
similarity index 71%
rename from www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
index 9ed8eba..8496c48 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Response.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
@@ -14,16 +14,16 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * Options for questions.
+ * Option (response) for questions.
  * @author jdecome
  *
  */
 @Data
 @Entity
-@Table(name = "response")
-public class Response {
+@Table(name = "surveyoption")
+public class SurveyOption {
     /**
-     * Reference of response.<br>
+     * Reference of options.<br>
      * Primary key.
      */
     @Id
@@ -31,13 +31,13 @@ public class Response {
     @Column(name = "id")
     private long id;
     /**
-     * Question related to response.
+     * Question related to option.
      */
     @OneToOne
-    @JoinColumn(name = "question")
-    private Question question;
+    @JoinColumn(name = "surveyquestion")
+    private SurveyQuestion question;
     /**
-     * Label of the response.
+     * Label of the option.
      */
     @Column(name = "description")
     private String description;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
similarity index 91%
rename from www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
rename to www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
index f618b1e..9b26589 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/Question.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
@@ -18,8 +18,8 @@ import lombok.Data;
  */
 @Data
 @Entity
-@Table(name = "question")
-public class Question {
+@Table(name = "surveyquestion")
+public class SurveyQuestion {
     /**
      * Reference of question.<br>
      * Primary key.
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
index 015174c..7aa74d3 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
@@ -17,7 +17,7 @@ import lombok.Data;
  */
 @Data
 @Entity
-@Table(name = "user_email")
+@Table(name = "useremail")
 public class UserEmail {
     /**
      * Primary key.
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index 2b68c16..0ab9c08 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -21,7 +21,7 @@ import lombok.Data;
  */
 @Data
 @Entity
-@Table(name = "user2response")
+@Table(name = "userresponse")
 public class UserResponse {
     /** Primary key. */
     @Id
@@ -33,19 +33,19 @@ public class UserResponse {
     private LocalDateTime dateTime;
     /** Related question. */
     @OneToOne
-    @JoinColumn(name = "question")
-    private Question question;
+    @JoinColumn(name = "surveyquestion")
+    private SurveyQuestion question;
     /**
-     * Related response.<br>
+     * Related option.<br>
      * Can be null if it's a other response.
      */
     @OneToOne
-    @JoinColumn(name = "response")
-    private Response response;
+    @JoinColumn(name = "surveyoption")
+    private SurveyOption option;
     /**
      * Other text response.<br>
      * Can be null if it's a related response.
      */
-    @Column(name = "other_text")
+    @Column(name = "othertext")
     private String otherText;
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index 37e6b19..d612dfd 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -8,12 +8,12 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
-import fr.agrometinfo.www.server.dao.QuestionsDao;
-import fr.agrometinfo.www.server.dao.ResponsesDao;
+import fr.agrometinfo.www.server.dao.SurveyQuestionDao;
+import fr.agrometinfo.www.server.dao.SurveyOptionDao;
 import fr.agrometinfo.www.server.dao.UserEmailDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
-import fr.agrometinfo.www.server.model.Question;
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.service.MailService;
 import fr.agrometinfo.www.server.util.EmailUtils;
 import fr.agrometinfo.www.shared.dto.ErrorResponseDTO;
@@ -42,22 +42,22 @@ import jakarta.ws.rs.core.MediaType;
 @RequestScoped
 public class SurveyFormResource implements SurveyFormService {
     /**
-     * Convert {@link Question} entity to dto.
+     * Convert {@link SurveyQuestion} entity to dto.
      * @param question entity
      * @return dto
      */
-    static SurveyQuestionDTO toDto(final Question question) {
+    static SurveyQuestionDTO toDto(final SurveyQuestion question) {
         final SurveyQuestionDTO dto = new SurveyQuestionDTO();
         dto.setId(question.getId());
         dto.setDescription(question.getDescription());
         return dto;
     }
     /**
-     * Convert {@link Response} entity to dto.
+     * Convert {@link SurveyOption} entity to dto.
      * @param response entity
      * @return dto
      */
-    static SurveyOptionDTO toDto(final Response response) {
+    static SurveyOptionDTO toDto(final SurveyOption response) {
         final SurveyOptionDTO dto = new SurveyOptionDTO();
         dto.setId(response.getId());
         dto.setQuestion(toDto(response.getQuestion()));
@@ -69,8 +69,8 @@ public class SurveyFormResource implements SurveyFormService {
      * @param dto question
      * @return entity
      */
-    private static Question toEntity(final SurveyQuestionDTO dto) {
-        final Question question = new Question();
+    private static SurveyQuestion toEntity(final SurveyQuestionDTO dto) {
+        final SurveyQuestion question = new SurveyQuestion();
         question.setId(dto.getId());
         question.setDescription(dto.getDescription());
         return question;
@@ -80,23 +80,23 @@ public class SurveyFormResource implements SurveyFormService {
      * @param dto response
      * @return entity
      */
-    private static Response toEntity(final SurveyOptionDTO dto) {
-        final Response response = new Response();
+    private static SurveyOption toEntity(final SurveyOptionDTO dto) {
+        final SurveyOption response = new SurveyOption();
         response.setId(dto.getId());
         response.setQuestion(toEntity(dto.getQuestion()));
         response.setDescription(dto.getDescription());
         return response;
     }
     /**
-     * DAO for questions ({@link QuestionsDao}).
+     * DAO for questions ({@link SurveyQuestionDao}).
      */
     @Inject
-    private QuestionsDao questionsDao;
+    private SurveyQuestionDao questionsDao;
     /**
-     * DAO for responses ({@link ResponsesDao}).
+     * DAO for responses ({@link SurveyOptionDao}).
      */
     @Inject
-    private ResponsesDao responsesDao;
+    private SurveyOptionDao responsesDao;
     /**
      * DAO for user responses ({@link UserResponsesDao}.
      */
@@ -144,9 +144,9 @@ public class SurveyFormResource implements SurveyFormService {
     public List<SurveyOptionDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef) {
         this.checkRequired(questionRef, "questionRef");
         if (this.checkIfQuestionExist(questionRef)) {
-            final List<Response> list = responsesDao.findAllByQuestion(questionRef);
+            final List<SurveyOption> list = responsesDao.findAllByQuestion(questionRef);
             final List<SurveyOptionDTO> responses = new ArrayList<>();
-            for (final Response r : list) {
+            for (final SurveyOption r : list) {
                 responses.add(toDto(r));
             }
             return responses;
@@ -188,7 +188,7 @@ public class SurveyFormResource implements SurveyFormService {
     private boolean checkIfQuestionExist(final Long questionRef) {
         if (questionRef != null) {
             // on cherche dans le DAO
-            final Question q = questionsDao.findByRef(questionRef);
+            final SurveyQuestion q = questionsDao.findByRef(questionRef);
             return q != null;
         }
         return false;
@@ -205,7 +205,7 @@ public class SurveyFormResource implements SurveyFormService {
         // traitement des réponses prédéfinies
         for (final SurveyQuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
             // Insérer les réponses associés
-            final Question q = toEntity(dto);
+            final SurveyQuestion q = toEntity(dto);
             final Long qRef = dto.getId();
             List<SurveyOptionDTO> associatedResponses = data.getResponses()
                     .stream().filter(f -> f.getQuestion().getId() == qRef)
@@ -213,7 +213,7 @@ public class SurveyFormResource implements SurveyFormService {
 
             // On insère les réponses prédéfinies
             for (final SurveyOptionDTO rDto : associatedResponses) {
-                final Response r = toEntity(rDto);
+                final SurveyOption r = toEntity(rDto);
                 this.userResponsesDao.insertResponse(q, r, null, datetime);
             }
 
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 6b1c3cd..7a161be 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -17,16 +17,16 @@ import java.util.concurrent.ThreadLocalRandom;
 
 import org.junit.jupiter.api.Test;
 
-import fr.agrometinfo.www.server.dao.QuestionsDao;
-import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
-import fr.agrometinfo.www.server.dao.ResponsesDao;
-import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.SurveyQuestionDao;
+import fr.agrometinfo.www.server.dao.SurveyQuestionDaoHibernate;
+import fr.agrometinfo.www.server.dao.SurveyOptionDao;
+import fr.agrometinfo.www.server.dao.SurveyOptionDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserEmailDao;
 import fr.agrometinfo.www.server.dao.UserEmailDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
-import fr.agrometinfo.www.server.model.Question;
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
 import lombok.extern.log4j.Log4j2;
@@ -41,19 +41,19 @@ public class SurveyFormHibernateTest {
     /** DAO for user's responses. */
     private UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
     /** DAO for questions. */
-    private QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    private SurveyQuestionDao questionsDao = new SurveyQuestionDaoHibernate();
     /** DAO for responses. */
-    private ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    private SurveyOptionDao responsesDao = new SurveyOptionDaoHibernate();
     /** DAO for email address of user. */
     private UserEmailDao userEmailDao = new UserEmailDaoHibernate();
     
     @Test
     public void getQuestion() {
-        final List<Question> list = questionsDao.findAll();
+        final List<SurveyQuestion> list = questionsDao.findAll();
         assertNotEquals(list.size(), 0, "La liste de questions est vide");
-        for (final Question q : list) {
+        for (final SurveyQuestion q : list) {
             // recherche de la question par sa référence
-            final Question foundedByRef = questionsDao.findByRef(q.getId());
+            final SurveyQuestion foundedByRef = questionsDao.findByRef(q.getId());
             assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
             assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
         }
@@ -61,22 +61,22 @@ public class SurveyFormHibernateTest {
     
     @Test
     public void getResponses() {
-        final List<Question> questions = questionsDao.findAll();
-        final List<Response> responses = responsesDao.findAll();
+        final List<SurveyQuestion> questions = questionsDao.findAll();
+        final List<SurveyOption> responses = responsesDao.findAll();
         assertNotEquals(responses.size(), 0, "La liste de réponses est vide");
-        for (final Response r : responses) {
+        for (final SurveyOption r : responses) {
             assertTrue(questions.contains(r.getQuestion()), "La liste de questions ne contient pas la question " + r.getQuestion().getId());
             
             // Test à partir de la question correspondante à la réponse
-            final Question q = r.getQuestion();
+            final SurveyQuestion q = r.getQuestion();
             assertNotNull(q, "La question correspondante à la réponse " + r.getId() + " est null");
             
-            final Question foundedByRef = questionsDao.findByRef(q.getId());
+            final SurveyQuestion foundedByRef = questionsDao.findByRef(q.getId());
             assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
             assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
             
             // recherche de la réponse par sa référence
-            final Response responseByRef = responsesDao.findByRef(r.getId());
+            final SurveyOption responseByRef = responsesDao.findByRef(r.getId());
             assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getId() + " est null");
             assertEquals(responseByRef, r, "La réponse correspondante à la réponse " + r.getId() + " est vide");
         }
@@ -85,31 +85,31 @@ public class SurveyFormHibernateTest {
     @Test
     public void setUserResponses() {
         this.resetUserResponses();
-        final List<Question> questions = questionsDao.findAll();
-        final Question profession = questions.stream().filter((q) -> q.getDescription().contains("profession")).findFirst().get();
+        final List<SurveyQuestion> questions = questionsDao.findAll();
+        final SurveyQuestion profession = questions.stream().filter((q) -> q.getDescription().contains("profession")).findFirst().get();
         assertNotNull(profession, "Aucune question ne correspond à la profession");
-        List<Response> responses = responsesDao.findAllByQuestion(profession.getId());
+        List<SurveyOption> responses = responsesDao.findAllByQuestion(profession.getId());
         assertNotNull(responses, "Aucune réponse ne correspond à la profession");
         assertNotEquals(responses.size(), 0, "La liste de réponses pour la profession est vide");
-        for (final Response r : responses) {
+        for (final SurveyOption r : responses) {
             assertEquals(r.getQuestion(), profession, "La question de la réponse ne correspond pas avec la question d'origine");
         }
         
         // Insertion d'une réponse
-        final List<Response> listOfResponses = new ArrayList<>();   // liste des réponses insérées
-        Response r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
+        final List<SurveyOption> listOfResponses = new ArrayList<>();   // liste des réponses insérées
+        SurveyOption r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
         userResponsesDao.insertResponse(profession, r, null);
         listOfResponses.add(r);
         
         List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getId());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), profession, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
-        assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
+        assertEquals(list.get(0).getOption(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
         assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
         
         // Insertion de plusieurs réponses
         final int nbToInsert = 3;
-        for (final Response res : pickNRandom(responses, nbToInsert)) {
+        for (final SurveyOption res : pickNRandom(responses, nbToInsert)) {
             userResponsesDao.insertResponse(profession, res, null);
             listOfResponses.add(res);
         }
@@ -117,8 +117,8 @@ public class SurveyFormHibernateTest {
         assertEquals(list.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + " + nbToInsert + " réponses supplémentaires");
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question");
-            assertTrue(listOfResponses.contains(ur.getResponse()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
-            assertEquals(ur.getResponse().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+            assertTrue(listOfResponses.contains(ur.getOption()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
+            assertEquals(ur.getOption().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
             assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
         }
 
@@ -133,16 +133,16 @@ public class SurveyFormHibernateTest {
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
             if (ur.getOtherText() != null) {    // on est sur une réponse libre
-                assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
+                assertNull(ur.getOption(), "L'objet response doit être null si il s'agit d'une réponse libre");
                 assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
             } else {
-                assertEquals(ur.getResponse().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                assertEquals(ur.getOption().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
                 assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
             }
         }
         
         // Essai de l'insertion par les références, avec une autre question
-        final Question useCase = questions.stream().filter((q) -> q.getId() == 3).findFirst().get();
+        final SurveyQuestion useCase = questions.stream().filter((q) -> q.getId() == 3).findFirst().get();
         assertNotNull(useCase, "Aucune question ne correspond aux cas d'utilisation");
         responses = responsesDao.findAllByQuestion(useCase.getId());
         assertNotNull(responses, "Aucune réponse ne correspond à la question des cas d'utilisation");
@@ -154,7 +154,7 @@ public class SurveyFormHibernateTest {
         list = userResponsesDao.findAllByQuestion(useCase.getId());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir un élément");
         assertEquals(list.get(0).getQuestion(), useCase, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
-        assertEquals(list.get(0).getResponse(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
+        assertEquals(list.get(0).getOption(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
         assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
         
         other = other.concat(" pour la question des cas d'utilisation");
@@ -165,10 +165,10 @@ public class SurveyFormHibernateTest {
         for (final UserResponse ur : list) {
             assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
             if (ur.getOtherText() != null) {    // on est sur une réponse libre
-                assertNull(ur.getResponse(), "L'objet response doit être null si il s'agit d'une réponse libre");
+                assertNull(ur.getOption(), "L'objet response doit être null si il s'agit d'une réponse libre");
                 assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
             } else {
-                assertEquals(ur.getResponse().getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
+                assertEquals(ur.getOption().getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
                 assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
             }
         }
@@ -179,7 +179,7 @@ public class SurveyFormHibernateTest {
         final String email = "jane.doe@inrae.fr";
         List<UserEmail> list = this.userEmailDao.getEmailAddressList();
         assertNotNull(list, "La liste des e-mail n'est pas initialisée");
-        assertEquals(list.size(), 0, "La liste des e-mail est vide");
+        assertEquals(list.size(), 0, "La liste des e-mail doit être vide");
         this.userEmailDao.insertEmailUserAddress(email, LocalDateTime.now());
         
         list = this.userEmailDao.getEmailAddressList();
@@ -192,8 +192,8 @@ public class SurveyFormHibernateTest {
      * @param nbElements to pick
      * @return
      */
-    private List<Response> pickNRandom(final List<Response> list, final int nbElements) {
-        final List<Response> copy = new ArrayList<Response>(list);
+    private List<SurveyOption> pickNRandom(final List<SurveyOption> list, final int nbElements) {
+        final List<SurveyOption> copy = new ArrayList<SurveyOption>(list);
         Collections.shuffle(copy);
         if (nbElements > copy.size()) {
             return copy.subList(0, copy.size());
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 12d1bc2..1797782 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -27,17 +27,17 @@ import org.glassfish.jersey.test.JerseyTest;
 import org.junit.jupiter.api.Test;
 
 
-import fr.agrometinfo.www.server.dao.QuestionsDao;
-import fr.agrometinfo.www.server.dao.QuestionsDaoHibernate;
-import fr.agrometinfo.www.server.dao.ResponsesDao;
-import fr.agrometinfo.www.server.dao.ResponsesDaoHibernate;
+import fr.agrometinfo.www.server.dao.SurveyQuestionDao;
+import fr.agrometinfo.www.server.dao.SurveyQuestionDaoHibernate;
+import fr.agrometinfo.www.server.dao.SurveyOptionDao;
+import fr.agrometinfo.www.server.dao.SurveyOptionDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserEmailDao;
 import fr.agrometinfo.www.server.dao.UserEmailDaoHibernate;
 import fr.agrometinfo.www.server.dao.UserResponsesDao;
 import fr.agrometinfo.www.server.dao.UserResponsesDaoHibernate;
 import fr.agrometinfo.www.server.exception.AgroMetInfoException;
-import fr.agrometinfo.www.server.model.Question;
-import fr.agrometinfo.www.server.model.Response;
+import fr.agrometinfo.www.server.model.SurveyQuestion;
+import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
 import fr.agrometinfo.www.server.service.MailService;
@@ -91,9 +91,9 @@ public class SurveyFormResourceTest extends JerseyTest {
     /** Parsing JSON to object. */
     private final ObjectMapper objectMapper = new ObjectMapper();
     /** DAO for Questions. */
-    private final QuestionsDao questionsDao = new QuestionsDaoHibernate();
+    private final SurveyQuestionDao questionsDao = new SurveyQuestionDaoHibernate();
     /** DAO for Responses. */
-    private final ResponsesDao responsesDao = new ResponsesDaoHibernate();
+    private final SurveyOptionDao responsesDao = new SurveyOptionDaoHibernate();
     /** DAO for UserResponses. */
     private final UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
     /** DAO for UserMail */
@@ -106,8 +106,8 @@ public class SurveyFormResourceTest extends JerseyTest {
         return new ResourceConfig(SurveyFormResource.class).register(new AbstractBinder() {
             @Override
             protected void configure() {
-                bind(questionsDao).to(QuestionsDao.class);
-                bind(responsesDao).to(ResponsesDao.class);
+                bind(questionsDao).to(SurveyQuestionDao.class);
+                bind(responsesDao).to(SurveyOptionDao.class);
                 bind(userResponsesDao).to(UserResponsesDao.class);
                 bind(userEmailDao).to(UserEmailDao.class);
                 bind(mailService).to(MailService.class);
@@ -122,7 +122,7 @@ public class SurveyFormResourceTest extends JerseyTest {
         assertNotNull(jsonQuestions, "Impossible de récupérer la ressource (objet null) pour les questions");
         assertFalse(jsonQuestions.isEmpty(), "La chaine de caractère JSON des questions est vide");
         try {
-            final List<Question> questions = objectMapper.readValue(jsonQuestions, new TypeReference<List<Question>>(){});
+            final List<SurveyQuestion> questions = objectMapper.readValue(jsonQuestions, new TypeReference<List<SurveyQuestion>>(){});
             assertNotNull(questions, "La conversion du JSON des questions en liste a échoué");
             assertNotEquals(questions.size(), 0, "La liste de questions est vide");
             
@@ -131,16 +131,16 @@ public class SurveyFormResourceTest extends JerseyTest {
             assertNotNull(jsonAllResponses, "Impossible de récupérer la ressource (objet null) des réponses");
             assertFalse(jsonAllResponses.isEmpty(), "La chaine de caractère JSON des réponses est vide");
             
-            final List<Response> allResponses = objectMapper.readValue(jsonAllResponses, new TypeReference<List<Response>>() {});
+            final List<SurveyOption> allResponses = objectMapper.readValue(jsonAllResponses, new TypeReference<List<SurveyOption>>() {});
             assertNotNull(allResponses, "La conversion du JSON des réponses en liste a échoué");
             assertNotEquals(allResponses.size(), 0, "La liste de réponses est vide");
             
-            for (final Response r : allResponses) {
+            for (final SurveyOption r : allResponses) {
                 assertTrue(questions.contains(r.getQuestion()), "La question de la réponse « " + r.getDescription() + " » n'est pas contenue dans la liste des questions");
             }
             
             // Pour chaque question, 
-            for (final Question q : questions) {
+            for (final SurveyQuestion q : questions) {
                 // on récupère les réponses associées via le webservice
                 final String jsonResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
                                                 .queryParam("questionRef", String.valueOf(q.getId()))
@@ -150,16 +150,16 @@ public class SurveyFormResourceTest extends JerseyTest {
                 assertNotNull(jsonResponses, "Impossible de récupérer la ressource (objet null)  pour la question n° " + q.getId() + " est vide");
                 assertFalse(jsonResponses.isEmpty(), "La chaine de caractère JSON pour la question n° " + q.getId() + " est vide");
                 
-                final List<Response> responses = objectMapper.readValue(jsonResponses, new TypeReference<List<Response>>() {});
+                final List<SurveyOption> responses = objectMapper.readValue(jsonResponses, new TypeReference<List<SurveyOption>>() {});
                 assertNotNull(responses, "La conversion du JSON des réponses de la question n° " + q.getId() + " n'a pas fonctionnée");
                 assertNotEquals(responses.size(), 0, " La liste de réponses pour la question n° " + q.getId() + " est vide");
                 
-                final List<Response> responsesByDao = responsesDao.findAllByQuestion(q.getId());
+                final List<SurveyOption> responsesByDao = responsesDao.findAllByQuestion(q.getId());
                 assertNotNull(responsesByDao, " La lecture des réponses pour la question n° " + q.getId() + " via le DAO n'a pas fonctionné");
                 assertNotEquals(responsesByDao.size(), 0, " La liste de réponses via le DAO pour la question n° " + q.getId() + " est vide");
                 assertEquals(responses, responsesByDao, "La liste de réponses via le webservice n'est pas identique avec la liste via le DAO");
                 
-                for (final Response r : responses) {
+                for (final SurveyOption r : responses) {
                     assertNotNull(r.getQuestion(), "La question de la réponse n° " + r.getId() + " est null");
                     assertEquals(r.getQuestion(), q, "La question de la réponse n° " + r.getId() + " ne correspond pas à la question");
                     assertTrue(responsesByDao.contains(r), "La liste via le DAO ne contient pas la réponse n° " + r.getId());
@@ -174,17 +174,17 @@ public class SurveyFormResourceTest extends JerseyTest {
     @Test
     public void testWithJsonResponses() {
         this.resetUserResponses();
-        final List<Question> questionsList = questionsDao.findAll();
+        final List<SurveyQuestion> questionsList = questionsDao.findAll();
         final List<SurveyQuestionDTO> questionsDTO = questionsList.stream().map(SurveyFormResource::toDto).toList();
         
-        final List<Response> responsesList = new ArrayList<>();
+        final List<SurveyOption> responsesList = new ArrayList<>();
         final HashMap<Long, String> otherTextMap = new HashMap<>();
         
-        for (final Question q : questionsList) {
-            final List<Response> responsesListForQuestion = responsesDao.findAllByQuestion(q.getId());
+        for (final SurveyQuestion q : questionsList) {
+            final List<SurveyOption> responsesListForQuestion = responsesDao.findAllByQuestion(q.getId());
             
             // on récupère un nombre aléatoire de réponse
-            final List<Response> randomList = this.pickNRandom(responsesListForQuestion, ThreadLocalRandom.current().nextInt(0, responsesListForQuestion.size() - 1));
+            final List<SurveyOption> randomList = this.pickNRandom(responsesListForQuestion, ThreadLocalRandom.current().nextInt(0, responsesListForQuestion.size() - 1));
            
             // si il y a au moins une réponse récupérée aléatoirement
             if (randomList.size() > 0) {
@@ -208,7 +208,7 @@ public class SurveyFormResourceTest extends JerseyTest {
         
         // Récupère toutes les réponses pour toutes les questions
         List<UserResponse> list = new ArrayList<>();
-        for (final Question q : questionsDao.findAll()) {
+        for (final SurveyQuestion q : questionsDao.findAll()) {
             final List<UserResponse> responsesByQuestion = userResponsesDao.findAllByQuestion(q.getId());
             assertNotNull(responsesByQuestion, "La liste de réponses de l'utilisateur pour la question « " + q.getId() + " » ne doit pas être null");
             responsesByQuestion.forEach((ur) -> {
@@ -221,11 +221,11 @@ public class SurveyFormResourceTest extends JerseyTest {
         list.forEach((ur) -> {
             assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
             assertTrue(ur.getId() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
-            if (ur.getResponse() != null) {
-                assertNotNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+            if (ur.getOption() != null) {
+                assertNotNull(ur.getOption(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
                 assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
             } else {
-                assertNull(ur.getResponse(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
+                assertNull(ur.getOption(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
                 assertNotNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
             }
         });
@@ -264,8 +264,8 @@ public class SurveyFormResourceTest extends JerseyTest {
      * @param nbElements to pick in list
      * @return randomized list with nbElements
      */
-    private List<Response> pickNRandom(final List<Response> list, final int nbElements) {
-        final List<Response> copy = new ArrayList<Response>(list);
+    private List<SurveyOption> pickNRandom(final List<SurveyOption> list, final int nbElements) {
+        final List<SurveyOption> copy = new ArrayList<SurveyOption>(list);
         Collections.shuffle(copy);
         if (nbElements > copy.size()) {
             return copy.subList(0, copy.size());
diff --git a/www-server/src/test/resources/META-INF/persistence.xml b/www-server/src/test/resources/META-INF/persistence.xml
index 9f6b455..3a55dd0 100644
--- a/www-server/src/test/resources/META-INF/persistence.xml
+++ b/www-server/src/test/resources/META-INF/persistence.xml
@@ -18,8 +18,8 @@
     <class>fr.agrometinfo.www.server.model.PraDailyValue</class>
     <class>fr.agrometinfo.www.server.model.Region</class>
     <class>fr.agrometinfo.www.server.model.Simulation</class>
-    <class>fr.agrometinfo.www.server.model.Question</class>
-    <class>fr.agrometinfo.www.server.model.Response</class>
+    <class>fr.agrometinfo.www.server.model.SurveyQuestion</class>
+    <class>fr.agrometinfo.www.server.model.SurveyOption</class>
     <class>fr.agrometinfo.www.server.model.UserResponse</class>
     <class>fr.agrometinfo.www.server.model.UserEmail</class>
     <properties>
-- 
GitLab


From 5d21693e40f55726c46003ac6ee58e3c8e52cb06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Fri, 31 May 2024 16:06:40 +0200
Subject: [PATCH 24/30] =?UTF-8?q?Suppression=20de=20la=20m=C3=A9thode=20d'?=
 =?UTF-8?q?insertion=20des=20r=C3=A9ponses,=20via=20le=20DAO,=20par=20les?=
 =?UTF-8?q?=20r=C3=A9f=C3=A9rences?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../www/server/dao/UserResponsesDao.java      | 20 ---------------
 .../server/dao/UserResponsesDaoHibernate.java | 25 -------------------
 .../www/server/rs/SurveyFormResource.java     | 14 ++++++-----
 .../www/server/SurveyFormHibernateTest.java   |  5 ++--
 4 files changed, 10 insertions(+), 54 deletions(-)

diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
index 9880b59..3a364db 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDao.java
@@ -37,26 +37,6 @@ public interface UserResponsesDao {
      * @param datetime provided localdatetime
      */
     void insertResponse(SurveyQuestion q, SurveyOption r, String otherText, LocalDateTime datetime);
-    /**
-     * Insert response or text for specified question, identified by reference.<br>
-     * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null.<br>
-     * datetime is defined by {@link LocalDateTime#now} in this method.
-     * @param questionRef reference of question to answer
-     * @param responseRef reference of response
-     * @param otherText text if response is other
-     */
-    void insertResponse(Long questionRef, Long responseRef, String otherText);
-    /**
-     * Insert response or text for specified question, identified by reference.<br>
-     * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null.
-     * @param questionRef reference of question to answer
-     * @param responseRef reference of response
-     * @param otherText text if response is other
-     * @param datetime provided datetime
-     */
-    void insertResponse(Long questionRef, Long responseRef, String otherText, LocalDateTime datetime);
     /**
      * Get all user responses.
      * @return number of responses
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 0e17273..0e8b457 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -18,10 +18,6 @@ import jakarta.enterprise.context.ApplicationScoped;
  */
 @ApplicationScoped
 public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implements UserResponsesDao {
-    /** DAO for questions. */
-    private SurveyQuestionDao questionsDao = new SurveyQuestionDaoHibernate();
-    /** DAO for responses. */
-    private SurveyOptionDao responsesDao = new SurveyOptionDaoHibernate();
     /** Default constructor. */
     public UserResponsesDaoHibernate() {
         super(UserResponse.class);
@@ -52,27 +48,6 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
     }
     /** */
     @Override
-    public void insertResponse(
-            final Long questionRef, final Long responseRef,
-            final String otherText, final LocalDateTime datetime) {
-        // recherche de la question
-        final SurveyQuestion q = questionsDao.findByRef(questionRef);
-        if (q != null) {
-            if (responseRef != null) {  // insertion d'une réponse prédéfinie, on recherche de la réponse
-                final SurveyOption r = responsesDao.findByRef(responseRef);
-                this.insertResponse(q, r, null, datetime);
-            } else {                    // insertion d'une réponse libre
-                this.insertResponse(q, null, otherText, datetime);
-            }
-        }
-    }
-    /** */
-    @Override
-    public void insertResponse(final Long questionRef, final Long responseRef, final String otherText) {
-        this.insertResponse(questionRef, responseRef, otherText, LocalDateTime.now());
-    }
-    /** */
-    @Override
     public int getNbUserResponses() {
         return super.findAll().size();
     }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index d612dfd..8af54fc 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -172,24 +172,26 @@ public class SurveyFormResource implements SurveyFormService {
         this.checkRequired(questionRef, "questionRef");
         final boolean responseRefIsNull = responseRef == null || responseRef.equals("null");
         final boolean otherTextIsNull = otherText == null || otherText.equals("null");
-        if (this.checkIfQuestionExist(questionRef)) {
+
+        final SurveyQuestion q = questionsDao.findByRef(questionRef);
+        if (q != null) {
             if (!responseRefIsNull && otherTextIsNull) {     // réponse prédéfinie
-                this.userResponsesDao.insertResponse(questionRef, Long.valueOf(responseRef), null, LocalDateTime.now());
+                final SurveyOption r = responsesDao.findByRef(Long.valueOf(responseRef));
+                this.userResponsesDao.insertResponse(q, r, null, LocalDateTime.now());
             } else if (responseRefIsNull && !otherTextIsNull) {  // réponse libre
-                this.userResponsesDao.insertResponse(questionRef, null, otherText, LocalDateTime.now());
+                this.userResponsesDao.insertResponse(q, null, otherText, LocalDateTime.now());
             }
         }
     }
     /**
      * Check on database if reference of question exists.
      * @param questionRef
-     * @return {@code true} if question exists or {@code false} otherwise
+     * @return {@code true} if question exists or {@code false} value otherwise
      */
     private boolean checkIfQuestionExist(final Long questionRef) {
         if (questionRef != null) {
             // on cherche dans le DAO
-            final SurveyQuestion q = questionsDao.findByRef(questionRef);
-            return q != null;
+            return questionsDao.findByRef(questionRef) != null;
         }
         return false;
     }
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 7a161be..d38049e 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -141,7 +141,6 @@ public class SurveyFormHibernateTest {
             }
         }
         
-        // Essai de l'insertion par les références, avec une autre question
         final SurveyQuestion useCase = questions.stream().filter((q) -> q.getId() == 3).findFirst().get();
         assertNotNull(useCase, "Aucune question ne correspond aux cas d'utilisation");
         responses = responsesDao.findAllByQuestion(useCase.getId());
@@ -149,7 +148,7 @@ public class SurveyFormHibernateTest {
         assertNotEquals(responses.size(), 0, "La liste de réponses pour la la question des cas d'utilisation est vide");
         
         r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
-        userResponsesDao.insertResponse(useCase.getId(), r.getId(), null);
+        userResponsesDao.insertResponse(useCase, r, null);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
         assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir un élément");
@@ -158,7 +157,7 @@ public class SurveyFormHibernateTest {
         assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
         
         other = other.concat(" pour la question des cas d'utilisation");
-        userResponsesDao.insertResponse(useCase.getId(), null, other);
+        userResponsesDao.insertResponse(useCase, null, other);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
         assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir deux éléments");
-- 
GitLab


From e0743d9816040b1eb8f0e191e68933b4d7380395 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 4 Jun 2024 11:08:44 +0200
Subject: [PATCH 25/30] =?UTF-8?q?Retrait=20des=20points=20d'entr=C3=A9es?=
 =?UTF-8?q?=20inutiles=20du=20webservice=20Commentaires=20en=20anglais=20M?=
 =?UTF-8?q?essages=20des=20tests=20en=20anglais=20Nettoyage?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../java/fr/agrometinfo/www/client/App.java   |   2 +-
 .../www/client/presenter/SurveyPresenter.java |  18 ++-
 .../www/client/view/SurveyView.java           |  26 ++--
 .../www/server/dao/SurveyOptionDao.java       |   3 -
 .../server/dao/SurveyOptionDaoHibernate.java  |   8 +-
 .../www/server/dao/SurveyQuestionDao.java     |   3 -
 .../dao/SurveyQuestionDaoHibernate.java       |   7 +-
 .../www/server/dao/UserEmailDao.java          |   2 +-
 .../www/server/dao/UserEmailDaoHibernate.java |   1 +
 .../server/dao/UserResponsesDaoHibernate.java |  10 +-
 .../www/server/model/SurveyOption.java        |  10 +-
 .../www/server/model/SurveyQuestion.java      |  10 +-
 .../www/server/model/UserEmail.java           |   1 -
 .../www/server/model/UserResponse.java        |  17 ++-
 .../www/server/rs/SurveyFormResource.java     |  88 +++----------
 .../www/server/util/EmailUtils.java           |  12 +-
 .../www/server/SurveyFormHibernateTest.java   | 123 +++++++++--------
 .../www/server/rs/SurveyFormResourceTest.java | 124 +++++++-----------
 .../www/shared/dto/SurveyOptionDTO.java       |   7 +-
 .../www/shared/dto/SurveyQuestionDTO.java     |   7 +-
 .../www/shared/dto/SurveyResponseDTO.java     |   4 -
 .../www/shared/service/SurveyFormService.java |  52 +-------
 22 files changed, 183 insertions(+), 352 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/App.java b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
index 2de0783..542e4c0 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/App.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/App.java
@@ -101,7 +101,7 @@ public class App implements EntryPoint {
 
         new LayoutPresenter().start();
 
-        // formulaire d'enquête
+        // display survey form
         new SurveyPresenter().start();
     }
 }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
index b40371f..00b81aa 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/presenter/SurveyPresenter.java
@@ -70,10 +70,9 @@ public final class SurveyPresenter implements Presenter {
         this.responses = list;
         final Map<SurveyQuestionDTO, List<SurveyOptionDTO>> responsesMap = new HashMap<>();
 
-        // extraction des questions, à partir des réponses retournées par le webservice
+        // extract questions from responses via webservice
         for (final SurveyOptionDTO dto : list) {
-            if (!responsesMap.containsKey(dto.getQuestion())) { // la map ne contient pas la question
-                // on crée l'entrée, avec une liste vide
+            if (!responsesMap.containsKey(dto.getQuestion())) {
                 responsesMap.put(dto.getQuestion(), new ArrayList<SurveyOptionDTO>());
             }
             responsesMap.get(dto.getQuestion()).add(dto);
@@ -97,36 +96,35 @@ public final class SurveyPresenter implements Presenter {
             final List<Long> responsesRef,
             final Map<Long, String> otherTextMap,
             final String email) {
-        GWT.log("Insère les réponses de l'utilisateur");
 
-        // traitement des réponses prédéfinies
+        // processing of predefined responses
         final Set<SurveyQuestionDTO> questionsDto = new LinkedHashSet<>();
         final Set<SurveyOptionDTO> responsesDto = new LinkedHashSet<>();
 
         for (final Long ref : responsesRef) {
-            // on récupère la réponse correspondante à la référence
+            // getting response by reference
             final SurveyOptionDTO rDto = this.responses.stream().filter(r -> r.getId() == ref).findFirst().get();
             if (rDto != null) {
                 responsesDto.add(rDto);
 
-                // on récupère la question correspondantes à la réponse
+                // getting question corresponding of response
                 questionsDto.add(rDto.getQuestion());
             }
         }
-        // ajout de la question, si celle çi n'a qu'une réponse libre
+        // addition of the question, if it only has a free answer
         this.questions.forEach((q) -> {
             if (otherTextMap.containsKey(q.getId())) {
                 questionsDto.add(q);
             }
         });
 
-        // envoi au webservice
+        // call to webservice for inserting data
         final SurveyResponseDTO data = new SurveyResponseDTO(
                 new ArrayList<SurveyQuestionDTO>(questionsDto),
                 new ArrayList<SurveyOptionDTO>(responsesDto),
                 otherTextMap);
 
-        // traitement de l'adresse e-mail
+        // if user has entered their email address, it is added to object
         if (!email.isEmpty()) {
             data.setEmail(email);
         }
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
index 63b0c02..b98369a 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/SurveyView.java
@@ -67,7 +67,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
     @Override
     public void init() {
         GWT.log("LoginView.init()");
-        // on vérifie que le formulaire n'a pas déjà été rempli par l'utilisateur, via le LocalStorage du navigateur
+        // check if survey form has not already been completed by user, via the browser's LocalStorage
         final Storage ls = Storage.getLocalStorageIfSupported();
         if (ls != null && ls.getItem(IS_VALIDATED_KEY) != null && ls.getItem(IS_VALIDATED_KEY).equals("true")) {
             GWT.log("Survey was already validated for this user, don't showing again");
@@ -83,12 +83,12 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
                 );
 
         for (Map.Entry<SurveyQuestionDTO, List<SurveyOptionDTO>> entry : this.availableResponses.entrySet()) {
-            final SurveyQuestionDTO k = entry.getKey(); // Pour chaque question
+            final SurveyQuestionDTO k = entry.getKey(); // For each question ...
 
-            // on affiche le libellé
+            // displaying label
             this.modal.appendChild(Elements.h(SECTION_TITLE_LEVEL, "• " + k.getDescription()));
 
-            // on affiche les réponses possibles
+            // displaying availables responses
             entry.getValue().forEach((r) -> {
                 final CheckBox cb = CheckBox.create(r.getDescription()).id(Long.toString(r.getId()))
                         .css("login-checkbox");
@@ -96,7 +96,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
                 this.checkBoxList.add(cb);
             });
 
-            // on affiche le champ pour la réponse libre
+            // displaying free response field
             final CheckBox otherCb = CheckBox.create(CSTS.surveyFormOtherTextCheckbox())
                     .css("login-checkbox");
             final TextArea otherText = TextArea.create()
@@ -117,30 +117,30 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
             this.modal.appendChild(otherCb).appendChild(otherText);
             otherTextArea.put(k.getId(), otherText);
         }
-        // active le bouton Valider si au moins une case a été cochée
+        // Validate button is activ if if at least one answer is checked
         this.checkBoxList.forEach((c) -> {
             c.addChangeHandler((v) -> this.activateValidateButton());
         });
 
-        // E-mail de l'utilisateur
+        // User's e-mail address
         this.modal.appendChild(Elements.p().textContent(CSTS.surveyFromEmailDescription()));
         this.email = TextBox.create().setType("email");
         this.modal.appendChild(this.email);
 
-        // Boutons de la fenêtre modale
+        // Window's button
         this.validate = Button.create(CSTS.validate()).linkify();
         this.validate.addClickListener((evt) -> {
             final HashMap<Long, String> otherTextMap = new HashMap<>();
 
-            // on traite les réponses prédéfinies (récupération des références des réponses),
-            // en excluant les cases à cocher pour les réponses libre
+            // process the predefined responses (retrieval of response references),
+            // excluding checkbox for free responses
             final List<Long> selectedResponsesRef = this.checkBoxList.stream()
                     .filter((v) -> v.getValue() && v.getId().matches("[0-9]+"))
                     .map(v -> Long.parseLong(v.getId())).collect(Collectors.toList());
 
-            // traitement des réponses libre de la question
+            // process the free responses
             otherTextArea.forEach((k, v) -> {
-                // si le champ est actif, c'est qu'il y a (potentiellement) une réponse et si le champ n'est pas vide
+                // if textarea is activ, there is (potentially) a response and if textarea is not empty
                 if (!v.isDisabled() && !v.getValue().equals("")) {
                     otherTextMap.put(k, v.getValue());
                 }
@@ -151,7 +151,7 @@ public final class SurveyView extends AbstractBaseView<SurveyPresenter> implemen
             ls.setItem(IS_VALIDATED_KEY, "true");
             this.modal.close();
         });
-        this.validate.setDisabled(true);  // le bouton Valider est désactivé par défaut
+        this.validate.setDisabled(true);  // Validate button is disable by default
         this.modal.appendFooterChild(validate);
 
         final Button ignore = Button.create(CSTS.ignore()).linkify();
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
index 52dab0b..214e608 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDao.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.dao;
 
 import fr.agrometinfo.www.server.model.SurveyOption;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
index d107e16..6b98c99 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyOptionDaoHibernate.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.dao;
 
 import java.util.List;
@@ -10,24 +7,23 @@ import fr.agrometinfo.www.server.model.SurveyOption;
 import jakarta.enterprise.context.ApplicationScoped;
 
 /**
+ * Implementation of {@link SurveyOptionDao}.
  * @author jdecome
  *
  */
 @ApplicationScoped
-public class SurveyOptionDaoHibernate extends DaoHibernate<SurveyOption> implements SurveyOptionDao {
+public final class SurveyOptionDaoHibernate extends DaoHibernate<SurveyOption> implements SurveyOptionDao {
     /**
      * Constructor.
      */
     public SurveyOptionDaoHibernate() {
         super(SurveyOption.class);
     }
-    /** */
     @Override
     public List<SurveyOption> findAllByQuestion(final long questionRef) {
         final String jpql = "SELECT r FROM SurveyOption r WHERE r.question.id = :ref";
         return super.findAllByJPQL(jpql, Map.of("ref", questionRef), SurveyOption.class);
     }
-    /** */
     @Override
     public SurveyOption findByRef(final long responseRef) {
         return super.find(responseRef);
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
index ef2d5e2..6a73dc5 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDao.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.dao;
 
 import java.util.List;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
index cddc768..0ef2956 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/SurveyQuestionDaoHibernate.java
@@ -1,24 +1,21 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.dao;
 
 import fr.agrometinfo.www.server.model.SurveyQuestion;
 import jakarta.enterprise.context.ApplicationScoped;
 
 /**
+ * Implementation of {@link SurveyQuestionDao}.
  * @author jdecome
  *
  */
 @ApplicationScoped
-public class SurveyQuestionDaoHibernate extends DaoHibernate<SurveyQuestion> implements SurveyQuestionDao {
+public final class SurveyQuestionDaoHibernate extends DaoHibernate<SurveyQuestion> implements SurveyQuestionDao {
     /**
      * Constructor.
      */
     public SurveyQuestionDaoHibernate() {
         super(SurveyQuestion.class);
     }
-    /** */
     @Override
     public SurveyQuestion findByRef(final long ref) {
         return super.find(ref);
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
index 4ab9254..e38e5b7 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDao.java
@@ -23,7 +23,7 @@ public interface UserEmailDao {
      */
     List<UserEmail> getEmailAddressList();
     /**
-     *
+     * Delete all rows in table.
      */
     void deleteAll();
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
index 80388d2..e45b99c 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserEmailDaoHibernate.java
@@ -7,6 +7,7 @@ import fr.agrometinfo.www.server.model.UserEmail;
 import jakarta.enterprise.context.ApplicationScoped;
 
 /**
+ * Implementation of {@link UserEmailDao}.
  * @author jdecome
  *
  */
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
index 0e8b457..01cd1ff 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/dao/UserResponsesDaoHibernate.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.dao;
 
 import java.time.LocalDateTime;
@@ -13,22 +10,21 @@ import fr.agrometinfo.www.server.model.UserResponse;
 import jakarta.enterprise.context.ApplicationScoped;
 
 /**
+ * Implementation of {@link UserResponsesDao}.
  * @author jdecome
  *
  */
 @ApplicationScoped
-public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implements UserResponsesDao {
+public final class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implements UserResponsesDao {
     /** Default constructor. */
     public UserResponsesDaoHibernate() {
         super(UserResponse.class);
     }
-    /** */
     @Override
     public List<UserResponse> findAllByQuestion(final long questionRef) {
         final String jpql = "SELECT u FROM UserResponse u WHERE u.question.id = :ref";
         return super.findAllByJPQL(jpql, Map.of("ref", questionRef), UserResponse.class);
     }
-    /** */
     @Override
     public void insertResponse(
             final SurveyQuestion q, final SurveyOption r,
@@ -41,12 +37,10 @@ public class UserResponsesDaoHibernate extends DaoHibernate<UserResponse> implem
 
         this.save(ur);
     }
-    /** */
     @Override
     public void insertResponse(final SurveyQuestion q, final SurveyOption r, final String otherText) {
        this.insertResponse(q, r, otherText, LocalDateTime.now());
     }
-    /** */
     @Override
     public int getNbUserResponses() {
         return super.findAll().size();
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
index 8496c48..ddc5fe7 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyOption.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.model;
 
 import jakarta.persistence.Column;
@@ -14,16 +11,15 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * Option (response) for questions.
+ * Option (response) for questions of survey form.
  * @author jdecome
- *
  */
 @Data
 @Entity
 @Table(name = "surveyoption")
 public class SurveyOption {
     /**
-     * Reference of options.<br>
+     * Reference of the option.<br>
      * Primary key.
      */
     @Id
@@ -31,7 +27,7 @@ public class SurveyOption {
     @Column(name = "id")
     private long id;
     /**
-     * Question related to option.
+     * Question related to the option.
      */
     @OneToOne
     @JoinColumn(name = "surveyquestion")
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
index 9b26589..6596c6d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/SurveyQuestion.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.model;
 
 import jakarta.persistence.Column;
@@ -12,16 +9,15 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * Questions for survey modal.
+ * Questions for survey form.
  * @author jdecome
- *
  */
 @Data
 @Entity
 @Table(name = "surveyquestion")
 public class SurveyQuestion {
     /**
-     * Reference of question.<br>
+     * Reference of the question.<br>
      * Primary key.
      */
     @Id
@@ -29,7 +25,7 @@ public class SurveyQuestion {
     @Column(name = "id")
     private long id;
     /**
-     * Label of question.
+     * Label of the question.
      */
     @Column(name = "description")
     private String description;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
index 7aa74d3..8c54efc 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserEmail.java
@@ -13,7 +13,6 @@ import lombok.Data;
 /**
  * Table for storage email adress.<br>
  * @author jdecome
- *
  */
 @Data
 @Entity
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
index 0ab9c08..b6e6147 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/model/UserResponse.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.model;
 
 import java.time.LocalDateTime;
@@ -16,22 +13,28 @@ import jakarta.persistence.Table;
 import lombok.Data;
 
 /**
- * User's responses values.
+ * User's responses values of survey form.
  * @author jdecome
  */
 @Data
 @Entity
 @Table(name = "userresponse")
 public class UserResponse {
-    /** Primary key. */
+    /**
+     * Primary key.
+     */
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     @Column(name = "id")
     private long id;
-    /** Date time of user responses (creation date). */
+    /**
+     * Date time of user responses (creation date).
+     */
     @Column(name = "datetime")
     private LocalDateTime dateTime;
-    /** Related question. */
+    /**
+     * Related question.
+     */
     @OneToOne
     @JoinColumn(name = "surveyquestion")
     private SurveyQuestion question;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index 8af54fc..1aec97d 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -1,10 +1,6 @@
-/**
- *
- */
 package fr.agrometinfo.www.server.rs;
 
 import java.time.LocalDateTime;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -28,8 +24,6 @@ import jakarta.ws.rs.GET;
 import jakarta.ws.rs.POST;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.QueryParam;
-import jakarta.ws.rs.FormParam;
 import jakarta.ws.rs.WebApplicationException;
 import jakarta.ws.rs.core.MediaType;
 
@@ -93,7 +87,7 @@ public class SurveyFormResource implements SurveyFormService {
     @Inject
     private SurveyQuestionDao questionsDao;
     /**
-     * DAO for responses ({@link SurveyOptionDao}).
+     * DAO for options ({@link SurveyOptionDao}).
      */
     @Inject
     private SurveyOptionDao responsesDao;
@@ -130,30 +124,6 @@ public class SurveyFormResource implements SurveyFormService {
         }
     }
     @GET
-    @Path(SurveyFormService.PATH_QUESTIONS_LIST)
-    @Produces(MediaType.APPLICATION_JSON)
-    @Override
-    public List<SurveyQuestionDTO> getQuestions() {
-        return questionsDao.findAll().stream()
-                .map(SurveyFormResource::toDto).collect(Collectors.toList());
-    }
-    @GET
-    @Path(SurveyFormService.PATH_RESPONSES_BY_QUESTION_LIST)
-    @Produces(MediaType.APPLICATION_JSON)
-    @Override
-    public List<SurveyOptionDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") final Long questionRef) {
-        this.checkRequired(questionRef, "questionRef");
-        if (this.checkIfQuestionExist(questionRef)) {
-            final List<SurveyOption> list = responsesDao.findAllByQuestion(questionRef);
-            final List<SurveyOptionDTO> responses = new ArrayList<>();
-            for (final SurveyOption r : list) {
-                responses.add(toDto(r));
-            }
-            return responses;
-        }
-        return null;
-    }
-    @GET
     @Path(SurveyFormService.PATH_RESPONSES_LIST)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
@@ -163,75 +133,47 @@ public class SurveyFormResource implements SurveyFormService {
     }
     @POST
     @Path(SurveyFormService.PATH_INSERT_RESPONSE)
-    @Produces(MediaType.TEXT_PLAIN)
-    @Override
-    public void insertResponse(
-            @FormParam(value = "questionRef") final Long questionRef,
-            @FormParam(value = "responseRef") final String responseRef,
-            @FormParam(value = "otherText") final String otherText) {
-        this.checkRequired(questionRef, "questionRef");
-        final boolean responseRefIsNull = responseRef == null || responseRef.equals("null");
-        final boolean otherTextIsNull = otherText == null || otherText.equals("null");
-
-        final SurveyQuestion q = questionsDao.findByRef(questionRef);
-        if (q != null) {
-            if (!responseRefIsNull && otherTextIsNull) {     // réponse prédéfinie
-                final SurveyOption r = responsesDao.findByRef(Long.valueOf(responseRef));
-                this.userResponsesDao.insertResponse(q, r, null, LocalDateTime.now());
-            } else if (responseRefIsNull && !otherTextIsNull) {  // réponse libre
-                this.userResponsesDao.insertResponse(q, null, otherText, LocalDateTime.now());
-            }
-        }
-    }
-    /**
-     * Check on database if reference of question exists.
-     * @param questionRef
-     * @return {@code true} if question exists or {@code false} value otherwise
-     */
-    private boolean checkIfQuestionExist(final Long questionRef) {
-        if (questionRef != null) {
-            // on cherche dans le DAO
-            return questionsDao.findByRef(questionRef) != null;
-        }
-        return false;
-    }
-    @POST
-    @Path(SurveyFormService.PATH_INSERT_RESPONSE_WITH_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     @Override
     public String insertAllResponses(final SurveyResponseDTO data) {
-        // datetime commun à tous les enregistrements (des réponses et de l'email)
+        this.checkRequired(data, "data");
+        // common datetime to all records (user responses and user email)
         final LocalDateTime datetime = LocalDateTime.now();
 
-        // traitement des réponses prédéfinies
-        for (final SurveyQuestionDTO dto : data.getQuestions()) { // Pour chaque question répondue
-            // Insérer les réponses associés
+        final List<SurveyQuestion> questions = questionsDao.findAll();
+
+        // proccessing of predefined responses
+        for (final SurveyQuestionDTO dto : data.getQuestions()) {
             final SurveyQuestion q = toEntity(dto);
+            // if question from survey doesn't exist, don't inserting response
+            if (!questions.contains(q)) {
+                continue;
+            }
+
             final Long qRef = dto.getId();
             List<SurveyOptionDTO> associatedResponses = data.getResponses()
                     .stream().filter(f -> f.getQuestion().getId() == qRef)
                     .collect(Collectors.toList());
 
-            // On insère les réponses prédéfinies
             for (final SurveyOptionDTO rDto : associatedResponses) {
                 final SurveyOption r = toEntity(rDto);
                 this.userResponsesDao.insertResponse(q, r, null, datetime);
             }
 
-            // Si il existe une réponse libre, l'insérer
+            // if free response present, inserted on database
             if (data.getOtherTextMap().containsKey(dto.getId())
                     && data.getOtherTextMap().get(dto.getId()) != null) {
                 this.userResponsesDao.insertResponse(q, null, data.getOtherTextMap().get(q.getId()), datetime);
             }
         }
 
-        // Si l'utilisateur a fourni son adresse e-mail
+        // if the user provided their email address and is vaild
         if (EmailUtils.isEmailAddressValid(data.getEmail())) {
             this.userEmailDao.insertEmailUserAddress(data.getEmail(), datetime);
         }
 
-        // une fois que tout est inséré, on envoi un mail au support
+        // once everything is inserted, send an email to support
         this.mailService.sendLoginFilled(data);
         return "0";
     }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java b/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java
index 855de26..1006b22 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/util/EmailUtils.java
@@ -2,20 +2,24 @@ package fr.agrometinfo.www.server.util;
 
 import java.util.regex.Pattern;
 
-import org.apache.commons.lang3.StringUtils;
-
 /**
  * @author jdecome
  *
  */
-public class EmailUtils extends StringUtils {
+public final class EmailUtils {
+    /**
+     * Private default constructor.
+     */
+    private EmailUtils() {
+
+    }
     /**
      * Check if email address provided is a valid address.<br>
      * Verification by not null and regex
      * @param email address
      * @return {@code true} or {@code false}
      */
-    public static final boolean isEmailAddressValid(final String email) {
+    public static boolean isEmailAddressValid(final String email) {
         if (email == null) {
             return false;
         }
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index d38049e..74ff21d 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package fr.agrometinfo.www.server;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -29,14 +26,12 @@ import fr.agrometinfo.www.server.model.SurveyQuestion;
 import fr.agrometinfo.www.server.model.SurveyOption;
 import fr.agrometinfo.www.server.model.UserEmail;
 import fr.agrometinfo.www.server.model.UserResponse;
-import lombok.extern.log4j.Log4j2;
 
 /**
  * Test of UserResponsesDaoHibernate.
  * @author jdecome
  *
  */
-@Log4j2
 public class SurveyFormHibernateTest {
     /** DAO for user's responses. */
     private UserResponsesDao userResponsesDao = new UserResponsesDaoHibernate();
@@ -50,12 +45,12 @@ public class SurveyFormHibernateTest {
     @Test
     public void getQuestion() {
         final List<SurveyQuestion> list = questionsDao.findAll();
-        assertNotEquals(list.size(), 0, "La liste de questions est vide");
+        assertNotEquals(list.size(), 0, "Questions list is empty");
         for (final SurveyQuestion q : list) {
-            // recherche de la question par sa référence
+            // search questio by reference.
             final SurveyQuestion foundedByRef = questionsDao.findByRef(q.getId());
-            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
-            assertEquals(foundedByRef, q, "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
+            assertNotNull(foundedByRef, "Object for question « " + q.getId() + " » is null");
+            assertEquals(foundedByRef, q, "Object corresponding to question « " + q.getId() + " » doesn't corresponding");
         }
     }
     
@@ -63,22 +58,22 @@ public class SurveyFormHibernateTest {
     public void getResponses() {
         final List<SurveyQuestion> questions = questionsDao.findAll();
         final List<SurveyOption> responses = responsesDao.findAll();
-        assertNotEquals(responses.size(), 0, "La liste de réponses est vide");
+        assertNotEquals(responses.size(), 0, "Options list is empty");
         for (final SurveyOption r : responses) {
-            assertTrue(questions.contains(r.getQuestion()), "La liste de questions ne contient pas la question " + r.getQuestion().getId());
+            assertTrue(questions.contains(r.getQuestion()), "Questions list doesn't contains question " + r.getQuestion().getId());
             
-            // Test à partir de la question correspondante à la réponse
+            // Test from question corresponding to response
             final SurveyQuestion q = r.getQuestion();
-            assertNotNull(q, "La question correspondante à la réponse " + r.getId() + " est null");
+            assertNotNull(q, "Question corresponding to response « " + r.getId() + " » is null");
             
             final SurveyQuestion foundedByRef = questionsDao.findByRef(q.getId());
-            assertNotNull(foundedByRef, "L'objet correspondant à la question " + q.getId() + " est null");
-            assertEquals(foundedByRef, r.getQuestion(), "L'objet correspondant à la question " + q.getId() + " ne correspond pas");
+            assertNotNull(foundedByRef, "Object corresponding to question « " + q.getId() + " » doesn't corresponding");
+            assertEquals(foundedByRef, r.getQuestion(), "Object corresponding to question « " + q.getId() + " » doesn't corresponding");
             
-            // recherche de la réponse par sa référence
+            // Search response by reference
             final SurveyOption responseByRef = responsesDao.findByRef(r.getId());
-            assertNotNull(responseByRef, "La réponse correspondante à la réponse " + r.getId() + " est null");
-            assertEquals(responseByRef, r, "La réponse correspondante à la réponse " + r.getId() + " est vide");
+            assertNotNull(responseByRef, "Object corresponding to response " + r.getId() + " is null");
+            assertEquals(responseByRef, r, "Object corresponding to response " + r.getId() + " is empty");
         }
     }
     
@@ -87,88 +82,88 @@ public class SurveyFormHibernateTest {
         this.resetUserResponses();
         final List<SurveyQuestion> questions = questionsDao.findAll();
         final SurveyQuestion profession = questions.stream().filter((q) -> q.getDescription().contains("profession")).findFirst().get();
-        assertNotNull(profession, "Aucune question ne correspond à la profession");
+        assertNotNull(profession, "No question object matching to question of question about profession");
         List<SurveyOption> responses = responsesDao.findAllByQuestion(profession.getId());
-        assertNotNull(responses, "Aucune réponse ne correspond à la profession");
-        assertNotEquals(responses.size(), 0, "La liste de réponses pour la profession est vide");
+        assertNotNull(responses, "No response object matching to question of question about profession");
+        assertNotEquals(responses.size(), 0, "List of responses about profession question is empty");
         for (final SurveyOption r : responses) {
-            assertEquals(r.getQuestion(), profession, "La question de la réponse ne correspond pas avec la question d'origine");
+            assertEquals(r.getQuestion(), profession, "Question of responses doesn't corresponding to original question");
         }
         
-        // Insertion d'une réponse
-        final List<SurveyOption> listOfResponses = new ArrayList<>();   // liste des réponses insérées
+        // Inserting a single response
+        final List<SurveyOption> listOfResponses = new ArrayList<>();   // list of inserted answers
         SurveyOption r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
         userResponsesDao.insertResponse(profession, r, null);
         listOfResponses.add(r);
         
         List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getId());
-        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur doit contenir un élément");
-        assertEquals(list.get(0).getQuestion(), profession, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
-        assertEquals(list.get(0).getOption(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
-        assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
+        assertEquals(list.size(), 1, "The user's response list must contain an item");
+        assertEquals(list.get(0).getQuestion(), profession, "The question in the user's response does not match the original question");
+        assertEquals(list.get(0).getOption(), r, "The answer contained in the user's response does not match the inserted answer");
+        assertNull(list.get(0).getOtherText(), "Free response must be null");
         
-        // Insertion de plusieurs réponses
+        // Inserting of multiple responses
         final int nbToInsert = 3;
         for (final SurveyOption res : pickNRandom(responses, nbToInsert)) {
             userResponsesDao.insertResponse(profession, res, null);
             listOfResponses.add(res);
         }
         list = userResponsesDao.findAllByQuestion(profession.getId());
-        assertEquals(list.size(), (1 + nbToInsert), "La liste de réponses de l'utilisateur doit contenir la réponse précédente + " + nbToInsert + " réponses supplémentaires");
+        assertEquals(list.size(), (1 + nbToInsert), "The user's answer list must contain the previous answer + " + nbToInsert + " additional answers");
         for (final UserResponse ur : list) {
-            assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question");
-            assertTrue(listOfResponses.contains(ur.getOption()), "La réponse contenue dans la réponse de l'utilisateur n'est pas dans la liste des réponses insérées");
-            assertEquals(ur.getOption().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
-            assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            assertEquals(ur.getQuestion(), profession, "The original question doesn't match with the Question object");
+            assertTrue(listOfResponses.contains(ur.getOption()), "The answer contained in the user's answer is not in the list of inserted answers");
+            assertEquals(ur.getOption().getQuestion(), profession, "The original question does not match with the Question object present in the Option object");
+            assertNull(ur.getOtherText(), "The text must be null if it is an answer from a choice");
         }
 
         final int newNbResponses = 1 + nbToInsert;
         
-        // Insertion d'une réponse à choix libre
-        String other = "Ceci est une réponse libre";
+        // Insertion of a free choice answer
+        String other = "This is a free answer";
         userResponsesDao.insertResponse(profession, null, other);
         
         list = userResponsesDao.findAllByQuestion(profession.getId());
-        assertEquals(list.size(), (newNbResponses + 1), "La liste de réponses de l'utilisateur doit contenir les " + newNbResponses + " + la réponse libre");
+        assertEquals(list.size(), (newNbResponses + 1), "The user's response list must contain the " + newNbResponses + "+ the free answer");
         for (final UserResponse ur : list) {
-            assertEquals(ur.getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
-            if (ur.getOtherText() != null) {    // on est sur une réponse libre
-                assertNull(ur.getOption(), "L'objet response doit être null si il s'agit d'une réponse libre");
-                assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
-            } else {
-                assertEquals(ur.getOption().getQuestion(), profession, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
-                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            assertEquals(ur.getQuestion(), profession, "The original question does not match with the Question object in the user's response");
+            if (ur.getOtherText() != null) {    // free response
+                assertNull(ur.getOption(), "The Option object must be null if it is a free response");
+                assertEquals(ur.getOtherText(), other, "The text does not correspond to what was inserted in the base");
+            } else {    // predefined response
+                assertEquals(ur.getOption().getQuestion(), profession, "The original question does not match with the Question object present in the Option object");
+                assertNull(ur.getOtherText(), "The text must be null if it is an answer from a choice");
             }
         }
         
         final SurveyQuestion useCase = questions.stream().filter((q) -> q.getId() == 3).findFirst().get();
-        assertNotNull(useCase, "Aucune question ne correspond aux cas d'utilisation");
+        assertNotNull(useCase, "No object match the use cases question");
         responses = responsesDao.findAllByQuestion(useCase.getId());
-        assertNotNull(responses, "Aucune réponse ne correspond à la question des cas d'utilisation");
-        assertNotEquals(responses.size(), 0, "La liste de réponses pour la la question des cas d'utilisation est vide");
+        assertNotNull(responses, "No answer matches the use case question object");
+        assertNotEquals(responses.size(), 0, "The answer list for the use case question object is empty");
         
         r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
         userResponsesDao.insertResponse(useCase, r, null);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
-        assertEquals(list.size(), 1, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir un élément");
-        assertEquals(list.get(0).getQuestion(), useCase, "La question contenue dans la réponse de l'utilisateur ne correspond pas avec la question d'origine");
-        assertEquals(list.get(0).getOption(), r, "La réponse contenue dans la réponse de l'utilisateur ne correspond pas avec la réponse insérée");
-        assertNull(list.get(0).getOtherText(), "Le texte libre doit être null");
+        assertEquals(list.size(), 1, "The user answer list for the use case question must contain an item");
+        assertEquals(list.get(0).getQuestion(), useCase, "The question in the user's response does not match the original question");
+        assertEquals(list.get(0).getOption(), r, "The answer contained in the user's response does not match the inserted answer");
+        assertNull(list.get(0).getOtherText(), "Free text must be null");
         
-        other = other.concat(" pour la question des cas d'utilisation");
+        other = other.concat(" for the question of use cases");
         userResponsesDao.insertResponse(useCase, null, other);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
-        assertEquals(list.size(), 2, "La liste de réponses de l'utilisateur pour la question des cas d'utilisation doit contenir deux éléments");
+        assertEquals(list.size(), 2, "The user answer list for the use case question must contain two items");
         for (final UserResponse ur : list) {
-            assertEquals(ur.getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question dans la réponse de l'utilisateur");
-            if (ur.getOtherText() != null) {    // on est sur une réponse libre
-                assertNull(ur.getOption(), "L'objet response doit être null si il s'agit d'une réponse libre");
-                assertEquals(ur.getOtherText(), other, "Le texte ne correspond pas à ce qui a été inséré en base");
-            } else {
-                assertEquals(ur.getOption().getQuestion(), useCase, "La question d'origine ne correspond pas avec l'objet Question présent dans l'objet Response");
-                assertNull(ur.getOtherText(), "Le texte doit être null si il s'agit d'une réponse parmi un choix");
+            assertEquals(ur.getQuestion(), useCase, "The original question does not match with the Question object in the user's response");
+            if (ur.getOtherText() != null) {    // free response
+                assertNull(ur.getOption(), "The response object must be null if it is a free response");
+                assertEquals(ur.getOtherText(), other, "The text does not correspond to what was inserted in the base");
+            } else {    // predefined response
+                assertEquals(ur.getOption().getQuestion(), useCase, "The original question does not match with the Question object present in the Option object");
+                assertNull(ur.getOtherText(), "The text must be null if it is an answer from a choice");
             }
         }
     }
@@ -177,13 +172,13 @@ public class SurveyFormHibernateTest {
         // just testing insertion of email user, without insertion of responses (already tested)
         final String email = "jane.doe@inrae.fr";
         List<UserEmail> list = this.userEmailDao.getEmailAddressList();
-        assertNotNull(list, "La liste des e-mail n'est pas initialisée");
-        assertEquals(list.size(), 0, "La liste des e-mail doit être vide");
+        assertNotNull(list, "The email list is not initialized");
+        assertEquals(list.size(), 0, "The email list must be empty");
         this.userEmailDao.insertEmailUserAddress(email, LocalDateTime.now());
         
         list = this.userEmailDao.getEmailAddressList();
-        assertEquals(list.size(), 1, "La liste des e-mail doit contenir au moins un élément");
-        assertEquals(list.get(0).getEmail(), email, "L'élément enregistré ne correspond pas avec le mail inséré « " + email + " »");
+        assertEquals(list.size(), 1, "The email list must contain an item");
+        assertEquals(list.get(0).getEmail(), email, "The saved item does not match the inserted email « " + email + " »");
     }
     /**
      * Return randomly responses list
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 1797782..e904187 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -1,6 +1,3 @@
-/**
- * 
- */
 package fr.agrometinfo.www.server.rs;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -55,7 +52,7 @@ import lombok.Getter;
 import lombok.extern.log4j.Log4j2;
 
 /**
- * Test login form webservice.
+ * Test survey form webservice.
  * @author jdecome
  *
  */
@@ -114,63 +111,31 @@ public class SurveyFormResourceTest extends JerseyTest {
             }
         });
     }
-    /** Test de la récupération des réponses. */
+    /** Testing response retrieval from webservice. */
     @Test
     public void getResponses() {
-        // On récupère les questions via le webservice
-        final String jsonQuestions = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_QUESTIONS_LIST).request().get(String.class);
-        assertNotNull(jsonQuestions, "Impossible de récupérer la ressource (objet null) pour les questions");
-        assertFalse(jsonQuestions.isEmpty(), "La chaine de caractère JSON des questions est vide");
         try {
-            final List<SurveyQuestion> questions = objectMapper.readValue(jsonQuestions, new TypeReference<List<SurveyQuestion>>(){});
-            assertNotNull(questions, "La conversion du JSON des questions en liste a échoué");
-            assertNotEquals(questions.size(), 0, "La liste de questions est vide");
+            // get all questions from DAO for verification.
+            final List<SurveyQuestion> questions = questionsDao.findAll();
+            assertNotEquals(questions.size(), 0, "Question list is empty");
             
-            // On récupère l'ensemble des réponses
+            // Get all responses (options) by webservice
             final String jsonAllResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_LIST).request().get(String.class);
-            assertNotNull(jsonAllResponses, "Impossible de récupérer la ressource (objet null) des réponses");
-            assertFalse(jsonAllResponses.isEmpty(), "La chaine de caractère JSON des réponses est vide");
+            assertNotNull(jsonAllResponses, "Unable to retrieve resource (null object) from responses");
+            assertFalse(jsonAllResponses.isEmpty(), "The JSON character string of the responses is empty");
             
             final List<SurveyOption> allResponses = objectMapper.readValue(jsonAllResponses, new TypeReference<List<SurveyOption>>() {});
-            assertNotNull(allResponses, "La conversion du JSON des réponses en liste a échoué");
-            assertNotEquals(allResponses.size(), 0, "La liste de réponses est vide");
+            assertNotNull(allResponses, "Conversion of JSON from responses to list failed");
+            assertNotEquals(allResponses.size(), 0, "The answer list is empty");
             
             for (final SurveyOption r : allResponses) {
-                assertTrue(questions.contains(r.getQuestion()), "La question de la réponse « " + r.getDescription() + " » n'est pas contenue dans la liste des questions");
-            }
-            
-            // Pour chaque question, 
-            for (final SurveyQuestion q : questions) {
-                // on récupère les réponses associées via le webservice
-                final String jsonResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_BY_QUESTION_LIST)
-                                                .queryParam("questionRef", String.valueOf(q.getId()))
-                                                .request()
-                                                .get(String.class);
-                
-                assertNotNull(jsonResponses, "Impossible de récupérer la ressource (objet null)  pour la question n° " + q.getId() + " est vide");
-                assertFalse(jsonResponses.isEmpty(), "La chaine de caractère JSON pour la question n° " + q.getId() + " est vide");
-                
-                final List<SurveyOption> responses = objectMapper.readValue(jsonResponses, new TypeReference<List<SurveyOption>>() {});
-                assertNotNull(responses, "La conversion du JSON des réponses de la question n° " + q.getId() + " n'a pas fonctionnée");
-                assertNotEquals(responses.size(), 0, " La liste de réponses pour la question n° " + q.getId() + " est vide");
-                
-                final List<SurveyOption> responsesByDao = responsesDao.findAllByQuestion(q.getId());
-                assertNotNull(responsesByDao, " La lecture des réponses pour la question n° " + q.getId() + " via le DAO n'a pas fonctionné");
-                assertNotEquals(responsesByDao.size(), 0, " La liste de réponses via le DAO pour la question n° " + q.getId() + " est vide");
-                assertEquals(responses, responsesByDao, "La liste de réponses via le webservice n'est pas identique avec la liste via le DAO");
-                
-                for (final SurveyOption r : responses) {
-                    assertNotNull(r.getQuestion(), "La question de la réponse n° " + r.getId() + " est null");
-                    assertEquals(r.getQuestion(), q, "La question de la réponse n° " + r.getId() + " ne correspond pas à la question");
-                    assertTrue(responsesByDao.contains(r), "La liste via le DAO ne contient pas la réponse n° " + r.getId());
-                }
-                
+                assertTrue(questions.contains(r.getQuestion()), "Question of the answer « " + r.getDescription() + " » is not contained in the list of questions");
             }
         } catch (final JsonProcessingException e) {
             log.info(e.getMessage());
         }
     }
-    /** Test en définissant les réponses pour les questions. */
+    /** Test by setting answers for questions. */
     @Test
     public void testWithJsonResponses() {
         this.resetUserResponses();
@@ -183,80 +148,79 @@ public class SurveyFormResourceTest extends JerseyTest {
         for (final SurveyQuestion q : questionsList) {
             final List<SurveyOption> responsesListForQuestion = responsesDao.findAllByQuestion(q.getId());
             
-            // on récupère un nombre aléatoire de réponse
+            // Getting a randomly list of response
             final List<SurveyOption> randomList = this.pickNRandom(responsesListForQuestion, ThreadLocalRandom.current().nextInt(0, responsesListForQuestion.size() - 1));
            
-            // si il y a au moins une réponse récupérée aléatoirement
             if (randomList.size() > 0) {
                 responsesList.addAll(randomList);
             }
             
-            otherTextMap.put(q.getId(), "Réponse libre pour la question « " + q.getDescription() + " »");
+            otherTextMap.put(q.getId(), "Free answer for the question « " + q.getDescription() + " »");
         }
         final List<SurveyOptionDTO> responsesDTO = responsesList.stream().map(SurveyFormResource::toDto).toList();
         
         final SurveyResponseDTO data = new SurveyResponseDTO(questionsDTO, responsesDTO, otherTextMap);
         data.setEmail("john.doe@inrae.fr");
-        assertEquals(data.getQuestions(), questionsDTO, "La liste de questions dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
-        assertEquals(data.getResponses(), responsesDTO, "La liste de réponses dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
-        assertEquals(data.getOtherTextMap(), otherTextMap, "La liste de réponses libre dans l'objet LoginFormData ne correspond pas avec la liste d'origine");
+        assertEquals(data.getQuestions(), questionsDTO, "List of questions in LoginFormData object does not match with original list");
+        assertEquals(data.getResponses(), responsesDTO, "List of responses in LoginFormData object does not match with the original list");
+        assertEquals(data.getOtherTextMap(), otherTextMap, "List of free responses in LoginFormData object does not match with the original list");
         
-        jakarta.ws.rs.core.Response ret = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE_WITH_JSON)
+        jakarta.ws.rs.core.Response ret = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_INSERT_RESPONSE)
                 .request(MediaType.APPLICATION_JSON).post(Entity.entity(data, MediaType.APPLICATION_JSON));
         
-        assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "Le point d'entrée d'insertion doit retourner le code status 200 (OK)");
+        assertEquals(ret.getStatus(), jakarta.ws.rs.core.Response.Status.OK.getStatusCode(), "The insert entry point should return status code 200 (OK)");
         
-        // Récupère toutes les réponses pour toutes les questions
+        // Retrieves all answers for all questions
         List<UserResponse> list = new ArrayList<>();
         for (final SurveyQuestion q : questionsDao.findAll()) {
             final List<UserResponse> responsesByQuestion = userResponsesDao.findAllByQuestion(q.getId());
-            assertNotNull(responsesByQuestion, "La liste de réponses de l'utilisateur pour la question « " + q.getId() + " » ne doit pas être null");
+            assertNotNull(responsesByQuestion, "The user's answer list for the question « " + q.getId() + " » must not be null");
             responsesByQuestion.forEach((ur) -> {
-                assertEquals(ur.getQuestion(), q, "La question contenue dans la réponse ne correspond pas avec la question d'origine « " + q.getDescription() + " »");
+                assertEquals(ur.getQuestion(), q, "The question in the answer does not match the original question « " + q.getDescription() + " »");
             });
             list.addAll(responsesByQuestion);
         }
         
-        // Test sur toutes les réponses
+        // Test on all answers
         list.forEach((ur) -> {
-            assertNotNull(ur.getDateTime(), "Le timestamp de la réponse ne doit pas être null");
-            assertTrue(ur.getId() > 0, "la référence de la réponse de l'utilisateur doit être supérieur à 0");
+            assertNotNull(ur.getDateTime(), "The response timestamp must not be null");
+            assertTrue(ur.getId() > 0, "Reference of the user response must be greater than 0");
             if (ur.getOption() != null) {
-                assertNotNull(ur.getOption(), "La réponse de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
-                assertNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, doit être null");
+                assertNotNull(ur.getOption(), "The user response, for a predefined response, must not be null");
+                assertNull(ur.getOtherText(), "The user's free response, for a predefined response, must be null");
             } else {
-                assertNull(ur.getOption(), "La réponse de l'utilisateur, pour une réponse libre, doit être null");
-                assertNotNull(ur.getOtherText(), "La réponse libre de l'utilisateur, pour une réponse prédéfinie, ne doit pas être null");
+                assertNull(ur.getOption(), "The user's response, for a free response, must be null");
+                assertNotNull(ur.getOtherText(), "The user's free response, for a predefined response, must not be null");
             }
         });
 
-        // on vérifie que tous les enregistrements ont le même datetime
-        assertEquals(list.stream().map((u) -> u.getDateTime()).distinct().count(), 1, "Toutes les réponses n'ont pas le même datetime");
+        // Check if all records has the same datetime
+        assertEquals(list.stream().map((u) -> u.getDateTime()).distinct().count(), 1, "Not all answers have the same datetime");
         final LocalDateTime datetimeResponse = list.stream().map((u) -> u.getDateTime()).distinct().findFirst().get();
-        assertNotNull(datetimeResponse, "Le datetime lue dans les réponses est null");
+        assertNotNull(datetimeResponse, "The datetime read in the responses is null");
         
         // dans ce cas de test, on a inséré l'adresse mail de l'utilisateur
         final List<UserEmail> emails = this.userEmailDao.getEmailAddressList();
-        assertNotNull(emails, "La liste des emails n'est pas initialisée");
-        assertNotEquals(emails.size(), 0, "La liste des emails est vide");
+        assertNotNull(emails, "The email list is not initialized (= null)");
+        assertNotEquals(emails.size(), 0, "Email list is empty");
         
         emails.forEach((e) -> {
-            assertTrue((e.getId() > 0), "L'id de l'objet doit être supérieur à 0");
-            assertNotNull(e.getDatetime(), "Le timestamp de la question ne doit pas être null");
-            assertNotNull(e.getEmail(), "L'adresse mail contenue dans l'objet ne doit pas être null");
+            assertTrue((e.getId() > 0), "The object id must be greater than 0");
+            assertNotNull(e.getDatetime(), "The timestamp of the question must not be null");
+            assertNotNull(e.getEmail(), "The email address contained in the subject must not be null");
         });
 
-        assertEquals(emails.stream().map((e) -> e.getDatetime()).distinct().count(), 1, "Toutes les adresses mails n'ont pas le même datetime");
+        assertEquals(emails.stream().map((e) -> e.getDatetime()).distinct().count(), 1, "Not all email addresses have the same datetime");
         final LocalDateTime datetimeEmail = emails.stream().map((e) -> e.getDatetime()).distinct().findFirst().get();
-        assertNotNull(datetimeEmail, "Le datetime lue dans les emails est null");
+        assertNotNull(datetimeEmail, "The datetime read in emails is null");
         
-        assertEquals(datetimeResponse, datetimeEmail, "Les deux datetime ne sont pas identique");
+        assertEquals(datetimeResponse, datetimeEmail, "The two datetimes are not identical");
         
-        // on vérifie le mail envoyé au support, lorsqu'un utilisateur a rempli le formulaire
+        // Check mail sended to support, when user filled survey form
         final Mail mail = ((MailServiceTest) this.mailService).getMail();
-        assertNotNull(mail, "L'objet mail ne doit pas être null");
-        assertNotNull(mail.getContent(), "Le corps du message n'est pas défini (= null)");
-        assertFalse(mail.getContent().isEmpty(), "Le corps du message est vide");
+        assertNotNull(mail, "The mail object must not be null");
+        assertNotNull(mail.getContent(), "Message body is not defined (= null)");
+        assertFalse(mail.getContent().isEmpty(), "The message body is empty");
     }
     /**
      * 
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
index 438217d..029b6de 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyOptionDTO.java
@@ -1,19 +1,16 @@
-/**
- *
- */
 package fr.agrometinfo.www.shared.dto;
 
 import org.dominokit.jackson.annotation.JSONMapper;
 
 /**
- *  DTO for responses (as options) for survey form.
+ *  DTO for options for survey form.
  * @author jdecome
  *
  */
 @JSONMapper
 public class SurveyOptionDTO {
     /**
-     * Ref of this response.
+     * Reference of this response.
      */
     private long id;
     /**
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
index 49b5545..265f2c5 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyQuestionDTO.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.shared.dto;
 
 import java.util.Objects;
@@ -8,14 +5,14 @@ import java.util.Objects;
 import org.dominokit.jackson.annotation.JSONMapper;
 
 /**
- * DTO for questions of login form.
+ * DTO for questions of survey form.
  * @author jdecome
  *
  */
 @JSONMapper
 public class SurveyQuestionDTO {
     /**
-     * Ref of question.
+     * Reference of question.
      */
     private long id;
     /**
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
index f8ad80b..67054b1 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/dto/SurveyResponseDTO.java
@@ -1,13 +1,9 @@
-/**
- *
- */
 package fr.agrometinfo.www.shared.dto;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-
 /**
  * Class contains all data of survey form.
  * @author jdecome
diff --git a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
index 4475dc5..b41ab70 100644
--- a/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
+++ b/www-shared/src/main/java/fr/agrometinfo/www/shared/service/SurveyFormService.java
@@ -1,6 +1,3 @@
-/**
- *
- */
 package fr.agrometinfo.www.shared.service;
 
 import java.util.List;
@@ -13,13 +10,10 @@ import org.dominokit.rest.shared.request.service.annotations.RequestFactory;
 import org.dominokit.rest.shared.request.service.annotations.RequestBody;
 
 import fr.agrometinfo.www.shared.dto.SurveyResponseDTO;
-import fr.agrometinfo.www.shared.dto.SurveyQuestionDTO;
 import fr.agrometinfo.www.shared.dto.SurveyOptionDTO;
-import jakarta.ws.rs.DefaultValue;
-import jakarta.ws.rs.FormParam;
-import jakarta.ws.rs.QueryParam;
 
 /**
+ * Interface for survey form client server resource.
  * @author jdecome
  *
  */
@@ -27,55 +21,23 @@ import jakarta.ws.rs.QueryParam;
 @Path(SurveyFormService.PATH)
 public interface SurveyFormService {
     /** Service base path. */
-    String PATH = "login";
-    /** Path for {@link SurveyFormService#getQuestions()}. */
-    String PATH_QUESTIONS_LIST = "questions";
+    String PATH = "survey";
     /** Path for {@link SurveyFormService#getResponses}. */
     String PATH_RESPONSES_LIST = "responses";
-    /** Path for {@link SurveyFormService#getResponsesByQuestion(long)}. */
-    String PATH_RESPONSES_BY_QUESTION_LIST = "responsesByQuestion";
-    /** Path for {@link SurveyFormService#getResponses()}. */
-    String PATH_INSERT_RESPONSE = "insert";
-    /** Path for {@link SurveyFormService#insertResponse(String)}. */
-    String PATH_INSERT_RESPONSE_WITH_JSON = "insertMultipleValues";
+    /** Path for {@link SurveyFormService#insertAllResponses(SurveyResponseDTO)}. */
+    String PATH_INSERT_RESPONSE = "insertResponses";
     /**
-     * @return list of questions.
+     * @return list of availables options for questions.
      */
     @GET
-    @Path(PATH_QUESTIONS_LIST)
-    List<SurveyQuestionDTO> getQuestions();
-    @GET
     @Path(PATH_RESPONSES_LIST)
     List<SurveyOptionDTO> getResponses();
     /**
-     * Returning the list of possible response for one question.
-     * @param questionRef ref of question
-     * @return list of question's responses
-     */
-    @GET
-    @Path(PATH_RESPONSES_BY_QUESTION_LIST)
-    List<SurveyOptionDTO> getResponsesByQuestion(@QueryParam(value = "questionRef") Long questionRef);
-    /**
-     * Insert response or text for specified question.<br>
-     * If responseRef is provided, {@code otherText} must be null.<br>
-     * If otherText is provided, {@code responseRef} must be null.
-     * @param questionRef ref of question
-     * @param responseRef ref of response or « null » String for null value
-     * @param otherText text if response is other
-     */
-    @POST
-    @Path(PATH_INSERT_RESPONSE)
-    void insertResponse(
-            @FormParam(value = "questionRef") Long questionRef,
-            @FormParam(value = "responseRef") @DefaultValue("null") String responseRef,
-            @FormParam(value = "otherText") @DefaultValue("null") String otherText
-    );
-    /**
-     * Insert all responses of login form.
+     * Insert all responses of survey form.
      * @param data object to insert
      * @return return code
      */
     @POST
-    @Path(PATH_INSERT_RESPONSE_WITH_JSON)
+    @Path(PATH_INSERT_RESPONSE)
     String insertAllResponses(@RequestBody SurveyResponseDTO data);
 }
-- 
GitLab


From f1e061af1f4df2672dbd630934c6ee859c6b922b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 4 Jun 2024 11:49:19 +0200
Subject: [PATCH 26/30] corrections test commit c5094fd0

---
 .../fr/agrometinfo/www/server/SurveyFormHibernateTest.java     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 74ff21d..8a30ec2 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -169,11 +169,12 @@ public class SurveyFormHibernateTest {
     }
     @Test
     public void setUserResponsesWithEmail() {
+        this.resetUserResponses();
         // just testing insertion of email user, without insertion of responses (already tested)
         final String email = "jane.doe@inrae.fr";
         List<UserEmail> list = this.userEmailDao.getEmailAddressList();
         assertNotNull(list, "The email list is not initialized");
-        assertEquals(list.size(), 0, "The email list must be empty");
+        assertTrue(list.isEmpty(), "The email list must be empty");
         this.userEmailDao.insertEmailUserAddress(email, LocalDateTime.now());
         
         list = this.userEmailDao.getEmailAddressList();
-- 
GitLab


From 7fe92185c95205e831c2a593286bb5db129177a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Tue, 4 Jun 2024 15:12:59 +0200
Subject: [PATCH 27/30] Modification des cas de tests

---
 .../www/server/SurveyFormHibernateTest.java       | 15 ++++++++-------
 .../www/server/rs/SurveyFormResourceTest.java     |  8 ++++----
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
index 8a30ec2..6e5f606 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/SurveyFormHibernateTest.java
@@ -1,6 +1,7 @@
 package fr.agrometinfo.www.server;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -45,9 +46,9 @@ public class SurveyFormHibernateTest {
     @Test
     public void getQuestion() {
         final List<SurveyQuestion> list = questionsDao.findAll();
-        assertNotEquals(list.size(), 0, "Questions list is empty");
+        assertFalse(list.isEmpty(), "Questions list is empty");
         for (final SurveyQuestion q : list) {
-            // search questio by reference.
+            // search question by reference.
             final SurveyQuestion foundedByRef = questionsDao.findByRef(q.getId());
             assertNotNull(foundedByRef, "Object for question « " + q.getId() + " » is null");
             assertEquals(foundedByRef, q, "Object corresponding to question « " + q.getId() + " » doesn't corresponding");
@@ -58,7 +59,7 @@ public class SurveyFormHibernateTest {
     public void getResponses() {
         final List<SurveyQuestion> questions = questionsDao.findAll();
         final List<SurveyOption> responses = responsesDao.findAll();
-        assertNotEquals(responses.size(), 0, "Options list is empty");
+        assertFalse(responses.isEmpty(), "Options list is empty");
         for (final SurveyOption r : responses) {
             assertTrue(questions.contains(r.getQuestion()), "Questions list doesn't contains question " + r.getQuestion().getId());
             
@@ -85,7 +86,7 @@ public class SurveyFormHibernateTest {
         assertNotNull(profession, "No question object matching to question of question about profession");
         List<SurveyOption> responses = responsesDao.findAllByQuestion(profession.getId());
         assertNotNull(responses, "No response object matching to question of question about profession");
-        assertNotEquals(responses.size(), 0, "List of responses about profession question is empty");
+        assertFalse(responses.isEmpty(), "List of responses about profession question is empty");
         for (final SurveyOption r : responses) {
             assertEquals(r.getQuestion(), profession, "Question of responses doesn't corresponding to original question");
         }
@@ -97,7 +98,7 @@ public class SurveyFormHibernateTest {
         listOfResponses.add(r);
         
         List<UserResponse> list = userResponsesDao.findAllByQuestion(profession.getId());
-        assertEquals(list.size(), 1, "The user's response list must contain an item");
+        assertEquals(list.size(), 1, "The user's response list must contain an item only");
         assertEquals(list.get(0).getQuestion(), profession, "The question in the user's response does not match the original question");
         assertEquals(list.get(0).getOption(), r, "The answer contained in the user's response does not match the inserted answer");
         assertNull(list.get(0).getOtherText(), "Free response must be null");
@@ -140,13 +141,13 @@ public class SurveyFormHibernateTest {
         assertNotNull(useCase, "No object match the use cases question");
         responses = responsesDao.findAllByQuestion(useCase.getId());
         assertNotNull(responses, "No answer matches the use case question object");
-        assertNotEquals(responses.size(), 0, "The answer list for the use case question object is empty");
+        assertFalse(responses.isEmpty(), "The answer list for the use case question object is empty");
         
         r = responses.get(ThreadLocalRandom.current().nextInt(0, responses.size() - 1));
         userResponsesDao.insertResponse(useCase, r, null);
         
         list = userResponsesDao.findAllByQuestion(useCase.getId());
-        assertEquals(list.size(), 1, "The user answer list for the use case question must contain an item");
+        assertEquals(list.size(), 1, "The user answer list for the use case question must contain an item only");
         assertEquals(list.get(0).getQuestion(), useCase, "The question in the user's response does not match the original question");
         assertEquals(list.get(0).getOption(), r, "The answer contained in the user's response does not match the inserted answer");
         assertNull(list.get(0).getOtherText(), "Free text must be null");
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index e904187..58c0c84 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -117,7 +117,7 @@ public class SurveyFormResourceTest extends JerseyTest {
         try {
             // get all questions from DAO for verification.
             final List<SurveyQuestion> questions = questionsDao.findAll();
-            assertNotEquals(questions.size(), 0, "Question list is empty");
+            assertFalse(questions.isEmpty(), "Question list is empty");
             
             // Get all responses (options) by webservice
             final String jsonAllResponses = target(SurveyFormResource.PATH + SEP + SurveyFormResource.PATH_RESPONSES_LIST).request().get(String.class);
@@ -126,7 +126,7 @@ public class SurveyFormResourceTest extends JerseyTest {
             
             final List<SurveyOption> allResponses = objectMapper.readValue(jsonAllResponses, new TypeReference<List<SurveyOption>>() {});
             assertNotNull(allResponses, "Conversion of JSON from responses to list failed");
-            assertNotEquals(allResponses.size(), 0, "The answer list is empty");
+            assertFalse(allResponses.isEmpty(), "The answer list is empty");
             
             for (final SurveyOption r : allResponses) {
                 assertTrue(questions.contains(r.getQuestion()), "Question of the answer « " + r.getDescription() + " » is not contained in the list of questions");
@@ -199,10 +199,10 @@ public class SurveyFormResourceTest extends JerseyTest {
         final LocalDateTime datetimeResponse = list.stream().map((u) -> u.getDateTime()).distinct().findFirst().get();
         assertNotNull(datetimeResponse, "The datetime read in the responses is null");
         
-        // dans ce cas de test, on a inséré l'adresse mail de l'utilisateur
+        // in this case, email address has inserted
         final List<UserEmail> emails = this.userEmailDao.getEmailAddressList();
         assertNotNull(emails, "The email list is not initialized (= null)");
-        assertNotEquals(emails.size(), 0, "Email list is empty");
+        assertFalse(emails.isEmpty(), "Email list is empty");
         
         emails.forEach((e) -> {
             assertTrue((e.getId() > 0), "The object id must be greater than 0");
-- 
GitLab


From 506a48b1704acc3f109a801cf1fb4ecd73b8bfc8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Wed, 12 Jun 2024 09:41:34 +0200
Subject: [PATCH 28/30] modification suite rebase

---
 sql/migration.sql                             | 63 +------------------
 .../www/client/view/LayoutView.java           | 47 +-------------
 .../www/server/rs/ApplicationResource.java    |  2 +-
 3 files changed, 4 insertions(+), 108 deletions(-)

diff --git a/sql/migration.sql b/sql/migration.sql
index cc5cb5d..423dc0d 100644
--- a/sql/migration.sql
+++ b/sql/migration.sql
@@ -85,65 +85,6 @@ END
 $$ LANGUAGE plpgsql;
 COMMENT ON FUNCTION drop_applied_migration_functions() IS 'Purge database from applied migration functions.';
 
----
---- #8
----
-CREATE OR REPLACE FUNCTION upgrade20231023() RETURNS boolean AS $BODY$
-BEGIN
-    ALTER TABLE indicator DROP COLUMN colorsequence;
-    DROP TABLE colorsequence;
-    ALTER TABLE indicator ADD COLUMN colorsequencename VARCHAR;
-    ALTER TABLE indicator ADD COLUMN nbofclasses INTEGER;
-    CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES');
-    ALTER TABLE indicator ADD COLUMN quantiletype QUANTILETYPE;
-    UPDATE indicator SET quantiletype='QUINTILES';
-    UPDATE indicator SET colorsequencename='Precipitation' WHERE code='rainsum';
-    UPDATE indicator SET colorsequencename='Blues' WHERE code='frostdaystmin';
-    UPDATE indicator SET colorsequencename='Temperature' WHERE code IN ('mint', 'meant', 'maxt');
-    UPDATE indicator SET colorsequencename='YlOrRd' WHERE code IN ('hdaystmax', 'hdaystmax1');
-    ALTER TABLE indicator ADD CONSTRAINT "CK_indicator_colorsequence" CHECK (nbofclasses IS NOT NULL OR quantiletype IS NOT NULL);
-    RETURN true;
-END
-$BODY$
-language plpgsql;
-
----
---- #8
----
-CREATE OR REPLACE FUNCTION upgrade20231030() RETURNS boolean AS $BODY$
-BEGIN
-    ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE VARCHAR USING QUANTILETYPE::VARCHAR;
-    DROP TYPE QUANTILETYPE;
-    CREATE TYPE QUANTILETYPE AS ENUM('CENTILES_05', 'QUANTILES', 'DECILES', 'QUINTILES', 'QUARTILES', 'SEXTILES');
-    ALTER TABLE "indicator" ALTER COLUMN quantiletype TYPE QUANTILETYPE USING quantiletype::quantiletype;
-    UPDATE indicator SET quantiletype='SEXTILES';
-    --- #12
-    CREATE TYPE AGGREGATIONTYPE AS ENUM('AVG', 'MAX');
-    ALTER TABLE "indicator" ADD COLUMN aggregationtype AGGREGATIONTYPE;
-    UPDATE indicator SET unit='mm' WHERE code IN ('rainsum');
-    UPDATE indicator SET unit='j' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1');
-    UPDATE indicator SET unit='°C' WHERE code IN ('maxt', 'meant', 'mint');
-    UPDATE indicator SET aggregationtype='MAX' WHERE code IN ('frostdaystmin', 'hdaystmax', 'hdaystmax1', 'rainsum');
-    UPDATE indicator SET aggregationtype='AVG' WHERE code IN ('maxt', 'meant', 'mint');
-    ALTER TABLE "indicator" ALTER COLUMN aggregationtype SET NOT NULL;
-    --- #8
-    UPDATE indicator SET colorsequencename='TemperatureReversed' WHERE code IN ('mint', 'meant', 'maxt');
-    RETURN true;
-END
-$BODY$
-language plpgsql;
-
---
--- 31
---
-CREATE OR REPLACE FUNCTION upgrade20231215() RETURNS boolean AS $BODY$
-BEGIN
-    INSERT INTO department (id, "name", region) VALUES(75, '75 - Paris', 75);
-    RETURN true;
-END
-$BODY$
-language plpgsql;
-
 --
 -- 47
 --
@@ -232,8 +173,8 @@ language plpgsql;
 CREATE OR REPLACE FUNCTION upgrade20240513() RETURNS boolean AS $BODY$
 BEGIN
     UPDATE dailyvalue AS d
-        FROM normalvalue AS n ON n.indicator=d.indicator AND n.cell=d.cell AND n.doy=EXTRACT(DOY FROM d.date)
-        SET d.comparedvalue=COALESCE(d.computedvalue - n."medianvalue", 0);
+	SET comparedvalue=COALESCE(d.computedvalue - n."medianvalue", 0)
+        FROM normalvalue AS n WHERE n.indicator=d.indicator AND n.cell=d.cell AND n.doy=EXTRACT(DOY FROM d.date);
     RETURN true;
 END;
 $BODY$
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
index 4dcdfe4..4032e30 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
@@ -380,21 +380,6 @@ implements LayoutPresenter.View, LoadingHandler {
         return layout.isRightPanelVisible();
     }
 
-    private void onChoiceChange() {
-        if (choice.isValid()) {
-            getPresenter().onChoiceChange(choice);
-        }
-    }
-
-    private void onComparisonChange(final boolean newValue) {
-        choice.setComparison(newValue);
-        onChoiceChange();
-    }
-
-    private void onIndicatorChange(final String newValue) {
-        choice.setIndicator(newValue);
-        onChoiceChange();
-    }
     @Override
     public void onLoading(final LoadingEvent event) {
         GWT.log("LayoutView.onLoading() " + event);
@@ -418,37 +403,7 @@ implements LayoutPresenter.View, LoadingHandler {
         }
         GWT.log("LayoutView.onLoading() done");
     }
-    private void onPeriodChange(final String newValue) {
-        choice.setPeriod(newValue);
-        choice.setIndicator(null);
-
-        // fill indicators
-        List<IndicatorDTO> list = null;
-        for (final PeriodDTO p : this.periods) {
-            if (p.getCode().equals(newValue)) {
-                list = p.getIndicators();
-                break;
-            }
-        }
-        if (list == null) {
-            return;
-        }
-        DomGlobal.console.info("Indicators : " + list);
-        new HTMLSelectElementBuilder<IndicatorDTO>() //
-        .setSelect(indicatorSelect) //
-        .setTextFunction(IndicatorDTO::getDescription) //
-        .setValueFunction(IndicatorDTO::getCode) //
-        .removeOptions() //
-        .addOptions(list) //
-        .build();
-        // select "meant"
-        if (list.stream().map(IndicatorDTO::getCode).anyMatch(DEFAULT_INDICATOR::equals)) {
-            indicatorSelect.element().value = DEFAULT_INDICATOR;
-            onIndicatorChange(DEFAULT_INDICATOR);
-        } else {
-            onChoiceChange();
-        }
-    }
+
     /**
      * To display an OpenLayers map, a container with a fixed height CSS property is
      * needed.
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
index c843f96..6b9e2e3 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationResource.java
@@ -11,7 +11,7 @@ import fr.agrometinfo.www.server.exception.AgroMetInfoException;
 import fr.agrometinfo.www.server.model.DailyVisit;
 import fr.agrometinfo.www.server.service.CacheService;
 import fr.agrometinfo.www.server.service.MailService;
-import fr.agrometinfo.www.server.service.MailService.Mail;
+import fr.agrometinfo.www.server.service.MailServiceImpl.Mail;
 import fr.agrometinfo.www.server.util.AppVersion;
 import fr.agrometinfo.www.server.util.ST;
 import fr.agrometinfo.www.server.util.ST.Key;
-- 
GitLab


From 99cc708d5de214e8939a01b9710b5b19f544df3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Wed, 12 Jun 2024 09:52:00 +0200
Subject: [PATCH 29/30] =?UTF-8?q?correction=20texte=20et=20modification=20?=
 =?UTF-8?q?nom=20m=C3=A9thode?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../fr/agrometinfo/www/client/i18n/AppConstants_fr.properties   | 2 +-
 .../java/fr/agrometinfo/www/server/rs/SurveyFormResource.java   | 2 +-
 .../java/fr/agrometinfo/www/server/service/MailService.java     | 2 +-
 .../java/fr/agrometinfo/www/server/service/MailServiceImpl.java | 2 +-
 .../fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java    | 2 +-
 5 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
index fc13d99..611ec51 100644
--- a/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
+++ b/www-client/src/main/resources/fr/agrometinfo/www/client/i18n/AppConstants_fr.properties
@@ -52,7 +52,7 @@ surveyFormTitle = Formulaire d'enquête
 surveyFormDescription = Bienvenu.e.s à la nouvelle version d'AgroMetInfo !<br/>Afin de nous aider à la faire évoluer et l'adapter le plus possible à vos besoins, <b>merci de renseigner</b> la petite enquête çi dessous (cela prendra 2 minutes maximum). L'ensemble des informations est anonyme, mais nous aidera à mieux comprendre l'utilisation de l'application. N'hésitez pas à <b>nous laisser votre adresse courriel</b> pour vous tenir informés de toutes les évolutions dans les mois et années à venir.<br/><br/>L'équipe AgroMetInfo.
 surveyFromEmailDescription = Votre adresse courriel (facultatif) :
 surveyFormOtherTextCheckbox = Autre, à préciser
-surveyFormOtherTextTooltip = Vous devez cliquer sur la case à cocher çi dessus pour activer ce champ de saisie.
+surveyFormOtherTextTooltip = Vous devez cliquer sur la case à cocher ci-dessus pour activer ce champ de saisie.
 surveyFormSuccess = Vos réponses au formulaire d'enquête ont bien été enregistrées.
 surveyFormFail = Vos réponses au formulaire n'ont pas pu être enregistrées, mais vous pouvez tout de même utiliser AgroMetInfo.
 toggleRightPanel = Afficher / masquer le panneau de droite
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
index 1aec97d..9ac3ddc 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/SurveyFormResource.java
@@ -174,7 +174,7 @@ public class SurveyFormResource implements SurveyFormService {
         }
 
         // once everything is inserted, send an email to support
-        this.mailService.sendLoginFilled(data);
+        this.mailService.sendSurveyFilled(data);
         return "0";
     }
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
index a472586..e85fe40 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailService.java
@@ -19,5 +19,5 @@ public interface MailService {
      * Send mail when user fill login form, with questions and responses.
      * @param data
      */
-    void sendLoginFilled(SurveyResponseDTO data);
+    void sendSurveyFilled(SurveyResponseDTO data);
 }
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
index 4f614ef..a6c2c42 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/service/MailServiceImpl.java
@@ -329,7 +329,7 @@ public class MailServiceImpl implements MailService {
      * Send an e-mail when user filled survey form.
      * @param data data of survey
      */
-    public void sendLoginFilled(final SurveyResponseDTO data) {
+    public void sendSurveyFilled(final SurveyResponseDTO data) {
         final Mail mail = createContentFromData(data);
         mail.setSubject("Un utilisateur a rempli le formulaire d'enquête");
         mail.setFromAddress(configuration.get(ConfigurationKey.APP_EMAIL));
diff --git a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
index 58c0c84..6c3a7f4 100644
--- a/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
+++ b/www-server/src/test/java/fr/agrometinfo/www/server/rs/SurveyFormResourceTest.java
@@ -68,7 +68,7 @@ public class SurveyFormResourceTest extends JerseyTest {
         @Getter
         private Mail mail = null;
         @Override
-        public void sendLoginFilled(SurveyResponseDTO data) {
+        public void sendSurveyFilled(SurveyResponseDTO data) {
             this.mail = MailServiceImpl.createContentFromData(data);
         }
         
-- 
GitLab


From 49daa6ec34d8d127b89511c18bf71ce37aadac87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?DECOME=20J=C3=A9r=C3=A9mie?= <jeremie.decome@inrae.fr>
Date: Wed, 12 Jun 2024 10:32:49 +0200
Subject: [PATCH 30/30] corrections checkstyle

---
 .../java/fr/agrometinfo/www/client/i18n/AppConstants.java     | 4 ++--
 .../main/java/fr/agrometinfo/www/client/view/LayoutView.java  | 4 ----
 .../java/fr/agrometinfo/www/server/rs/ApplicationConfig.java  | 3 ++-
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
index 8d5c483..6a0dee6 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/i18n/AppConstants.java
@@ -334,7 +334,7 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     String welcomeBody();
 
     /**
-     * @return
+     * @return translation
      */
     @DefaultStringValue("Welcome to the new version of AgroMetInfo !<br/>"
             + "To help us improve the application and make it as responsive as possible to your needs, "
@@ -358,7 +358,7 @@ public interface AppConstants extends com.google.gwt.i18n.client.ConstantsWithLo
     String welcomeTitle();
 
     /**
-     * @return
+     * @return translation
      */
     @DefaultStringValue("Other")
     String surveyFormOtherTextCheckbox();
diff --git a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
index 4032e30..7380db7 100644
--- a/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
+++ b/www-client/src/main/java/fr/agrometinfo/www/client/view/LayoutView.java
@@ -6,7 +6,6 @@ import static org.jboss.elemento.Elements.img;
 import static org.jboss.elemento.Elements.li;
 
 import java.util.List;
-import java.util.StringJoiner;
 
 import org.dominokit.domino.ui.grid.flex.FlexItem;
 import org.dominokit.domino.ui.icons.Icons;
@@ -16,9 +15,6 @@ import org.dominokit.domino.ui.loaders.Loader;
 import org.dominokit.domino.ui.loaders.LoaderEffect;
 import org.dominokit.domino.ui.menu.Menu;
 import org.dominokit.domino.ui.menu.MenuItem;
-import org.dominokit.domino.ui.notifications.Notification;
-import org.dominokit.domino.ui.popover.PopupPosition;
-import org.dominokit.domino.ui.popover.Tooltip;
 import org.dominokit.domino.ui.style.Styles;
 import org.dominokit.domino.ui.utils.DominoElement;
 import org.dominokit.domino.ui.utils.DominoUIConfig;
diff --git a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
index de1362f..b6ff6d4 100644
--- a/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
+++ b/www-server/src/main/java/fr/agrometinfo/www/server/rs/ApplicationConfig.java
@@ -43,7 +43,8 @@ public class ApplicationConfig extends Application {
                 // Jackson configuration
                 JacksonConfig.class,
                 // JAX-RS resources
-                ApplicationResource.class, GeometryResource.class, IndicatorResource.class, SurveyFormResource.class, UserResource.class,
+                ApplicationResource.class, GeometryResource.class, IndicatorResource.class,
+                SurveyFormResource.class, UserResource.class,
                 // POJO
                 // Dependencies of resources
                 LogFilter.class
-- 
GitLab