Skip to content

Commit

Permalink
Adaptive learning: Fix competency progress update for exercise and le…
Browse files Browse the repository at this point in the history
…cture unit operations (#8976)
  • Loading branch information
JohannesStoehr authored Jul 14, 2024
1 parent 4e0b3cd commit c7f1bc2
Show file tree
Hide file tree
Showing 73 changed files with 794 additions and 348 deletions.
4 changes: 2 additions & 2 deletions src/main/java/de/tum/in/www1/artemis/domain/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ private void validateExamExerciseIncludedInScoreCompletely() {
* Just setting the collections to {@code null} breaks the automatic orphan removal and change detection in the database.
*/
public void disconnectRelatedEntities() {
Stream.of(competencies, teams, gradingCriteria, studentParticipations, tutorParticipations, exampleSubmissions, attachments, posts, plagiarismCases)
.filter(Objects::nonNull).forEach(Collection::clear);
Stream.of(teams, gradingCriteria, studentParticipations, tutorParticipations, exampleSubmissions, attachments, posts, plagiarismCases).filter(Objects::nonNull)
.forEach(Collection::clear);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ default List<AttachmentUnit> findAllByLectureIdAndAttachmentTypeElseThrow(Long l
SELECT attachmentUnit
FROM AttachmentUnit attachmentUnit
LEFT JOIN FETCH attachmentUnit.slides slides
LEFT JOIN FETCH attachmentUnit.competencies
WHERE attachmentUnit.id = :attachmentUnitId
""")
AttachmentUnit findOneWithSlides(@Param("attachmentUnitId") long attachmentUnitId);
AttachmentUnit findOneWithSlidesAndCompetencies(@Param("attachmentUnitId") long attachmentUnitId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import de.tum.in.www1.artemis.domain.Course;
import de.tum.in.www1.artemis.domain.competency.Competency;
import de.tum.in.www1.artemis.domain.competency.LearningPath;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;

/**
Expand Down Expand Up @@ -118,14 +117,6 @@ Page<Competency> findForImport(@Param("partialTitle") String partialTitle, @Para
@Cacheable(cacheNames = "competencyTitle", key = "#competencyId", unless = "#result == null")
String getCompetencyTitle(@Param("competencyId") long competencyId);

@Query("""
SELECT c
FROM Competency c
LEFT JOIN FETCH c.learningPaths lp
WHERE lp = :learningPath
""")
Set<Competency> findAllByLearningPath(@Param("learningPath") LearningPath learningPath);

default Competency findByIdWithExercisesElseThrow(long competencyId) {
return getValueElseThrow(findByIdWithExercises(competencyId), competencyId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.LearningObject;
import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.competency.CourseCompetency;
import de.tum.in.www1.artemis.domain.lecture.LectureUnit;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;
import de.tum.in.www1.artemis.web.rest.dto.metrics.CompetencyExerciseMasteryCalculationDTO;
import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException;
Expand Down Expand Up @@ -97,6 +100,22 @@ GROUP BY ex.maxPoints, ex.difficulty, TYPE(ex), sS.lastScore, tS.lastScore, sS.l
""")
Optional<CourseCompetency> findByIdWithExercisesAndLectureUnitsBidirectional(@Param("competencyId") long competencyId);

@Query("""
SELECT c.id
FROM CourseCompetency c
LEFT JOIN c.exercises ex
WHERE :exercise = ex
""")
Set<Long> findAllIdsByExercise(@Param("exercise") Exercise exercise);

@Query("""
SELECT c.id
FROM CourseCompetency c
LEFT JOIN c.lectureUnits lu
WHERE :lectureUnit = lu
""")
Set<Long> findAllIdsByLectureUnit(@Param("lectureUnit") LectureUnit lectureUnit);

/**
* Finds a list of competencies by id and verifies that the user is at least editor in the respective courses.
* If any of the competencies are not accessible, throws a {@link EntityNotFoundException}
Expand Down Expand Up @@ -139,5 +158,19 @@ default CourseCompetency findByIdWithExercisesAndLectureUnitsBidirectionalElseTh
return getValueElseThrow(findByIdWithExercisesAndLectureUnitsBidirectional(competencyId), competencyId);
}

/**
* Finds the set of ids of course competencies that are linked to a given learning object
*
* @param learningObject the learning object to find the course competencies for
* @return the set of ids of course competencies linked to the learning object
*/
default Set<Long> findAllIdsByLearningObject(LearningObject learningObject) {
return switch (learningObject) {
case LectureUnit lectureUnit -> findAllIdsByLectureUnit(lectureUnit);
case Exercise exercise -> findAllIdsByExercise(exercise);
default -> throw new IllegalArgumentException("Unknown LearningObject type: " + learningObject.getClass());
};
}

List<CourseCompetency> findByCourseIdOrderById(long courseId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.util.List;
import java.util.Optional;

import jakarta.validation.constraints.NotNull;

import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
Expand All @@ -30,6 +32,14 @@ public interface FileUploadExerciseRepository extends ArtemisJpaRepository<FileU
""")
List<FileUploadExercise> findByCourseIdWithCategories(@Param("courseId") Long courseId);

@EntityGraph(type = LOAD, attributePaths = { "competencies" })
Optional<FileUploadExercise> findWithEagerCompetenciesById(Long exerciseId);

@EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencies" })
Optional<FileUploadExercise> findWithEagerTeamAssignmentConfigAndCategoriesAndCompetenciesById(Long exerciseId);

@NotNull
default FileUploadExercise findWithEagerCompetenciesByIdElseThrow(Long exerciseId) {
return getValueElseThrow(findWithEagerCompetenciesById(exerciseId), exerciseId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.lecture.LectureUnit;
import de.tum.in.www1.artemis.domain.lecture.LectureUnitCompletion;
import de.tum.in.www1.artemis.repository.base.ArtemisJpaRepository;
Expand Down Expand Up @@ -44,4 +45,12 @@ SELECT COUNT(lectureUnitCompletion)
AND lectureUnitCompletion.user.id = :userId
""")
int countByLectureUnitIdsAndUserId(@Param("lectureUnitIds") Collection<Long> lectureUnitIds, @Param("userId") Long userId);

@Query("""
SELECT user
FROM LectureUnitCompletion lectureUnitCompletion
LEFT JOIN lectureUnitCompletion.user user
WHERE lectureUnitCompletion.lectureUnit = :lectureUnit
""")
Set<User> findCompletedUsersForLectureUnit(@Param("lectureUnit") LectureUnit lectureUnit);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,6 @@
@Repository
public interface LectureUnitRepository extends ArtemisJpaRepository<LectureUnit, Long> {

@Query("""
SELECT lu
FROM LectureUnit lu
LEFT JOIN FETCH lu.competencies
LEFT JOIN FETCH lu.exercise exercise
LEFT JOIN FETCH exercise.competencies
WHERE lu.id = :lectureUnitId
""")
Optional<LectureUnit> findWithCompetenciesById(@Param("lectureUnitId") Long lectureUnitId);

@Query("""
SELECT lu
FROM LectureUnit lu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public interface ModelingExerciseRepository extends ArtemisJpaRepository<Modelin
"plagiarismDetectionConfig" })
Optional<ModelingExercise> findWithEagerExampleSubmissionsAndCompetenciesAndPlagiarismDetectionConfigById(Long exerciseId);

@EntityGraph(type = LOAD, attributePaths = { "competencies" })
Optional<ModelingExercise> findWithEagerCompetenciesById(Long exerciseId);

@Query("""
SELECT modelingExercise
FROM ModelingExercise modelingExercise
Expand Down Expand Up @@ -112,4 +115,9 @@ default ModelingExercise findByIdWithExampleSubmissionsAndResultsAndPlagiarismDe
default ModelingExercise findByIdWithStudentParticipationsSubmissionsResultsElseThrow(long exerciseId) {
return findWithStudentParticipationsSubmissionsResultsById(exerciseId).orElseThrow(() -> new EntityNotFoundException("Modeling Exercise", exerciseId));
}

@NotNull
default ModelingExercise findWithEagerCompetenciesByIdElseThrow(long exerciseId) {
return getValueElseThrow(findWithEagerCompetenciesById(exerciseId), exerciseId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

import static de.tum.in.www1.artemis.config.Constants.PROFILE_CORE;

import java.util.Optional;

import jakarta.validation.constraints.NotNull;

import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import de.tum.in.www1.artemis.domain.lecture.OnlineUnit;
Expand All @@ -15,4 +21,16 @@
@Repository
public interface OnlineUnitRepository extends ArtemisJpaRepository<OnlineUnit, Long> {

@Query("""
SELECT ou
FROM OnlineUnit ou
LEFT JOIN FETCH ou.competencies
WHERE ou.id = :onlineUnitId
""")
Optional<OnlineUnit> findByIdWithCompetencies(@Param("onlineUnitId") long onlineUnitId);

@NotNull
default OnlineUnit findByIdWithCompetenciesElseThrow(long onlineUnitId) {
return getValueElseThrow(findByIdWithCompetencies(onlineUnitId), onlineUnitId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public interface ProgrammingExerciseRepository extends DynamicSpecificationRepos
@EntityGraph(type = LOAD, attributePaths = "auxiliaryRepositories")
Optional<ProgrammingExercise> findWithAuxiliaryRepositoriesById(long exerciseId);

@EntityGraph(type = LOAD, attributePaths = { "auxiliaryRepositories", "competencies" })
Optional<ProgrammingExercise> findWithAuxiliaryRepositoriesAndCompetenciesById(long exerciseId);

@EntityGraph(type = LOAD, attributePaths = "submissionPolicy")
Optional<ProgrammingExercise> findWithSubmissionPolicyById(long exerciseId);

Expand Down Expand Up @@ -520,6 +523,17 @@ default ProgrammingExercise findByIdWithAuxiliaryRepositoriesElseThrow(long prog
return findWithAuxiliaryRepositoriesById(programmingExerciseId).orElseThrow(() -> new EntityNotFoundException("Programming Exercise", programmingExerciseId));
}

/**
* Find a programming exercise with auxiliary repositories and competencies by its id and throw an {@link EntityNotFoundException} if it cannot be found
*
* @param programmingExerciseId of the programming exercise.
* @return The programming exercise related to the given id
*/
@NotNull
default ProgrammingExercise findByIdWithAuxiliaryRepositoriesAndCompetenciesElseThrow(long programmingExerciseId) throws EntityNotFoundException {
return getValueElseThrow(findWithAuxiliaryRepositoriesAndCompetenciesById(programmingExerciseId), programmingExerciseId);
}

/**
* Find a programming exercise with the submission policy by its id and throw an EntityNotFoundException if it cannot be found
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,12 @@ public interface QuizExerciseRepository extends ArtemisJpaRepository<QuizExercis
@EntityGraph(type = LOAD, attributePaths = { "quizQuestions" })
Optional<QuizExercise> findWithEagerQuestionsById(Long quizExerciseId);

@EntityGraph(type = LOAD, attributePaths = { "quizQuestions", "competencies" })
Optional<QuizExercise> findWithEagerQuestionsAndCompetenciesById(Long quizExerciseId);

@EntityGraph(type = LOAD, attributePaths = { "quizBatches" })
Optional<QuizExercise> findWithEagerBatchesById(Long quizExerciseId);

@NotNull
default QuizExercise findWithEagerQuestionsByIdOrElseThrow(Long quizExerciseId) {
return findWithEagerQuestionsById(quizExerciseId).orElseThrow(() -> new EntityNotFoundException("QuizExercise", quizExerciseId));
}

@NotNull
default QuizExercise findWithEagerBatchesByIdOrElseThrow(Long quizExerciseId) {
return findWithEagerBatchesById(quizExerciseId).orElseThrow(() -> new EntityNotFoundException("QuizExercise", quizExerciseId));
Expand Down Expand Up @@ -111,6 +109,17 @@ default QuizExercise findByIdWithQuestionsElseThrow(Long quizExerciseId) {
return findWithEagerQuestionsById(quizExerciseId).orElseThrow(() -> new EntityNotFoundException("Quiz Exercise", quizExerciseId));
}

/**
* Get one quiz exercise by id and eagerly load questions
*
* @param quizExerciseId the id of the entity
* @return the entity
*/
@NotNull
default QuizExercise findByIdWithQuestionsAndCompetenciesElseThrow(Long quizExerciseId) {
return getValueElseThrow(findWithEagerQuestionsAndCompetenciesById(quizExerciseId), quizExerciseId);
}

/**
* Get one quiz exercise by id and eagerly load batches
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ public interface StudentScoreRepository extends ArtemisJpaRepository<StudentScor
""")
List<StudentScore> findAllByExerciseAndUserWithEagerExercise(@Param("exercises") Set<Exercise> exercises, @Param("user") User user);

@Query("""
SELECT stud
FROM StudentScore s
LEFT JOIN s.user stud
WHERE s.exercise = :exercise
""")
Set<User> findAllUsersWithScoresByExercise(@Param("exercise") Exercise exercise);

@Transactional // ok because of delete
@Modifying
void deleteByExerciseAndUser(Exercise exercise, User user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ public interface TeamScoreRepository extends ArtemisJpaRepository<TeamScore, Lon
""")
List<TeamScore> findAllByExercisesAndUser(@Param("exercises") Set<Exercise> exercises, @Param("user") User user);

@Query("""
SELECT studs
FROM TeamScore s
LEFT JOIN s.team t
LEFT JOIN t.students studs
WHERE s.exercise = :exercise
""")
Set<User> findAllUsersWithScoresByExercise(@Param("exercise") Exercise exercise);

@Transactional // ok because of delete
@Modifying
void deleteByExerciseAndTeam(Exercise exercise, Team team);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ public interface TextExerciseRepository extends ArtemisJpaRepository<TextExercis
""")
List<TextExercise> findByCourseIdWithCategories(@Param("courseId") long courseId);

@EntityGraph(type = LOAD, attributePaths = { "competencies" })
Optional<TextExercise> findWithEagerCompetenciesById(long exerciseId);

@EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencies" })
Optional<TextExercise> findWithEagerTeamAssignmentConfigAndCategoriesAndCompetenciesById(long exerciseId);

Expand Down Expand Up @@ -65,6 +68,11 @@ default TextExercise findWithGradingCriteriaByIdElseThrow(long exerciseId) {
return findWithGradingCriteriaById(exerciseId).orElseThrow(() -> new EntityNotFoundException("Text Exercise", exerciseId));
}

@NotNull
default TextExercise findWithEagerCompetenciesByIdElseThrow(long exerciseId) {
return getValueElseThrow(findWithEagerCompetenciesById(exerciseId), exerciseId);
}

@NotNull
default TextExercise findByIdWithExampleSubmissionsAndResultsElseThrow(long exerciseId) {
return findWithExampleSubmissionsAndResultsById(exerciseId).orElseThrow(() -> new EntityNotFoundException("Text Exercise", exerciseId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,4 @@ public interface TextUnitRepository extends ArtemisJpaRepository<TextUnit, Long>
""")
Optional<TextUnit> findByIdWithCompetencies(@Param("textUnitId") Long textUnitId);

@Query("""
SELECT tu
FROM TextUnit tu
LEFT JOIN FETCH tu.competencies c
LEFT JOIN FETCH c.lectureUnits
WHERE tu.id = :textUnitId
""")
Optional<TextUnit> findByIdWithCompetenciesBidirectional(@Param("textUnitId") Long textUnitId);

}
32 changes: 21 additions & 11 deletions src/main/java/de/tum/in/www1/artemis/repository/UserRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ OR LOWER(user.login) = LOWER(:searchInput)
@EntityGraph(type = LOAD, attributePaths = { "groups", "authorities" })
Set<User> findAllWithGroupsAndAuthoritiesByIsDeletedIsFalseAndGroupsContains(String groupName);

@Query("""
SELECT DISTINCT user
FROM User user
LEFT JOIN FETCH user.groups userGroup
LEFT JOIN FETCH user.authorities userAuthority
WHERE user.isDeleted = FALSE
AND userGroup IN :groupNames
""")
Set<User> findAllWithGroupsAndAuthoritiesByIsDeletedIsFalseAndGroupsContains(@Param("groupNames") Set<String> groupNames);

Set<User> findAllByIsDeletedIsFalseAndGroupsContains(String groupName);

@Query("""
Expand Down Expand Up @@ -760,17 +770,6 @@ default User getUserWithGroupsAndAuthorities(@NotNull String username) {
return unwrapOptionalUser(user, username);
}

/**
* Get user with authorities with the username (i.e. user.getLogin() or principal.getName())
*
* @param username the username of the user who should be retrieved from the database
* @return the user that belongs to the given principal with eagerly loaded authorities
*/
default User getUserWithAuthorities(@NotNull String username) {
Optional<User> user = findOneWithAuthoritiesByLogin(username);
return unwrapOptionalUser(user, username);
}

/**
* Finds a single user with groups and authorities using the registration number
*
Expand Down Expand Up @@ -877,6 +876,17 @@ default Set<User> getInstructors(Course course) {
return findAllWithGroupsAndAuthoritiesByIsDeletedIsFalseAndGroupsContains(course.getInstructorGroupName());
}

/**
* Get all users for a given course
*
* @param course The course for which to fetch all users
* @return all users in the course
*/
default Set<User> getUsersInCourse(Course course) {
Set<String> groupNames = Set.of(course.getStudentGroupName(), course.getTeachingAssistantGroupName(), course.getEditorGroupName(), course.getInstructorGroupName());
return findAllWithGroupsAndAuthoritiesByIsDeletedIsFalseAndGroupsContains(groupNames);
}

/**
* Finds all users that are part of the specified group, but are not contained in the collection of excluded users
*
Expand Down
Loading

0 comments on commit c7f1bc2

Please sign in to comment.