Ajout d'une page d'édition des quizz
This commit is contained in:
parent
8ae7457016
commit
c62f070e39
@ -41,6 +41,7 @@ dependencies {
|
|||||||
implementation Spring.session.jdbc
|
implementation Spring.session.jdbc
|
||||||
implementation 'jakarta.validation:jakarta.validation-api:_'
|
implementation 'jakarta.validation:jakarta.validation-api:_'
|
||||||
runtimeOnly 'org.webjars:jquery:_'
|
runtimeOnly 'org.webjars:jquery:_'
|
||||||
|
runtimeOnly 'org.webjars:lodash:_'
|
||||||
developmentOnly Spring.boot.devTools
|
developmentOnly Spring.boot.devTools
|
||||||
runtimeOnly 'org.postgresql:postgresql:_'
|
runtimeOnly 'org.postgresql:postgresql:_'
|
||||||
testImplementation Spring.boot.test
|
testImplementation Spring.boot.test
|
||||||
|
|||||||
@ -7,15 +7,12 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="questions")
|
@Table(name="questions")
|
||||||
public class Question {
|
public class Question {
|
||||||
@ -26,15 +23,19 @@ public class Question {
|
|||||||
private long id;
|
private long id;
|
||||||
|
|
||||||
@Column(nullable=false)
|
@Column(nullable=false)
|
||||||
|
@Setter
|
||||||
QTypes type;
|
QTypes type;
|
||||||
|
|
||||||
@Column(nullable=false)
|
@Column(nullable=false)
|
||||||
|
@Setter
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "quizz",nullable = false)
|
@JoinColumn(name = "quizz",nullable = false)
|
||||||
|
@Setter
|
||||||
private Quizz quizz;
|
private Quizz quizz;
|
||||||
|
|
||||||
|
@Setter
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
transient QuestionType qtype = null;
|
transient QuestionType qtype = null;
|
||||||
@ -44,7 +45,7 @@ public class Question {
|
|||||||
JsonNode jsNode;
|
JsonNode jsNode;
|
||||||
try {
|
try {
|
||||||
jsNode = om.readTree(value);
|
jsNode = om.readTree(value);
|
||||||
qtype = type.getConstructor().apply(jsNode);
|
qtype = type.construct(jsNode);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,20 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.Spliterator;
|
||||||
|
import java.util.Spliterators;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.bernard.misael.service.exception.MalformedAnswerException;
|
import com.bernard.misael.service.exception.MalformedAnswerException;
|
||||||
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
||||||
import com.bernard.misael.service.exception.QuestionTypeException;
|
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
@ -82,4 +90,28 @@ public class DccQuestion implements QuestionType{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Logger LOG = LoggerFactory.getLogger(DccQuestion.class);
|
||||||
|
|
||||||
|
public static boolean validate(JsonNode data) {
|
||||||
|
LOG.info(data.toPrettyString());
|
||||||
|
LOG.info(Stream.of(data.get("wrong")).map(p -> p.isTextual()+"->"+p).collect(Collectors.joining(";")));
|
||||||
|
return data.isObject() &&
|
||||||
|
data.has("text") && data.get("text").isTextual() &&
|
||||||
|
data.has("good") && data.get("good").isTextual() &&
|
||||||
|
data.has("wrong") && data.get("wrong").isArray() &&
|
||||||
|
data.get("wrong").size() >= 3 &&
|
||||||
|
StreamSupport.stream(Spliterators.spliterator(data.get("wrong").elements(),data.get("wrong").size(),Spliterator.SIZED),false).allMatch(p ->
|
||||||
|
p.isTextual()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final JsonNode getDefaultDccQuestion() {
|
||||||
|
ObjectNode q = JsonNodeFactory.instance.objectNode();
|
||||||
|
q.set("text",JsonNodeFactory.instance.textNode(""));
|
||||||
|
q.set("good",JsonNodeFactory.instance.textNode(""));
|
||||||
|
ArrayNode wrong = JsonNodeFactory.instance.arrayNode(3);
|
||||||
|
for(int i=0;i<3;i++)wrong.add(JsonNodeFactory.instance.textNode(""));
|
||||||
|
q.set("wrong",wrong);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,15 +7,18 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum QTypes {
|
public enum QTypes {
|
||||||
|
|
||||||
DCC(1,DccQuestion.class,DccQuestion::new);
|
DCC(1,DccQuestion.class,DccQuestion::new,DccQuestion::validate,DccQuestion.getDefaultDccQuestion());
|
||||||
|
|
||||||
private int id;
|
private int id;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private Class<? extends QuestionType> type;
|
private Class<? extends QuestionType> type;
|
||||||
private Function<JsonNode,QuestionType> constructor;
|
private Function<JsonNode,QuestionType> constructor;
|
||||||
|
private Function<JsonNode,Boolean> validator;
|
||||||
|
@Getter
|
||||||
|
private JsonNode defaultQuestion;
|
||||||
|
|
||||||
public static QTypes findById(final int id) {
|
public static QTypes findById(final int id) {
|
||||||
for(QTypes value : QTypes.values())
|
for(QTypes value : QTypes.values())
|
||||||
@ -24,4 +27,12 @@ public enum QTypes {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public QuestionType construct(JsonNode data){
|
||||||
|
return this.constructor.apply(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate(JsonNode data){
|
||||||
|
return this.validator.apply(data);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.bernard.misael.repository;
|
package com.bernard.misael.repository;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import com.bernard.misael.model.Question;
|
import com.bernard.misael.model.Question;
|
||||||
@ -9,5 +11,6 @@ import com.bernard.misael.model.Quizz;
|
|||||||
public interface QuestionRepository extends JpaRepository<Question,Long> {
|
public interface QuestionRepository extends JpaRepository<Question,Long> {
|
||||||
|
|
||||||
public Question findByQuizzAndIndex(Quizz quizz, int index);
|
public Question findByQuizzAndIndex(Quizz quizz, int index);
|
||||||
|
public Set<Question> findByQuizz(Quizz quizz);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.bernard.misael.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
public interface QuizzManager {
|
public interface QuizzManager {
|
||||||
@ -13,5 +14,15 @@ public interface QuizzManager {
|
|||||||
|
|
||||||
public boolean canAccessQuizz(User user, long quizzId);
|
public boolean canAccessQuizz(User user, long quizzId);
|
||||||
public List<Quizz> accessibleQuizz(User user);
|
public List<Quizz> accessibleQuizz(User user);
|
||||||
|
|
||||||
|
public boolean canEditQuizz(User user, long quizzId);
|
||||||
|
|
||||||
|
public JsonNode getQuizzInfo(User user, long quizzId);
|
||||||
|
public JsonNode setQuizzName(User user, long quizzId, String newName);
|
||||||
|
public JsonNode addQuestion(User user, long quizzId);
|
||||||
|
public JsonNode removeQuestion(User user, long quizzId, long questionId);
|
||||||
|
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder);
|
||||||
|
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value);
|
||||||
|
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -1,11 +1,15 @@
|
|||||||
package com.bernard.misael.service;
|
package com.bernard.misael.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@ -14,6 +18,7 @@ import com.bernard.misael.model.Question;
|
|||||||
import com.bernard.misael.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.model.QuizzForm;
|
import com.bernard.misael.model.QuizzForm;
|
||||||
import com.bernard.misael.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.bernard.misael.questions.QuestionType.AnswerResult;
|
import com.bernard.misael.questions.QuestionType.AnswerResult;
|
||||||
import com.bernard.misael.repository.AnswerRepository;
|
import com.bernard.misael.repository.AnswerRepository;
|
||||||
import com.bernard.misael.repository.QuestionRepository;
|
import com.bernard.misael.repository.QuestionRepository;
|
||||||
@ -22,7 +27,11 @@ import com.bernard.misael.repository.QuizzRepository;
|
|||||||
import com.bernard.misael.service.exception.MalformedAnswerException;
|
import com.bernard.misael.service.exception.MalformedAnswerException;
|
||||||
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
import com.bernard.misael.service.exception.MalformedClientAnswerException;
|
||||||
import com.bernard.misael.service.exception.QuestionTypeException;
|
import com.bernard.misael.service.exception.QuestionTypeException;
|
||||||
|
import com.bernard.misael.web.QuestionsController;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
|
||||||
@ -198,4 +207,227 @@ public class QuizzManagerImpl implements QuizzManager {
|
|||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
|
||||||
|
@Override
|
||||||
|
public boolean canEditQuizz(User user, long quizzId) {
|
||||||
|
try {
|
||||||
|
Quizz quizz = qRepository.getReferenceById(quizzId);
|
||||||
|
logger.info("Quizz owner is "+quizz.getOwner().getName());
|
||||||
|
return quizz.getOwner().equals(user);
|
||||||
|
} catch (EntityNotFoundException e) {
|
||||||
|
logger.info("Could not find quizz of id "+quizzId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<JsonNode> checkEditQuizz(User user, long quizzId) {
|
||||||
|
if(user == null)
|
||||||
|
return Optional.of(errorNode("You need to be logged in to edit the quizz"));
|
||||||
|
if(!canEditQuizz(user, quizzId))
|
||||||
|
return Optional.of(errorNode("User has no right to edit quizz"));
|
||||||
|
Optional<Quizz> oquizz = qRepository.findById(quizzId);
|
||||||
|
if(!oquizz.isPresent())
|
||||||
|
return Optional.of(errorNode("Could not find quizz with id "+quizzId));
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode getQuizzInfo(User user, long quizzId) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
ArrayNode n = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
|
||||||
|
for(int i = 0;i<quizz.getQuestionCount();i++)n.add(JsonNodeFactory.instance.nullNode());
|
||||||
|
for(Question q : quizz.getQuestions()) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
n.set(q.getIndex(),nn);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
out.set("questions", n);
|
||||||
|
out.set("name", JsonNodeFactory.instance.textNode(quizz.getName()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode setQuizzName(User user, long quizzId, String newName) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
if(newName.isBlank() | newName.length()>255)
|
||||||
|
return errorNode("Le nom est invalide");
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
quizz.setName(newName);
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final QTypes DEFAULT_QTYPE = QTypes.DCC;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode addQuestion(User user, long quizzId) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
Question q = new Question();
|
||||||
|
q.setType(DEFAULT_QTYPE);
|
||||||
|
JsonNode n = DEFAULT_QTYPE.getDefaultQuestion();
|
||||||
|
q.setValue(n.toString());
|
||||||
|
q.setQuizz(quizz);
|
||||||
|
q.setIndex(quizz.getQuestionCount());
|
||||||
|
quizz.setQuestionCount(quizz.getQuestionCount()+1);
|
||||||
|
questionRepository.save(q);
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
out.set("type", JsonNodeFactory.instance.textNode(DEFAULT_QTYPE.name()));
|
||||||
|
out.set("value", n);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode removeQuestion(User user, long quizzId, long questionId) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
final Question q;
|
||||||
|
try {
|
||||||
|
q = questionRepository.getReferenceById(questionId);
|
||||||
|
} catch (EntityNotFoundException e){
|
||||||
|
return errorNode("Could not find question with id "+questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
questionRepository.findByQuizz(quizz).forEach(qq -> {
|
||||||
|
if(qq.getIndex()>q.getIndex())
|
||||||
|
qq.setIndex(qq.getIndex()-1);
|
||||||
|
});
|
||||||
|
questionRepository.delete(q);
|
||||||
|
quizz.setQuestionCount(quizz.getQuestionCount()-1);
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
// We need that the base set of neworder is the list of ids
|
||||||
|
// 1) The length is right
|
||||||
|
if(quizz.getQuestionCount() != newOrder.size())
|
||||||
|
return errorNode("You must put every question in order");
|
||||||
|
// 2) There is no duplicates
|
||||||
|
if(new HashSet<Long>(newOrder).size() != newOrder.size())
|
||||||
|
return errorNode("You shouldn't put duplicates in the new order");
|
||||||
|
// 3) All ids correspond to an actual question of the right quizz
|
||||||
|
List<Question> questions = new ArrayList<>(newOrder.size());
|
||||||
|
for(int i = 0;i<newOrder.size();i++){
|
||||||
|
try {
|
||||||
|
Question q = questionRepository.getReferenceById(newOrder.get(i));
|
||||||
|
if(!q.getQuizz().equals(quizz))
|
||||||
|
return errorNode("The question id "+newOrder.get(i)+" is associated to another quizz");
|
||||||
|
questions.add(q);
|
||||||
|
} catch (EntityNotFoundException e){
|
||||||
|
return errorNode("Could not find question with id "+newOrder.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All Checks passed
|
||||||
|
for(int i=0;i<questions.size();i++){
|
||||||
|
questions.get(i).setIndex(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
final Question q;
|
||||||
|
try {
|
||||||
|
q = questionRepository.getReferenceById(questionId);
|
||||||
|
} catch (EntityNotFoundException e){
|
||||||
|
return errorNode("Could not find question with id "+questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!q.getQuizz().equals(quizz))
|
||||||
|
return errorNode("Question is not associated with the right quizzId");
|
||||||
|
|
||||||
|
if(!q.getType().validate(value))
|
||||||
|
return errorNode("Invalid question value");
|
||||||
|
q.setValue(value.toString());
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type) {
|
||||||
|
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
|
||||||
|
if(authCheck.isPresent()) return authCheck.get();
|
||||||
|
|
||||||
|
Quizz quizz = qRepository.findById(quizzId).get();
|
||||||
|
|
||||||
|
final Question q;
|
||||||
|
try {
|
||||||
|
q = questionRepository.getReferenceById(questionId);
|
||||||
|
} catch (EntityNotFoundException e){
|
||||||
|
return errorNode("Could not find question with id "+questionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!q.getQuizz().equals(quizz))
|
||||||
|
return errorNode("Question is not associated with the right quizzId");
|
||||||
|
|
||||||
|
// If type is the same, we don't change (and dont reset the value)
|
||||||
|
JsonNode n;
|
||||||
|
if(!type.equals(q.getType())) {
|
||||||
|
// Then we need to change
|
||||||
|
q.setType(type);
|
||||||
|
n = type.getDefaultQuestion();
|
||||||
|
q.setValue(n.toString());
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectNode out = JsonNodeFactory.instance.objectNode();
|
||||||
|
out.set("success", JsonNodeFactory.instance.booleanNode(true));
|
||||||
|
out.set("new", n);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
package com.bernard.misael.web;
|
package com.bernard.misael.web;
|
||||||
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
@ -11,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
|
|
||||||
import com.bernard.misael.model.Quizz;
|
import com.bernard.misael.model.Quizz;
|
||||||
import com.bernard.misael.model.User;
|
import com.bernard.misael.model.User;
|
||||||
|
import com.bernard.misael.questions.QTypes;
|
||||||
import com.bernard.misael.repository.QuizzRepository;
|
import com.bernard.misael.repository.QuizzRepository;
|
||||||
import com.bernard.misael.repository.UserRepository;
|
import com.bernard.misael.repository.UserRepository;
|
||||||
import com.bernard.misael.service.QuizzManager;
|
import com.bernard.misael.service.QuizzManager;
|
||||||
@ -24,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestBody;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/questions")
|
@RequestMapping("/questions")
|
||||||
public class QuestionsController {
|
public class QuestionsController {
|
||||||
@ -89,7 +97,125 @@ public class QuestionsController {
|
|||||||
JsonNode out = qm.answer(u, quizzId, data);
|
JsonNode out = qm.answer(u, quizzId, data);
|
||||||
return new ResponseEntity<>(out, HttpStatus.OK);
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
|
||||||
|
@GetMapping("/quizz-edit/{q}")
|
||||||
|
public Object quizzEdit(@PathVariable("q") long quizzId, Principal p, Model m) {
|
||||||
|
if (p==null)
|
||||||
|
return "redirect:/login?restricted";
|
||||||
|
User u = ur.findByName(p.getName());
|
||||||
|
logger.info("An INFO Message");
|
||||||
|
if (u==null || !qm.canEditQuizz(u, quizzId))
|
||||||
|
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
|
||||||
|
m.addAttribute("quizzId", quizzId);
|
||||||
|
|
||||||
|
return "quizz-edit";
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/get")
|
||||||
|
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
JsonNode out = qm.getQuizzInfo(u, quizzId);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/set-name")
|
||||||
|
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, @RequestBody String data, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
JsonNode out = qm.setQuizzName(u, quizzId, data);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/add-question")
|
||||||
|
public ResponseEntity<JsonNode> quizzAddQuestion(@PathVariable("q") long quizzId, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
JsonNode out = qm.addQuestion(u, quizzId);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/remove-question/{qi}")
|
||||||
|
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
JsonNode out = qm.removeQuestion(u, quizzId, questionId);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/reorder-questions")
|
||||||
|
public ResponseEntity<JsonNode> quizzReorderQuestions(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
if(!data.isArray())
|
||||||
|
return new ResponseEntity<>(
|
||||||
|
JsonNodeFactory.instance.textNode("Data should be an array"),
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
List<Long> idz = new ArrayList<>(data.size());
|
||||||
|
for(int i=0;i<data.size();i++)
|
||||||
|
if(data.get(i).isNumber())
|
||||||
|
idz.add(data.get(i).asLong());
|
||||||
|
else
|
||||||
|
return new ResponseEntity<>(
|
||||||
|
JsonNodeFactory.instance.textNode("Data should be an array of numbers"),
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
JsonNode out = qm.reorderQuestions(u, quizzId, idz);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/edit-question/{qi}")
|
||||||
|
public ResponseEntity<JsonNode> quizzEditQuestion(@PathVariable("q") long quizzId,
|
||||||
|
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
|
||||||
|
JsonNode out = qm.editQuestion(u, quizzId, questionId, data);
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/quizz-edit/{q}/set-question-type/{qi}")
|
||||||
|
public ResponseEntity<JsonNode> quizzSetQuestionType(@PathVariable("q") long quizzId,
|
||||||
|
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) {
|
||||||
|
User u = null;
|
||||||
|
if (p!=null)
|
||||||
|
u = ur.findByName(p.getName());
|
||||||
|
if(u==null)
|
||||||
|
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
|
||||||
|
if(!data.isTextual())
|
||||||
|
return new ResponseEntity<>(
|
||||||
|
JsonNodeFactory.instance.textNode("Data should be a string"),
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
Optional<QTypes> qtype = Arrays.stream(QTypes.values())
|
||||||
|
.filter(q -> q.name().equals(data.textValue()))
|
||||||
|
.findAny();
|
||||||
|
if(qtype.isEmpty())
|
||||||
|
return new ResponseEntity<>(
|
||||||
|
JsonNodeFactory.instance.textNode("Unknown qtype"),
|
||||||
|
HttpStatus.BAD_REQUEST);
|
||||||
|
|
||||||
|
JsonNode out = qm.setQuestionType(u, quizzId, questionId, qtype.get());
|
||||||
|
return new ResponseEntity<>(out, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,3 +33,7 @@ spring:
|
|||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
generate-ddl: true
|
generate-ddl: true
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
root: INFO
|
||||||
41
src/main/resources/static/css/quizz-edit.css
Normal file
41
src/main/resources/static/css/quizz-edit.css
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
ol#questions-list {
|
||||||
|
display: table;
|
||||||
|
border-spacing: 2px;
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
ol#questions-list li {
|
||||||
|
width: 90%;
|
||||||
|
background-color: lemonchiffon;
|
||||||
|
border: 2pt solid;
|
||||||
|
display: table-row;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol#questions-list li div.button-box {
|
||||||
|
display:table-cell;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol#questions-list li div.button-box button{
|
||||||
|
width: 7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol#questions-list li div.content-box {
|
||||||
|
display:table-cell;
|
||||||
|
background-color: gainsboro;
|
||||||
|
color: black;
|
||||||
|
font-size: 12pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* DCC */
|
||||||
|
div.dcc-box h5, div.dcc-box h5 input{
|
||||||
|
font-size: 16pt;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
204
src/main/resources/templates/quizz-edit.html
Normal file
204
src/main/resources/templates/quizz-edit.html
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<div th:replace="~{html-head}"/>
|
||||||
|
<link rel="stylesheet" th:href="@{/css/quizz-edit.css}"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div th:replace="~{header}"/>
|
||||||
|
<script th:src="@{/webjars/jquery/1.9.1/jquery.min.js}" type="text/javascript"></script>
|
||||||
|
<script th:src="@{/webjars/lodash/4.17.21/lodash.js}" type="text/javascript"></script>
|
||||||
|
<main>
|
||||||
|
<h1>Quizz <span id="question-name"></span><button id="edit-question-name">Edit</button></h1>
|
||||||
|
<ol id="questions-list">
|
||||||
|
</ol>
|
||||||
|
<span id="error-textbox" style="color: red"></span>
|
||||||
|
<script th:inline="javascript">
|
||||||
|
/*<![CDATA[*/
|
||||||
|
var quizzid = /*[[${quizzId}]]*/ -1;
|
||||||
|
/*]]>*/
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
function error(txt) {
|
||||||
|
console.log(txt)
|
||||||
|
$("#error-textbox").text(txt)
|
||||||
|
}
|
||||||
|
QTYPES = {
|
||||||
|
"DCC": {
|
||||||
|
"display" : makeAnswerBlockBCC,
|
||||||
|
"edit": makeEditBlockBCC,
|
||||||
|
"read": readEditBlockBCC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
questions={}
|
||||||
|
function getdata() {
|
||||||
|
$.ajax({
|
||||||
|
url: "/questions/quizz-edit/"+quizzid+"/get",
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
success: function(res) {
|
||||||
|
console.log(res)
|
||||||
|
if(!res["success"]) {
|
||||||
|
console.log(res["message"])
|
||||||
|
error(res["message"])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
questions=res["questions"]
|
||||||
|
qname=res["name"]
|
||||||
|
$("#question-name").text(qname)
|
||||||
|
for(var i=0;i<questions.length;i++){
|
||||||
|
qu = questions[i]
|
||||||
|
qid = qu["id"]
|
||||||
|
newhtml = `
|
||||||
|
<li id="question-display-${qid}">
|
||||||
|
<div class="content-box" id="question-display-${qid}-content"/>
|
||||||
|
<div class="content-box" id="question-display-${qid}-edit"/>
|
||||||
|
<div class="button-box">
|
||||||
|
<button id="question-display-${qid}-button-edit">Edit</button>
|
||||||
|
<button id="question-display-${qid}-type-edit">Type</button>
|
||||||
|
<button id="question-display-${qid}-validate-edit">Valider</button>
|
||||||
|
<button id="question-display-${qid}-cancel-edit">Annuler</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
`
|
||||||
|
newdom = $('<li/>').html(newhtml).contents()
|
||||||
|
$("#questions-list").append(newdom)
|
||||||
|
const thei = i
|
||||||
|
const theqid = qid
|
||||||
|
$(`#question-display-${qid}-button-edit`).on('click',e => edit(thei,theqid))
|
||||||
|
$(`#question-display-${qid}-type-edit`).on('click',e => setType(thei,theqid))
|
||||||
|
$(`#question-display-${qid}-validate-edit`).on('click',e => commitEdit(thei,theqid))
|
||||||
|
$(`#question-display-${qid}-cancel-edit`).on('click',e => cancelEdit(thei,theqid))
|
||||||
|
|
||||||
|
newcontent = QTYPES[qu["type"]]["display"](qid,qu["value"])
|
||||||
|
$(`#question-display-${qid}-content`).replaceWith(newcontent)
|
||||||
|
newedit = QTYPES[qu["type"]]["edit"](qid,qu["value"])
|
||||||
|
$(`#question-display-${qid}-edit`).replaceWith(newedit)
|
||||||
|
|
||||||
|
contentMode(qid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function contentMode(qid) {
|
||||||
|
$(`#question-display-${qid}-content`).show()
|
||||||
|
$(`#question-display-${qid}-button-edit`).show()
|
||||||
|
$(`#question-display-${qid}-type-edit`).show()
|
||||||
|
$(`#question-display-${qid}-edit`).hide()
|
||||||
|
$(`#question-display-${qid}-validate-edit`).hide()
|
||||||
|
$(`#question-display-${qid}-cancel-edit`).hide()
|
||||||
|
}
|
||||||
|
function editMode(qid) {
|
||||||
|
$(`#question-display-${qid}-content`).hide()
|
||||||
|
$(`#question-display-${qid}-button-edit`).hide()
|
||||||
|
$(`#question-display-${qid}-type-edit`).hide()
|
||||||
|
$(`#question-display-${qid}-edit`).show()
|
||||||
|
$(`#question-display-${qid}-validate-edit`).show()
|
||||||
|
$(`#question-display-${qid}-cancel-edit`).show()
|
||||||
|
}
|
||||||
|
function lockEdit(qid) {
|
||||||
|
$(`#question-display-${qid}-edit *`).prop('disabled', true)
|
||||||
|
$(`#question-display-${qid}-validate-edit`).prop('disabled', true)
|
||||||
|
$(`#question-display-${qid}-cancel-edit`).prop('disabled', true)
|
||||||
|
}
|
||||||
|
function unlockEdit(qid) {
|
||||||
|
$(`#question-display-${qid}-edit *`).prop('disabled', false)
|
||||||
|
$(`#question-display-${qid}-validate-edit`).prop('disabled', false)
|
||||||
|
$(`#question-display-${qid}-cancel-edit`).prop('disabled', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setType(i,id) {
|
||||||
|
//TODO à faire ^^
|
||||||
|
}
|
||||||
|
|
||||||
|
function edit(id, qid) {
|
||||||
|
editMode(qid)
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitEdit(i,qid) {
|
||||||
|
const newvalue = readEditBlockBCC(qid)
|
||||||
|
lockEdit(qid)
|
||||||
|
$.ajax({
|
||||||
|
url: "/questions/quizz-edit/"+quizzid+"/edit-question/"+qid,
|
||||||
|
type: "POST",
|
||||||
|
data: JSON.stringify(newvalue),
|
||||||
|
dataType: "json",
|
||||||
|
contentType: 'application/json',
|
||||||
|
success: function(res) {
|
||||||
|
console.log(res)
|
||||||
|
if(!res["success"]) {
|
||||||
|
console.log(res["message"])
|
||||||
|
error(res["message"])
|
||||||
|
unlockEdit(qid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
questions[i]["value"] = newvalue
|
||||||
|
newcontent = QTYPES[questions[i]["type"]]["display"](qid,questions[i]["value"])
|
||||||
|
$(`#question-display-${qid}-content`).replaceWith(newcontent)
|
||||||
|
|
||||||
|
unlockEdit(qid)
|
||||||
|
contentMode(qid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function cancelEdit(i,qid) {
|
||||||
|
newedit = QTYPES[questions[i]["type"]]["edit"](qid,questions[i]["value"])
|
||||||
|
$(`#question-display-${qid}-edit`).replaceWith(newedit)
|
||||||
|
contentMode(qid)
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeEditBlockBCC(id,question) {
|
||||||
|
html = `
|
||||||
|
<div class="content-box dcc-box" id="question-display-${id}-edit">
|
||||||
|
<h5><input id="question-display-${id}-edit-question"
|
||||||
|
type="text" value="${_.escape(question["text"])}"/></h5>
|
||||||
|
<ol>
|
||||||
|
<li class="right">☑ <input id="question-display-${id}-edit-good"
|
||||||
|
type="text" value="${_.escape(question["good"])}"/></li>
|
||||||
|
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong1"
|
||||||
|
type="text" value="${_.escape(question["wrong"][0])}"/></li>
|
||||||
|
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong2"
|
||||||
|
type="text" value="${_.escape(question["wrong"][1])}"/></li>
|
||||||
|
<li class="wrong">☒ <input id="question-display-${id}-edit-wrong3"
|
||||||
|
type="text" value="${_.escape(question["wrong"][2])}"/></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
out = $('<div/>').html(html).contents()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
function readEditBlockBCC(id) {
|
||||||
|
questionText = $(`#question-display-${id}-edit-question`).val()
|
||||||
|
goodText = $(`#question-display-${id}-edit-good`).val()
|
||||||
|
wrongText1 = $(`#question-display-${id}-edit-wrong1`).val()
|
||||||
|
wrongText2 = $(`#question-display-${id}-edit-wrong2`).val()
|
||||||
|
wrongText3 = $(`#question-display-${id}-edit-wrong3`).val()
|
||||||
|
return {
|
||||||
|
"text": questionText,
|
||||||
|
"good": goodText,
|
||||||
|
"wrong": [wrongText1,wrongText2,wrongText3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function makeAnswerBlockBCC(id,question) {
|
||||||
|
html = `
|
||||||
|
<div class="content-box dcc-box" id="question-display-${id}-content">
|
||||||
|
<h5>${_.escape(question["text"])}</h5>
|
||||||
|
<ol>
|
||||||
|
<li class="right">☑ ${_.escape(question["good"])}</li>
|
||||||
|
<li class="wrong">☒ ${_.escape(question["wrong"][0])}</li>
|
||||||
|
<li class="wrong">☒ ${_.escape(question["wrong"][1])}</li>
|
||||||
|
<li class="wrong">☒ ${_.escape(question["wrong"][2])}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
out = $('<div/>').html(html).contents()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
getdata()
|
||||||
|
</script>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
<div th:replace="~{header}"/>
|
<div th:replace="~{header}"/>
|
||||||
<main>
|
<main>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Premier item ?</li>
|
<li th:if="${#lists.isEmpty(quizz)}">Aucun quizz de disponible malheureusement :(</li>
|
||||||
<li th:each="q : ${quizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
<li th:each="q : ${quizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package com.bernard.misael;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
import com.bernard.misael.questions.QTypes;
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
class MisaelApplicationTests {
|
class MisaelApplicationTests {
|
||||||
|
|
||||||
@ -10,4 +12,11 @@ class MisaelApplicationTests {
|
|||||||
void contextLoads() {
|
void contextLoads() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateDefaultQuestions() {
|
||||||
|
for(QTypes qt : QTypes.values()){
|
||||||
|
assert qt.validate(qt.getDefaultQuestion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,3 +96,5 @@ plugin.org.flywaydb.flyway=11.3.2
|
|||||||
## # available=11.3.4
|
## # available=11.3.4
|
||||||
|
|
||||||
plugin.io.spring.dependency-management=1.1.7
|
plugin.io.spring.dependency-management=1.1.7
|
||||||
|
|
||||||
|
version.org.webjars..lodash=4.17.21
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user