Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.requestMatchers(HttpMethod.GET, "/api/users/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/courses/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers(HttpMethod.GET, "/api/courses").permitAll()
.requestMatchers(HttpMethod.GET, "/api/courses/**").permitAll();
// Rutas que requieren autenticación con roles específicos (Agregadas al final)
req.requestMatchers(HttpMethod.POST, "/api/courses").hasRole("INSTRUCTOR")
.requestMatchers(HttpMethod.PUT, "/api/courses/**").hasRole("INSTRUCTOR")
.requestMatchers(HttpMethod.DELETE, "/api/courses/**").hasAnyRole("INSTRUCTOR", "ADMIN")
.requestMatchers(HttpMethod.PATCH, "/api/courses/**").hasRole("ADMIN")
.requestMatchers("/api-docs/**", "/swagger-ui.html", "/swagger-ui/**", "/webjars/**", "/swagger-resources/**").permitAll();
req.anyRequest().authenticated();
}) .headers(headers -> headers
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/learning/platform/controller/CourseController.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@ public ResponseEntity<CourseResponseDTO> updateCourse(@Valid @PathVariable Long
return ResponseEntity.ok(courseService.updateCourse(id, dto, user));
}

/**
* Publicar o despublicar un curso (Solamente puede el ADMIN)
*/
@PatchMapping("/{id}/publish")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<CourseResponseDTO> publishCourse(
@PathVariable Long id,
@RequestParam boolean published
) {
return ResponseEntity.ok(courseService.publishCourse(id, published));
}

/**
* Delete a course (INSTRUCTOR or ADMIN)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
import learning.platform.dto.QuizResponse;
import learning.platform.dto.QuizSubmissionRequest;
import learning.platform.dto.QuizSubmissionResponse;
import learning.platform.entity.User;
import learning.platform.service.QuizService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand Down Expand Up @@ -92,8 +95,9 @@ public ResponseEntity<QuizResponse> getQuizByLesson(
@PostMapping("/{quizId}/submit")
public ResponseEntity<QuizSubmissionResponse> submitQuiz(
@Parameter(description = "ID del quiz a resolver") @PathVariable Long quizId,
@Valid @RequestBody QuizSubmissionRequest request) {
QuizSubmissionResponse response = quizService.submitQuiz(quizId, request);
@Valid @RequestBody QuizSubmissionRequest request,
@AuthenticationPrincipal User student) {
QuizSubmissionResponse response = quizService.submitQuiz(quizId, request, student);
return ResponseEntity.ok(response);
}

Expand Down
13 changes: 1 addition & 12 deletions src/main/java/learning/platform/dto/CourseRequestDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,13 @@ public class CourseRequestDTO {
@NotBlank(message = "La categoría no puede estar vacía.")
private String category;

@NotNull(message = "El estado de publicación es requerido.")
private Boolean published;

@Size(max = 150)
private String urlPhoto;

@Size(max = 255)
private String about;

//Agregamos los getters y setters

//getters y setters

public @Size(max = 150) String getUrlPhoto() {
return urlPhoto;
Expand Down Expand Up @@ -68,11 +64,4 @@ public void setCategory(String category) {
this.category = category;
}

public Boolean getPublished() {
return published;
}

public void setPublished(Boolean published) {
this.published = published;
}
}
8 changes: 7 additions & 1 deletion src/main/java/learning/platform/dto/QuizResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class QuizResponse {
@Schema(description = "ID del curso asociado", example = "10")
private Long courseId;

@Schema(description = "ID de la lección asociada", example = "20")
private Long lessonId;

@Schema(description = "Preguntas del quiz")
private List<QuestionResponse> questions;

Expand All @@ -41,6 +44,9 @@ public class QuizResponse {
public Long getCourseId() { return courseId; }
public void setCourseId(Long courseId) { this.courseId = courseId; }

public Long getLessonId() { return lessonId; }
public void setLessonId(Long lessonId) { this.lessonId = lessonId; }

public List<QuestionResponse> getQuestions() { return questions; }
public void setQuestions(List<QuestionResponse> questions) { this.questions = questions; }
}
}
30 changes: 14 additions & 16 deletions src/main/java/learning/platform/dto/QuizSubmissionRequest.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
package learning.platform.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

import java.util.List;

@Schema(
name = "Solicitud de Envío de Quiz",
description = "DTO para enviar las respuestas del quizz."
)
public class QuizSubmissionRequest {

@NotNull(message = "El enrollmentId es obligatorio")
private Long enrollmentId;

@NotEmpty(message = "Debe enviar al menos una respuesta")
private List<AnswerSubmissionRequest> answers;
@NotNull(message = "La lista de respuestas no puede ser nula")
@Schema(description = "Lista de respuestas del estudiante")
private List<AnswerSubmissionItem> answers;

public QuizSubmissionRequest() {}

public QuizSubmissionRequest(Long enrollmentId, List<AnswerSubmissionRequest> answers) {
this.enrollmentId = enrollmentId;
public QuizSubmissionRequest(List<AnswerSubmissionItem> answers) {
this.answers = answers;
}

public Long getEnrollmentId() {
return enrollmentId;
}

public void setEnrollmentId(Long enrollmentId) {
this.enrollmentId = enrollmentId;
}
// --- Getters y Setters ---

public List<AnswerSubmissionRequest> getAnswers() {
public List<AnswerSubmissionItem> getAnswers() {
return answers;
}

public void setAnswers(List<AnswerSubmissionRequest> answers) {
public void setAnswers(List<AnswerSubmissionItem> answers) {
this.answers = answers;
}
}
}
26 changes: 26 additions & 0 deletions src/main/java/learning/platform/entity/AnswerOption.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package learning.platform.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "answer_options")
public class AnswerOption {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "question_id", nullable = false)
private Question question;

@Column(nullable = false, length = 1000)
private String text;

// Getters y Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Question getQuestion() { return question; }
public void setQuestion(Question question) { this.question = question; }
public String getText() { return text; }
public void setText(String text) { this.text = text; }
}
40 changes: 40 additions & 0 deletions src/main/java/learning/platform/entity/AnswerSubmission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package learning.platform.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "answer_submissions")
public class AnswerSubmission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "quiz_submission_id", nullable = false)
private QuizSubmission quizSubmission;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "question_id", nullable = false)
private Question question;

// For multiple-choice questions
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "selected_option_id")
private AnswerOption selectedOption;

// For open-ended questions (not yet implemented in DTOs)
@Column(length = 2000)
private String answerText;

// Getters y Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public QuizSubmission getQuizSubmission() { return quizSubmission; }
public void setQuizSubmission(QuizSubmission quizSubmission) { this.quizSubmission = quizSubmission; }
public Question getQuestion() { return question; }
public void setQuestion(Question question) { this.question = question; }
public AnswerOption getSelectedOption() { return selectedOption; }
public void setSelectedOption(AnswerOption selectedOption) { this.selectedOption = selectedOption; }
public String getAnswerText() { return answerText; }
public void setAnswerText(String answerText) { this.answerText = answerText; }
}
35 changes: 32 additions & 3 deletions src/main/java/learning/platform/entity/Course.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,39 @@

import jakarta.persistence.*;

// Se elimina @Data de Lombok
@Entity
@Table(name = "courses")
public class Course extends Profile {
public class Course {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private String slug;

@Column(length = 100, nullable = false)
private String title;

private String description;

@Column(length = 25, nullable = false)
private String category;

// Se corrige el nombre del atributo para que sea el mismo que en el DTO
@Column(name = "profile_photo", length = 150)
private String urlPhoto;

@Column(length = 255)
private String about;

private boolean published = false;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "instructor_id", nullable = false)
private User instructor;

// --- Getters y Setters añadidos manualmente ---
// --- Getters y Setters corregidos ---

public Long getId() {
return id;
Expand Down Expand Up @@ -79,4 +91,21 @@ public User getInstructor() {
public void setInstructor(User instructor) {
this.instructor = instructor;
}

// Se corrige el nombre del getter y setter
public String getUrlPhoto() {
return urlPhoto;
}

public void setUrlPhoto(String urlPhoto) {
this.urlPhoto = urlPhoto;
}

public String getAbout() {
return about;
}

public void setAbout(String about) {
this.about = about;
}
}
4 changes: 4 additions & 0 deletions src/main/java/learning/platform/entity/Enrollment.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public User getStudent() {
return student;
}
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/learning/platform/entity/Question.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package learning.platform.entity;

import jakarta.persistence.*;
import java.util.List;

@Entity
@Table(name = "questions")
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "quiz_id", nullable = false)
private Quiz quiz;

@Column(nullable = false, length = 2000)
private String text;

// A question has many options.
@OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true)
private List<AnswerOption> options;

@Column(name = "correct_option_index", nullable = false)
private Integer correctOptionIndex;

@Column
private Integer points;

@Column(name = "time_limit_seconds")
private Integer timeLimitSeconds;

// Getters y Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Quiz getQuiz() { return quiz; }
public void setQuiz(Quiz quiz) { this.quiz = quiz; }
public String getText() { return text; }
public void setText(String text) { this.text = text; }
public List<AnswerOption> getOptions() { return options; }
public void setOptions(List<AnswerOption> options) { this.options = options; }
public Integer getCorrectOptionIndex() { return correctOptionIndex; }
public void setCorrectOptionIndex(Integer correctOptionIndex) { this.correctOptionIndex = correctOptionIndex; }
public Integer getPoints() { return points; }
public void setPoints(Integer points) { this.points = points; }
public Integer getTimeLimitSeconds() { return timeLimitSeconds; }
public void setTimeLimitSeconds(Integer timeLimitSeconds) { this.timeLimitSeconds = timeLimitSeconds; }
}
Loading