Skip to content

Commit

Permalink
Merge branch 'develop' into feature/adaptive-learning/import-exercise…
Browse files Browse the repository at this point in the history
…s-lecture-units

# Conflicts:
#	src/main/webapp/app/course/learning-paths/services/base-api-http.service.ts
#	src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java
#	src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java
#	src/test/javascript/spec/component/learning-paths/pages/learning-path-student-page.component.spec.ts
  • Loading branch information
JohannesStoehr committed Oct 3, 2024
2 parents 92a1f3e + ad160ff commit eeb9063
Show file tree
Hide file tree
Showing 56 changed files with 764 additions and 442 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ public class LearningPath extends DomainObject {
@Column(name = "progress")
private int progress;

/**
* flag indicating if a student started the learning path
*/
@Column(name = "started_by_student")
private boolean startedByStudent = false;

@ManyToOne
@JoinColumn(name = "user_id")
private User user;
Expand Down Expand Up @@ -89,8 +95,16 @@ public void removeCompetency(CourseCompetency competency) {
this.competencies.remove(competency);
}

public boolean isStartedByStudent() {
return startedByStudent;
}

public void setStartedByStudent(boolean startedByStudent) {
this.startedByStudent = startedByStudent;
}

@Override
public String toString() {
return "LearningPath{" + "id=" + getId() + ", user=" + user + ", course=" + course + ", competencies=" + competencies + '}';
return "LearningPath{" + "id=" + getId() + ", user=" + user + ", course=" + course + ", competencies=" + competencies + ", startedByStudent=" + startedByStudent + "}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.tum.cit.aet.artemis.atlas.dto;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record LearningPathDTO(long id, boolean startedByStudent, int progress) {

public static LearningPathDTO of(LearningPath learningPath) {
return new LearningPathDTO(learningPath.getId(), learningPath.isStartedByStudent(), learningPath.getProgress());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import de.tum.cit.aet.artemis.atlas.dto.CompetencyGraphEdgeDTO;
import de.tum.cit.aet.artemis.atlas.dto.CompetencyGraphNodeDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathCompetencyGraphDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathHealthDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathInformationDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationOverviewDTO;
Expand All @@ -39,6 +40,7 @@
import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO;
import de.tum.cit.aet.artemis.core.dto.pageablesearch.SearchTermPageableSearchDTO;
import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException;
import de.tum.cit.aet.artemis.core.exception.ConflictException;
import de.tum.cit.aet.artemis.core.repository.CourseRepository;
import de.tum.cit.aet.artemis.core.repository.UserRepository;
import de.tum.cit.aet.artemis.core.util.PageUtil;
Expand Down Expand Up @@ -243,6 +245,52 @@ private void updateLearningPathProgress(@NotNull LearningPath learningPath) {
log.debug("Updated LearningPath (id={}) for user (id={})", learningPath.getId(), userId);
}

/**
* Get the learning path for the current user in the given course.
*
* @param courseId the id of the course
* @return the learning path of the current user
*/
public LearningPathDTO getLearningPathForCurrentUser(long courseId) {
final var currentUser = userRepository.getUser();
final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(courseId, currentUser.getId());
return LearningPathDTO.of(learningPath);
}

/**
* Generate a learning path for the current user in the given course.
*
* @param courseId the id of the course
* @return the generated learning path
*/
public LearningPathDTO generateLearningPathForCurrentUser(long courseId) {
final var currentUser = userRepository.getUser();
final var course = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(courseId);
if (learningPathRepository.findByCourseIdAndUserId(courseId, currentUser.getId()).isPresent()) {
throw new ConflictException("Learning path already exists.", "LearningPath", "learningPathAlreadyExists");
}
final var learningPath = generateLearningPathForUser(course, currentUser);
return LearningPathDTO.of(learningPath);
}

/**
* Start the learning path for the current user
*
* @param learningPathId the id of the learning path
*/
public void startLearningPathForCurrentUser(long learningPathId) {
final var learningPath = learningPathRepository.findByIdElseThrow(learningPathId);
final var currentUser = userRepository.getUser();
if (!learningPath.getUser().equals(currentUser)) {
throw new AccessForbiddenException("You are not allowed to start this learning path.");
}
else if (learningPath.isStartedByStudent()) {
throw new ConflictException("Learning path already started.", "LearningPath", "learningPathAlreadyStarted");
}
learningPath.setStartedByStudent(true);
learningPathRepository.save(learningPath);
}

/**
* Gets the health status of learning paths for the given course.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.springframework.context.annotation.Profile;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
Expand All @@ -28,6 +29,7 @@
import de.tum.cit.aet.artemis.atlas.dto.CompetencyNameDTO;
import de.tum.cit.aet.artemis.atlas.dto.CompetencyProgressForLearningPathDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathCompetencyGraphDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathHealthDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathInformationDTO;
import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationDTO;
Expand Down Expand Up @@ -301,19 +303,31 @@ private ResponseEntity<NgxLearningPathDTO> getLearningPathNgx(@PathVariable long
}

/**
* GET courses/:courseId/learning-path-id : Gets the id of the learning path.
* GET courses/:courseId/learning-path/me : Gets the learning path of the current user in the course.
*
* @param courseId the id of the course from which the learning path id should be fetched
* @return the ResponseEntity with status 200 (OK) and with body the id of the learning path
* @param courseId the id of the course for which the learning path should be fetched
* @return the ResponseEntity with status 200 (OK) and with body the learning path
*/
@GetMapping("courses/{courseId}/learning-path-id")
@GetMapping("courses/{courseId}/learning-path/me")
@EnforceAtLeastStudentInCourse
public ResponseEntity<Long> getLearningPathId(@PathVariable long courseId) {
log.debug("REST request to get learning path id for course with id: {}", courseId);
public ResponseEntity<LearningPathDTO> getLearningPathForCurrentUser(@PathVariable long courseId) {
log.debug("REST request to get learning path of current user for course with id: {}", courseId);
courseService.checkLearningPathsEnabledElseThrow(courseId);
User user = userRepository.getUser();
final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(courseId, user.getId());
return ResponseEntity.ok(learningPath.getId());
return ResponseEntity.ok(learningPathService.getLearningPathForCurrentUser(courseId));
}

/**
* PATCH learning-path/:learningPathId/start : Starts the learning path for the current user.
*
* @param learningPathId the id of the learning path to start
* @return the ResponseEntity with status 204 (NO_CONTENT)
*/
@PatchMapping("learning-path/{learningPathId}/start")
@EnforceAtLeastStudent
public ResponseEntity<Void> startLearningPathForCurrentUser(@PathVariable long learningPathId) {
log.debug("REST request to start learning path with id: {}", learningPathId);
learningPathService.startLearningPathForCurrentUser(learningPathId);
return ResponseEntity.noContent().build();
}

/**
Expand All @@ -324,20 +338,11 @@ public ResponseEntity<Long> getLearningPathId(@PathVariable long courseId) {
*/
@PostMapping("courses/{courseId}/learning-path")
@EnforceAtLeastStudentInCourse
public ResponseEntity<Long> generateLearningPath(@PathVariable long courseId) throws URISyntaxException {
log.debug("REST request to generate learning path for user in course with id: {}", courseId);
public ResponseEntity<LearningPathDTO> generateLearningPathForCurrentUser(@PathVariable long courseId) throws URISyntaxException {
log.debug("REST request to generate learning path for current user in course with id: {}", courseId);
courseService.checkLearningPathsEnabledElseThrow(courseId);

User user = userRepository.getUser();
final var learningPathOptional = learningPathRepository.findByCourseIdAndUserId(courseId, user.getId());

if (learningPathOptional.isPresent()) {
throw new BadRequestException("Learning path already exists.");
}

final var course = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(courseId);
final var learningPath = learningPathService.generateLearningPathForUser(course, user);
return ResponseEntity.created(new URI("api/learning-path/" + learningPath.getId())).body(learningPath.getId());
final var learningPathDTO = learningPathService.generateLearningPathForCurrentUser(courseId);
return ResponseEntity.created(new URI("api/learning-path/" + learningPathDTO.id())).body(learningPathDTO);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,14 @@ else if (courseUpdate.getCourseIcon() == null && existingCourse.getCourseIcon()
}

/**
* PUT courses/:courseId/onlineCourseConfiguration : Updates the onlineCourseConfiguration for the given course.
* PUT courses/:courseId/online-course-configuration : Updates the onlineCourseConfiguration for the given course.
*
* @param courseId the id of the course to update
* @param onlineCourseConfiguration the online course configuration to update
* @return the ResponseEntity with status 200 (OK) and with body the updated online course configuration
*/
// TODO: move into LTIResource
@PutMapping("courses/{courseId}/onlineCourseConfiguration")
@PutMapping("courses/{courseId}/online-course-configuration")
@EnforceAtLeastInstructor
@Profile(PROFILE_LTI)
public ResponseEntity<OnlineCourseConfiguration> updateOnlineCourseConfiguration(@PathVariable Long courseId,
Expand Down Expand Up @@ -821,12 +821,12 @@ public ResponseEntity<Course> getCourseWithOrganizations(@PathVariable Long cour
}

/**
* GET /courses/:courseId/lockedSubmissions Get locked submissions for course for user
* GET /courses/:courseId/locked-submissions Get locked submissions for course for user
*
* @param courseId the id of the course
* @return the ResponseEntity with status 200 (OK) and with body the course, or with status 404 (Not Found)
*/
@GetMapping("courses/{courseId}/lockedSubmissions")
@GetMapping("courses/{courseId}/locked-submissions")
@EnforceAtLeastTutor
public ResponseEntity<List<Submission>> getLockedSubmissionsForCourse(@PathVariable Long courseId) {
log.debug("REST request to get all locked submissions for course : {}", courseId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1170,13 +1170,13 @@ public ResponseEntity<ExamInformationDTO> getLatestIndividualEndDateOfExam(@Path
}

/**
* GET /courses/:courseId/exams/:examId/lockedSubmissions Get locked submissions for exam for user
* GET /courses/:courseId/exams/:examId/locked-submissions Get locked submissions for exam for user
*
* @param courseId - the id of the course
* @param examId - the id of the exam
* @return the ResponseEntity with status 200 (OK) and with body the course, or with status 404 (Not Found)
*/
@GetMapping("courses/{courseId}/exams/{examId}/lockedSubmissions")
@GetMapping("courses/{courseId}/exams/{examId}/locked-submissions")
@EnforceAtLeastInstructor
public ResponseEntity<List<Submission>> getLockedSubmissionsForExam(@PathVariable Long courseId, @PathVariable Long examId) {
log.debug("REST request to get all locked submissions for course : {}", courseId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public ExerciseGroupResource(ExerciseGroupRepository exerciseGroupRepository, Ex
}

/**
* POST /courses/{courseId}/exams/{examId}/exerciseGroups : Create a new exercise group.
* POST /courses/{courseId}/exams/{examId}/exercise-groups : Create a new exercise group.
*
* @param courseId the course to which the exercise group belongs to
* @param examId the exam to which the exercise group belongs to
Expand All @@ -92,7 +92,7 @@ public ExerciseGroupResource(ExerciseGroupRepository exerciseGroupRepository, Ex
* or with status 400 (Bad Request) if the exerciseGroup has already an ID
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PostMapping("courses/{courseId}/exams/{examId}/exerciseGroups")
@PostMapping("courses/{courseId}/exams/{examId}/exercise-groups")
@EnforceAtLeastEditor
public ResponseEntity<ExerciseGroup> createExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @RequestBody ExerciseGroup exerciseGroup)
throws URISyntaxException {
Expand All @@ -117,19 +117,19 @@ public ResponseEntity<ExerciseGroup> createExerciseGroup(@PathVariable Long cour
Exam savedExam = examRepository.save(examFromDB);
ExerciseGroup savedExerciseGroup = savedExam.getExerciseGroups().getLast();

return ResponseEntity.created(new URI("/api/courses/" + courseId + "/exams/" + examId + "/exerciseGroups/" + savedExerciseGroup.getId())).body(savedExerciseGroup);
return ResponseEntity.created(new URI("/api/courses/" + courseId + "/exams/" + examId + "/exercise-groups/" + savedExerciseGroup.getId())).body(savedExerciseGroup);
}

/**
* PUT /courses/{courseId}/exams/{examId}/exerciseGroups : Update an existing exercise group.
* PUT /courses/{courseId}/exams/{examId}/exercise-groups : Update an existing exercise group.
*
* @param courseId the course to which the exercise group belongs to
* @param examId the exam to which the exercise group belongs to
* @param updatedExerciseGroup the exercise group to update
* @return the ResponseEntity with status 200 (OK) and with the body of the updated exercise group
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PutMapping("courses/{courseId}/exams/{examId}/exerciseGroups")
@PutMapping("courses/{courseId}/exams/{examId}/exercise-groups")
@EnforceAtLeastEditor
public ResponseEntity<ExerciseGroup> updateExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @RequestBody ExerciseGroup updatedExerciseGroup)
throws URISyntaxException {
Expand Down Expand Up @@ -170,14 +170,14 @@ public ResponseEntity<List<ExerciseGroup>> importExerciseGroup(@PathVariable Lon
}

/**
* GET /courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId} : Find an exercise group by id.
* GET /courses/{courseId}/exams/{examId}/exercise-groups/{exerciseGroupId} : Find an exercise group by id.
*
* @param courseId the course to which the exercise group belongs to
* @param examId the exam to which the exercise group belongs to
* @param exerciseGroupId the id of the exercise group to find
* @return the ResponseEntity with status 200 (OK) and with the found exercise group as body
*/
@GetMapping("courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId}")
@GetMapping("courses/{courseId}/exams/{examId}/exercise-groups/{exerciseGroupId}")
@EnforceAtLeastEditor
public ResponseEntity<ExerciseGroup> getExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @PathVariable Long exerciseGroupId) {
log.debug("REST request to get exercise group : {}", exerciseGroupId);
Expand All @@ -189,13 +189,13 @@ public ResponseEntity<ExerciseGroup> getExerciseGroup(@PathVariable Long courseI
}

/**
* GET courses/{courseId}/exams/{examId}/exerciseGroups : Get all exercise groups of the given exam
* GET courses/{courseId}/exams/{examId}/exercise-groups : Get all exercise groups of the given exam
*
* @param courseId the course to which the exercise groups belong to
* @param examId the exam to which the exercise groups belong to
* @return the ResponseEntity with status 200 (OK) and a list of exercise groups. The list can be empty
*/
@GetMapping("courses/{courseId}/exams/{examId}/exerciseGroups")
@GetMapping("courses/{courseId}/exams/{examId}/exercise-groups")
@EnforceAtLeastEditor
public ResponseEntity<List<ExerciseGroup>> getExerciseGroupsForExam(@PathVariable Long courseId, @PathVariable Long examId) {
log.debug("REST request to get all exercise groups for exam : {}", examId);
Expand All @@ -207,7 +207,7 @@ public ResponseEntity<List<ExerciseGroup>> getExerciseGroupsForExam(@PathVariabl
}

/**
* DELETE /courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId} : Delete the exercise group with the given id.
* DELETE /courses/{courseId}/exams/{examId}/exercise-groups/{exerciseGroupId} : Delete the exercise group with the given id.
*
* @param courseId the course to which the exercise group belongs to
* @param examId the exam to which the exercise group belongs to
Expand All @@ -218,7 +218,7 @@ public ResponseEntity<List<ExerciseGroup>> getExerciseGroupsForExam(@PathVariabl
* LocalCI, it does not make sense to keep these artifacts
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId}")
@DeleteMapping("courses/{courseId}/exams/{examId}/exercise-groups/{exerciseGroupId}")
@EnforceAtLeastInstructor
public ResponseEntity<Void> deleteExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @PathVariable Long exerciseGroupId,
@RequestParam(defaultValue = "true") boolean deleteStudentReposBuildPlans, @RequestParam(defaultValue = "true") boolean deleteBaseReposBuildPlans) {
Expand Down
Loading

0 comments on commit eeb9063

Please sign in to comment.