Skip to content

Commit

Permalink
Adaptive learning: Visualize competencies linked to exercises correct…
Browse files Browse the repository at this point in the history
…ly for exercise lecture units (#9726)
  • Loading branch information
MaximilianAnzinger authored Nov 12, 2024
1 parent de36813 commit 8205b58
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

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

import java.util.List;

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.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink;
Expand All @@ -12,4 +16,10 @@
@Repository
public interface CompetencyExerciseLinkRepository extends ArtemisJpaRepository<CompetencyExerciseLink, Long> {

@Query("""
SELECT cel FROM CompetencyExerciseLink cel
LEFT JOIN FETCH cel.competency
WHERE cel.exercise.id = :exerciseId
""")
List<CompetencyExerciseLink> findByExerciseIdWithCompetency(@Param("exerciseId") long exerciseId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
import java.util.HashMap;
import java.util.List;
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.competency.Competency;
import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink;
import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency;
import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO;
import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO;
import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository;
import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository;
import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository;
import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository;
Expand All @@ -27,6 +30,8 @@
import de.tum.cit.aet.artemis.core.repository.CourseRepository;
import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
import de.tum.cit.aet.artemis.exercise.service.ExerciseService;
import de.tum.cit.aet.artemis.lecture.domain.ExerciseUnit;
import de.tum.cit.aet.artemis.lecture.domain.Lecture;
import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository;
import de.tum.cit.aet.artemis.lecture.service.LectureUnitService;

Expand All @@ -39,15 +44,19 @@ public class CompetencyService extends CourseCompetencyService {

private final CompetencyRepository competencyRepository;

private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository;

public CompetencyService(CompetencyRepository competencyRepository, AuthorizationCheckService authCheckService, CompetencyRelationRepository competencyRelationRepository,
LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService,
CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository,
StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService,
LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository, CourseRepository courseRepository) {
LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository, CourseRepository courseRepository,
CompetencyExerciseLinkRepository competencyExerciseLinkRepository) {
super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService,
learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService,
competencyLectureUnitLinkRepository, courseRepository);
this.competencyRepository = competencyRepository;
this.competencyExerciseLinkRepository = competencyExerciseLinkRepository;
}

/**
Expand Down Expand Up @@ -121,4 +130,26 @@ public List<Competency> findCompetenciesWithProgressForUserByCourseId(Long cours
List<Competency> competencies = competencyRepository.findByCourseIdOrderById(courseId);
return findProgressForCompetenciesAndUser(competencies, userId);
}

/**
* Creates competency links for exercise units of the lecture.
* <p>
* As exercise units can not be linked to competencies but only via the exercise itself, we add temporary links to the exercise units.
* Although they can not be persisted, this makes it easier to display the linked competencies in the client consistently across all lecture unit type.
*
* @param lecture the lecture to augment the exercise unit links for
*/
public void addCompetencyLinksToExerciseUnits(Lecture lecture) {
var exerciseUnits = lecture.getLectureUnits().stream().filter(unit -> unit instanceof ExerciseUnit);
exerciseUnits.forEach(unit -> {
var exerciseUnit = (ExerciseUnit) unit;
var exercise = exerciseUnit.getExercise();
if (exercise != null) {
var competencyExerciseLinks = competencyExerciseLinkRepository.findByExerciseIdWithCompetency(exercise.getId());
var competencyLectureUnitLinks = competencyExerciseLinks.stream().map(link -> new CompetencyLectureUnitLink(link.getCompetency(), exerciseUnit, link.getWeight()))
.collect(Collectors.toSet());
exerciseUnit.setCompetencyLinks(competencyLectureUnitLinks);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Transient;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink;
import de.tum.cit.aet.artemis.exercise.domain.Exercise;
Expand All @@ -32,6 +34,13 @@ public class ExerciseUnit extends LectureUnit {
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Exercise exercise;

// Competency links are not persisted in this entity but only in the exercise itself
@Transient
@JsonSerialize
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties("lectureUnit")
private Set<CompetencyLectureUnitLink> competencyLinks = new HashSet<>();

public Exercise getExercise() {
return exercise;
}
Expand Down Expand Up @@ -66,15 +75,16 @@ public void setReleaseDate(ZonedDateTime releaseDate) {
}

@Override
@JsonIgnore
@JsonSerialize
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties("lectureUnit")
public Set<CompetencyLectureUnitLink> getCompetencyLinks() {
// Set the links in the associated exercise instead
return new HashSet<>();
return competencyLinks;
}

@Override
public void setCompetencyLinks(Set<CompetencyLectureUnitLink> competencyLinks) {
// Retrieve the link in the associated exercise instead"
this.competencyLinks = competencyLinks;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyService;
import de.tum.cit.aet.artemis.communication.domain.conversation.Channel;
import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository;
import de.tum.cit.aet.artemis.communication.service.conversation.ChannelService;
Expand Down Expand Up @@ -66,6 +67,8 @@ public class LectureResource {

private static final String ENTITY_NAME = "lecture";

private final CompetencyService competencyService;

@Value("${jhipster.clientApp.name}")
private String applicationName;

Expand All @@ -89,7 +92,7 @@ public class LectureResource {

public LectureResource(LectureRepository lectureRepository, LectureService lectureService, LectureImportService lectureImportService, CourseRepository courseRepository,
UserRepository userRepository, AuthorizationCheckService authCheckService, ExerciseService exerciseService, ChannelService channelService,
ChannelRepository channelRepository) {
ChannelRepository channelRepository, CompetencyService competencyService) {
this.lectureRepository = lectureRepository;
this.lectureService = lectureService;
this.lectureImportService = lectureImportService;
Expand All @@ -99,6 +102,7 @@ public LectureResource(LectureRepository lectureRepository, LectureService lectu
this.exerciseService = exerciseService;
this.channelService = channelService;
this.channelRepository = channelRepository;
this.competencyService = competencyService;
}

/**
Expand Down Expand Up @@ -300,6 +304,7 @@ public ResponseEntity<Boolean> ingestLectures(@PathVariable Long courseId, @Requ
public ResponseEntity<Lecture> getLectureWithDetails(@PathVariable Long lectureId) {
log.debug("REST request to get lecture {} with details", lectureId);
Lecture lecture = lectureRepository.findByIdWithAttachmentsAndPostsAndLectureUnitsAndCompetenciesAndCompletionsElseThrow(lectureId);
competencyService.addCompetencyLinksToExerciseUnits(lecture);
Course course = lecture.getCourse();
if (course == null) {
return ResponseEntity.badRequest().build();
Expand All @@ -326,9 +331,10 @@ public ResponseEntity<Lecture> getLectureWithDetailsAndSlides(@PathVariable long
if (course == null) {
return ResponseEntity.badRequest().build();
}
authCheckService.checkIsAllowedToSeeLectureElseThrow(lecture, userRepository.getUserWithGroupsAndAuthorities());

User user = userRepository.getUserWithGroupsAndAuthorities();
authCheckService.checkIsAllowedToSeeLectureElseThrow(lecture, user);

competencyService.addCompetencyLinksToExerciseUnits(lecture);
lectureService.filterActiveAttachmentUnits(lecture);
lectureService.filterActiveAttachments(lecture, user);
return ResponseEntity.ok(lecture);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ void initTestCase() throws Exception {
channel.setLecture(this.lecture1);
channelRepository.save(channel);
textExercise = textExerciseRepository.findByCourseIdWithCategories(course1.getId()).stream().findFirst().orElseThrow();

// Add users that are not in the course
userUtilService.createAndSaveUser(TEST_PREFIX + "student42");
userUtilService.createAndSaveUser(TEST_PREFIX + "instructor42");
Expand All @@ -126,6 +127,7 @@ void initTestCase() throws Exception {
lecture1 = lectureUtilService.addLectureUnitsToLecture(this.lecture1, List.of(exerciseUnit, attachmentUnit, videoUnit, textUnit, onlineUnit));

competency = competencyUtilService.createCompetency(course1);
competencyUtilService.linkExerciseToCompetency(competency, textExercise);
}

private void addAttachmentToLecture() {
Expand Down Expand Up @@ -296,6 +298,8 @@ void getLecture_ExerciseAndAttachmentReleased_shouldGetLectureWithAllLectureUnit
Lecture receivedLectureWithDetails = request.get("/api/lectures/" + lecture1.getId() + "/details", HttpStatus.OK, Lecture.class);
assertThat(receivedLectureWithDetails.getId()).isEqualTo(lecture1.getId());
assertThat(receivedLectureWithDetails.getLectureUnits()).hasSize(5);
assertThat(receivedLectureWithDetails.getLectureUnits().stream().filter(lectureUnit -> lectureUnit instanceof ExerciseUnit).toList().getFirst().getCompetencyLinks())
.hasSize(1);
assertThat(receivedLectureWithDetails.getAttachments()).hasSize(2);

testGetLecture(lecture1.getId());
Expand Down

0 comments on commit 8205b58

Please sign in to comment.