Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adaptive learning: Visualize competencies linked to exercises correctly for exercise lecture units #9726

Merged
merged 4 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

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.stereotype.Repository;

import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink;
Expand All @@ -12,4 +15,11 @@
@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(long exerciseId);
MaximilianAnzinger marked this conversation as resolved.
Show resolved Hide resolved

}
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);
MaximilianAnzinger marked this conversation as resolved.
Show resolved Hide resolved
Course course = lecture.getCourse();
if (course == null) {
return ResponseEntity.badRequest().build();
Expand All @@ -322,6 +327,7 @@ public ResponseEntity<Lecture> getLectureWithDetails(@PathVariable Long lectureI
public ResponseEntity<Lecture> getLectureWithDetailsAndSlides(@PathVariable long lectureId) {
log.debug("REST request to get lecture {} with details with slides ", lectureId);
Lecture lecture = lectureRepository.findByIdWithLectureUnitsAndSlidesAndAttachmentsElseThrow(lectureId);
competencyService.addCompetencyLinksToExerciseUnits(lecture);
Course course = lecture.getCourse();
if (course == null) {
return ResponseEntity.badRequest().build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class CompetenciesPopoverComponent implements OnInit {
constructor() {}

ngOnInit(): void {
console.log(this.competencyLinks);
MaximilianAnzinger marked this conversation as resolved.
Show resolved Hide resolved
if (this.courseId) {
switch (this.navigateTo) {
case 'courseCompetencies': {
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
Loading