Skip to content

Commit

Permalink
Adaptive learning: Add learner profile (#9673)
Browse files Browse the repository at this point in the history
  • Loading branch information
N0W0RK authored Jan 16, 2025
1 parent 247e7fb commit 0113946
Show file tree
Hide file tree
Showing 25 changed files with 663 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package de.tum.cit.aet.artemis.atlas.api;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import java.util.Set;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Controller;

import de.tum.cit.aet.artemis.atlas.service.profile.CourseLearnerProfileService;
import de.tum.cit.aet.artemis.atlas.service.profile.LearnerProfileService;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.User;

@Controller
@Profile(PROFILE_CORE)
public class LearnerProfileApi extends AbstractAtlasApi {

private final LearnerProfileService learnerProfileService;

private final CourseLearnerProfileService courseLearnerProfileService;

public LearnerProfileApi(LearnerProfileService learnerProfileService, CourseLearnerProfileService courseLearnerProfileService) {
this.learnerProfileService = learnerProfileService;
this.courseLearnerProfileService = courseLearnerProfileService;
}

public void deleteAllForCourse(Course course) {
courseLearnerProfileService.deleteAllForCourse(course);
}

public void createCourseLearnerProfile(Course course, User user) {
courseLearnerProfileService.createCourseLearnerProfile(course, user);
}

public void createCourseLearnerProfiles(Course course, Set<User> students) {
courseLearnerProfileService.createCourseLearnerProfiles(course, students);
}

public void deleteCourseLearnerProfile(Course course, User user) {
courseLearnerProfileService.deleteCourseLearnerProfile(course, user);
}

public void createProfile(User user) {
learnerProfileService.createProfile(user);
}

public void deleteProfile(User user) {
learnerProfileService.deleteProfile(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package de.tum.cit.aet.artemis.atlas.domain.profile;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;

import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.DomainObject;

@Entity
@Table(name = "course_learner_profile")
public class CourseLearnerProfile extends DomainObject {

@ManyToOne
@JoinColumn(name = "learner_profile_id")
private LearnerProfile learnerProfile;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "course_id")
private Course course;

@Column(name = "aim_for_grade_or_bonus")
@Min(0)
@Max(5)
private int aimForGradeOrBonus;

@Column(name = "time_investment")
@Min(0)
@Max(5)
private int timeInvestment;

@Column(name = "repetition_intensity")
@Min(0)
@Max(5)
private int repetitionIntensity;

public void setLearnerProfile(LearnerProfile learnerProfile) {
this.learnerProfile = learnerProfile;
}

public LearnerProfile getLearnerProfile() {
return this.learnerProfile;
}

public void setCourse(Course course) {
this.course = course;
}

public Course getCourse() {
return this.course;
}

public int getAimForGradeOrBonus() {
return aimForGradeOrBonus;
}

public void setAimForGradeOrBonus(int aimForGradeOrBonus) {
this.aimForGradeOrBonus = aimForGradeOrBonus;
}

public int getTimeInvestment() {
return timeInvestment;
}

public void setTimeInvestment(int timeInvestment) {
this.timeInvestment = timeInvestment;
}

public int getRepetitionIntensity() {
return repetitionIntensity;
}

public void setRepetitionIntensity(int repetitionIntensity) {
this.repetitionIntensity = repetitionIntensity;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package de.tum.cit.aet.artemis.atlas.domain.profile;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;

import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.core.domain.User;

@Entity
@Table(name = "learner_profile")
public class LearnerProfile extends DomainObject {

@OneToOne(mappedBy = "learnerProfile", cascade = CascadeType.PERSIST)
private User user;

@OneToMany(mappedBy = "learnerProfile", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<CourseLearnerProfile> courseLearnerProfiles = new HashSet<>();

public void setUser(User user) {
this.user = user;
}

public User getUser() {
return this.user;
}

public void setCourseLearnerProfiles(Set<CourseLearnerProfile> courseLearnerProfiles) {
this.courseLearnerProfiles = courseLearnerProfiles;
}

public Set<CourseLearnerProfile> getCourseLearnerProfiles() {
return this.courseLearnerProfiles;
}

public boolean addCourseLearnerProfile(CourseLearnerProfile courseLearnerProfile) {
return this.courseLearnerProfiles.add(courseLearnerProfile);
}

public boolean addAllCourseLearnerProfiles(Collection<? extends CourseLearnerProfile> courseLearnerProfiles) {
return this.courseLearnerProfiles.addAll(courseLearnerProfiles);
}

public boolean removeCourseLearnerProfile(CourseLearnerProfile courseLearnerProfile) {
return this.courseLearnerProfiles.remove(courseLearnerProfile);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.tum.cit.aet.artemis.atlas.repository;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

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

import de.tum.cit.aet.artemis.atlas.domain.profile.CourseLearnerProfile;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.User;
import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository;

@Profile(PROFILE_CORE)
@Repository
public interface CourseLearnerProfileRepository extends ArtemisJpaRepository<CourseLearnerProfile, Long> {

@Transactional // ok because of delete
@Modifying
@Query("""
DELETE FROM CourseLearnerProfile clp
WHERE clp.course = :course AND clp.learnerProfile.user = :user
""")
void deleteByCourseAndUser(@Param("course") Course course, @Param("user") User user);

@Transactional // ok because of delete
@Modifying
void deleteAllByCourse(Course couese);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.tum.cit.aet.artemis.atlas.repository;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import java.util.Optional;

import org.springframework.context.annotation.Profile;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import de.tum.cit.aet.artemis.atlas.domain.profile.LearnerProfile;
import de.tum.cit.aet.artemis.core.domain.User;
import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository;

@Profile(PROFILE_CORE)
@Repository
public interface LearnerProfileRepository extends ArtemisJpaRepository<LearnerProfile, Long> {

Optional<LearnerProfile> findByUser(User user);

default LearnerProfile findByUserElseThrow(User user) {
return getValueElseThrow(findByUser(user));
}

@Transactional // ok because of delete
@Modifying
void deleteByUser(User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository;
import de.tum.cit.aet.artemis.atlas.repository.LearningPathRepository;
import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService;
import de.tum.cit.aet.artemis.atlas.service.profile.CourseLearnerProfileService;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.User;
import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO;
Expand Down Expand Up @@ -90,10 +91,13 @@ public class LearningPathService {

private final CourseCompetencyRepository courseCompetencyRepository;

private final CourseLearnerProfileService courseLearnerProfileService;

public LearningPathService(UserRepository userRepository, LearningPathRepository learningPathRepository, CompetencyProgressRepository competencyProgressRepository,
LearningPathNavigationService learningPathNavigationService, CourseRepository courseRepository, CompetencyRepository competencyRepository,
CompetencyRelationRepository competencyRelationRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository,
StudentParticipationRepository studentParticipationRepository, CourseCompetencyRepository courseCompetencyRepository) {
StudentParticipationRepository studentParticipationRepository, CourseCompetencyRepository courseCompetencyRepository,
CourseLearnerProfileService courseLearnerProfileService) {
this.userRepository = userRepository;
this.learningPathRepository = learningPathRepository;
this.competencyProgressRepository = competencyProgressRepository;
Expand All @@ -104,6 +108,7 @@ public LearningPathService(UserRepository userRepository, LearningPathRepository
this.lectureUnitCompletionRepository = lectureUnitCompletionRepository;
this.studentParticipationRepository = studentParticipationRepository;
this.courseCompetencyRepository = courseCompetencyRepository;
this.courseLearnerProfileService = courseLearnerProfileService;
}

/**
Expand All @@ -113,7 +118,9 @@ public LearningPathService(UserRepository userRepository, LearningPathRepository
*/
public void enableLearningPathsForCourse(@NotNull Course course) {
course.setLearningPathsEnabled(true);
generateLearningPaths(course);
Set<User> students = userRepository.getStudentsWithLearnerProfile(course);
courseLearnerProfileService.createCourseLearnerProfiles(course, students);
generateLearningPaths(course, students);
courseRepository.save(course);
log.debug("Enabled learning paths for course (id={})", course.getId());
}
Expand All @@ -124,7 +131,17 @@ public void enableLearningPathsForCourse(@NotNull Course course) {
* @param course course the learning paths are created for
*/
public void generateLearningPaths(@NotNull Course course) {
var students = userRepository.getStudents(course);
Set<User> students = userRepository.getStudentsWithLearnerProfile(course);
generateLearningPaths(course, students);
}

/**
* Generate learning paths for all students enrolled in the course
*
* @param course course the learning paths are created for
* @param students students for which the learning paths are generated with eager loaded learner profiles
*/
public void generateLearningPaths(@NotNull Course course, Set<User> students) {
students.forEach(student -> generateLearningPathForUser(course, student));
log.debug("Successfully created learning paths for all {} students in course (id={})", students.size(), course.getId());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package de.tum.cit.aet.artemis.atlas.service.profile;

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;

import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

import de.tum.cit.aet.artemis.atlas.domain.profile.CourseLearnerProfile;
import de.tum.cit.aet.artemis.atlas.repository.CourseLearnerProfileRepository;
import de.tum.cit.aet.artemis.atlas.repository.LearnerProfileRepository;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.domain.User;

@Profile(PROFILE_CORE)
@Service
public class CourseLearnerProfileService {

private final CourseLearnerProfileRepository courseLearnerProfileRepository;

private final LearnerProfileRepository learnerProfileRepository;

public CourseLearnerProfileService(CourseLearnerProfileRepository courseLearnerProfileRepository, LearnerProfileRepository learnerProfileRepository) {
this.courseLearnerProfileRepository = courseLearnerProfileRepository;
this.learnerProfileRepository = learnerProfileRepository;
}

/**
* Create a course learner profile for a user and saves it in the database
*
* @param course the course for which the profile is created
* @param user the user for which the profile is created
*/
public void createCourseLearnerProfile(Course course, User user) {
var courseProfile = new CourseLearnerProfile();
courseProfile.setCourse(course);

var learnerProfile = learnerProfileRepository.findByUserElseThrow(user);
courseProfile.setLearnerProfile(learnerProfile);

courseLearnerProfileRepository.save(courseProfile);
}

/**
* Create course learner profiles for a set of users and saves them in the database.
*
* @param course the course for which the profiles are created
* @param users the users for which the profiles are created with eagerly loaded learner profiles
*/
public void createCourseLearnerProfiles(Course course, Set<User> users) {
Set<CourseLearnerProfile> courseProfiles = users.stream().map(user -> {
var courseProfile = new CourseLearnerProfile();
courseProfile.setCourse(course);
courseProfile.setLearnerProfile(user.getLearnerProfile());

return courseProfile;
}).collect(Collectors.toSet());

courseLearnerProfileRepository.saveAll(courseProfiles);
}

/**
* Delete a course learner profile for a user
*
* @param course the course for which the profile is deleted
* @param user the user for which the profile is deleted
*/
public void deleteCourseLearnerProfile(Course course, User user) {
courseLearnerProfileRepository.deleteByCourseAndUser(course, user);
}

/**
* Delete all course learner profiles for a course
*
* @param course the course for which the profiles are deleted
*/
public void deleteAllForCourse(Course course) {
courseLearnerProfileRepository.deleteAllByCourse(course);
}
}
Loading

0 comments on commit 0113946

Please sign in to comment.