Skip to content

Commit

Permalink
Development: Improve course overview and course details performance (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
krusche authored Dec 23, 2023
1 parent 0932837 commit 6a666eb
Show file tree
Hide file tree
Showing 21 changed files with 218 additions and 115 deletions.
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ module.exports = {
// TODO: in the future, the following values should increase to at least 90%
statements: 86.7,
branches: 73.7,
functions: 80.7,
functions: 80.8,
lines: 86.7,
},
},
Expand Down
44 changes: 44 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/Course.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,50 @@ public class Course extends DomainObject {
@Transient
private Long numberOfStudentsTransient;

@Transient
private Long numberOfLecturesTransient;

@Transient
private Long numberOfTutorialGroupsTransient;

@Transient
private Long numberOfCompetenciesTransient;

@Transient
private Long numberOfPrerequisitesTransient;

public Long getNumberOfLectures() {
return numberOfLecturesTransient;
}

public Long getNumberOfTutorialGroups() {
return numberOfTutorialGroupsTransient;
}

public Long getNumberOfCompetencies() {
return numberOfCompetenciesTransient;
}

public Long getNumberOfPrerequisites() {
return numberOfPrerequisitesTransient;
}

public void setNumberOfLectures(Long numberOfLectures) {
this.numberOfLecturesTransient = numberOfLectures;
}

public void setNumberOfTutorialGroups(Long numberOfTutorialGroups) {
this.numberOfTutorialGroupsTransient = numberOfTutorialGroups;
}

public void setNumberOfCompetencies(Long numberOfCompetencies) {
this.numberOfCompetenciesTransient = numberOfCompetencies;
}

public void setNumberOfPrerequisites(Long numberOfPrerequisites) {
this.numberOfPrerequisitesTransient = numberOfPrerequisites;
}

public String getTitle() {
return title;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package de.tum.in.www1.artemis.repository;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.*;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
Expand All @@ -11,6 +9,7 @@
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import de.tum.in.www1.artemis.domain.competency.Competency;
import de.tum.in.www1.artemis.domain.competency.CompetencyProgress;

@Repository
Expand Down Expand Up @@ -40,6 +39,15 @@ public interface CompetencyProgressRepository extends JpaRepository<CompetencyPr
""")
Optional<CompetencyProgress> findByCompetencyIdAndUserId(@Param("competencyId") Long competencyId, @Param("userId") Long userId);

@Query("""
SELECT cp
FROM CompetencyProgress cp
LEFT JOIN cp.learningGoal
WHERE cp.learningGoal in :competencies
AND cp.user.id = :userId
""")
Set<CompetencyProgress> findByCompetenciesAndUser(@Param("competencies") Collection<Competency> competencies, @Param("userId") Long userId);

@Query("""
SELECT cp
FROM CompetencyProgress cp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public interface CompetencyRelationRepository extends JpaRepository<CompetencyRe
WHERE relation.headCompetency.course.id = :courseId
AND relation.tailCompetency.course.id = :courseId
""")
Set<CompetencyRelation> findAllByCourseId(@Param("courseId") Long courseId);
Set<CompetencyRelation> findAllWithHeadAndTailByCourseId(@Param("courseId") Long courseId);

@Query("""
SELECT count(cr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ public interface CompetencyRepository extends JpaRepository<Competency, Long> {
@Query("""
SELECT c
FROM Competency c
LEFT JOIN FETCH c.userProgress progress
WHERE c.course.id = :courseId
WHERE c.course.id = :courseId
""")
Set<Competency> findAllForCourse(@Param("courseId") Long courseId);

Expand Down Expand Up @@ -114,12 +113,20 @@ public interface CompetencyRepository extends JpaRepository<Competency, Long> {
@Query("""
SELECT pr
FROM Competency pr
LEFT JOIN FETCH pr.consecutiveCourses c
LEFT JOIN pr.consecutiveCourses c
WHERE c.id = :courseId
ORDER BY pr.title
""")
Set<Competency> findPrerequisitesByCourseId(@Param("courseId") Long courseId);

@Query("""
SELECT COUNT(*)
FROM Competency pr
LEFT JOIN pr.consecutiveCourses c
WHERE c.id = :courseId
""")
Long countPrerequisitesByCourseId(@Param("courseId") Long courseId);

/**
* Query which fetches all competencies for which the user is editor or instructor in the course and
* matching the search criteria.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,20 @@ SELECT CASE WHEN (count(c) > 0) THEN true ELSE false END
@EntityGraph(type = LOAD, attributePaths = { "competencies", "learningPaths", "learningPaths.competencies" })
Optional<Course> findWithEagerLearningPathsAndCompetenciesById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "lectures" })
// Note: we load attachments directly because otherwise, they will be loaded in subsequent DB calls due to the EAGER relationship
@EntityGraph(type = LOAD, attributePaths = { "lectures", "lectures.attachments" })
Optional<Course> findWithEagerLecturesById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "exercises", "lectures" })
@EntityGraph(type = LOAD, attributePaths = { "exercises", "lectures", "lectures.attachments" })
Optional<Course> findWithEagerExercisesAndLecturesById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "lectures", "lectures.lectureUnits" })
@EntityGraph(type = LOAD, attributePaths = { "lectures", "lectures.lectureUnits", "lectures.attachments" })
Optional<Course> findWithEagerLecturesAndLectureUnitsById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "organizations", "competencies", "prerequisites", "tutorialGroupsConfiguration", "onlineCourseConfiguration" })
Optional<Course> findForUpdateById(long courseId);

@EntityGraph(type = LOAD, attributePaths = { "exercises", "lectures", "lectures.lectureUnits", "competencies", "prerequisites" })
@EntityGraph(type = LOAD, attributePaths = { "exercises", "lectures", "lectures.lectureUnits", "lectures.attachments", "competencies", "prerequisites" })
Optional<Course> findWithEagerExercisesAndLecturesAndLectureUnitsAndCompetenciesById(long courseId);

@Query("""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
public interface TutorialGroupFreePeriodRepository extends JpaRepository<TutorialGroupFreePeriod, Long> {

@Query("""
SELECT tutorialGroupFreePeriod
FROM TutorialGroupFreePeriod tutorialGroupFreePeriod
WHERE tutorialGroupFreePeriod.start <= :#{#toInclusive} AND tutorialGroupFreePeriod.end >= :#{#fromInclusive}
AND tutorialGroupFreePeriod.tutorialGroupsConfiguration.course = :#{#course}""")
SELECT period
FROM TutorialGroupFreePeriod period
WHERE period.start <= :#{#toInclusive}
AND period.end >= :#{#fromInclusive}
AND period.tutorialGroupsConfiguration.course = :#{#course}
""")
Optional<TutorialGroupFreePeriod> findOverlappingInSameCourse(@Param("course") Course course, @Param("fromInclusive") ZonedDateTime fromInclusive,
@Param("toInclusive") ZonedDateTime toInclusive);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface TutorialGroupRepository extends JpaRepository<TutorialGroup, Lo
@Query("""
SELECT tutorialGroup
FROM TutorialGroup tutorialGroup
LEFT JOIN FETCH tutorialGroup.tutorialGroupChannel
LEFT JOIN FETCH tutorialGroup.tutorialGroupChannel
WHERE tutorialGroup.id = :#{#tutorialGroupId}
""")
Optional<TutorialGroup> getTutorialGroupWithChannel(@Param("tutorialGroupId") Long tutorialGroupId);
Expand Down Expand Up @@ -83,19 +83,24 @@ WHERE tutorialGroup.course.instructorGroupName IN (:#{#userGroups}) AND tutorial
@Query("""
SELECT tutorialGroup
FROM TutorialGroup tutorialGroup
LEFT JOIN FETCH tutorialGroup.teachingAssistant
LEFT JOIN FETCH tutorialGroup.registrations
LEFT JOIN FETCH tutorialGroup.teachingAssistant
LEFT JOIN FETCH tutorialGroup.registrations
LEFT JOIN FETCH tutorialGroup.tutorialGroupSessions
LEFT JOIN FETCH tutorialGroup.tutorialGroupSchedule
LEFT JOIN FETCH tutorialGroup.tutorialGroupChannel
WHERE tutorialGroup.course.id = :#{#courseId}
ORDER BY tutorialGroup.title""")
Set<TutorialGroup> findAllByCourseIdWithTeachingAssistantAndRegistrations(@Param("courseId") Long courseId);
ORDER BY tutorialGroup.title
""")
Set<TutorialGroup> findAllByCourseIdWithTeachingAssistantRegistrationsAndSchedule(@Param("courseId") Long courseId);

@Query("""
SELECT tutorialGroup
FROM TutorialGroup tutorialGroup
LEFT JOIN FETCH tutorialGroup.teachingAssistant
LEFT JOIN FETCH tutorialGroup.registrations
LEFT JOIN FETCH tutorialGroup.tutorialGroupSessions
LEFT JOIN FETCH tutorialGroup.tutorialGroupSchedule
LEFT JOIN FETCH tutorialGroup.teachingAssistant
LEFT JOIN FETCH tutorialGroup.registrations
LEFT JOIN FETCH tutorialGroup.tutorialGroupSessions
LEFT JOIN FETCH tutorialGroup.tutorialGroupSchedule
LEFT JOIN FETCH tutorialGroup.tutorialGroupChannel
WHERE tutorialGroup.id = :#{#tutorialGroupId}
""")
Optional<TutorialGroup> findByIdWithTeachingAssistantAndRegistrationsAndSessions(@Param("tutorialGroupId") long tutorialGroupId);
Expand Down Expand Up @@ -161,4 +166,6 @@ default TutorialGroup findByIdWithTeachingAssistantAndRegistrationsAndSessionsEl
default Optional<Channel> getTutorialGroupChannel(Long tutorialGroupId) {
return getTutorialGroupWithChannel(tutorialGroupId).map(TutorialGroup::getTutorialGroupChannel);
}

Long countByCourse(Course course);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,49 +22,58 @@
public interface TutorialGroupSessionRepository extends JpaRepository<TutorialGroupSession, Long> {

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.tutorialGroup.id = :#{#tutorialGroupId}
AND tutorialGroupSession.status = :#{#status}
AND tutorialGroupSession.start >= :#{#now}
ORDER BY tutorialGroupSession.start""")
SELECT session
FROM TutorialGroupSession session
WHERE session.tutorialGroup.id = :#{#tutorialGroupId}
AND session.status = :#{#status}
AND session.start >= :#{#now}
ORDER BY session.start
""")
List<TutorialGroupSession> findNextSessionsOfStatus(@Param("tutorialGroupId") Long tutorialGroupId, @Param("now") ZonedDateTime now,
@Param("status") TutorialGroupSessionStatus status);

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.tutorialGroup.id = :#{#tutorialGroupId}""")
SELECT session
FROM TutorialGroupSession session
WHERE session.tutorialGroup.id = :#{#tutorialGroupId}
""")
Set<TutorialGroupSession> findAllByTutorialGroupId(@Param("tutorialGroupId") Long tutorialGroupId);

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.tutorialGroupSchedule.id = :#{#scheduleId}""")
SELECT session
FROM TutorialGroupSession session
WHERE session.tutorialGroupSchedule.id = :#{#scheduleId}
""")
Set<TutorialGroupSession> findAllByScheduleId(@Param("scheduleId") Long scheduleId);

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.start <= :#{#end} AND tutorialGroupSession.end >= :#{#start}
AND tutorialGroupSession.tutorialGroup = :#{#tutorialGroup}""")
SELECT session
FROM TutorialGroupSession session
WHERE session.start <= :#{#end}
AND session.end >= :#{#start}
AND session.tutorialGroup = :#{#tutorialGroup}
""")
Set<TutorialGroupSession> findOverlappingInSameTutorialGroup(@Param("tutorialGroup") TutorialGroup tutorialGroup, @Param("start") ZonedDateTime start,
@Param("end") ZonedDateTime end);

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.start <= :#{#end} AND tutorialGroupSession.end >= :#{#start}
AND tutorialGroupSession.tutorialGroupSchedule IS NULL
AND tutorialGroupSession.tutorialGroup = :#{#tutorialGroup}""")
SELECT session
FROM TutorialGroupSession session
WHERE session.start <= :#{#end}
AND session.end >= :#{#start}
AND session.tutorialGroupSchedule IS NULL
AND session.tutorialGroup = :#{#tutorialGroup}
""")
Set<TutorialGroupSession> findOverlappingIndividualSessionsInSameTutorialGroup(@Param("tutorialGroup") TutorialGroup tutorialGroup, @Param("start") ZonedDateTime start,
@Param("end") ZonedDateTime end);

@Query("""
SELECT tutorialGroupSession
FROM TutorialGroupSession tutorialGroupSession
WHERE tutorialGroupSession.start <= :#{#end} AND tutorialGroupSession.end >= :#{#start}
AND tutorialGroupSession.tutorialGroup.course = :#{#course}""")
SELECT session
FROM TutorialGroupSession session
WHERE session.start <= :#{#end}
AND session.end >= :#{#start}
AND session.tutorialGroup.course = :#{#course}
""")
Set<TutorialGroupSession> findAllBetween(@Param("course") Course course, @Param("start") ZonedDateTime start, @Param("end") ZonedDateTime end);

Set<TutorialGroupSession> findAllByTutorialGroupFreePeriodId(Long freePeriodId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
public interface TutorialGroupsConfigurationRepository extends JpaRepository<TutorialGroupsConfiguration, Long> {

@Query("""
SELECT tutorialGroupConfiguration
FROM TutorialGroupsConfiguration tutorialGroupConfiguration LEFT JOIN tutorialGroupConfiguration.tutorialGroupFreePeriods
WHERE tutorialGroupConfiguration.id = :#{#tutorialGroupConfigurationId}
SELECT t
FROM TutorialGroupsConfiguration t
LEFT JOIN t.tutorialGroupFreePeriods
WHERE t.id = :#{#tutorialGroupConfigurationId}
""")
Optional<TutorialGroupsConfiguration> findByIdWithEagerTutorialGroupFreePeriods(@Param("tutorialGroupConfigurationId") Long tutorialGroupConfigurationId);

Expand All @@ -26,9 +27,10 @@ default TutorialGroupsConfiguration findByIdWithEagerTutorialGroupFreePeriodsEls
}

@Query("""
SELECT tutorialGroupConfiguration
FROM TutorialGroupsConfiguration tutorialGroupConfiguration LEFT JOIN FETCH tutorialGroupConfiguration.tutorialGroupFreePeriods
WHERE tutorialGroupConfiguration.course.id = :#{#courseId}
SELECT t
FROM TutorialGroupsConfiguration t
LEFT JOIN FETCH t.tutorialGroupFreePeriods
WHERE t.course.id = :#{#courseId}
""")
Optional<TutorialGroupsConfiguration> findByCourseIdWithEagerTutorialGroupFreePeriods(Long courseId);
}
12 changes: 12 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/service/CourseService.java
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,14 @@ public Course findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialG
course.setExercises(exerciseService.filterExercisesForCourse(course, user));
exerciseService.loadExerciseDetailsIfNecessary(course, user);
course.setExams(examRepository.findByCourseIdsForUser(Set.of(course.getId()), user.getId(), user.getGroups(), ZonedDateTime.now()));
// TODO: in the future, we only want to know if lectures exist, the actual lectures will be loaded when the user navigates into the lecture
course.setLectures(lectureService.filterVisibleLecturesWithActiveAttachments(course, course.getLectures(), user));
// NOTE: in this call we only want to know if competencies exist in the course, we will load them when the user navigates into them
course.setNumberOfCompetencies(competencyRepository.countByCourse(course));
// NOTE: in this call we only want to know if prerequisites exist in the course, we will load them when the user navigates into them
course.setNumberOfPrerequisites(competencyRepository.countPrerequisitesByCourseId(course.getId()));
// NOTE: in this call we only want to know if tutorial groups exist in the course, we will load them when the user navigates into them
course.setNumberOfTutorialGroups(tutorialGroupRepository.countByCourse(course));
if (authCheckService.isOnlyStudentInCourse(course, user)) {
course.setExams(examRepository.filterVisibleExams(course.getExams()));
}
Expand All @@ -292,6 +299,7 @@ public Set<Course> findAllActiveForUser(User user) {
*/
public List<Course> findAllActiveWithExercisesAndLecturesAndExamsForUser(User user) {
long start = System.nanoTime();
// TODO: in the future we only need the number of lectures and no additional values
var userVisibleCourses = courseRepository.findAllActiveWithLectures().stream().filter(course -> isCourseVisibleForUser(user, course)).toList();

if (log.isDebugEnabled()) {
Expand All @@ -313,7 +321,11 @@ public List<Course> findAllActiveWithExercisesAndLecturesAndExamsForUser(User us
course.setExercises(exerciseService.filterExercisesForCourse(course, user));
exerciseService.loadExerciseDetailsIfNecessary(course, user);
course.setExams(allExams.stream().filter(ex -> ex.getCourse().getId().equals(course.getId())).collect(Collectors.toSet()));
// TODO: we only need the number of exams here
course.setLectures(lectureService.filterVisibleLecturesWithActiveAttachments(course, course.getLectures(), user));
course.setNumberOfLectures((long) course.getLectures().size());
// we do not send them to the client, not needed
course.setLectures(Set.of());
}).toList();

if (log.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ private void generateNgxGraphRepresentationForCompetency(LearningPath learningPa
* @param edges set of edges to store the new edges
*/
private void generateNgxGraphRepresentationForRelations(LearningPath learningPath, Set<NgxLearningPathDTO.Node> nodes, Set<NgxLearningPathDTO.Edge> edges) {
final var relations = competencyRelationRepository.findAllByCourseId(learningPath.getCourse().getId());
final var relations = competencyRelationRepository.findAllWithHeadAndTailByCourseId(learningPath.getCourse().getId());

// compute match clusters
Map<Long, Integer> competencyToMatchCluster = new HashMap<>();
Expand Down
Loading

0 comments on commit 6a666eb

Please sign in to comment.