Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -206,14 +206,20 @@ public ResponseEntity<Section> updateSection(
}

// Quizzes
@PostMapping("/courses/sections/quizzes")
public ResponseEntity<Quiz> createQuiz(@Valid @RequestBody CreateQuizRequest req) {
@PostMapping(
value = "/courses/sections/quizzes",
consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Quiz> createQuiz(@Valid @ModelAttribute CreateQuizRequest req)
throws FileUploadException, JsonMappingException, JsonProcessingException {
return ResponseEntity.status(HttpStatus.CREATED).body(quizService.createQuiz(req));
}

@PutMapping("/courses/sections/quizzes/{quizId}")
@PutMapping(
value = "/courses/sections/quizzes/{quizId}",
consumes = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Quiz> updateQuiz(
@Valid @RequestBody UpdateQuizRequest req, @PathVariable String quizId) {
@Valid @ModelAttribute UpdateQuizRequest req, @PathVariable String quizId)
throws FileUploadException, JsonMappingException, JsonProcessingException {
return ResponseEntity.status(HttpStatus.ACCEPTED).body(quizService.updateQuiz(quizId, req));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
*/
package moneybuddy.fr.moneybuddy.dtos.quiz;

import java.util.List;
import java.util.Map;

import jakarta.validation.constraints.*;
import lombok.*;
import moneybuddy.fr.moneybuddy.model.Quiz.WrongResponse;
import moneybuddy.fr.moneybuddy.model.enums.QuizType;
import org.springframework.web.multipart.MultipartFile;

@Data
@Builder
Expand All @@ -20,11 +24,33 @@ public class CreateQuizRequest {
private String question;

private String response;
private MultipartFile file;
private List<String> moneyValues;
private List<WrongResponse> wrongAnswers;

@NotNull(message = "At least two options are required")
private Map<String, String> options;
private List<MultipartFile> optionsImages;

@Min(0)
@NotNull(message = "CorrectAnswerIndex has to have at least one correct answer")
private int correctAnswerIndex;
private Integer correctAnswerIndex;

@NotNull(message = "Insert quiz type")
private QuizType quizType;

@AssertTrue(
message = "QuizType CALCULATE has to have moneyValues length > 0 and options lenght = 1")
public boolean isQuizTypeCorrect() {
if (quizType == null) return true;

if (QuizType.CALCULATE.equals(quizType))
return moneyValues != null
&& !moneyValues.isEmpty()
&& options != null
&& options.size() == 1;

if (QuizType.IMAGES.equals(quizType)) return optionsImages != null && optionsImages.size() >= 2;

return options != null && options.size() >= 2;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@

import jakarta.validation.constraints.Min;
import lombok.Data;
import moneybuddy.fr.moneybuddy.model.Quiz.WrongResponse;
import moneybuddy.fr.moneybuddy.model.enums.QuizType;
import org.springframework.web.multipart.MultipartFile;

@Data
public class UpdateQuizRequest {
private String question;
private String response;

private MultipartFile file;
private List<String> moneyValues;
private List<WrongResponse> wrongAnswers;
private QuizType quizType;

private List<String> options;
private List<MultipartFile> optionsImages;

@Min(0)
private int correctAnswerIndex;
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/moneybuddy/fr/moneybuddy/model/Quiz.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
import moneybuddy.fr.moneybuddy.model.enums.QuizType;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

Expand All @@ -20,19 +21,31 @@
@NoArgsConstructor
@AllArgsConstructor
public class Quiz {
@Id String id;
@Id private String id;
private String sectionId;
private String courseId;

private String question;
private String imageUrl;
private String response;
private QuizType quizType;

@JsonIgnore @Builder.Default private Map<String, String> options = new HashMap<>();
private int correctAnswerIndex;
private List<WrongResponse> wrongAnswers;

private List<String> moneyValues;

@JsonProperty("options")
public List<String> getOptionsAsList() {
return new ArrayList<>(options.values());
}

private int correctAnswerIndex;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class WrongResponse {
private int answerIndex;
private String response;
}
}
13 changes: 13 additions & 0 deletions src/main/java/moneybuddy/fr/moneybuddy/model/enums/QuizType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright moneybuddy.fr moneybuddy
*/
package moneybuddy.fr.moneybuddy.model.enums;

import io.swagger.v3.oas.annotations.media.Schema;

@Schema(enumAsRef = true)
public enum QuizType {
IMAGES,
TEXT,
CALCULATE
}
66 changes: 60 additions & 6 deletions src/main/java/moneybuddy/fr/moneybuddy/service/QuizService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,32 @@
*/
package moneybuddy.fr.moneybuddy.service;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import lombok.RequiredArgsConstructor;
import moneybuddy.fr.moneybuddy.dtos.quiz.CreateQuizRequest;
import moneybuddy.fr.moneybuddy.dtos.quiz.UpdateQuizRequest;
import moneybuddy.fr.moneybuddy.exception.QuizNotFoundException;
import moneybuddy.fr.moneybuddy.exception.SectionNotFoundException;
import moneybuddy.fr.moneybuddy.model.Quiz;
import moneybuddy.fr.moneybuddy.model.Section;
import moneybuddy.fr.moneybuddy.model.enums.QuizType;
import moneybuddy.fr.moneybuddy.repository.QuizRepository;
import moneybuddy.fr.moneybuddy.repository.SectionRepository;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class QuizService {
private final QuizRepository quizRepository;
private final SectionRepository sectionRepository;
private final CloudflareService cloudflareService;

public Quiz getById(String id) {
return quizRepository.findById(id).orElseThrow(() -> new QuizNotFoundException(id));
Expand All @@ -34,18 +40,40 @@ public Section getSection(String sectionId) {
.orElseThrow(() -> new SectionNotFoundException(sectionId));
}

public Quiz createQuiz(CreateQuizRequest req) {
public Quiz createQuiz(CreateQuizRequest req)
throws FileUploadException, JsonMappingException, JsonProcessingException {
Section section = getSection(req.getSectionId());

Map<String, String> resolvedOptions;
if (QuizType.IMAGES.equals(req.getQuizType())) {
resolvedOptions = new LinkedHashMap<>();
List<org.springframework.web.multipart.MultipartFile> images = req.getOptionsImages();
for (int i = 0; i < images.size(); i++) {
String key = cloudflareService.uploadImage(images.get(i));
resolvedOptions.put("option" + (i + 1), key);
}
} else {
resolvedOptions = req.getOptions();
}

Quiz quiz =
Quiz.builder()
.correctAnswerIndex(req.getCorrectAnswerIndex())
.options(req.getOptions())
.options(resolvedOptions)
.question(req.getQuestion())
.response(req.getResponse())
.sectionId(req.getSectionId())
.courseId(section.getCourseId())
.quizType(req.getQuizType())
.wrongAnswers(req.getWrongAnswers())
.moneyValues(req.getMoneyValues())
.build();

if (req.getFile() != null && req.getFile().getSize() > 0) {
String imageUrl = cloudflareService.uploadImage(req.getFile());
quiz.setImageUrl(imageUrl);
}

quiz = quizRepository.save(quiz);

section.getQuiz().put(quiz.getId(), quiz);
Expand All @@ -54,14 +82,40 @@ public Quiz createQuiz(CreateQuizRequest req) {
return quiz;
}

public Quiz updateQuiz(String quizId, UpdateQuizRequest req) {
public Quiz updateQuiz(String quizId, UpdateQuizRequest req)
throws FileUploadException, JsonMappingException, JsonProcessingException {
Quiz quiz = getById(quizId);

if (req.getCorrectAnswerIndex() >= 0) quiz.setCorrectAnswerIndex(req.getCorrectAnswerIndex());

quiz.setQuestion(Optional.ofNullable(req.getQuestion()).orElse(quiz.getQuestion()));
quiz.setResponse(Optional.ofNullable(req.getResponse()).orElse(quiz.getResponse()));
if (req.getOptions() != null && !req.getOptions().isEmpty()) {
Map<String, String> options = new HashMap<>();
quiz.setQuizType(Optional.ofNullable(req.getQuizType()).orElse(quiz.getQuizType()));
quiz.setWrongAnswers(Optional.ofNullable(req.getWrongAnswers()).orElse(quiz.getWrongAnswers()));
quiz.setMoneyValues(Optional.ofNullable(req.getMoneyValues()).orElse(quiz.getMoneyValues()));

if (req.getFile() != null && req.getFile().getSize() > 0) {
String image_url = cloudflareService.uploadImage(req.getFile());
cloudflareService.remove(quiz.getImageUrl());
quiz.setImageUrl(image_url);
}

if (QuizType.IMAGES.equals(quiz.getQuizType())
&& req.getOptionsImages() != null
&& !req.getOptionsImages().isEmpty()) {
// Delete old R2 images before uploading new ones
if (quiz.getOptions() != null) {
quiz.getOptions().values().forEach(cloudflareService::remove);
}
Map<String, String> newOptions = new LinkedHashMap<>();
List<org.springframework.web.multipart.MultipartFile> images = req.getOptionsImages();
for (int i = 0; i < images.size(); i++) {
String key = cloudflareService.uploadImage(images.get(i));
newOptions.put("option" + (i + 1), key);
}
quiz.setOptions(newOptions);
} else if (req.getOptions() != null && !req.getOptions().isEmpty()) {
Map<String, String> options = new LinkedHashMap<>();
for (int i = 0; i < req.getOptions().size(); i++) {
options.put("option" + (i + 1), req.getOptions().get(i));
}
Expand Down
Loading