diff --git a/src/main/java/com/bernard/misael/model/Question.java b/src/main/java/com/bernard/misael/model/Question.java index c0717d9..948cb56 100644 --- a/src/main/java/com/bernard/misael/model/Question.java +++ b/src/main/java/com/bernard/misael/model/Question.java @@ -2,9 +2,8 @@ package com.bernard.misael.model; import com.bernard.misael.questions.QTypes; import com.bernard.misael.questions.QuestionType; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.bernard.misael.service.JsonNodeConverter; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.*; import lombok.Getter; @@ -36,19 +35,13 @@ public class Question { private Quizz quizz; @Setter - private String value; - + @Convert(converter = JsonNodeConverter.class) + private JsonNode value; + transient QuestionType qtype = null; public QuestionType getQT() { if(qtype==null){ - ObjectMapper om = new ObjectMapper(); - JsonNode jsNode; - try { - jsNode = om.readTree(value); - qtype = type.construct(jsNode); - } catch (JsonProcessingException e) { - throw new IllegalStateException(e); - } + qtype = type.construct(value); } return qtype; } diff --git a/src/main/java/com/bernard/misael/repository/AnswerRepository.java b/src/main/java/com/bernard/misael/repository/AnswerRepository.java index a79db2e..97b1247 100644 --- a/src/main/java/com/bernard/misael/repository/AnswerRepository.java +++ b/src/main/java/com/bernard/misael/repository/AnswerRepository.java @@ -1,5 +1,7 @@ package com.bernard.misael.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import com.bernard.misael.model.Answer; @@ -9,5 +11,6 @@ import com.bernard.misael.model.QuizzForm; public interface AnswerRepository extends JpaRepository { public Answer findByFormAndQuestion(QuizzForm qf, Question q); + public List findByFormAndQuestionIn(QuizzForm qf, List qz); } diff --git a/src/main/java/com/bernard/misael/repository/QuestionRepository.java b/src/main/java/com/bernard/misael/repository/QuestionRepository.java index cdc7a2d..81f44e1 100644 --- a/src/main/java/com/bernard/misael/repository/QuestionRepository.java +++ b/src/main/java/com/bernard/misael/repository/QuestionRepository.java @@ -1,5 +1,6 @@ package com.bernard.misael.repository; +import java.util.List; import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; @@ -12,5 +13,6 @@ public interface QuestionRepository extends JpaRepository { public Question findByQuizzAndIndex(Quizz quizz, int index); public Set findByQuizz(Quizz quizz); + public List findByQuizzOrderByIndexAsc(Quizz quizz); } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/repository/QuizzFormRepository.java b/src/main/java/com/bernard/misael/repository/QuizzFormRepository.java index f19e851..6606d38 100644 --- a/src/main/java/com/bernard/misael/repository/QuizzFormRepository.java +++ b/src/main/java/com/bernard/misael/repository/QuizzFormRepository.java @@ -1,5 +1,7 @@ package com.bernard.misael.repository; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; import com.bernard.misael.model.Quizz; @@ -8,6 +10,10 @@ import com.bernard.misael.model.User; public interface QuizzFormRepository extends JpaRepository { + public List findByQuizz(Quizz q); + public QuizzForm findByUserAndQuizz(User u, Quizz q); + public List findByUserAndDoneTrue(User u); + public List findByUserAndDoneFalse(User u); } diff --git a/src/main/java/com/bernard/misael/service/QuizzManager.java b/src/main/java/com/bernard/misael/service/QuizzManager.java index 033ad48..2e87bbb 100644 --- a/src/main/java/com/bernard/misael/service/QuizzManager.java +++ b/src/main/java/com/bernard/misael/service/QuizzManager.java @@ -1,8 +1,10 @@ package com.bernard.misael.service; import java.util.List; +import java.util.Optional; import com.bernard.misael.model.Quizz; +import com.bernard.misael.model.QuizzForm; import com.bernard.misael.model.User; import com.bernard.misael.questions.QTypes; import com.fasterxml.jackson.databind.JsonNode; @@ -19,6 +21,8 @@ public interface QuizzManager { public List answerableQuizz(User user); public boolean canEditQuizz(User user, long quizzId); + public Optional canViewQuizzForm(User user, long quizzFormId); + public Optional canViewQuizzFormsOfQuizz(User user, long quizzId); public JsonNode getQuizzInfo(User user, long quizzId); public JsonNode setQuizzName(User user, long quizzId, String newName); @@ -28,4 +32,7 @@ public interface QuizzManager { public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value); public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type); + public JsonNode getQuizzFormData(User user, long quizzFormId); + public JsonNode getQuizzFormAdvancments(User user, long quizzId); + } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/service/QuizzManagerImpl.java b/src/main/java/com/bernard/misael/service/QuizzManagerImpl.java index 71aeb1b..db3eeaf 100644 --- a/src/main/java/com/bernard/misael/service/QuizzManagerImpl.java +++ b/src/main/java/com/bernard/misael/service/QuizzManagerImpl.java @@ -14,16 +14,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.bernard.misael.model.Answer; +import com.bernard.misael.model.Privilege; import com.bernard.misael.model.Question; import com.bernard.misael.model.Quizz; import com.bernard.misael.model.QuizzForm; import com.bernard.misael.model.User; import com.bernard.misael.questions.QTypes; import com.bernard.misael.questions.QuestionType.AnswerResult; -import com.bernard.misael.repository.AnswerRepository; -import com.bernard.misael.repository.QuestionRepository; -import com.bernard.misael.repository.QuizzFormRepository; -import com.bernard.misael.repository.QuizzRepository; +import com.bernard.misael.repository.*; import com.bernard.misael.service.exception.MalformedAnswerException; import com.bernard.misael.service.exception.MalformedClientAnswerException; import com.bernard.misael.service.exception.QuestionTypeException; @@ -40,6 +38,9 @@ import jakarta.persistence.EntityNotFoundException; @Service public class QuizzManagerImpl implements QuizzManager { + @Autowired + UserRepository uRepository; + @Autowired QuizzFormRepository qfRepository; @@ -52,6 +53,11 @@ public class QuizzManagerImpl implements QuizzManager { @Autowired AnswerRepository answerRepository; + @Autowired + UserService uService; + + + @Override public JsonNode answer(User user, long quizzId, JsonNode data) { if(!data.has("index") || !data.get("index").isInt()) @@ -268,12 +274,7 @@ public class QuizzManagerImpl implements QuizzManager { ObjectNode nn = JsonNodeFactory.instance.objectNode(); nn.set("id",JsonNodeFactory.instance.numberNode(q.getId())); nn.set("type", JsonNodeFactory.instance.textNode(q.getType().name())); - ObjectMapper om = new ObjectMapper(); - try { - nn.set("value",om.readTree(q.getValue())); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Value stored for question "+q.getId()+" is illegal (non-json)",e); - } + nn.set("value",q.getValue()); n.set(q.getIndex(),nn); } @@ -312,7 +313,7 @@ public class QuizzManagerImpl implements QuizzManager { Question q = new Question(); q.setType(DEFAULT_QTYPE); JsonNode n = DEFAULT_QTYPE.getDefaultQuestion(); - q.setValue(n.toString()); + q.setValue(n); q.setQuizz(quizz); q.setIndex(quizz.getQuestionCount()); quizz.setQuestionCount(quizz.getQuestionCount()+1); @@ -408,7 +409,7 @@ public class QuizzManagerImpl implements QuizzManager { if(!q.getType().validate(value)) return errorNode("Invalid question value"); - q.setValue(value.toString()); + q.setValue(value); ObjectNode out = JsonNodeFactory.instance.objectNode(); out.set("success", JsonNodeFactory.instance.booleanNode(true)); @@ -438,14 +439,9 @@ public class QuizzManagerImpl implements QuizzManager { // Then we need to change q.setType(type); n = type.getDefaultQuestion(); - q.setValue(n.toString()); + q.setValue(n); } else { - ObjectMapper om = new ObjectMapper(); - try { - n = om.readTree(q.getValue()); - } catch (JsonProcessingException e) { - throw new IllegalStateException("Value stored for question "+questionId+" is illegal (non-json)",e); - } + n =q.getValue(); } ObjectNode out = JsonNodeFactory.instance.objectNode(); @@ -454,4 +450,77 @@ public class QuizzManagerImpl implements QuizzManager { return out; } + @Override + public Optional canViewQuizzForm(User user, long quizzFormId) { + Optional oqf = qfRepository.findById(quizzFormId); + if(oqf.isEmpty()) return oqf; + QuizzForm qf = oqf.get(); + if(!qf.isDone()) return Optional.empty(); + if(qf.getUser().equals(user)) return oqf; + if(uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)) return oqf; + return Optional.empty(); + } + + @Override + public Optional canViewQuizzFormsOfQuizz(User user, long quizzId) { + Optional oq = qRepository.findById(quizzId); + if(oq.isEmpty()) return oq; + Quizz q = oq.get(); + if(q.getOwner().equals(user)) return oq; + if(uService.hasPrivilege(user, Privilege.VIEW_ALL_FORMS)) return oq; + return Optional.empty(); + } + + @Override + public JsonNode getQuizzFormData(User user, long quizzFormId) { + Optional oqf = canViewQuizzForm(user, quizzFormId); + if(oqf.isEmpty()) + return errorNode("Could not access the quizzform"); //TODO more precise error node + QuizzForm form = oqf.get(); + List questions = questionRepository.findByQuizzOrderByIndexAsc(form.getQuizz()); + List answers = answerRepository.findByFormAndQuestionIn(form, questions); + assert questions.size() == answers.size(); + ArrayNode answersNode = JsonNodeFactory.instance.arrayNode(); + for(int i=0;i oq = canViewQuizzFormsOfQuizz(user, quizzId); + if(oq.isEmpty()) + return errorNode("Could not access the forms for this quizz"); //TODO more precise error node + Quizz quizz = oq.get(); + List quizzForms = qfRepository.findByQuizz(quizz); + quizzForms.sort((qfa,qfb) -> qfa.getUser().getName().compareTo(qfb.getUser().getName())); + + ArrayNode formsNode = JsonNodeFactory.instance.arrayNode(); + for(QuizzForm qf : quizzForms) { + ObjectNode anode = JsonNodeFactory.instance.objectNode(); + anode.set("qfid", JsonNodeFactory.instance.numberNode(qf.getId())); + anode.set("done", JsonNodeFactory.instance.booleanNode(qf.isDone())); + anode.set("position", JsonNodeFactory.instance.numberNode(qf.getCurrentQuestion())); + anode.set("step", JsonNodeFactory.instance.numberNode(qf.getAnswerStep())); + anode.set("username", JsonNodeFactory.instance.textNode(qf.getUser().getName())); + formsNode.add(anode); + } + ObjectNode out = JsonNodeFactory.instance.objectNode(); + out.set("success", JsonNodeFactory.instance.booleanNode(true)); + out.set("data", formsNode); + return out; + } + + + } diff --git a/src/main/java/com/bernard/misael/service/UserService.java b/src/main/java/com/bernard/misael/service/UserService.java index 4969a7a..ad59bea 100644 --- a/src/main/java/com/bernard/misael/service/UserService.java +++ b/src/main/java/com/bernard/misael/service/UserService.java @@ -2,6 +2,7 @@ package com.bernard.misael.service; import java.util.List; +import com.bernard.misael.model.Privilege; import com.bernard.misael.model.User; import com.bernard.misael.service.dto.UserDto; @@ -13,4 +14,6 @@ public interface UserService { User findUserByName(String name); List findAllUsers(); + + boolean hasPrivilege(User u, Privilege p); } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/service/UserServiceImpl.java b/src/main/java/com/bernard/misael/service/UserServiceImpl.java index b137e0f..f79040b 100644 --- a/src/main/java/com/bernard/misael/service/UserServiceImpl.java +++ b/src/main/java/com/bernard/misael/service/UserServiceImpl.java @@ -5,6 +5,7 @@ import java.util.stream.Collectors; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import com.bernard.misael.model.Privilege; import com.bernard.misael.model.User; import com.bernard.misael.repository.UserRepository; import com.bernard.misael.service.dto.UserDto; @@ -54,4 +55,10 @@ public class UserServiceImpl implements UserService { userDto.setName(user.getName()); return userDto; } + + @Override + public boolean hasPrivilege(User u, Privilege p) { + //TODO faire une query sql propre avec ça + return u.getRoles().stream().anyMatch(r -> r.getPrivileges().contains(p)); + } } \ No newline at end of file diff --git a/src/main/java/com/bernard/misael/web/AuthController.java b/src/main/java/com/bernard/misael/web/AuthController.java index eb29b98..1887986 100644 --- a/src/main/java/com/bernard/misael/web/AuthController.java +++ b/src/main/java/com/bernard/misael/web/AuthController.java @@ -43,7 +43,6 @@ public class AuthController { @GetMapping("/") public String index(Model model) { - return "index"; } diff --git a/src/main/java/com/bernard/misael/web/QuestionsController.java b/src/main/java/com/bernard/misael/web/QuestionsController.java index 46c9b23..61791cb 100644 --- a/src/main/java/com/bernard/misael/web/QuestionsController.java +++ b/src/main/java/com/bernard/misael/web/QuestionsController.java @@ -10,16 +10,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; -import com.bernard.misael.model.Privilege; import com.bernard.misael.model.Quizz; +import com.bernard.misael.model.QuizzForm; import com.bernard.misael.model.User; import com.bernard.misael.questions.QTypes; +import com.bernard.misael.repository.QuizzFormRepository; import com.bernard.misael.repository.QuizzRepository; import com.bernard.misael.repository.UserRepository; import com.bernard.misael.service.QuizzManager; @@ -47,10 +49,13 @@ public class QuestionsController { @Autowired QuizzRepository qrepo; - @GetMapping("/quizz") + @Autowired + QuizzFormRepository qfrepo; + /* - * List all quizz - */ + * List all quizz + */ + @GetMapping("/quizz") public String getQuizz(Model model, Principal p) { User u = null; if (p!=null) @@ -63,14 +68,88 @@ public class QuestionsController { return "quizz.html"; } - @GetMapping("/forms") /* - * List all forms started or finished by the user - */ - public String getForms() { + * List all forms started or finished by the user + */ + @GetMapping("/forms") + public String getForms(Model model, Principal p) { + User u = null; + if (p!=null) + u = ur.findByName(p.getName()); + if(u!=null) { + model.addAttribute("finishedForms",qfrepo.findByUserAndDoneTrue(u)); + model.addAttribute("openForms",qfrepo.findByUserAndDoneFalse(u)); + } + return "forms.html"; } + /* + * Show one (completed) form of one user + */ + @GetMapping("/showform/{id}") + public Object showForm(@PathVariable("id") long id, Model m, Principal p) { + User u = null; + if (p!=null) + u = ur.findByName(p.getName()); + if(u==null) + return "redirect:/login?restricted"; + Optional oqf = qm.canViewQuizzForm(u, id); + if (oqf.isEmpty()) + //TODO Faire un mesasge d'erreur dépendant des circonstances (unatuhorized, not found, not complete ...) + return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); + + m.addAttribute("formId", id); + return "showform.html"; + } + + /* + * Show one (completed) form of one user + */ + @GetMapping("/showformsadvancements/{id}") + public Object showFormsAdvancements(@PathVariable("id") long id, Model m, Principal p) { + User u = null; + if (p!=null) + u = ur.findByName(p.getName()); + if(u==null) + return "redirect:/login?restricted"; + Optional oq = qm.canViewQuizzFormsOfQuizz(u, id); + if (oq.isEmpty()) + //TODO Faire un mesasge d'erreur dépendant des circonstances (unatuhorized, not found, not complete ...) + return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED); + + m.addAttribute("quizzId", id); + return "showformadvancements.html"; + } + + /* + * API get the form + */ + @PostMapping("/getformdata/{id}") + public Object showFormApi(@PathVariable("id") long id, Principal p) { + User u = null; + if (p!=null) + u = ur.findByName(p.getName()); + if(u==null) + return "redirect:/login?restricted"; + JsonNode out = qm.getQuizzFormData(u, id); + return new ResponseEntity<>(out, HttpStatus.OK); + } + + /* + * API get the form + */ + @PostMapping("/getformadvancements/{id}") + public Object showFormAdvancements(@PathVariable("id") long id, Principal p) { + User u = null; + if (p!=null) + u = ur.findByName(p.getName()); + if(u==null) + return "redirect:/login?restricted"; + JsonNode out = qm.getQuizzFormAdvancments(u, id); + return new ResponseEntity<>(out, HttpStatus.OK); + } + @GetMapping("/form/{q}") public String formpage(@PathVariable("q") long quizzId, Principal p, Model m) { if (p==null) diff --git a/src/main/resources/static/css/showform.css b/src/main/resources/static/css/showform.css new file mode 100644 index 0000000..4a0a460 --- /dev/null +++ b/src/main/resources/static/css/showform.css @@ -0,0 +1,28 @@ +main h1 input, main h1 span{ + font-size: 30pt; + font-weight: bold; + color: teal; +} + +ol#questions-list { + width: 90%; +} +ol#questions-list li { + width: 90%; + background-color: lemonchiffon; + list-style-type: none; +} + + + +/* DCC */ +ol#questions-list li.dcc-box { + background-color: gainsboro; + color: black; + font-size: 12pt; +} +li.dcc-box h5 { + font-size: 12pt; + font-weight: bold; + width: 100%; +} \ No newline at end of file diff --git a/src/main/resources/static/css/showformadvancements.css b/src/main/resources/static/css/showformadvancements.css new file mode 100644 index 0000000..8be45de --- /dev/null +++ b/src/main/resources/static/css/showformadvancements.css @@ -0,0 +1,33 @@ +main h1 input, main h1 span{ + font-size: 30pt; + font-weight: bold; + color: teal; +} + +table#forms-table { + width: 90%; +} +table#forms-table tr { + width: 90%; + background-color: lemonchiffon; + list-style-type: none; +} + + + +/* DCC */ +table#forms-table tbody tr.done-true { + background-color: rgb(161, 161, 161); + color: black; + font-size: 12pt; + font-style: italic; +} +table#forms-table tbody tr { + background-color: gainsboro; + color: black; + font-size: 12pt; +} +table#forms-table thead tr { + font-size: 14pt; + font-weight: bold; +} \ No newline at end of file diff --git a/src/main/resources/templates/forms.html b/src/main/resources/templates/forms.html index 8f8f5cd..8249ce1 100644 --- a/src/main/resources/templates/forms.html +++ b/src/main/resources/templates/forms.html @@ -7,11 +7,15 @@
- +

Formulaires terminés

+

Formulaires non terminés

+
    +
  • Aucun formulaire ici !
  • +
  • Quizz
diff --git a/src/main/resources/templates/showform.html b/src/main/resources/templates/showform.html new file mode 100644 index 0000000..f77dba3 --- /dev/null +++ b/src/main/resources/templates/showform.html @@ -0,0 +1,83 @@ + + + +
+ + + + +
+ + +
+ +
    +
+ + + + + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/showformadvancements.html b/src/main/resources/templates/showformadvancements.html new file mode 100644 index 0000000..1eed223 --- /dev/null +++ b/src/main/resources/templates/showformadvancements.html @@ -0,0 +1,81 @@ + + + +
+ + + + +
+ + +
+ + + + + + + + + + + + + +
UserPositionStep
+ + + + + +
+ + \ No newline at end of file