From 6d8fa75c631360f91a705ace5694ececcf1c1ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Tue, 29 Oct 2024 15:57:11 +0100 Subject: [PATCH 1/7] Remove ngx graph --- .../artemis/atlas/dto/NgxLearningPathDTO.java | 47 -- .../learningpath/LearningPathNgxService.java | 393 ------------- .../learningpath/LearningPathService.java | 31 +- .../atlas/web/LearningPathResource.java | 41 -- .../LearningPathIntegrationTest.java | 108 ---- .../service/LearningPathServiceTest.java | 556 ------------------ 6 files changed, 2 insertions(+), 1174 deletions(-) delete mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/dto/NgxLearningPathDTO.java delete mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/NgxLearningPathDTO.java b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/NgxLearningPathDTO.java deleted file mode 100644 index 71e04139223d..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/NgxLearningPathDTO.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.tum.cit.aet.artemis.atlas.dto; - -import java.util.Set; - -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * Represents simplified learning path optimized for Ngx representation - */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record NgxLearningPathDTO(Set nodes, Set edges) { - - public record Node(String id, NodeType type, Long linkedResource, Long linkedResourceParent, boolean completed, String label) { - - public static Node of(String id, NodeType type, Long linkedResource, Long linkedResourceParent, boolean completed, String label) { - return new Node(id, type, linkedResource, linkedResourceParent, completed, label); - } - - public static Node of(String id, NodeType type, Long linkedResource, boolean completed, String label) { - return of(id, type, linkedResource, null, completed, label); - } - - public static Node of(String id, NodeType type, Long linkedResource, String label) { - return of(id, type, linkedResource, false, label); - } - - public static Node of(String id, NodeType type, String label) { - return of(id, type, null, label); - } - - public static Node of(String id, NodeType type, Long linkedResource) { - return of(id, type, linkedResource, ""); - } - - public static Node of(String id, NodeType type) { - return of(id, type, null, ""); - } - } - - @JsonInclude(JsonInclude.Include.NON_EMPTY) - public record Edge(String id, String source, String target) { - } - - public enum NodeType { - COMPETENCY_START, COMPETENCY_END, MATCH_START, MATCH_END, EXERCISE, LECTURE_UNIT, - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java deleted file mode 100644 index 8a3b228a27e5..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java +++ /dev/null @@ -1,393 +0,0 @@ -package de.tum.cit.aet.artemis.atlas.service.learningpath; - -import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import java.util.stream.LongStream; -import java.util.stream.Stream; - -import jakarta.validation.constraints.NotNull; - -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Service; - -import de.tum.cit.aet.artemis.atlas.domain.LearningObject; -import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; -import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath; -import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; -import de.tum.cit.aet.artemis.atlas.dto.NgxLearningPathDTO; -import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; -import de.tum.cit.aet.artemis.atlas.service.util.UnionFind; -import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; - -/** - * Service Implementation for the generation of ngx representations of learning paths. - */ -@Profile(PROFILE_CORE) -@Service -public class LearningPathNgxService { - - private final CompetencyRelationRepository competencyRelationRepository; - - private final LearningPathRecommendationService learningPathRecommendationService; - - protected LearningPathNgxService(CompetencyRelationRepository competencyRelationRepository, LearningPathRecommendationService learningPathRecommendationService) { - this.competencyRelationRepository = competencyRelationRepository; - this.learningPathRecommendationService = learningPathRecommendationService; - } - - /** - * Generates Ngx graph representation of the learning path graph. - * - * @param learningPath the learning path for which the Ngx representation should be created - * @return Ngx graph representation of the learning path - * @see NgxLearningPathDTO - */ - public NgxLearningPathDTO generateNgxGraphRepresentation(@NotNull LearningPath learningPath) { - Set nodes = new HashSet<>(); - Set edges = new HashSet<>(); - learningPath.getCompetencies().forEach(competency -> generateNgxGraphRepresentationForCompetency(learningPath, competency, nodes, edges)); - generateNgxGraphRepresentationForRelations(learningPath, nodes, edges); - return new NgxLearningPathDTO(nodes, edges); - } - - /** - * Generates Ngx graph representation for competency. - *

- * A competency's representation consists of - *

    - *
  • start node
  • - *
  • end node
  • - *
  • a node for each learning unit (exercises or lecture unit)
  • - *
  • edges from start node to each learning unit
  • - *
  • edges from each learning unit to end node
  • - *
- * - * @param learningPath the learning path for which the representation should be created - * @param competency the competency for which the representation will be created - * @param nodes set of nodes to store the new nodes - * @param edges set of edges to store the new edges - */ - private void generateNgxGraphRepresentationForCompetency(LearningPath learningPath, CourseCompetency competency, Set nodes, - Set edges) { - Set currentCluster = new HashSet<>(); - // generates start and end node - final var startNodeId = getCompetencyStartNodeId(competency.getId()); - final var endNodeId = getCompetencyEndNodeId(competency.getId()); - currentCluster.add(NgxLearningPathDTO.Node.of(startNodeId, NgxLearningPathDTO.NodeType.COMPETENCY_START, competency.getId())); - currentCluster.add(NgxLearningPathDTO.Node.of(endNodeId, NgxLearningPathDTO.NodeType.COMPETENCY_END, competency.getId())); - - // generate nodes and edges for lecture units - competency.getLectureUnits().forEach(lectureUnit -> { - currentCluster.add(NgxLearningPathDTO.Node.of(getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), NgxLearningPathDTO.NodeType.LECTURE_UNIT, - lectureUnit.getId(), lectureUnit.getLecture().getId(), lectureUnit.isCompletedFor(learningPath.getUser()), lectureUnit.getName())); - edges.add(new NgxLearningPathDTO.Edge(getLectureUnitInEdgeId(competency.getId(), lectureUnit.getId()), startNodeId, - getLectureUnitNodeId(competency.getId(), lectureUnit.getId()))); - edges.add(new NgxLearningPathDTO.Edge(getLectureUnitOutEdgeId(competency.getId(), lectureUnit.getId()), getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), - endNodeId)); - }); - // generate nodes and edges for exercises - competency.getExercises().forEach(exercise -> { - currentCluster.add(NgxLearningPathDTO.Node.of(getExerciseNodeId(competency.getId(), exercise.getId()), NgxLearningPathDTO.NodeType.EXERCISE, exercise.getId(), false, - exercise.getTitle())); - edges.add(new NgxLearningPathDTO.Edge(getExerciseInEdgeId(competency.getId(), exercise.getId()), startNodeId, getExerciseNodeId(competency.getId(), exercise.getId()))); - edges.add(new NgxLearningPathDTO.Edge(getExerciseOutEdgeId(competency.getId(), exercise.getId()), getExerciseNodeId(competency.getId(), exercise.getId()), endNodeId)); - }); - // if no linked learning units exist directly link start to end - if (currentCluster.size() == 2) { - edges.add(new NgxLearningPathDTO.Edge(getDirectEdgeId(competency.getId()), startNodeId, endNodeId)); - } - - nodes.addAll(currentCluster); - } - - /** - * Generates Ngx graph representations for competency relations. - *

- * The representation will contain: - *

    - *
  • - * For each matching cluster (transitive closure of competencies that are in a match relation): - *
      - *
    • two nodes (start and end of cluster) will be created
    • - *
    • edges from the start node of the cluster to each start node of the competencies
    • - *
    • edges from each end node of the competency to the end node of the cluster
    • - *
    - *
  • - *
  • - * For each other relation: edge from head competency end node to tail competency start node. If competency is part of a matching cluster, the edge will be linked to the - * corresponding cluster start/end node. - *
  • - *
- * - * two nodes (start and end of cluster) will be created. - * - * @param learningPath the learning path for which the Ngx representation should be created - * @param nodes set of nodes to store the new nodes - * @param edges set of edges to store the new edges - */ - private void generateNgxGraphRepresentationForRelations(LearningPath learningPath, Set nodes, Set edges) { - final var relations = competencyRelationRepository.findAllWithHeadAndTailByCourseId(learningPath.getCourse().getId()); - - // compute match clusters - Map competencyToMatchCluster = new HashMap<>(); - final var competenciesInMatchRelation = relations.stream().filter(relation -> relation.getType().equals(RelationType.MATCHES)) - .flatMap(relation -> Stream.of(relation.getHeadCompetency().getId(), relation.getTailCompetency().getId())).collect(Collectors.toSet()); - if (!competenciesInMatchRelation.isEmpty()) { - UnionFind matchClusters = new UnionFind<>(competenciesInMatchRelation); - relations.stream().filter(relation -> relation.getType().equals(RelationType.MATCHES)) - .forEach(relation -> matchClusters.union(relation.getHeadCompetency().getId(), relation.getTailCompetency().getId())); - - // generate map between competencies and cluster node - AtomicInteger matchClusterId = new AtomicInteger(); - relations.stream().filter(relation -> relation.getType().equals(RelationType.MATCHES)) - .flatMapToLong(relation -> LongStream.of(relation.getHeadCompetency().getId(), relation.getTailCompetency().getId())).distinct().forEach(competencyId -> { - var parentId = matchClusters.find(competencyId); - var clusterId = competencyToMatchCluster.computeIfAbsent(parentId, (key) -> matchClusterId.getAndIncrement()); - competencyToMatchCluster.put(competencyId, clusterId); - }); - - // generate match cluster start and end nodes - for (int i = 0; i < matchClusters.numberOfSets(); i++) { - nodes.add(NgxLearningPathDTO.Node.of(getMatchingClusterStartNodeId(i), NgxLearningPathDTO.NodeType.MATCH_START)); - nodes.add(NgxLearningPathDTO.Node.of(getMatchingClusterEndNodeId(i), NgxLearningPathDTO.NodeType.MATCH_END)); - } - - // generate edges between match cluster nodes and corresponding competencies - competencyToMatchCluster.forEach((competency, cluster) -> { - edges.add(new NgxLearningPathDTO.Edge(getInEdgeId(competency), getMatchingClusterStartNodeId(cluster), getCompetencyStartNodeId(competency))); - edges.add(new NgxLearningPathDTO.Edge(getOutEdgeId(competency), getCompetencyEndNodeId(competency), getMatchingClusterEndNodeId(cluster))); - }); - } - - // generate edges for remaining relations - final Set createdRelations = new HashSet<>(); - relations.stream().filter(relation -> !relation.getType().equals(RelationType.MATCHES)) - .forEach(relation -> generateNgxGraphRepresentationForRelation(relation, competencyToMatchCluster, createdRelations, edges)); - } - - /** - * Generates Ngx graph representations for competency relation. - * - * @param relation the relation for which the Ngx representation should be created - * @param competencyToMatchCluster map from competencies to corresponding cluster - * @param createdRelations set of edge ids that have already been created - * @param edges set of edges to store the new edges - */ - private void generateNgxGraphRepresentationForRelation(CompetencyRelation relation, Map competencyToMatchCluster, Set createdRelations, - Set edges) { - final var sourceId = relation.getHeadCompetency().getId(); - String sourceNodeId; - if (competencyToMatchCluster.containsKey(sourceId)) { - sourceNodeId = getMatchingClusterEndNodeId(competencyToMatchCluster.get(sourceId)); - } - else { - sourceNodeId = getCompetencyEndNodeId(sourceId); - } - final var targetId = relation.getTailCompetency().getId(); - String targetNodeId; - if (competencyToMatchCluster.containsKey(targetId)) { - targetNodeId = getMatchingClusterStartNodeId(competencyToMatchCluster.get(targetId)); - } - else { - targetNodeId = getCompetencyStartNodeId(targetId); - } - final String relationEdgeId = getEdgeFromToId(sourceNodeId, targetNodeId); - // skip if relation has already been created (possible for edges linked to matching cluster start/end nodes) - if (!createdRelations.contains(relationEdgeId)) { - final var edge = new NgxLearningPathDTO.Edge(relationEdgeId, sourceNodeId, targetNodeId); - edges.add(edge); - createdRelations.add(relationEdgeId); - } - } - - /** - * Generates Ngx path representation of the learning path. - * - * @param learningPath the learning path for which the Ngx representation should be created - * @return Ngx path representation of the learning path - * @see NgxLearningPathDTO - */ - public NgxLearningPathDTO generateNgxPathRepresentation(@NotNull LearningPath learningPath) { - Set nodes = new HashSet<>(); - Set edges = new HashSet<>(); - - var recommendationState = learningPathRecommendationService.getRecommendedOrderOfNotMasteredCompetencies(learningPath); - var recommendedOrderOfCompetencies = recommendationState.recommendedOrderOfCompetencies().stream() - .map(id -> learningPath.getCompetencies().stream().filter(competency -> competency.getId().equals(id)).findFirst().get()).toList(); - - // generate ngx representation of recommended competencies - recommendedOrderOfCompetencies.forEach(competency -> generateNgxPathRepresentationForCompetency(learningPath.getUser(), competency, nodes, edges, recommendationState)); - // generate edges between competencies - for (int i = 0; i < recommendedOrderOfCompetencies.size() - 1; i++) { - var sourceNodeId = getCompetencyEndNodeId(recommendationState.recommendedOrderOfCompetencies().get(i)); - var targetNodeId = getCompetencyStartNodeId(recommendationState.recommendedOrderOfCompetencies().get(i + 1)); - edges.add(new NgxLearningPathDTO.Edge(getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - } - - return new NgxLearningPathDTO(nodes, edges); - } - - /** - * Generates Ngx graph representation for competency. - *

- * A competency's representation consists of - *

    - *
  • start node
  • - *
  • end node
  • - *
  • a node for each learning unit (exercises or lecture unit)
  • - *
  • edges from start node to each learning unit
  • - *
  • edges from each learning unit to end node
  • - *
- * - * @param user the user for which the representation should be created - * @param competency the competency for which the representation will be created - * @param nodes set of nodes to store the new nodes - * @param edges set of edges to store the new edges - */ - private void generateNgxPathRepresentationForCompetency(User user, CourseCompetency competency, Set nodes, Set edges, - LearningPathRecommendationService.RecommendationState state) { - Set currentCluster = new HashSet<>(); - // generates start and end node - final var startNodeId = getCompetencyStartNodeId(competency.getId()); - final var endNodeId = getCompetencyEndNodeId(competency.getId()); - currentCluster.add(NgxLearningPathDTO.Node.of(startNodeId, NgxLearningPathDTO.NodeType.COMPETENCY_START, competency.getId())); - currentCluster.add(NgxLearningPathDTO.Node.of(endNodeId, NgxLearningPathDTO.NodeType.COMPETENCY_END, competency.getId())); - - final var recommendedLearningObjects = learningPathRecommendationService.getRecommendedOrderOfLearningObjects(user, competency, state); - for (int i = 0; i < recommendedLearningObjects.size(); i++) { - - // add node for learning object - addNodeForLearningObject(competency, recommendedLearningObjects.get(i), currentCluster); - - // add edges between learning objects - if (i != 0) { - addEdgeBetweenLearningObjects(competency, recommendedLearningObjects.get(i - 1), recommendedLearningObjects.get(i), edges); - } - } - - // if no linked learning units exist directly link start to end (can't happen for valid recommendations) - if (currentCluster.size() == 2) { - edges.add(new NgxLearningPathDTO.Edge(getDirectEdgeId(competency.getId()), startNodeId, endNodeId)); - } - else { - // add edge from competency start to first learning object - addEdgeFromCompetencyStartToLearningObject(competency, recommendedLearningObjects.getFirst(), edges); - - // add edge from last learning object to competency end - addEdgeFromLearningObjectToCompetencyEnd(competency, recommendedLearningObjects.getLast(), edges); - } - - nodes.addAll(currentCluster); - } - - private static void addNodeForLearningObject(CourseCompetency competency, LearningObject learningObject, Set nodes) { - if (learningObject instanceof LectureUnit lectureUnit) { - nodes.add(NgxLearningPathDTO.Node.of(getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), NgxLearningPathDTO.NodeType.LECTURE_UNIT, lectureUnit.getId(), - lectureUnit.getLecture().getId(), false, lectureUnit.getName())); - } - else if (learningObject instanceof Exercise exercise) { - nodes.add(NgxLearningPathDTO.Node.of(getExerciseNodeId(competency.getId(), exercise.getId()), NgxLearningPathDTO.NodeType.EXERCISE, exercise.getId(), false, - exercise.getTitle())); - } - } - - private static void addEdgeBetweenLearningObjects(CourseCompetency competency, LearningObject source, LearningObject target, Set edges) { - final var sourceId = getLearningObjectNodeId(competency.getId(), source); - final var targetId = getLearningObjectNodeId(competency.getId(), target); - edges.add(new NgxLearningPathDTO.Edge(getEdgeFromToId(sourceId, targetId), sourceId, targetId)); - } - - private static void addEdgeFromCompetencyStartToLearningObject(CourseCompetency competency, LearningObject learningObject, Set edges) { - addEdgeFromSourceToTarget(getCompetencyStartNodeId(competency.getId()), getLearningObjectNodeId(competency.getId(), learningObject), edges); - } - - private static void addEdgeFromLearningObjectToCompetencyEnd(CourseCompetency competency, LearningObject learningObject, Set edges) { - addEdgeFromSourceToTarget(getLearningObjectNodeId(competency.getId(), learningObject), getCompetencyEndNodeId(competency.getId()), edges); - } - - private static void addEdgeFromSourceToTarget(String sourceId, String targetId, Set edges) { - edges.add(new NgxLearningPathDTO.Edge(getEdgeFromToId(sourceId, targetId), sourceId, targetId)); - } - - public static String getCompetencyStartNodeId(long competencyId) { - return "node-" + competencyId + "-start"; - } - - public static String getCompetencyEndNodeId(long competencyId) { - return "node-" + competencyId + "-end"; - } - - public static String getLectureUnitNodeId(long competencyId, long lectureUnitId) { - return "node-" + competencyId + "-lu-" + lectureUnitId; - } - - public static String getExerciseNodeId(long competencyId, long exerciseId) { - return "node-" + competencyId + "-ex-" + exerciseId; - } - - public static String getMatchingClusterStartNodeId(long matchingClusterId) { - return "matching-" + matchingClusterId + "-start"; - } - - public static String getMatchingClusterEndNodeId(long matchingClusterId) { - return "matching-" + matchingClusterId + "-end"; - } - - public static String getLectureUnitInEdgeId(long competencyId, long lectureUnitId) { - return "edge-" + competencyId + "-lu-" + getInEdgeId(lectureUnitId); - } - - public static String getLectureUnitOutEdgeId(long competencyId, long lectureUnitId) { - return "edge-" + competencyId + "-lu-" + getOutEdgeId(lectureUnitId); - } - - public static String getExerciseInEdgeId(long competencyId, long exercise) { - return "edge-" + competencyId + "-ex-" + getInEdgeId(exercise); - } - - public static String getExerciseOutEdgeId(long competencyId, long exercise) { - return "edge-" + competencyId + "-ex-" + getOutEdgeId(exercise); - } - - public static String getInEdgeId(long id) { - return "edge-" + id + "-in"; - } - - public static String getOutEdgeId(long id) { - return "edge-" + id + "-out"; - } - - public static String getEdgeFromToId(String sourceNodeId, String targetNodeId) { - return "edge-" + sourceNodeId + "-" + targetNodeId; - } - - public static String getDirectEdgeId(long competencyId) { - return "edge-" + competencyId + "-direct"; - } - - /** - * Gets the node id of the given lecture unit or exercise. - * - * @param competencyId the id of the competency that the learning object is linked to - * @param learningObject the lecture unit or exercise - * @return the ngx node id of the given lecture unit or exercise - */ - public static String getLearningObjectNodeId(long competencyId, LearningObject learningObject) { - if (learningObject instanceof LectureUnit) { - return getLectureUnitNodeId(competencyId, learningObject.getId()); - } - else if (learningObject instanceof Exercise) { - return getExerciseNodeId(competencyId, learningObject.getId()); - } - return null; - } -} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java index ea2a4bd9ec37..feed1711fbf1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java @@ -29,7 +29,6 @@ import de.tum.cit.aet.artemis.atlas.dto.LearningPathHealthDTO; import de.tum.cit.aet.artemis.atlas.dto.LearningPathInformationDTO; import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationOverviewDTO; -import de.tum.cit.aet.artemis.atlas.dto.NgxLearningPathDTO; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; @@ -84,8 +83,6 @@ public class LearningPathService { private final CompetencyRelationRepository competencyRelationRepository; - private final LearningPathNgxService learningPathNgxService; - private final LectureUnitCompletionRepository lectureUnitCompletionRepository; private final StudentParticipationRepository studentParticipationRepository; @@ -94,9 +91,8 @@ public class LearningPathService { public LearningPathService(UserRepository userRepository, LearningPathRepository learningPathRepository, CompetencyProgressRepository competencyProgressRepository, LearningPathNavigationService learningPathNavigationService, CourseRepository courseRepository, CompetencyRepository competencyRepository, - CompetencyRelationRepository competencyRelationRepository, LearningPathNgxService learningPathNgxService, - LectureUnitCompletionRepository lectureUnitCompletionRepository, StudentParticipationRepository studentParticipationRepository, - CourseCompetencyRepository courseCompetencyRepository) { + CompetencyRelationRepository competencyRelationRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, + StudentParticipationRepository studentParticipationRepository, CourseCompetencyRepository courseCompetencyRepository) { this.userRepository = userRepository; this.learningPathRepository = learningPathRepository; this.competencyProgressRepository = competencyProgressRepository; @@ -104,7 +100,6 @@ public LearningPathService(UserRepository userRepository, LearningPathRepository this.courseRepository = courseRepository; this.competencyRepository = competencyRepository; this.competencyRelationRepository = competencyRelationRepository; - this.learningPathNgxService = learningPathNgxService; this.lectureUnitCompletionRepository = lectureUnitCompletionRepository; this.studentParticipationRepository = studentParticipationRepository; this.courseCompetencyRepository = courseCompetencyRepository; @@ -381,28 +376,6 @@ public LearningPathCompetencyGraphDTO generateLearningPathCompetencyInstructorGr return new LearningPathCompetencyGraphDTO(progressDTOs, relationDTOs); } - /** - * Generates Ngx graph representation of the learning path graph. - * - * @param learningPath the learning path for which the Ngx representation should be created - * @return Ngx graph representation of the learning path - * @see NgxLearningPathDTO - */ - public NgxLearningPathDTO generateNgxGraphRepresentation(@NotNull LearningPath learningPath) { - return this.learningPathNgxService.generateNgxGraphRepresentation(learningPath); - } - - /** - * Generates Ngx path representation of the learning path. - * - * @param learningPath the learning path for which the Ngx representation should be created - * @return Ngx path representation of the learning path - * @see NgxLearningPathDTO - */ - public NgxLearningPathDTO generateNgxPathRepresentation(@NotNull LearningPath learningPath) { - return this.learningPathNgxService.generateNgxPathRepresentation(learningPath); - } - /** * Get the navigation overview for a given learning path. * diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/LearningPathResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/LearningPathResource.java index 43a8135f27cb..6bbdaaa3b91b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/LearningPathResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/LearningPathResource.java @@ -36,7 +36,6 @@ import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationObjectDTO; import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationObjectDTO.LearningObjectType; import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationOverviewDTO; -import de.tum.cit.aet.artemis.atlas.dto.NgxLearningPathDTO; 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.learningpath.LearningPathNavigationService; @@ -218,32 +217,6 @@ public ResponseEntity getLearningPathCompetencyI return ResponseEntity.ok(learningPathService.generateLearningPathCompetencyInstructorGraph(courseId)); } - /** - * GET learning-path/:learningPathId/graph : Gets the ngx representation of the learning path as a graph. - * - * @param learningPathId the id of the learning path that should be fetched - * @return the ResponseEntity with status 200 (OK) and with body the ngx representation of the learning path - */ - @GetMapping("learning-path/{learningPathId}/graph") - @EnforceAtLeastStudent - public ResponseEntity getLearningPathNgxGraph(@PathVariable long learningPathId) { - log.debug("REST request to get ngx graph representation of learning path with id: {}", learningPathId); - return getLearningPathNgx(learningPathId, NgxRequestType.GRAPH); - } - - /** - * GET learning-path/:learningPathId/path : Gets the ngx representation of the learning path as a sequential path. - * - * @param learningPathId the id of the learning path that should be fetched - * @return the ResponseEntity with status 200 (OK) and with body the ngx representation of the learning path - */ - @GetMapping("learning-path/{learningPathId}/path") - @EnforceAtLeastStudent - public ResponseEntity getLearningPathNgxPath(@PathVariable long learningPathId) { - log.debug("REST request to get ngx path representation of learning path with id: {}", learningPathId); - return getLearningPathNgx(learningPathId, NgxRequestType.PATH); - } - /** * GET learning-path/:learningPathId/relative-navigation : Gets the navigation information for the learning path relative to a learning object. * @@ -293,20 +266,6 @@ public ResponseEntity getLearningPathNavigati return ResponseEntity.ok(learningPathService.getLearningPathNavigationOverview(learningPathId)); } - private ResponseEntity getLearningPathNgx(@PathVariable long learningPathId, NgxRequestType type) { - LearningPath learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); - Course course = courseRepository.findByIdElseThrow(learningPath.getCourse().getId()); - courseService.checkLearningPathsEnabledElseThrow(course); - - checkLearningPathAccessElseThrow(Optional.of(course), learningPath, Optional.empty()); - - NgxLearningPathDTO ngxLearningPathDTO = switch (type) { - case GRAPH -> learningPathService.generateNgxGraphRepresentation(learningPath); - case PATH -> learningPathService.generateNgxPathRepresentation(learningPath); - }; - return ResponseEntity.ok(ngxLearningPathDTO); - } - /** * GET courses/:courseId/learning-path/me : Gets the learning path of the current user in the course. * diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java index 7ec124ecc537..7d5ff3e33486 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; @@ -41,9 +40,7 @@ import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationDTO; import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationObjectDTO; import de.tum.cit.aet.artemis.atlas.dto.LearningPathNavigationOverviewDTO; -import de.tum.cit.aet.artemis.atlas.dto.NgxLearningPathDTO; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; -import de.tum.cit.aet.artemis.atlas.web.LearningPathResource; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @@ -133,7 +130,6 @@ private void testAllPreAuthorize() throws Exception { final var search = pageableSearchUtilService.configureSearch(""); request.getSearchResult("/api/courses/" + course.getId() + "/learning-paths", HttpStatus.FORBIDDEN, LearningPath.class, pageableSearchUtilService.searchMapping(search)); request.get("/api/courses/" + course.getId() + "/learning-path-health", HttpStatus.FORBIDDEN, LearningPathHealthDTO.class); - request.get("/api/courses/" + course.getId() + "/learning-path/competency-instructor-graph", HttpStatus.FORBIDDEN, NgxLearningPathDTO.class); } private void enableLearningPathsRESTCall(Course course) throws Exception { @@ -387,24 +383,6 @@ void testGetHealthStatusForCourse() throws Exception { request.get("/api/courses/" + course.getId() + "/learning-path-health", HttpStatus.OK, LearningPathHealthDTO.class); } - @Test - @WithMockUser(username = STUDENT1_OF_COURSE, roles = "USER") - void testGetLearningPathWithOwner() throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - request.get("/api/learning-path/" + learningPath.getId(), HttpStatus.OK, NgxLearningPathDTO.class); - } - - @Test - @WithMockUser(username = STUDENT1_OF_COURSE, roles = "USER") - void testGetLearningPathOfOtherUser() throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var otherStudent = userTestRepository.findOneByLogin(SECOND_STUDENT_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), otherStudent.getId()); - request.get("/api/learning-path/" + learningPath.getId(), HttpStatus.FORBIDDEN, NgxLearningPathDTO.class); - } - @Test @WithMockUser(username = STUDENT1_OF_COURSE, roles = "USER") void testGetLearningPathCompetencyGraphOfOtherUser() throws Exception { @@ -442,92 +420,6 @@ void testGetLearningPathCompetencyGraph() throws Exception { && relation.getTailCompetency().getId() == Long.parseLong(relationDTO.target()) && relation.getHeadCompetency().getId() == Long.parseLong(relationDTO.source()))); } - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = STUDENT1_OF_COURSE, roles = "USER") - void testGetLearningPathNgxForLearningPathsDisabled(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - course.setLearningPathsEnabled(false); - courseRepository.save(course); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.BAD_REQUEST, NgxLearningPathDTO.class); - } - - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = SECOND_STUDENT_OF_COURSE, roles = "USER") - void testGetLearningPathNgxForOtherStudent(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.FORBIDDEN, NgxLearningPathDTO.class); - } - - /** - * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. - * - * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest - */ - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = STUDENT1_OF_COURSE, roles = "USER") - void testGetLearningPathNgxAsStudent(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.OK, NgxLearningPathDTO.class); - } - - /** - * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. - * - * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest - */ - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = TUTOR_OF_COURSE, roles = "TA") - void testGetLearningPathNgxAsTutor(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var tutor = userTestRepository.findOneByLogin(TUTOR_OF_COURSE).orElseThrow(); - final var learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, tutor); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.OK, NgxLearningPathDTO.class); - } - - /** - * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. - * - * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest - */ - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = EDITOR_OF_COURSE, roles = "EDITOR") - void testGetLearningPathNgxAsEditor(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var editor = userTestRepository.findOneByLogin(EDITOR_OF_COURSE).orElseThrow(); - final var learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, editor); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.OK, NgxLearningPathDTO.class); - } - - /** - * This only tests if the end point successfully retrieves the graph representation. The correctness of the response is tested in LearningPathServiceTest. - * - * @throws Exception the request failed - * @see de.tum.cit.aet.artemis.atlas.service.LearningPathServiceTest - */ - @ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}") - @EnumSource(LearningPathResource.NgxRequestType.class) - @WithMockUser(username = INSTRUCTOR_OF_COURSE, roles = "INSTRUCTOR") - void testGetLearningPathNgxAsInstructor(LearningPathResource.NgxRequestType type) throws Exception { - course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); - final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); - final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - request.get("/api/learning-path/" + learningPath.getId() + "/" + type, HttpStatus.OK, NgxLearningPathDTO.class); - } - @Nested class GetLearningPath { diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java index 471d3752c728..d18a5ed65662 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java @@ -4,45 +4,28 @@ import java.lang.reflect.Field; import java.time.ZonedDateTime; -import java.util.Arrays; import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; import de.tum.cit.aet.artemis.assessment.util.StudentScoreUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyProgressUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; -import de.tum.cit.aet.artemis.atlas.domain.LearningObject; -import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; -import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath; import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; import de.tum.cit.aet.artemis.atlas.dto.LearningPathHealthDTO; -import de.tum.cit.aet.artemis.atlas.dto.NgxLearningPathDTO; import de.tum.cit.aet.artemis.atlas.learningpath.util.LearningPathUtilService; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; -import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathNgxService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathRecommendationService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; -import de.tum.cit.aet.artemis.atlas.web.LearningPathResource; 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.security.SecurityUtils; import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.core.util.CourseFactory; import de.tum.cit.aet.artemis.core.util.CourseUtilService; -import de.tum.cit.aet.artemis.exercise.domain.DifficultyLevel; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.lecture.domain.Lecture; -import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitRepository; import de.tum.cit.aet.artemis.lecture.util.LectureUtilService; import de.tum.cit.aet.artemis.programming.util.ProgrammingExerciseUtilService; @@ -141,153 +124,6 @@ void testHealthStatusNoRelations() { } } - @Nested - class GenerateNgxGraphRepresentationBase { - - @Test - void testEmptyLearningPath() { - NgxLearningPathDTO expected = new NgxLearningPathDTO(Set.of(), Set.of()); - generateGraphAndAssert(expected); - } - - @Test - void testEmptyCompetency() { - final var competency = competencyUtilService.createCompetency(course); - final var startNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); - final var endNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); - Set expectedNodes = getExpectedNodesOfEmptyCompetency(competency); - Set expectedEdges = Set.of(new NgxLearningPathDTO.Edge(LearningPathNgxService.getDirectEdgeId(competency.getId()), startNodeId, endNodeId)); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - - @Test - void testCompetencyWithLectureUnitAndExercise() { - var competency = competencyUtilService.createCompetency(course); - var lecture = lectureUtilService.createLecture(course, ZonedDateTime.now()); - final var lectureUnit = lectureUtilService.createTextUnit(); - lectureUtilService.addLectureUnitsToLecture(lecture, List.of(lectureUnit)); - competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnit); - final var exercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); - exercise.setReleaseDate(null); - competencyUtilService.linkExerciseToCompetency(competency, exercise); - final var startNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); - final var endNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); - Set expectedNodes = getExpectedNodesOfEmptyCompetency(competency); - expectedNodes.add(getNodeForLectureUnit(competency, lectureUnit)); - expectedNodes.add(getNodeForExercise(competency, exercise)); - Set expectedEdges = Set.of( - new NgxLearningPathDTO.Edge(LearningPathNgxService.getLectureUnitInEdgeId(competency.getId(), lectureUnit.getId()), startNodeId, - LearningPathNgxService.getLectureUnitNodeId(competency.getId(), lectureUnit.getId())), - new NgxLearningPathDTO.Edge(LearningPathNgxService.getLectureUnitOutEdgeId(competency.getId(), lectureUnit.getId()), - LearningPathNgxService.getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), endNodeId), - new NgxLearningPathDTO.Edge(LearningPathNgxService.getExerciseInEdgeId(competency.getId(), exercise.getId()), startNodeId, - LearningPathNgxService.getExerciseNodeId(competency.getId(), exercise.getId())), - new NgxLearningPathDTO.Edge(LearningPathNgxService.getExerciseOutEdgeId(competency.getId(), exercise.getId()), - LearningPathNgxService.getExerciseNodeId(competency.getId(), exercise.getId()), endNodeId)); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - - @Test - void testMultipleCompetencies() { - Competency[] competencies = { competencyUtilService.createCompetency(course), competencyUtilService.createCompetency(course), - competencyUtilService.createCompetency(course) }; - String[] startNodeIds = Arrays.stream(competencies).map(Competency::getId).map(LearningPathNgxService::getCompetencyStartNodeId).toArray(String[]::new); - String[] endNodeIds = Arrays.stream(competencies).map(Competency::getId).map(LearningPathNgxService::getCompetencyEndNodeId).toArray(String[]::new); - Set expectedNodes = new HashSet<>(); - Set expectedEdges = new HashSet<>(); - for (int i = 0; i < competencies.length; i++) { - expectedNodes.addAll(getExpectedNodesOfEmptyCompetency(competencies[i])); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getDirectEdgeId(competencies[i].getId()), startNodeIds[i], endNodeIds[i])); - } - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - } - - @Nested - class GenerateNgxGraphRepresentationRelation { - - private Competency competency1; - - private Competency competency2; - - private Set expectedNodes; - - Set expectedEdges; - - @BeforeEach - void setup() { - competency1 = competencyUtilService.createCompetency(course); - competency2 = competencyUtilService.createCompetency(course); - expectedNodes = new HashSet<>(); - expectedEdges = new HashSet<>(); - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, competency1, competency2); - } - - void testSimpleRelation(RelationType type) { - competencyUtilService.addRelation(competency1, type, competency2); - final var sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency2.getId()); - final var targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency1.getId()); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - - @Test - void testSingleAssumes() { - testSimpleRelation(RelationType.ASSUMES); - } - - @Test - void testSingleExtends() { - testSimpleRelation(RelationType.EXTENDS); - } - - @Test - void testSingleMatches() { - competencyUtilService.addRelation(competency1, RelationType.MATCHES, competency2); - expectedNodes.add(NgxLearningPathDTO.Node.of(LearningPathNgxService.getMatchingClusterStartNodeId(0), NgxLearningPathDTO.NodeType.MATCH_START, null, "")); - expectedNodes.add(NgxLearningPathDTO.Node.of(LearningPathNgxService.getMatchingClusterEndNodeId(0), NgxLearningPathDTO.NodeType.MATCH_END, null, "")); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getInEdgeId(competency1.getId()), LearningPathNgxService.getMatchingClusterStartNodeId(0), - LearningPathNgxService.getCompetencyStartNodeId(competency1.getId()))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getOutEdgeId(competency1.getId()), - LearningPathNgxService.getCompetencyEndNodeId(competency1.getId()), LearningPathNgxService.getMatchingClusterEndNodeId(0))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getInEdgeId(competency2.getId()), LearningPathNgxService.getMatchingClusterStartNodeId(0), - LearningPathNgxService.getCompetencyStartNodeId(competency2.getId()))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getOutEdgeId(competency2.getId()), - LearningPathNgxService.getCompetencyEndNodeId(competency2.getId()), LearningPathNgxService.getMatchingClusterEndNodeId(0))); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - - @Test - void testMatchesTransitive() { - var competency3 = competencyUtilService.createCompetency(course); - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, competency3); - - competencyUtilService.addRelation(competency1, RelationType.MATCHES, competency2); - competencyUtilService.addRelation(competency2, RelationType.MATCHES, competency3); - expectedNodes.add(NgxLearningPathDTO.Node.of(LearningPathNgxService.getMatchingClusterStartNodeId(0), NgxLearningPathDTO.NodeType.MATCH_START, null, "")); - expectedNodes.add(NgxLearningPathDTO.Node.of(LearningPathNgxService.getMatchingClusterEndNodeId(0), NgxLearningPathDTO.NodeType.MATCH_END, null, "")); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getInEdgeId(competency1.getId()), LearningPathNgxService.getMatchingClusterStartNodeId(0), - LearningPathNgxService.getCompetencyStartNodeId(competency1.getId()))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getOutEdgeId(competency1.getId()), - LearningPathNgxService.getCompetencyEndNodeId(competency1.getId()), LearningPathNgxService.getMatchingClusterEndNodeId(0))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getInEdgeId(competency2.getId()), LearningPathNgxService.getMatchingClusterStartNodeId(0), - LearningPathNgxService.getCompetencyStartNodeId(competency2.getId()))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getOutEdgeId(competency2.getId()), - LearningPathNgxService.getCompetencyEndNodeId(competency2.getId()), LearningPathNgxService.getMatchingClusterEndNodeId(0))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getInEdgeId(competency3.getId()), LearningPathNgxService.getMatchingClusterStartNodeId(0), - LearningPathNgxService.getCompetencyStartNodeId(competency3.getId()))); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getOutEdgeId(competency3.getId()), - LearningPathNgxService.getCompetencyEndNodeId(competency3.getId()), LearningPathNgxService.getMatchingClusterEndNodeId(0))); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generateGraphAndAssert(expected); - } - } - @Nested class GenerateNgxPathRepresentation { @@ -310,397 +146,5 @@ void testUtilityConstantsValid() throws NoSuchFieldException, IllegalAccessExcep final var assumesUtilityRatio = assumesUtilityRatioField.getDouble(null); assertThat(extendsUtilityRatio).isLessThan(assumesUtilityRatio); } - - @Test - void testEmptyLearningPath() { - NgxLearningPathDTO expected = new NgxLearningPathDTO(Set.of(), Set.of()); - generatePathAndAssert(expected); - } - - @Test - void testEmptyCompetency() { - final var competency = competencyUtilService.createCompetency(course); - final var startNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); - final var endNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); - Set expectedNodes = getExpectedNodesOfEmptyCompetency(competency); - Set expectedEdges = Set.of(new NgxLearningPathDTO.Edge(LearningPathNgxService.getDirectEdgeId(competency.getId()), startNodeId, endNodeId)); - NgxLearningPathDTO expected = new NgxLearningPathDTO(expectedNodes, expectedEdges); - generatePathAndAssert(expected); - } - } - - @Nested - class GenerateNgxPathRepresentationCompetencyOrder { - - private Competency competency1; - - private Competency competency2; - - private Set expectedNodes; - - private Set expectedEdges; - - @BeforeEach - void setup() { - final var users = userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 0); - user = users.getFirst(); - course = CourseFactory.generateCourse(null, ZonedDateTime.now().minusDays(8), ZonedDateTime.now().minusDays(8), new HashSet<>(), TEST_PREFIX + "tumuser", - TEST_PREFIX + "tutor", TEST_PREFIX + "editor", TEST_PREFIX + "instructor"); - course = courseRepository.save(course); - - competency1 = competencyUtilService.createCompetency(course); - competency2 = competencyUtilService.createCompetency(course); - expectedNodes = new HashSet<>(); - expectedEdges = new HashSet<>(); - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, competency1, competency2); - } - - private static Stream orderOfCompetenciesByDueDateUtilityProvider() { - return Stream.of(Arguments.of(future(10), future(20)), Arguments.of(future(20), future(10)), Arguments.of(past(3), future(3)), Arguments.of(future(3), past(3)), - Arguments.of(past(10), past(20)), Arguments.of(past(20), past(10))); - } - - @ParameterizedTest - @MethodSource("orderOfCompetenciesByDueDateUtilityProvider") - void testOrderOfCompetenciesByDueDateUtility(ZonedDateTime time1, ZonedDateTime time2) { - competency1.setSoftDueDate(time1); - competency1 = competencyRepository.save(competency1); - competency2.setSoftDueDate(time2); - competency2 = competencyRepository.save(competency2); - - String sourceNodeId; - String targetNodeId; - if (time1.isBefore(time2)) { - sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency1.getId()); - targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency2.getId()); - } - else { - sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency2.getId()); - targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency1.getId()); - } - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testOrderOfCompetenciesByPriorUtility() { - competency1.setSoftDueDate(future(150)); - competencyRepository.save(competency1); - competency2.setSoftDueDate(future(200)); - competencyRepository.save(competency2); - Competency[] priors1 = competencyUtilService.createCompetencies(course, future(110), future(112), future(114)); - Competency[] priors2 = competencyUtilService.createCompetencies(course, future(111), future(113), future(115)); - - for (var competency : priors1) { - competencyUtilService.addRelation(competency1, RelationType.ASSUMES, competency); - } - for (var competency : priors2) { - competencyUtilService.addRelation(competency2, RelationType.ASSUMES, competency); - } - masterCompetencies(priors1); - masterCompetencies(priors2[0]); - - Competency[] expectedOrder = new Competency[] { priors2[1], priors2[2], competency1, competency2 }; - for (int i = 0; i < expectedOrder.length - 1; i++) { - var sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(expectedOrder[i].getId()); - var targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(expectedOrder[i + 1].getId()); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - } - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, priors2[1], priors2[2]); - - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testOrderOfCompetenciesByExtendsOrAssumesUtility() { - competency1.setSoftDueDate(future(200)); - competencyRepository.save(competency1); - competency2.setSoftDueDate(future(200)); - competencyRepository.save(competency2); - Competency[] priors1 = competencyUtilService.createCompetencies(course, future(110), future(112), future(114)); - Competency[] priors2 = competencyUtilService.createCompetencies(course, future(111), future(113), future(115)); - - competencyUtilService.addRelation(competency1, RelationType.EXTENDS, priors1[0]); - competencyUtilService.addRelation(competency1, RelationType.EXTENDS, priors1[1]); - competencyUtilService.addRelation(competency1, RelationType.ASSUMES, priors1[2]); - competencyUtilService.addRelation(competency2, RelationType.EXTENDS, priors2[0]); - competencyUtilService.addRelation(competency2, RelationType.ASSUMES, priors2[1]); - competencyUtilService.addRelation(competency2, RelationType.ASSUMES, priors2[2]); - - Competency[] expectedOrder = new Competency[] { priors1[0], priors2[0], priors1[1], priors2[1], priors1[2], priors2[2], competency1, competency2 }; - for (int i = 0; i < expectedOrder.length - 1; i++) { - var sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(expectedOrder[i].getId()); - var targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(expectedOrder[i + 1].getId()); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - } - - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, priors1); - addExpectedComponentsForEmptyCompetencies(expectedNodes, expectedEdges, priors2); - - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testOrderOfCompetenciesByMasteryUtility() { - competency1.setMasteryThreshold(100); - competency1 = competencyRepository.save(competency1); - competency2.setMasteryThreshold(100); - competency2 = competencyRepository.save(competency2); - - competencyProgressUtilService.createCompetencyProgress(competency1, user, 30, 1.1); - competencyProgressUtilService.createCompetencyProgress(competency2, user, 10, 0.9); - - var sourceNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency1.getId()); - var targetNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency2.getId()); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceNodeId, targetNodeId), sourceNodeId, targetNodeId)); - - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - private void masterCompetencies(Competency... competencies) { - for (var competency : competencies) { - competencyProgressUtilService.createCompetencyProgress(competency, user, 100, 1); - } - } - } - - @Nested - class GenerateNgxPathRepresentationLearningObjectOrder { - - private Competency competency; - - private Lecture lecture; - - private LectureUnit[] lectureUnits; - - private Exercise[] exercises; - - private Set expectedNodes; - - private Set expectedEdges; - - @BeforeEach - void setup() { - final var users = userUtilService.addUsers(TEST_PREFIX, 1, 0, 0, 0); - user = users.getFirst(); - course = CourseFactory.generateCourse(null, ZonedDateTime.now().minusDays(8), ZonedDateTime.now().minusDays(8), new HashSet<>(), TEST_PREFIX + "tumuser", - TEST_PREFIX + "tutor", TEST_PREFIX + "editor", TEST_PREFIX + "instructor"); - course = courseRepository.save(course); - - lecture = lectureUtilService.createLecture(course, ZonedDateTime.now()); - - competency = competencyUtilService.createCompetency(course); - competency.setMasteryThreshold(100); - competency = competencyRepository.save(competency); - expectedNodes = new HashSet<>(getExpectedNodesOfEmptyCompetency(competency)); - expectedEdges = new HashSet<>(); - } - - @Test - void testCompetencyWithLectureUnitAndExercise() { - competency.setMasteryThreshold(70); - competency = competencyRepository.save(competency); - - generateLectureUnits(1); - generateExercises(1); - - addNodes(lectureUnits); - addNodes(exercises); - addEdges(competency, lectureUnits[0], exercises[0]); - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testOrderByDifficultyLevel() { - generateExercises(3); - exercises[0].setDifficulty(DifficultyLevel.HARD); - exercises[1].setDifficulty(DifficultyLevel.EASY); - exercises[2].setDifficulty(DifficultyLevel.MEDIUM); - exerciseRepository.saveAll(List.of(exercises)); - - addNodes(exercises); - addEdges(competency, exercises[1], exercises[2], exercises[0]); - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testAvoidReschedulingCompletedLearningObjects() { - generateLectureUnits(2); - lectureUnits[0] = lectureUtilService.completeLectureUnitForUser(lectureUnits[0], user); - generateExercises(6); - exercises[0].setDifficulty(DifficultyLevel.EASY); - studentScoreUtilService.createStudentScore(exercises[0], user, 100); - exercises[1].setDifficulty(DifficultyLevel.EASY); - exercises[2].setDifficulty(DifficultyLevel.MEDIUM); - studentScoreUtilService.createStudentScore(exercises[2], user, 100); - exercises[3].setDifficulty(DifficultyLevel.MEDIUM); - exercises[4].setDifficulty(DifficultyLevel.HARD); - studentScoreUtilService.createStudentScore(exercises[4], user, 100); - exercises[5].setDifficulty(DifficultyLevel.HARD); - exerciseRepository.saveAll(List.of(exercises)); - - addNodes(lectureUnits[1]); - addNodes(exercises[1], exercises[3], exercises[5]); - addEdges(competency, lectureUnits[1], exercises[1], exercises[3], exercises[5]); - generatePathAndAssert(new NgxLearningPathDTO(expectedNodes, expectedEdges)); - } - - @Test - void testRecommendCorrectAmountOfLearningObjects() { - competency.setMasteryThreshold(40); - competency = competencyRepository.save(competency); - - generateLectureUnits(1); - generateExercises(9); - exercises[0].setDifficulty(DifficultyLevel.EASY); - exercises[1].setDifficulty(DifficultyLevel.MEDIUM); - exercises[2].setDifficulty(DifficultyLevel.HARD); - exerciseRepository.saveAll(List.of(exercises)); - - LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); - learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); - NgxLearningPathDTO actual = learningPathService.generateNgxPathRepresentation(learningPath); - // competency start & end, lecture unit, and one exercise per difficulty level - assertThat(actual.nodes()).hasSize(6); - } - - @Test - void testDoesNotLeakUnreleasedLearningObjects() { - generateLectureUnits(3); - generateExercises(3); - - lectureUnits[0].setReleaseDate(ZonedDateTime.now().plusDays(1)); - lectureUnits[1].setReleaseDate(ZonedDateTime.now().minusDays(1)); - lectureUnits[2].setReleaseDate(null); - lectureUnitRepository.saveAll(List.of(lectureUnits)); - - exercises[0].setReleaseDate(ZonedDateTime.now().plusDays(1)); - exercises[1].setReleaseDate(ZonedDateTime.now().minusDays(1)); - exercises[2].setReleaseDate(null); - exerciseRepository.saveAll(List.of(exercises)); - - LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); - learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); - NgxLearningPathDTO actual = learningPathService.generateNgxPathRepresentation(learningPath); - // competency start & end, lecture unit, and one exercise per difficulty level - assertThat(actual.nodes()).hasSize(6); - } - - private void generateLectureUnits(int numberOfLectureUnits) { - lectureUnits = new LectureUnit[numberOfLectureUnits]; - for (int i = 0; i < lectureUnits.length; i++) { - lectureUnits[i] = lectureUtilService.createTextUnit(); - lectureUtilService.addLectureUnitsToLecture(lecture, List.of(lectureUnits[i])); - lectureUnits[i] = competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnits[i]); - } - } - - private void generateExercises(int numberOfExercises) { - exercises = new Exercise[numberOfExercises]; - for (int i = 0; i < exercises.length; i++) { - exercises[i] = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); - exercises[i].setReleaseDate(null); - exercises[i] = competencyUtilService.linkExerciseToCompetency(competency, exercises[i]); - } - } - - private void addNodes(LearningObject... learningObjects) { - for (var learningObject : learningObjects) { - if (learningObject instanceof LectureUnit lectureUnit) { - expectedNodes.add(getNodeForLectureUnit(competency, lectureUnit)); - } - else if (learningObject instanceof Exercise exercise) { - expectedNodes.add(getNodeForExercise(competency, exercise)); - } - } - } - - private void addEdges(Competency competency, LearningObject... learningObjects) { - addEdge(competency, learningObjects[0]); - addEdges(competency.getId(), learningObjects); - addEdge(learningObjects[learningObjects.length - 1], competency); - } - - private void addEdges(long competencyId, LearningObject... learningObjects) { - for (int i = 1; i < learningObjects.length; i++) { - final var sourceId = LearningPathNgxService.getLearningObjectNodeId(competencyId, learningObjects[i - 1]); - final var targetId = LearningPathNgxService.getLearningObjectNodeId(competencyId, learningObjects[i]); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceId, targetId), sourceId, targetId)); - } - } - - private void addEdge(Competency competency, LearningObject learningObject) { - final var sourceId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); - final var targetId = LearningPathNgxService.getLearningObjectNodeId(competency.getId(), learningObject); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceId, targetId), sourceId, targetId)); - } - - private void addEdge(LearningObject learningObject, Competency competency) { - final var sourceId = LearningPathNgxService.getLearningObjectNodeId(competency.getId(), learningObject); - final var targetId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getEdgeFromToId(sourceId, targetId), sourceId, targetId)); - } - } - - private void generateGraphAndAssert(NgxLearningPathDTO expected) { - generateAndAssert(expected, LearningPathResource.NgxRequestType.GRAPH); - } - - private void generatePathAndAssert(NgxLearningPathDTO expected) { - generateAndAssert(expected, LearningPathResource.NgxRequestType.PATH); - } - - private void generateAndAssert(NgxLearningPathDTO expected, LearningPathResource.NgxRequestType type) { - LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); - learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); - NgxLearningPathDTO actual = switch (type) { - case GRAPH -> learningPathService.generateNgxGraphRepresentation(learningPath); - case PATH -> learningPathService.generateNgxPathRepresentation(learningPath); - }; - assertThat(actual).isNotNull(); - assertNgxRepEquals(actual, expected); - } - - private void assertNgxRepEquals(NgxLearningPathDTO was, NgxLearningPathDTO expected) { - assertThat(was.nodes()).as("correct nodes").containsExactlyInAnyOrderElementsOf(expected.nodes()); - assertThat(was.edges()).as("correct edges").containsExactlyInAnyOrderElementsOf(expected.edges()); - } - - private static Set getExpectedNodesOfEmptyCompetency(Competency competency) { - return new HashSet<>(Set.of( - NgxLearningPathDTO.Node.of(LearningPathNgxService.getCompetencyStartNodeId(competency.getId()), NgxLearningPathDTO.NodeType.COMPETENCY_START, competency.getId(), - ""), - NgxLearningPathDTO.Node.of(LearningPathNgxService.getCompetencyEndNodeId(competency.getId()), NgxLearningPathDTO.NodeType.COMPETENCY_END, competency.getId(), ""))); - } - - private static NgxLearningPathDTO.Node getNodeForLectureUnit(Competency competency, LectureUnit lectureUnit) { - return NgxLearningPathDTO.Node.of(LearningPathNgxService.getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), NgxLearningPathDTO.NodeType.LECTURE_UNIT, - lectureUnit.getId(), lectureUnit.getLecture().getId(), false, lectureUnit.getName()); - } - - private static NgxLearningPathDTO.Node getNodeForExercise(Competency competency, Exercise exercise) { - return NgxLearningPathDTO.Node.of(LearningPathNgxService.getExerciseNodeId(competency.getId(), exercise.getId()), NgxLearningPathDTO.NodeType.EXERCISE, exercise.getId(), - exercise.getTitle()); - } - - private void addExpectedComponentsForEmptyCompetencies(Set expectedNodes, Set expectedEdges, Competency... competencies) { - for (var competency : competencies) { - expectedNodes.addAll(getExpectedNodesOfEmptyCompetency(competency)); - expectedEdges.add(new NgxLearningPathDTO.Edge(LearningPathNgxService.getDirectEdgeId(competency.getId()), - LearningPathNgxService.getCompetencyStartNodeId(competency.getId()), LearningPathNgxService.getCompetencyEndNodeId(competency.getId()))); - } - } - - private static ZonedDateTime now() { - return ZonedDateTime.now(); - } - - private static ZonedDateTime past(long days) { - return now().minusDays(days); - } - - private static ZonedDateTime future(long days) { - return now().plusDays(days); } } From 43021a2656111c38e402034230db22e6cdf14057 Mon Sep 17 00:00:00 2001 From: Johannes Wiest Date: Tue, 29 Oct 2024 17:06:21 +0100 Subject: [PATCH 2/7] remove legacy client code and tests --- .../learning-path-graph.component.html | 54 ----- .../learning-path-graph.component.scss | 87 ------- .../learning-path-graph.component.ts | 141 ----------- .../learning-path-graph.module.ts | 27 --- .../learning-path-legend.component.html | 42 ---- .../learning-path-legend.component.ts | 19 -- .../learning-path-node.component.html | 37 --- .../learning-path-node.component.ts | 46 ---- .../learning-path.component.html | 28 --- .../learning-path.component.scss | 21 -- .../learning-path.component.ts | 76 ------ .../competency-node-details.component.html | 25 -- .../competency-node-details.component.ts | 52 ---- .../exercise-node-details.component.html | 12 - .../exercise-node-details.component.ts | 43 ---- .../lecture-unit-node-details.component.html | 12 - .../lecture-unit-node-details.component.ts | 45 ---- ...-path-health-status-warning.component.html | 11 - ...ng-path-health-status-warning.component.ts | 16 -- .../learning-path-management.component.html | 90 ------- .../learning-path-management.component.ts | 224 ------------------ .../learning-path-management.module.ts | 12 - .../learning-path-paging.service.ts | 24 -- .../learning-paths/learning-path.service.ts | 79 ------ .../learning-paths/learning-paths.module.ts | 73 ------ .../learning-path-container.component.html | 49 ---- .../learning-path-container.component.scss | 35 --- .../learning-path-container.component.ts | 219 ----------------- .../learning-path-storage.service.ts | 155 ------------ ...ning-path-lecture-unit-view.component.html | 27 --- ...ning-path-lecture-unit-view.component.scss | 3 - ...arning-path-lecture-unit-view.component.ts | 42 ---- .../learning-path-lecture-unit-view.module.ts | 30 --- ...earning-path-progress-modal.component.html | 25 -- ...earning-path-progress-modal.component.scss | 26 -- .../learning-path-progress-modal.component.ts | 36 --- .../learning-path-progress-nav.component.html | 18 -- .../learning-path-progress-nav.component.ts | 18 -- .../learning-path-progress.module.ts | 12 - .../course/manage/course-management.module.ts | 2 - .../app/overview/courses-routing.module.ts | 4 +- ...learning-path-graph-node.component.spec.ts | 51 ---- .../learning-path-graph.component.spec.ts | 87 ------- .../learning-path-legend.component.spec.ts | 69 ------ .../graph/learning-path.component.spec.ts | 82 ------- .../competency-node-details.component.spec.ts | 65 ----- .../exercise-node-details.component.spec.ts | 55 ----- ...ecture-unit-node-details.component.spec.ts | 57 ----- ...th-health-status-warning.component.spec.ts | 68 ------ ...learning-path-management.component.spec.ts | 210 ---------------- ...ning-path-progress-modal.component.spec.ts | 64 ----- ...arning-path-progress-nav.component.spec.ts | 72 ------ .../learning-path-container.component.spec.ts | 213 ----------------- ...g-path-lecture-unit-view.component.spec.ts | 107 --------- .../learning-path-paging.service.spec.ts | 49 ---- .../learning-path-storage.service.spec.ts | 189 --------------- .../learning-path.service.spec.ts | 85 ------- 57 files changed, 3 insertions(+), 3517 deletions(-) delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.scss delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.module.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.scss delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.module.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path-paging.service.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-path.service.ts delete mode 100644 src/main/webapp/app/course/learning-paths/learning-paths.module.ts delete mode 100644 src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.scss delete mode 100644 src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/participate/learning-path-storage.service.ts delete mode 100644 src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.scss delete mode 100644 src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.module.ts delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.scss delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.html delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.ts delete mode 100644 src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress.module.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/learning-path-graph-node.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/learning-path-graph.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/learning-path-legend.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/learning-path.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/node-details/competency-node-details.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/graph/node-details/lecture-unit-node-details.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/management/learning-path-health-status-warning.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/management/learning-path-management.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/management/learning-path-progress-modal.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/management/learning-path-progress-nav.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts delete mode 100644 src/test/javascript/spec/component/learning-paths/participate/learning-path-lecture-unit-view.component.spec.ts delete mode 100644 src/test/javascript/spec/service/learning-path-paging.service.spec.ts delete mode 100644 src/test/javascript/spec/service/learning-path-storage.service.spec.ts delete mode 100644 src/test/javascript/spec/service/learning-path/learning-path.service.spec.ts diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.html deleted file mode 100644 index c284787fb4c9..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.html +++ /dev/null @@ -1,54 +0,0 @@ -
- @if (ngxLearningPath) { - - - - - - - - - - - - - - - - - - - - - - - - - } - -
diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.scss b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.scss deleted file mode 100644 index f75db7176e6b..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.scss +++ /dev/null @@ -1,87 +0,0 @@ -.graph-container { - display: flex; - width: 100%; - height: 100%; - overflow: hidden; - - .node { - display: flex; - width: 100%; - height: 100%; - } - - #arrow { - stroke: var(--body-color); - fill: var(--body-color); - } - - .edge { - stroke: var(--body-color) !important; - marker-end: url(#arrow); - } - - .floating-icon-button { - position: absolute; - right: 0; - padding: 0.25em; - } - .floating-icon-button:hover { - cursor: pointer; - } -} - -jhi-learning-path-graph-node:hover { - cursor: pointer; -} - -.node-icon-container { - width: 100%; - display: flex; - - fa-icon { - width: 100%; - display: flex; - } -} - -:host::ng-deep .graph-container .node-icon-container fa-icon svg { - margin: 10%; - width: 80%; - height: 80%; -} - -.node-xs { - width: 40%; - margin: 30%; -} - -.node-s { - width: 60%; - margin: 20%; -} - -.completed { - color: var(--bs-success); -} - -.current { - color: var(--bs-warning); -} - -.node-details { - display: block; - max-width: 40vw; -} - -.legend { - position: absolute; - right: 0; - bottom: 0; - margin-right: 0.5em; - margin-bottom: 0.5em; -} - -.legend-wrapper { - background: var(--bs-body-bg); - border-radius: 5px; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.ts deleted file mode 100644 index 9bce9d732d2d..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.component.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Layout, NgxGraphZoomOptions } from '@swimlane/ngx-graph'; -import * as shape from 'd3-shape'; -import { Subject } from 'rxjs'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { CompetencyProgressForLearningPathDTO, NgxLearningPathDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; - -@Component({ - selector: 'jhi-learning-path-graph', - styleUrls: ['./learning-path-graph.component.scss'], - templateUrl: './learning-path-graph.component.html', -}) -export class LearningPathGraphComponent implements OnInit { - isLoading = false; - @Input() learningPathId: number; - @Input() courseId: number; - @Output() nodeClicked: EventEmitter = new EventEmitter(); - ngxLearningPath: NgxLearningPathDTO; - nodeTypes: Set = new Set(); - competencyProgress: Map = new Map(); - - layout: string | Layout = 'dagreCluster'; - curve = shape.curveBundle; - - private _draggingEnabled = false; - private _panningEnabled = false; - private _zoomEnabled = false; - private _panOnZoom = false; - private _showMiniMap = false; - - update$: Subject = new Subject(); - center$: Subject = new Subject(); - zoomToFit$: Subject = new Subject(); - - protected readonly NodeType = NodeType; - - constructor(private learningPathService: LearningPathService) {} - - ngOnInit() { - if (this.learningPathId) { - this.loadDataAndRender(); - } - } - - @Input() set draggingEnabled(value) { - this._draggingEnabled = value; - } - - get draggingEnabled() { - return this._draggingEnabled; - } - - @Input() set panningEnabled(value) { - this._panningEnabled = value; - } - - get panningEnabled() { - return this._panningEnabled; - } - - @Input() set zoomEnabled(value) { - this._zoomEnabled = value; - } - - get zoomEnabled() { - return this._zoomEnabled; - } - - @Input() set panOnZoom(value) { - this._panOnZoom = value; - } - - get panOnZoom() { - return this._panOnZoom; - } - - @Input() set showMiniMap(value) { - this._showMiniMap = value; - } - - get showMiniMap() { - return this._showMiniMap; - } - - refreshData() { - this.loadDataAndRender(); - } - - loadDataAndRender() { - this.learningPathService.getCompetencyProgressForLearningPath(this.learningPathId).subscribe({ - next: (response) => { - response.body!.forEach((progress) => { - this.competencyProgress.set(progress.competencyId!, progress); - }); - }, - complete: () => { - this.loadGraphRepresentation(true); - }, - }); - } - - loadGraphRepresentation(render: boolean) { - this.isLoading = true; - this.learningPathService.getLearningPathNgxGraph(this.learningPathId).subscribe((ngxLearningPathResponse) => { - ngxLearningPathResponse.body!.nodes.forEach((node) => { - this.defineNodeDimensions(node); - }); - this.ngxLearningPath = ngxLearningPathResponse.body!; - - // update contained node types - this.nodeTypes = new Set(); - this.ngxLearningPath.nodes.forEach((node) => { - this.nodeTypes.add(node.type!); - }); - - if (render) { - this.update$.next(true); - } - this.isLoading = false; - }); - } - - defineNodeDimensions(node: NgxLearningPathNode) { - if (node.type === NodeType.COMPETENCY_START) { - node.dimension = { width: 75, height: 75 }; - } else { - node.dimension = { width: 50, height: 50 }; - } - } - - onResize() { - this.update$.next(true); - this.center$.next(true); - this.zoomToFit$.next({ autoCenter: true }); - } - - onCenterView() { - this.zoomToFit$.next({ autoCenter: true }); - this.center$.next(true); - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.module.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.module.ts deleted file mode 100644 index 01b9007cdaad..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-graph.module.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { NgxGraphModule } from '@swimlane/ngx-graph'; -import { LearningPathGraphComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.component'; -import { LearningPathNodeComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-node.component'; -import { CompetencyNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component'; -import { ExerciseNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component'; -import { LectureUnitNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component'; -import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; -import { LearningPathComponent } from 'app/course/learning-paths/learning-path-graph/learning-path.component'; -import { LearningPathLegendComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-legend.component'; -import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; - -@NgModule({ - imports: [ArtemisSharedModule, NgxGraphModule, ArtemisCompetenciesModule, ArtemisMarkdownModule], - declarations: [ - LearningPathGraphComponent, - LearningPathNodeComponent, - CompetencyNodeDetailsComponent, - ExerciseNodeDetailsComponent, - LectureUnitNodeDetailsComponent, - LearningPathComponent, - LearningPathLegendComponent, - ], - exports: [LearningPathGraphComponent, LearningPathComponent], -}) -export class ArtemisLearningPathGraphModule {} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.html deleted file mode 100644 index 15702855fa6a..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.html +++ /dev/null @@ -1,42 +0,0 @@ -
- @if (nodeTypes.has(NodeType.COMPETENCY_END)) { -
-
-
- -
-
- } - @if (nodeTypes.has(NodeType.MATCH_START)) { -
-
-
- -
-
- } - @if (nodeTypes.has(NodeType.MATCH_END)) { -
-
-
- -
-
- } - @if (nodeTypes.has(NodeType.LECTURE_UNIT) || nodeTypes.has(NodeType.EXERCISE)) { -
-
-
- -
-
- } - @if (nodeTypes.has(NodeType.LECTURE_UNIT) || nodeTypes.has(NodeType.EXERCISE)) { -
-
-
- -
-
- } -
diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.ts deleted file mode 100644 index 2c27d25b08db..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-legend.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { NgxLearningPathNode, NodeType, getIcon } from 'app/entities/competency/learning-path.model'; - -@Component({ - selector: 'jhi-learning-path-legend', - styleUrls: ['./learning-path-graph.component.scss'], - templateUrl: './learning-path-legend.component.html', -}) -export class LearningPathLegendComponent { - @Input() nodeTypes: Set; - - protected readonly getIcon = getIcon; - protected readonly competencyEnd = { id: '', type: NodeType.COMPETENCY_END } as NgxLearningPathNode; - protected readonly matchStart = { id: '', type: NodeType.MATCH_START } as NgxLearningPathNode; - protected readonly matchEnd = { id: '', type: NodeType.MATCH_END } as NgxLearningPathNode; - protected readonly learningObject = { id: '', type: NodeType.LECTURE_UNIT } as NgxLearningPathNode; - protected readonly completedLearningObject = { id: '', type: NodeType.LECTURE_UNIT, completed: true } as NgxLearningPathNode; - protected readonly NodeType = NodeType; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.html deleted file mode 100644 index 2b02af7c1524..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.html +++ /dev/null @@ -1,37 +0,0 @@ -@if (node.type === NodeType.EXERCISE || node.type === NodeType.LECTURE_UNIT) { -
- -
-} @else { - @if (node.type === NodeType.COMPETENCY_START || node.type === NodeType.COMPETENCY_END) { -
- @if (node.type === NodeType.COMPETENCY_START) { - - } - @if (node.type === NodeType.COMPETENCY_END) { - - } -
- } @else { -
- -
- } -} - - @if (node.type === NodeType.EXERCISE) { - - } - @if (node.type === NodeType.LECTURE_UNIT) { - - } - - - - diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.ts deleted file mode 100644 index 99f64933cb4b..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path-node.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { CompetencyProgressForLearningPathDTO, NgxLearningPathNode, NodeType, getIcon } from 'app/entities/competency/learning-path.model'; -import { Competency, CompetencyProgress, getMastery, getProgress } from 'app/entities/competency.model'; -import { Exercise } from 'app/entities/exercise.model'; -import { Lecture } from 'app/entities/lecture.model'; -import { LectureUnitForLearningPathNodeDetailsDTO } from 'app/entities/lecture-unit/lectureUnit.model'; - -class NodeDetailsData { - competency?: Competency; - competencyProgress?: CompetencyProgress; - exercise?: Exercise; - lecture?: Lecture; - lectureUnit?: LectureUnitForLearningPathNodeDetailsDTO; -} - -@Component({ - selector: 'jhi-learning-path-graph-node', - styleUrls: ['./learning-path-graph.component.scss'], - templateUrl: './learning-path-node.component.html', -}) -export class LearningPathNodeComponent implements OnInit { - @Input() courseId: number; - @Input() node: NgxLearningPathNode; - @Input() competencyProgressDTO?: CompetencyProgressForLearningPathDTO; - - nodeDetailsData = new NodeDetailsData(); - - protected readonly NodeType = NodeType; - protected readonly getIcon = getIcon; - - constructor() {} - - ngOnInit() { - if (this.competencyProgressDTO) { - this.nodeDetailsData.competencyProgress = { progress: this.competencyProgressDTO.progress, confidence: this.competencyProgressDTO.confidence }; - } - } - - get progress() { - return getProgress(this.nodeDetailsData.competencyProgress!); - } - - get mastery() { - return getMastery(this.nodeDetailsData.competencyProgress); - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html deleted file mode 100644 index 864bcc2920b0..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.html +++ /dev/null @@ -1,28 +0,0 @@ -@if (isLoading) { -
-
- -
-
-} @else { -
- @for (node of path; track node; let last = $last) { -
-
- -
- @if (!last) { -
- -
- } -
- } -
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.scss b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.scss deleted file mode 100644 index 6300448b1260..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.scss +++ /dev/null @@ -1,21 +0,0 @@ -.path-edge { - display: block; - text-align: center; - - fa-icon { - font-size: 1em; - } -} - -.path-node { - jhi-learning-path-graph-node { - display: block; - width: fit-content; - font-size: 3em; - margin: auto; - } -} - -.highlighted-node { - color: var(--bs-warning); -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.ts deleted file mode 100644 index 4d5d5e1fc95c..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/learning-path.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { NgxLearningPathNode, getIcon } from 'app/entities/competency/learning-path.model'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; -import { faChevronDown } from '@fortawesome/free-solid-svg-icons'; - -@Component({ - selector: 'jhi-learning-path', - styleUrls: ['./learning-path.component.scss'], - templateUrl: './learning-path.component.html', -}) -export class LearningPathComponent implements OnInit { - @Input() learningPathId: number; - @Input() courseId: number; - @Output() nodeClicked: EventEmitter = new EventEmitter(); - - isLoading = false; - path: NgxLearningPathNode[] = []; - highlightedNode?: NgxLearningPathNode; - - // Icons - faChevronDown = faChevronDown; - - protected readonly getIcon = getIcon; - - constructor( - private learningPathService: LearningPathService, - private learningPathStorageService: LearningPathStorageService, - ) {} - - ngOnInit() { - this.loadData(); - } - - private loadData() { - if (!this.learningPathId) { - return; - } - this.isLoading = true; - this.learningPathService.getLearningPathNgxPath(this.learningPathId).subscribe((ngxLearningPathResponse) => { - const body = ngxLearningPathResponse.body!; - this.learningPathStorageService.getRecommendations(this.learningPathId)?.forEach((entry) => { - let node; - if (entry instanceof LectureUnitEntry) { - node = body.nodes.find((node) => { - return node.linkedResource === entry.lectureUnitId && node.linkedResourceParent === entry.lectureId; - }); - } else if (entry instanceof ExerciseEntry) { - node = body.nodes.find((node) => { - return node.linkedResource === entry.exerciseId && !node.linkedResourceParent; - }); - } - if (node) { - this.path.push(node); - } - }); - this.isLoading = false; - }); - } - - highlightNode(learningObject: LectureUnitEntry | ExerciseEntry) { - if (learningObject instanceof LectureUnitEntry) { - this.highlightedNode = this.path.find((node) => { - return node.linkedResource === learningObject.lectureUnitId && node.linkedResourceParent === learningObject.lectureId; - }); - } else { - this.highlightedNode = this.path.find((node) => { - return node.linkedResource === learningObject.exerciseId && !node.linkedResourceParent; - }); - } - } - - clearHighlighting() { - this.highlightedNode = undefined; - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.html deleted file mode 100644 index 0793b2254bb4..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.html +++ /dev/null @@ -1,25 +0,0 @@ -@if (competency) { -
-
-

- - {{ competency.title }} - @if (this.mastery >= 100) { - - } - @if (competency.optional) { - - } -

- @if (competency.description) { -
- } -
-
-
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.ts deleted file mode 100644 index 36f59d79cd62..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { HttpErrorResponse } from '@angular/common/http'; -import { onError } from 'app/shared/util/global.utils'; -import { Competency, CompetencyProgress, getIcon, getMastery, getProgress } from 'app/entities/competency.model'; -import { AlertService } from 'app/core/util/alert.service'; -import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; - -@Component({ - selector: 'jhi-competency-node-details', - templateUrl: './competency-node-details.component.html', -}) -export class CompetencyNodeDetailsComponent implements OnInit { - @Input() courseId: number; - @Input() competencyId: number; - @Input() competency?: Competency; - @Output() competencyChange = new EventEmitter(); - @Input() competencyProgress: CompetencyProgress; - - isLoading = false; - - protected readonly getIcon = getIcon; - - constructor( - private courseCompetencyService: CourseCompetencyService, - private alertService: AlertService, - ) {} - - ngOnInit() { - if (!this.competency) { - this.loadData(); - } - } - private loadData() { - this.isLoading = true; - this.courseCompetencyService.findById(this.competencyId!, this.courseId!).subscribe({ - next: (resp) => { - this.competency = resp.body!; - this.isLoading = false; - this.competencyChange.emit(this.competency); - }, - error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), - }); - } - - get progress(): number { - return getProgress(this.competencyProgress!); - } - - get mastery(): number { - return getMastery(this.competencyProgress); - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.html deleted file mode 100644 index 7b56203d9bb7..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.html +++ /dev/null @@ -1,12 +0,0 @@ -@if (exercise) { -
-
-

- @if (exercise.type) { - - } - {{ exercise.title }} -

-
-
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.ts deleted file mode 100644 index 65259f79acfb..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { HttpErrorResponse } from '@angular/common/http'; -import { onError } from 'app/shared/util/global.utils'; -import { AlertService } from 'app/core/util/alert.service'; -import { Exercise, getIcon, getIconTooltip } from 'app/entities/exercise.model'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; - -@Component({ - selector: 'jhi-exercise-node-details', - templateUrl: './exercise-node-details.component.html', -}) -export class ExerciseNodeDetailsComponent implements OnInit { - @Input() exerciseId: number; - @Input() exercise?: Exercise; - @Output() exerciseChange = new EventEmitter(); - - isLoading = false; - - constructor( - private exerciseService: ExerciseService, - private alertService: AlertService, - ) {} - - ngOnInit() { - if (!this.exercise) { - this.loadData(); - } - } - private loadData() { - this.isLoading = true; - this.exerciseService.find(this.exerciseId).subscribe({ - next: (exerciseResponse) => { - this.exercise = exerciseResponse.body!; - this.isLoading = false; - this.exerciseChange.emit(this.exercise); - }, - error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), - }); - } - - protected readonly getIcon = getIcon; - protected readonly getIconTooltip = getIconTooltip; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.html b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.html deleted file mode 100644 index c55f987e8eaa..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.html +++ /dev/null @@ -1,12 +0,0 @@ -@if (lectureUnit) { -
-
-

- @if (lectureUnit.type) { - - } - {{ lectureUnit.name }} -

-
-
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.ts deleted file mode 100644 index 300909fde0f7..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { HttpErrorResponse } from '@angular/common/http'; -import { onError } from 'app/shared/util/global.utils'; -import { AlertService } from 'app/core/util/alert.service'; -import { LectureUnit, getIcon, getIconTooltip } from 'app/entities/lecture-unit/lectureUnit.model'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; - -@Component({ - selector: 'jhi-lecture-unit-node-details', - templateUrl: './lecture-unit-node-details.component.html', -}) -export class LectureUnitNodeDetailsComponent implements OnInit { - @Input() lectureUnitId: number; - - @Input() lectureUnit?: LectureUnit; - @Output() lectureUnitChange = new EventEmitter(); - - isLoading = false; - - constructor( - private lectureUnitService: LectureUnitService, - private alertService: AlertService, - ) {} - - ngOnInit() { - if (!this.lectureUnit) { - this.loadData(); - } - } - private loadData() { - this.isLoading = true; - - this.lectureUnitService.getLectureUnitForLearningPathNodeDetails(this.lectureUnitId!).subscribe({ - next: (lectureUnitResult) => { - this.lectureUnit = lectureUnitResult.body!; - this.isLoading = false; - this.lectureUnitChange.emit(this.lectureUnit); - }, - error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), - }); - } - - protected readonly getIcon = getIcon; - protected readonly getIconTooltip = getIconTooltip; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.html b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.html deleted file mode 100644 index d5e1b63c2a62..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.html +++ /dev/null @@ -1,11 +0,0 @@ -@if (status) { -
-
-
{{ getWarningTitle(status) | artemisTranslate }}
-

{{ getWarningBody(status) | artemisTranslate }}

- -
-
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.ts deleted file mode 100644 index 0084bf57ed9c..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { HealthStatus, getWarningAction, getWarningBody, getWarningHint, getWarningTitle } from 'app/entities/competency/learning-path-health.model'; - -@Component({ - selector: 'jhi-learning-path-health-status-warning', - templateUrl: './learning-path-health-status-warning.component.html', -}) -export class LearningPathHealthStatusWarningComponent { - @Input() status: HealthStatus; - @Output() onButtonClicked: EventEmitter = new EventEmitter(); - - readonly getWarningTitle = getWarningTitle; - readonly getWarningBody = getWarningBody; - readonly getWarningHint = getWarningHint; - readonly getWarningAction = getWarningAction; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html deleted file mode 100644 index fa2f5b75d63b..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.html +++ /dev/null @@ -1,90 +0,0 @@ -@if (isLoading) { -
-
- -
-
-} -@if (!isLoading && health) { -
-

-
- -
- -
-
- @if (health.status?.includes(HealthStatus.MISSING)) { - - } - @if (health.status?.includes(HealthStatus.NO_COMPETENCIES)) { - - } - @if (health.status?.includes(HealthStatus.NO_RELATIONS)) { - - } -
-
- - - @if (searchLoading) { - - } -
- - - - - - - - - - - - @for (learningPath of content.resultsOnPage; track trackId($index, learningPath)) { - - - - - - - - } - -
- # - - - - - - - - - - -
- {{ learningPath.id }} - - - - - - - - -
-
- -
-
-
-} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.ts b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.ts deleted file mode 100644 index bff608a8cb93..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.component.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Subject } from 'rxjs'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { debounceTime, finalize, switchMap, tap } from 'rxjs/operators'; -import { HttpErrorResponse } from '@angular/common/http'; -import { onError } from 'app/shared/util/global.utils'; -import { AlertService } from 'app/core/util/alert.service'; -import { SearchResult, SearchTermPageableSearch, SortingOrder } from 'app/shared/table/pageable-table'; -import { LearningPathPagingService } from 'app/course/learning-paths/learning-path-paging.service'; -import { SortService } from 'app/shared/service/sort.service'; -import { LearningPathInformationDTO } from 'app/entities/competency/learning-path.model'; -import { faSort, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons'; -import { HealthStatus, LearningPathHealthDTO, getWarningAction, getWarningBody, getWarningHint, getWarningTitle } from 'app/entities/competency/learning-path-health.model'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { LearningPathProgressModalComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-modal.component'; - -export enum TableColumn { - ID = 'ID', - USER_NAME = 'USER_NAME', - USER_LOGIN = 'USER_LOGIN', - PROGRESS = 'PROGRESS', -} - -@Component({ - selector: 'jhi-learning-path-management', - templateUrl: './learning-path-management.component.html', -}) -export class LearningPathManagementComponent implements OnInit { - isLoading = false; - - courseId: number; - health: LearningPathHealthDTO; - - searchLoading = false; - readonly column = TableColumn; - state: SearchTermPageableSearch = { - page: 1, - pageSize: 50, - searchTerm: '', - sortingOrder: SortingOrder.ASCENDING, - sortedColumn: TableColumn.ID, - }; - content: SearchResult; - total = 0; - - private search = new Subject(); - private sort = new Subject(); - - // icons - faSort = faSort; - faTriangleExclamation = faTriangleExclamation; - - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private learningPathService: LearningPathService, - private alertService: AlertService, - private pagingService: LearningPathPagingService, - private sortService: SortService, - private modalService: NgbModal, - ) {} - - get page(): number { - return this.state.page; - } - - set page(page: number) { - this.setSearchParam({ page }); - } - - get listSorting(): boolean { - return this.state.sortingOrder === SortingOrder.ASCENDING; - } - - /** - * Set the list sorting direction - * - * @param ascending {boolean} Ascending order set - */ - set listSorting(ascending: boolean) { - const sortingOrder = ascending ? SortingOrder.ASCENDING : SortingOrder.DESCENDING; - this.setSearchParam({ sortingOrder }); - } - - /** - * Gives the ID for any item in the table, so that it can be tracked/identified by ngFor - * - * @param index The index of the element in the ngFor - * @param item The item itself - * @returns The ID of the item - */ - trackId(index: number, item: LearningPathInformationDTO): number { - return item.id!; - } - - get sortedColumn(): string { - return this.state.sortedColumn; - } - - set sortedColumn(sortedColumn: string) { - this.setSearchParam({ sortedColumn }); - } - - get searchTerm(): string { - return this.state.searchTerm; - } - - set searchTerm(searchTerm: string) { - this.state.searchTerm = searchTerm; - this.search.next(); - } - - ngOnInit(): void { - this.content = { resultsOnPage: [], numberOfPages: 0 }; - - this.activatedRoute.parent!.params.subscribe((params) => { - this.courseId = params['courseId']; - if (this.courseId) { - this.loadData(); - } - }); - } - - private loadData() { - this.isLoading = true; - - this.learningPathService - .getHealthStatusForCourse(this.courseId) - .pipe( - finalize(() => { - this.isLoading = false; - }), - ) - .subscribe({ - next: (res) => { - this.health = res.body!; - this.performSearch(this.sort, 0); - this.performSearch(this.search, 300); - }, - error: (res: HttpErrorResponse) => onError(this.alertService, res), - }); - } - - enableLearningPaths() { - this.isLoading = true; - this.learningPathService.enableLearningPaths(this.courseId).subscribe({ - next: () => { - this.loadData(); - }, - error: (res: HttpErrorResponse) => onError(this.alertService, res), - }); - } - - generateMissing() { - this.isLoading = true; - this.learningPathService.generateMissingLearningPathsForCourse(this.courseId).subscribe({ - next: () => { - this.loadData(); - }, - error: (res: HttpErrorResponse) => onError(this.alertService, res), - }); - } - - routeToCompetencyManagement() { - this.router.navigate(['../competency-management'], { relativeTo: this.activatedRoute }); - } - - /** - * Method to perform the search based on a search subject - * - * @param searchSubject The search subject which we use to search. - * @param debounce The delay we apply to delay the feedback / wait for input - */ - performSearch(searchSubject: Subject, debounce: number): void { - searchSubject - .pipe( - debounceTime(debounce), - tap(() => (this.searchLoading = true)), - switchMap(() => this.pagingService.search(this.state, { courseId: this.courseId })), - ) - .subscribe((resp) => { - this.content = resp; - this.searchLoading = false; - this.total = resp.numberOfPages * this.state.pageSize; - }); - } - - sortRows() { - this.sortService.sortByProperty(this.content.resultsOnPage, this.sortedColumn, this.listSorting); - } - - private setSearchParam(patch: Partial): void { - Object.assign(this.state, patch); - this.sort.next(); - } - - /** - * Callback function when the user navigates through the page results - * - * @param pageNumber The current page number - */ - onPageChange(pageNumber: number) { - if (pageNumber) { - this.page = pageNumber; - } - } - - viewLearningPath(learningPath: LearningPathInformationDTO) { - const modalRef = this.modalService.open(LearningPathProgressModalComponent, { - size: 'xl', - backdrop: 'static', - windowClass: 'learning-path-modal', - }); - modalRef.componentInstance.courseId = this.courseId; - modalRef.componentInstance.learningPath = learningPath; - } - - protected readonly HealthStatus = HealthStatus; - protected readonly getWarningTitle = getWarningTitle; - protected readonly getWarningBody = getWarningBody; - protected readonly getWarningAction = getWarningAction; - protected readonly getWarningHint = getWarningHint; -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.module.ts b/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.module.ts deleted file mode 100644 index d67070ca7763..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-management/learning-path-management.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { ArtemisLearningPathProgressModule } from 'app/course/learning-paths/progress-modal/learning-path-progress.module'; -import { LearningPathManagementComponent } from 'app/course/learning-paths/learning-path-management/learning-path-management.component'; -import { LearningPathHealthStatusWarningComponent } from 'app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component'; -import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; - -@NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisLearningPathProgressModule], - declarations: [LearningPathManagementComponent, LearningPathHealthStatusWarningComponent], -}) -export class ArtemisLearningPathManagementModule {} diff --git a/src/main/webapp/app/course/learning-paths/learning-path-paging.service.ts b/src/main/webapp/app/course/learning-paths/learning-path-paging.service.ts deleted file mode 100644 index c7c828f97f77..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path-paging.service.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { PagingService } from 'app/exercises/shared/manage/paging.service'; -import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { LearningPathInformationDTO } from 'app/entities/competency/learning-path.model'; - -type EntityResponseType = SearchResult; -@Injectable({ providedIn: 'root' }) -export class LearningPathPagingService extends PagingService { - public resourceUrl = 'api'; - - constructor(private http: HttpClient) { - super(); - } - - override search(pageable: SearchTermPageableSearch, options: { courseId: number }): Observable { - const params = this.createHttpParams(pageable); - return this.http - .get(`${this.resourceUrl}/courses/${options.courseId}/learning-paths`, { params, observe: 'response' }) - .pipe(map((resp: HttpResponse) => resp && resp.body!)); - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-path.service.ts b/src/main/webapp/app/course/learning-paths/learning-path.service.ts deleted file mode 100644 index 8220e1034921..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-path.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { LearningPathHealthDTO } from 'app/entities/competency/learning-path-health.model'; -import { - CompetencyProgressForLearningPathDTO, - LearningPathInformationDTO, - LearningPathNavigationOverviewDTO, - NgxLearningPathDTO, -} from 'app/entities/competency/learning-path.model'; -import { map, tap } from 'rxjs/operators'; -import { LearningPathStorageService } from 'app/course/learning-paths/participate/learning-path-storage.service'; - -@Injectable({ providedIn: 'root' }) -export class LearningPathService { - private resourceURL = 'api'; - - constructor( - private httpClient: HttpClient, - private learningPathStorageService: LearningPathStorageService, - ) {} - - enableLearningPaths(courseId: number): Observable> { - return this.httpClient.put(`${this.resourceURL}/courses/${courseId}/learning-paths/enable`, null, { observe: 'response' }); - } - - generateMissingLearningPathsForCourse(courseId: number): Observable> { - return this.httpClient.put(`${this.resourceURL}/courses/${courseId}/learning-paths/generate-missing`, null, { observe: 'response' }); - } - - getHealthStatusForCourse(courseId: number) { - return this.httpClient.get(`${this.resourceURL}/courses/${courseId}/learning-path-health`, { observe: 'response' }); - } - - getLearningPath(learningPathId: number): Observable> { - return this.httpClient.get(`${this.resourceURL}/learning-path/${learningPathId}`, { observe: 'response' }); - } - - getLearningPathNavigationOverview(learningPathId: number): Observable> { - return this.httpClient.get(`${this.resourceURL}/learning-path/${learningPathId}/navigation-overview`, { observe: 'response' }); - } - - getLearningPathNgxGraph(learningPathId: number): Observable> { - return this.httpClient.get(`${this.resourceURL}/learning-path/${learningPathId}/graph`, { observe: 'response' }).pipe( - map((ngxLearningPathResponse) => { - return this.sanitizeNgxLearningPathResponse(ngxLearningPathResponse); - }), - ); - } - - getLearningPathNgxPath(learningPathId: number): Observable> { - return this.httpClient.get(`${this.resourceURL}/learning-path/${learningPathId}/path`, { observe: 'response' }).pipe( - map((ngxLearningPathResponse) => { - return this.sanitizeNgxLearningPathResponse(ngxLearningPathResponse); - }), - tap((ngxLearningPathResponse) => { - this.learningPathStorageService.storeRecommendations(learningPathId, ngxLearningPathResponse.body!); - }), - ); - } - - private sanitizeNgxLearningPathResponse(ngxLearningPathResponse: HttpResponse) { - ngxLearningPathResponse.body!.nodes ??= []; - ngxLearningPathResponse.body!.edges ??= []; - return ngxLearningPathResponse; - } - - getLearningPathId(courseId: number) { - return this.httpClient.get(`${this.resourceURL}/courses/${courseId}/learning-path-id`, { observe: 'response' }); - } - - generateLearningPath(courseId: number) { - return this.httpClient.post(`${this.resourceURL}/courses/${courseId}/learning-path`, null, { observe: 'response' }); - } - - getCompetencyProgressForLearningPath(learningPathId: number) { - return this.httpClient.get(`${this.resourceURL}/learning-path/${learningPathId}/competency-progress`, { observe: 'response' }); - } -} diff --git a/src/main/webapp/app/course/learning-paths/learning-paths.module.ts b/src/main/webapp/app/course/learning-paths/learning-paths.module.ts deleted file mode 100644 index 00ce64c85ed7..000000000000 --- a/src/main/webapp/app/course/learning-paths/learning-paths.module.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { NgxGraphModule } from '@swimlane/ngx-graph'; -import { ArtemisLectureUnitsModule } from 'app/overview/course-lectures/lecture-units.module'; -import { LearningPathContainerComponent } from 'app/course/learning-paths/participate/learning-path-container.component'; -import { Authority } from 'app/shared/constants/authority.constants'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { RouterModule, Routes } from '@angular/router'; -import { ArtemisLearningPathProgressModule } from 'app/course/learning-paths/progress-modal/learning-path-progress.module'; -import { ArtemisLearningPathGraphModule } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.module'; -import { LearningPathStudentPageComponent } from 'app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component'; - -const routes: Routes = [ - { - path: '', - component: LearningPathStudentPageComponent, - data: { - authorities: [Authority.USER], - pageTitle: 'overview.learningPath', - }, - canActivate: [UserRouteAccessService], - children: [ - { - path: 'lecture-unit', - pathMatch: 'full', - children: [ - { - path: '', - pathMatch: 'full', - loadChildren: () => - import('app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.module').then( - (m) => m.ArtemisLearningPathLectureUnitViewModule, - ), - }, - ], - }, - { - path: 'exercise', - pathMatch: 'full', - children: [ - { - path: '', - pathMatch: 'full', - loadChildren: () => import('app/overview/exercise-details/course-exercise-details.module').then((m) => m.CourseExerciseDetailsModule), - }, - ], - }, - { - path: 'exercises/:exerciseId', - loadChildren: () => import('app/overview/exercise-details/course-exercise-details.module').then((m) => m.CourseExerciseDetailsModule), - }, - ], - }, -]; - -@NgModule({ - imports: [ - ArtemisSharedModule, - FormsModule, - ReactiveFormsModule, - ArtemisSharedComponentModule, - NgxGraphModule, - ArtemisLectureUnitsModule, - RouterModule.forChild(routes), - ArtemisLearningPathGraphModule, - ArtemisLearningPathProgressModule, - ], - declarations: [LearningPathContainerComponent], - exports: [LearningPathContainerComponent], -}) -export class ArtemisLearningPathsModule {} diff --git a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.html b/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.html deleted file mode 100644 index b9e467deb974..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
- -
-
-
- @if (!lectureUnit && !exercise) { -
- -
- } - @if (lectureUnit || exercise) { - - } -
-
-
diff --git a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.scss b/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.scss deleted file mode 100644 index ca440c6ae649..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.scss +++ /dev/null @@ -1,35 +0,0 @@ -.lp-participation-view-container { - width: 100%; - margin-left: 0; -} - -.graph-wrapper { - max-width: min-content; -} - -.learning-object-wrapper { - border-style: none; -} - -.sticky-sidebar { - position: sticky; - top: 0; - text-align: center; -} - -.path { - display: flex; - width: 100px; - max-height: 60vh; - overflow-y: auto; - scrollbar-width: thin; - - jhi-learning-path { - width: 100%; - min-height: fit-content; - } -} - -.view-progress-button { - margin: 0.5em; -} diff --git a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.ts b/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.ts deleted file mode 100644 index e1dd536f7254..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/learning-path-container.component.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { Component, Input, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { faChevronDown, faChevronUp, faEye } from '@fortawesome/free-solid-svg-icons'; -import { Exercise } from 'app/entities/exercise.model'; -import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; -import { Lecture } from 'app/entities/lecture.model'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { LectureService } from 'app/lecture/lecture.service'; -import { onError } from 'app/shared/util/global.utils'; -import { HttpErrorResponse } from '@angular/common/http'; -import { AlertService } from 'app/core/util/alert.service'; -import { LearningPathLectureUnitViewComponent } from 'app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component'; -import { CourseExerciseDetailsComponent } from 'app/overview/exercise-details/course-exercise-details.component'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry, StorageEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; -import { LearningPathComponent } from 'app/course/learning-paths/learning-path-graph/learning-path.component'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { CompetencyGraphModalComponent } from 'app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component'; - -@Component({ - selector: 'jhi-learning-path-container', - styleUrls: ['./learning-path-container.component.scss'], - templateUrl: './learning-path-container.component.html', -}) -export class LearningPathContainerComponent implements OnInit { - @ViewChild('learningPathComponent') learningPathComponent: LearningPathComponent; - - @Input() courseId: number; - learningPathId: number; - - learningObjectId?: number; - lectureId?: number; - lecture?: Lecture; - lectureUnit?: LectureUnit; - exercise?: Exercise; - - // icons - faChevronUp = faChevronUp; - faChevronDown = faChevronDown; - faEye = faEye; - - constructor( - private router: Router, - private activatedRoute: ActivatedRoute, - private alertService: AlertService, - private learningPathService: LearningPathService, - private lectureService: LectureService, - private exerciseService: ExerciseService, - private modalService: NgbModal, - public learningPathStorageService: LearningPathStorageService, - ) {} - - ngOnInit() { - if (!this.courseId) { - this.activatedRoute.parent!.parent!.params.subscribe((params) => { - this.courseId = params['courseId']; - }); - } - this.learningPathService.getLearningPathId(this.courseId).subscribe({ - next: (learningPathIdResponse) => { - this.learningPathId = learningPathIdResponse.body!; - }, - error: (res: HttpErrorResponse) => { - if (res.status === 404) { - // if the learning path does not exist, we do need to create it - this.learningPathService.generateLearningPath(this.courseId).subscribe((learningPathIdResponse) => { - this.learningPathId = learningPathIdResponse.body!; - }); - } else { - onError(this.alertService, res); - } - }, - }); - } - - onNextTask() { - const entry = this.currentStateToEntry(); - // reset state to avoid invalid states - this.undefineAll(); - if (this.learningPathStorageService.hasNextRecommendation(this.learningPathId, entry)) { - this.loadEntry(this.learningPathStorageService.getNextRecommendation(this.learningPathId, entry)); - } - } - - onPrevTask() { - const entry = this.currentStateToEntry(); - // reset state to avoid invalid states - this.undefineAll(); - if (this.learningPathStorageService.hasPrevRecommendation(this.learningPathId, entry)) { - this.loadEntry(this.learningPathStorageService.getPrevRecommendation(this.learningPathId, entry)); - } - } - - currentStateToEntry() { - if (this.lectureUnit?.id) { - return new LectureUnitEntry(this.lectureId!, this.lectureUnit.id); - } else if (this.exercise?.id) { - return new ExerciseEntry(this.exercise.id); - } - } - - private undefineAll() { - // reset ids - this.lectureId = undefined; - this.learningObjectId = undefined; - // reset models - this.lecture = undefined; - this.lectureUnit = undefined; - this.exercise = undefined; - } - - private loadEntry(entry: StorageEntry | undefined) { - if (entry instanceof LectureUnitEntry) { - this.learningObjectId = entry.lectureUnitId; - this.lectureId = entry.lectureId; - this.loadLectureUnit(); - } else if (entry instanceof ExerciseEntry) { - this.learningObjectId = entry.exerciseId; - this.loadExercise(); - } else { - this.learningPathComponent.clearHighlighting(); - return; - } - this.learningPathComponent.highlightNode(entry); - if (this.learningPathComponent.highlightedNode) { - this.scrollTo(this.learningPathComponent.highlightedNode); - } - } - - loadLectureUnit() { - this.lectureService.findWithDetails(this.lectureId!).subscribe({ - next: (findLectureResult) => { - this.lecture = findLectureResult.body!; - if (this.lecture?.lectureUnits) { - this.lectureUnit = this.lecture.lectureUnits.find((lectureUnit) => lectureUnit.id === this.learningObjectId); - } - }, - error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), - }); - this.router.navigate(['lecture-unit'], { relativeTo: this.activatedRoute }); - } - - loadExercise() { - this.exerciseService.getExerciseDetails(this.learningObjectId!).subscribe({ - next: (exerciseResponse) => { - this.exercise = exerciseResponse.body!.exercise; - }, - error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), - }); - this.router.navigate(['exercise'], { relativeTo: this.activatedRoute }); - } - - /** - * This function gets called if the router outlet gets activated. This is - * used only for the LearningPathLectureUnitViewComponent - * @param instance The component instance - */ - onChildActivate(instance: LearningPathLectureUnitViewComponent | CourseExerciseDetailsComponent) { - if (instance instanceof LearningPathLectureUnitViewComponent) { - this.setupLectureUnitView(instance); - } else { - this.setupExerciseView(instance); - } - } - - setupLectureUnitView(instance: LearningPathLectureUnitViewComponent) { - if (this.lecture && this.lectureUnit) { - instance.lecture = this.lecture; - instance.lectureUnit = this.lectureUnit!; - } - } - - setupExerciseView(instance: CourseExerciseDetailsComponent) { - if (this.exercise) { - instance.learningPathMode = true; - instance.courseId = this.courseId; - instance.exerciseId = this.learningObjectId!; - } - } - - onNodeClicked(node: NgxLearningPathNode) { - if (node.type !== NodeType.LECTURE_UNIT && node.type !== NodeType.EXERCISE) { - return; - } - // reset state to avoid invalid states - this.undefineAll(); - this.learningObjectId = node.linkedResource!; - this.lectureId = node.linkedResourceParent; - if (node.type === NodeType.LECTURE_UNIT) { - this.loadLectureUnit(); - this.learningPathComponent.highlightNode(new LectureUnitEntry(this.lectureId!, this.learningObjectId)); - } else if (node.type === NodeType.EXERCISE) { - this.loadExercise(); - this.learningPathComponent.highlightNode(new ExerciseEntry(this.learningObjectId)); - } - if (this.learningPathComponent.highlightedNode) { - this.scrollTo(this.learningPathComponent.highlightedNode); - } - } - - scrollTo(node: NgxLearningPathNode) { - document.getElementById(node.id)?.scrollIntoView({ - behavior: 'smooth', - }); - } - - viewProgress() { - const modalRef = this.modalService.open(CompetencyGraphModalComponent, { - size: 'xl', - backdrop: 'static', - windowClass: 'competency-graph-modal', - }); - modalRef.componentInstance.learningPathId = this.learningPathId; - // modalRef.componentInstance.learningPath = learningPathResponse.body!; - // this.learningPathService.getLearningPath(this.learningPathId).subscribe((learningPathResponse) => { - // }); - } -} diff --git a/src/main/webapp/app/course/learning-paths/participate/learning-path-storage.service.ts b/src/main/webapp/app/course/learning-paths/participate/learning-path-storage.service.ts deleted file mode 100644 index 499327210268..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/learning-path-storage.service.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { Injectable } from '@angular/core'; -import { NgxLearningPathDTO, NodeType } from 'app/entities/competency/learning-path.model'; - -/** - * This service is used to store the recommendations of learning path participation for the currently logged-in user. - */ -@Injectable({ providedIn: 'root' }) -export class LearningPathStorageService { - private readonly learningPathRecommendations: Map = new Map(); - - /** - * Simplifies and stores the recommended order of learning objects for the given learning path - * - * @param learningPathId the id of the learning path - * @param learningPath the learning path dto that should be stored - */ - storeRecommendations(learningPathId: number, learningPath: NgxLearningPathDTO) { - this.learningPathRecommendations.set(learningPathId, []); - let currentId = learningPath.nodes.map((node) => node.id).find((id) => !learningPath.edges.find((edge) => edge.target == id)); - while (currentId) { - const currentNode = learningPath.nodes.find((node) => node.id == currentId)!; - if (currentNode.type === NodeType.LECTURE_UNIT) { - this.learningPathRecommendations.get(learningPathId)!.push(new LectureUnitEntry(currentNode.linkedResourceParent!, currentNode.linkedResource!)); - } else if (currentNode.type === NodeType.EXERCISE) { - this.learningPathRecommendations.get(learningPathId)!.push(new ExerciseEntry(currentNode.linkedResource!)); - } - const edge = learningPath.edges.find((edge) => edge.source == currentId); - if (edge) { - currentId = edge.target; - } else { - currentId = undefined; - } - } - } - - /** - * Gets all recommendations of the learning path in recommended order - * - * @param learningPathId the id of the learning path - */ - getRecommendations(learningPathId: number) { - return this.learningPathRecommendations.get(learningPathId); - } - - /** - * Gets if the given learning object has a successor. - * - * @param learningPathId the id of the learning path - * @param entry the entry for which the successor should be checked - */ - hasNextRecommendation(learningPathId: number, entry?: StorageEntry): boolean { - if (!this.learningPathRecommendations.has(learningPathId)) { - return false; - } - if (!entry) { - return !!this.learningPathRecommendations.get(learningPathId)?.length; - } - const index = this.getIndexOf(learningPathId, entry); - return 0 <= index && index + 1 < this.learningPathRecommendations.get(learningPathId)!.length; - } - - /** - * Gets the next recommended entry for a learning object. - *

- * First entry, if given entry undefined. - * Undefined if the current entry has no successor. - * @param learningPathId the id of the learning path - * @param entry the entry for which the successor should be returned - */ - getNextRecommendation(learningPathId: number, entry?: StorageEntry): StorageEntry | undefined { - if (!this.hasNextRecommendation(learningPathId, entry)) { - return undefined; - } - if (!entry) { - return this.learningPathRecommendations.get(learningPathId)![0]; - } - const nextIndex = this.getIndexOf(learningPathId, entry) + 1; - return this.learningPathRecommendations.get(learningPathId)![nextIndex]; - } - - /** - * Gets if the given learning object has a predecessor. - * - * @param learningPathId the id of the learning path - * @param entry the entry for which the predecessor should be checked - */ - hasPrevRecommendation(learningPathId: number, entry?: StorageEntry): boolean { - if (!this.learningPathRecommendations.has(learningPathId) || !entry) { - return false; - } - return 0 < this.getIndexOf(learningPathId, entry); - } - - /** - * Gets the prior recommended entry for a learning object. - *

- * Undefined if the current entry has no predecessor. - * @param learningPathId the id of the learning path - * @param entry the entry for which the predecessor should be returned - */ - getPrevRecommendation(learningPathId: number, entry?: StorageEntry): StorageEntry | undefined { - if (!this.hasPrevRecommendation(learningPathId, entry)) { - return undefined; - } - const prevIndex = this.getIndexOf(learningPathId, entry!) - 1; - return this.learningPathRecommendations.get(learningPathId)![prevIndex]; - } - - private getIndexOf(learningPathId: number, entry: StorageEntry) { - if (!this.learningPathRecommendations.has(learningPathId)) { - return -1; - } - return this.learningPathRecommendations.get(learningPathId)!.findIndex((e: StorageEntry) => { - return entry.equals(e); - }); - } -} - -export abstract class StorageEntry { - abstract equals(other: StorageEntry): boolean; -} - -export class LectureUnitEntry extends StorageEntry { - readonly lectureUnitId: number; - readonly lectureId: number; - - constructor(lectureId: number, lectureUnitId: number) { - super(); - this.lectureId = lectureId; - this.lectureUnitId = lectureUnitId; - } - - equals(other: StorageEntry): boolean { - if (other instanceof LectureUnitEntry) { - return this.lectureId === other.lectureId && this.lectureUnitId === other.lectureUnitId; - } - return false; - } -} - -export class ExerciseEntry extends StorageEntry { - readonly exerciseId: number; - - constructor(exerciseId: number) { - super(); - this.exerciseId = exerciseId; - } - - equals(other: StorageEntry): boolean { - if (other instanceof ExerciseEntry) { - return this.exerciseId === other.exerciseId; - } - return false; - } -} diff --git a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.html b/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.html deleted file mode 100644 index 9ce19a4b12ba..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.html +++ /dev/null @@ -1,27 +0,0 @@ -

- @if (lectureUnit) { -
-
- @switch (lectureUnit.type) { - @case (LectureUnitType.ATTACHMENT) { - - } - @case (LectureUnitType.VIDEO) { - - } - @case (LectureUnitType.TEXT) { - - } - @case (LectureUnitType.ONLINE) { - - } - } -
-
- } -
- @if (lecture && (isCommunicationEnabled(lecture.course) || isMessagingEnabled(lecture.course))) { - - } -
-
diff --git a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.scss b/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.scss deleted file mode 100644 index 886feddbee00..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.communication-wrapper { - max-width: min-content; -} diff --git a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.ts b/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.ts deleted file mode 100644 index 7d96b1de44ea..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { isCommunicationEnabled, isMessagingEnabled } from 'app/entities/course.model'; -import { LectureUnit, LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model'; -import { Lecture } from 'app/entities/lecture.model'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; -import { DiscussionSectionComponent } from 'app/overview/discussion-section/discussion-section.component'; - -export interface LectureUnitCompletionEvent { - lectureUnit: LectureUnit; - completed: boolean; -} - -@Component({ - selector: 'jhi-learning-path-lecture-unit-view', - styleUrls: ['./learning-path-lecture-unit-view.component.scss'], - templateUrl: './learning-path-lecture-unit-view.component.html', -}) -export class LearningPathLectureUnitViewComponent { - @Input() lecture: Lecture; - @Input() lectureUnit: LectureUnit; - readonly LectureUnitType = LectureUnitType; - - discussionComponent?: DiscussionSectionComponent; - - protected readonly isMessagingEnabled = isMessagingEnabled; - protected readonly isCommunicationEnabled = isCommunicationEnabled; - - constructor(private lectureUnitService: LectureUnitService) {} - - completeLectureUnit(event: LectureUnitCompletionEvent): void { - this.lectureUnitService.completeLectureUnit(this.lecture, event); - } - - /** - * This function gets called if the router outlet gets activated. This is - * used only for the DiscussionComponent - * @param instance The component instance - */ - onChildActivate(instance: DiscussionSectionComponent) { - this.discussionComponent = instance; // save the reference to the component instance - } -} diff --git a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.module.ts b/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.module.ts deleted file mode 100644 index 090649b6990f..000000000000 --- a/src/main/webapp/app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { LearningPathLectureUnitViewComponent } from 'app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component'; -import { ArtemisLectureUnitsModule } from 'app/overview/course-lectures/lecture-units.module'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { Authority } from 'app/shared/constants/authority.constants'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { AttachmentUnitComponent } from 'app/overview/course-lectures/attachment-unit/attachment-unit.component'; -import { VideoUnitComponent } from 'app/overview/course-lectures/video-unit/video-unit.component'; -import { TextUnitComponent } from 'app/overview/course-lectures/text-unit/text-unit.component'; -import { OnlineUnitComponent } from 'app/overview/course-lectures/online-unit/online-unit.component'; - -const routes: Routes = [ - { - path: '', - component: LearningPathLectureUnitViewComponent, - data: { - authorities: [Authority.USER], - pageTitle: 'overview.learningPath', - }, - canActivate: [UserRouteAccessService], - }, -]; - -@NgModule({ - imports: [ArtemisSharedModule, RouterModule.forChild(routes), ArtemisLectureUnitsModule, AttachmentUnitComponent, VideoUnitComponent, TextUnitComponent, OnlineUnitComponent], - declarations: [LearningPathLectureUnitViewComponent], - exports: [LearningPathLectureUnitViewComponent], -}) -export class ArtemisLearningPathLectureUnitViewModule {} diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.html b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.html deleted file mode 100644 index 4e90b63ec25d..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.html +++ /dev/null @@ -1,25 +0,0 @@ - diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.scss b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.scss deleted file mode 100644 index cc798cd46adb..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.scss +++ /dev/null @@ -1,26 +0,0 @@ -.modal-container { - display: flex; - flex-wrap: wrap; - height: 90vh; - width: 100%; - padding-top: 8px; - - .row { - width: 100%; - margin-left: 0; - margin-right: 0; - } - - .modal-nav { - height: max-content; - } - - .graph { - width: 100%; - overflow: hidden; - } -} - -.learning-path-modal .modal-dialog .modal-content { - min-height: 500px; -} diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.ts b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.ts deleted file mode 100644 index 7ac8bcab4209..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-modal.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, Input, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { LearningPathGraphComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.component'; -import { LearningPathInformationDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; - -@Component({ - selector: 'jhi-learning-path-progress-modal', - styleUrls: ['./learning-path-progress-modal.component.scss'], - templateUrl: './learning-path-progress-modal.component.html', -}) -export class LearningPathProgressModalComponent { - @Input() courseId: number; - @Input() learningPath: LearningPathInformationDTO; - @ViewChild('learningPathGraphComponent') learningPathGraphComponent: LearningPathGraphComponent; - - constructor( - private activeModal: NgbActiveModal, - private router: Router, - ) {} - - close() { - this.activeModal.close(); - } - - onNodeClicked(node: NgxLearningPathNode) { - if (node.type === NodeType.COMPETENCY_START || node.type === NodeType.COMPETENCY_END) { - this.navigateToCompetency(node); - } - } - - navigateToCompetency(node: NgxLearningPathNode) { - this.router.navigate(['courses', this.courseId, 'competencies', node.linkedResource]); - this.close(); - } -} diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.html b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.html deleted file mode 100644 index a4ea2f528c2b..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
- @if (learningPath) { -

{{ learningPath.user?.name }} ({{ learningPath.user?.login }})

- } -
-
- - - -
-
diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.ts b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.ts deleted file mode 100644 index 592fd92cc190..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress-nav.component.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { faArrowsRotate, faArrowsToEye, faXmark } from '@fortawesome/free-solid-svg-icons'; -import { LearningPathInformationDTO } from 'app/entities/competency/learning-path.model'; - -@Component({ - selector: 'jhi-learning-path-progress-nav', - templateUrl: './learning-path-progress-nav.component.html', -}) -export class LearningPathProgressNavComponent { - @Input() learningPath: LearningPathInformationDTO; - @Output() onRefresh: EventEmitter = new EventEmitter(); - @Output() onCenterView: EventEmitter = new EventEmitter(); - @Output() onClose: EventEmitter = new EventEmitter(); - - faXmark = faXmark; - faArrowsToEye = faArrowsToEye; - faArrowsRotate = faArrowsRotate; -} diff --git a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress.module.ts b/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress.module.ts deleted file mode 100644 index e298d4a2fb5f..000000000000 --- a/src/main/webapp/app/course/learning-paths/progress-modal/learning-path-progress.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { LearningPathProgressNavComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-nav.component'; -import { LearningPathProgressModalComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-modal.component'; -import { ArtemisLearningPathGraphModule } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.module'; - -@NgModule({ - imports: [ArtemisSharedModule, ArtemisLearningPathGraphModule], - declarations: [LearningPathProgressNavComponent, LearningPathProgressModalComponent], - exports: [LearningPathProgressModalComponent], -}) -export class ArtemisLearningPathProgressModule {} diff --git a/src/main/webapp/app/course/manage/course-management.module.ts b/src/main/webapp/app/course/manage/course-management.module.ts index 11b1d70070b3..4788d338648d 100644 --- a/src/main/webapp/app/course/manage/course-management.module.ts +++ b/src/main/webapp/app/course/manage/course-management.module.ts @@ -62,7 +62,6 @@ import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseCategoriesModule } from 'app/shared/exercise-categories/exercise-categories.module'; import { CourseManagementTabBarComponent } from 'app/course/manage/course-management-tab-bar/course-management-tab-bar.component'; import { ArtemisExerciseCreateButtonsModule } from 'app/exercises/shared/manage/exercise-create-buttons.module'; -import { ArtemisLearningPathManagementModule } from 'app/course/learning-paths/learning-path-management/learning-path-management.module'; import { IrisModule } from 'app/iris/iris.module'; import { DetailModule } from 'app/detail-overview-list/detail.module'; import { BuildQueueComponent } from 'app/localci/build-queue/build-queue.component'; @@ -119,7 +118,6 @@ import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown ExerciseCategoriesModule, NgbNavModule, ArtemisExerciseCreateButtonsModule, - ArtemisLearningPathManagementModule, IrisModule, DetailModule, SubmissionResultStatusModule, diff --git a/src/main/webapp/app/overview/courses-routing.module.ts b/src/main/webapp/app/overview/courses-routing.module.ts index b40c8c952f85..1307396134e0 100644 --- a/src/main/webapp/app/overview/courses-routing.module.ts +++ b/src/main/webapp/app/overview/courses-routing.module.ts @@ -189,7 +189,9 @@ const routes: Routes = [ }, { path: 'learning-path', - loadChildren: () => import('app/course/learning-paths/learning-paths.module').then((m) => m.ArtemisLearningPathsModule), + loadComponent: () => + import('app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component').then((c) => c.LearningPathStudentPageComponent), + // loadChildren: () => import('app/course/learning-paths/learning-paths.module').then((m) => m.ArtemisLearningPathsModule), data: { authorities: [Authority.USER], pageTitle: 'overview.learningPath', diff --git a/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph-node.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph-node.component.spec.ts deleted file mode 100644 index 190858547cad..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph-node.component.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { MockComponent, MockDirective } from 'ng-mocks'; -import { By } from '@angular/platform-browser'; -import { LearningPathNodeComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-node.component'; -import { CompetencyProgressForLearningPathDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { StickyPopoverDirective } from 'app/shared/sticky-popover/sticky-popover.directive'; -import { CompetencyRingsComponent } from 'app/course/competencies/competency-rings/competency-rings.component'; - -describe('LearningPathGraphNodeComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathNodeComponent; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - declarations: [LearningPathNodeComponent, MockDirective(StickyPopoverDirective), MockComponent(CompetencyRingsComponent)], - providers: [], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathNodeComponent); - comp = fixture.componentInstance; - }); - }); - - it.each([NodeType.EXERCISE, NodeType.LECTURE_UNIT])('should display correct icon for completed learning object', (type: NodeType) => { - comp.node = { id: '1', type: type, completed: true } as NgxLearningPathNode; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('.completed'))).toBeTruthy(); - }); - - it.each([NodeType.EXERCISE, NodeType.LECTURE_UNIT])('should display correct icon for not completed learning object', (type: NodeType) => { - comp.node = { id: '1', type: type, completed: false } as NgxLearningPathNode; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('#learning-object'))).toBeTruthy(); - }); - - it.each([NodeType.COMPETENCY_START, NodeType.COMPETENCY_END])('should display correct icon for competency node', (type: NodeType) => { - comp.node = { id: '1', type: type } as NgxLearningPathNode; - comp.competencyProgressDTO = { competencyId: 1, masteryThreshold: 0, progress: 0, confidence: 0 } as CompetencyProgressForLearningPathDTO; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('#competency' + (type === NodeType.COMPETENCY_START ? '-start' : '-end'))).nativeElement).toBeTruthy(); - }); - - it.each([NodeType.MATCH_START, NodeType.MATCH_END])('should display correct icon for match node', (type: NodeType) => { - comp.node = { id: '1', type: type } as NgxLearningPathNode; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('#match')).nativeElement).toBeTruthy(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph.component.spec.ts deleted file mode 100644 index b85c8c2aab81..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/learning-path-graph.component.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { LearningPathGraphComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.component'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { CompetencyProgressForLearningPathDTO, NgxLearningPathDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { HttpResponse } from '@angular/common/http'; -import { of } from 'rxjs'; -import { MockComponent, MockDirective, MockModule, MockPipe } from 'ng-mocks'; -import { NgxGraphModule } from '@swimlane/ngx-graph'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import { LearningPathLegendComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-legend.component'; - -describe('LearningPathGraphComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathGraphComponent; - let learningPathService: LearningPathService; - let getCompetencyProgressForLearningPathStub: jest.SpyInstance; - let getLearningPathNgxGraphStub: jest.SpyInstance; - let getLearningPathNgxPathStub: jest.SpyInstance; - const progressDTO = { competencyId: 1 } as CompetencyProgressForLearningPathDTO; - const ngxGraph = { - nodes: [ - { id: '1', linkedResource: 1, type: NodeType.EXERCISE } as NgxLearningPathNode, - { id: '2', linkedResource: 2, linkedResourceParent: 3, type: NodeType.LECTURE_UNIT } as NgxLearningPathNode, - ], - edges: [], - } as NgxLearningPathDTO; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(NgxGraphModule), MockPipe(ArtemisTranslatePipe), MockDirective(NgbTooltip), MockComponent(LearningPathLegendComponent)], - declarations: [LearningPathGraphComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathGraphComponent); - comp = fixture.componentInstance; - learningPathService = TestBed.inject(LearningPathService); - const competencyProgressResponse: HttpResponse = new HttpResponse({ - body: [progressDTO], - status: 200, - }); - getCompetencyProgressForLearningPathStub = jest.spyOn(learningPathService, 'getCompetencyProgressForLearningPath').mockReturnValue(of(competencyProgressResponse)); - const ngxGraphResponse: HttpResponse = new HttpResponse({ - body: ngxGraph, - status: 200, - }); - getLearningPathNgxGraphStub = jest.spyOn(learningPathService, 'getLearningPathNgxGraph').mockReturnValue(of(ngxGraphResponse)); - getLearningPathNgxPathStub = jest.spyOn(learningPathService, 'getLearningPathNgxPath').mockImplementation(); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load progress and learning path from service', () => { - comp.learningPathId = 1; - fixture.detectChanges(); - expect(getCompetencyProgressForLearningPathStub).toHaveBeenCalledExactlyOnceWith(1); - expect(getLearningPathNgxGraphStub).toHaveBeenCalledExactlyOnceWith(1); - expect(getLearningPathNgxPathStub).not.toHaveBeenCalled(); - - expect(comp.competencyProgress.get(1)).toEqual(progressDTO); - }); - - it('should update, center, and zoom to fit on resize', () => { - const updateStub = jest.spyOn(comp.update$, 'next'); - const centerStub = jest.spyOn(comp.center$, 'next'); - const zoomToFitStub = jest.spyOn(comp.zoomToFit$, 'next'); - fixture.detectChanges(); - comp.onResize(); - expect(updateStub).toHaveBeenCalledExactlyOnceWith(true); - expect(centerStub).toHaveBeenCalledExactlyOnceWith(true); - expect(zoomToFitStub).toHaveBeenCalledExactlyOnceWith({ autoCenter: true }); - }); - - it('should zoom to fit and center on resize', () => { - const zoomToFitStub = jest.spyOn(comp.zoomToFit$, 'next'); - const centerStub = jest.spyOn(comp.center$, 'next'); - fixture.detectChanges(); - comp.onCenterView(); - expect(zoomToFitStub).toHaveBeenCalledExactlyOnceWith({ autoCenter: true }); - expect(centerStub).toHaveBeenCalledExactlyOnceWith(true); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/learning-path-legend.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/learning-path-legend.component.spec.ts deleted file mode 100644 index c8a278f72c1a..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/learning-path-legend.component.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { MockPipe } from 'ng-mocks'; -import { LearningPathLegendComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-legend.component'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NodeType } from 'app/entities/competency/learning-path.model'; -import { By } from '@angular/platform-browser'; -import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; - -describe('LearningPathLegendComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathLegendComponent; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockPipe(ArtemisTranslatePipe), NgbTooltipMocksModule], - declarations: [LearningPathLegendComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathLegendComponent); - comp = fixture.componentInstance; - }); - }); - - it('should initialize', () => { - expect(comp).not.toBeNull(); - }); - - it.each([NodeType.COMPETENCY_END, NodeType.MATCH_START, NodeType.MATCH_END, NodeType.LECTURE_UNIT, NodeType.EXERCISE])( - 'should only show label for present node type', - (type: NodeType) => { - comp.nodeTypes = new Set(); - comp.nodeTypes.add(type); - fixture.detectChanges(); - - const competencyEnd = fixture.debugElement.query(By.css('#competency-end')); - if (type === NodeType.COMPETENCY_END) { - expect(competencyEnd).toBeTruthy(); - } else { - expect(competencyEnd).toBeNull(); - } - - const matchStart = fixture.debugElement.query(By.css('#match-start')); - if (type === NodeType.MATCH_START) { - expect(matchStart).toBeTruthy(); - } else { - expect(matchStart).toBeNull(); - } - - const matchEnd = fixture.debugElement.query(By.css('#match-end')); - if (type === NodeType.MATCH_END) { - expect(matchEnd).toBeTruthy(); - } else { - expect(matchEnd).toBeNull(); - } - - const learningObject = fixture.debugElement.query(By.css('#learning-object')); - const completedLearningObject = fixture.debugElement.query(By.css('#completed-learning-object')); - if (type === NodeType.LECTURE_UNIT || type === NodeType.EXERCISE) { - expect(learningObject).toBeTruthy(); - expect(completedLearningObject).toBeTruthy(); - } else { - expect(learningObject).toBeNull(); - expect(completedLearningObject).toBeNull(); - } - }, - ); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/learning-path.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/learning-path.component.spec.ts deleted file mode 100644 index 31ea1a0f92c5..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/learning-path.component.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { NgxLearningPathDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { HttpResponse } from '@angular/common/http'; -import { of } from 'rxjs'; -import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import { LearningPathComponent } from 'app/course/learning-paths/learning-path-graph/learning-path.component'; -import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; -import { LearningPathNodeComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-node.component'; - -describe('LearningPathComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathComponent; - let learningPathService: LearningPathService; - let getLearningPathNgxGraphStub: jest.SpyInstance; - let getLearningPathNgxPathStub: jest.SpyInstance; - let learningPathStorageService: LearningPathStorageService; - let getRecommendationsStub: jest.SpyInstance; - const ngxPath = { - nodes: [ - { id: '1', linkedResource: 1, type: NodeType.EXERCISE } as NgxLearningPathNode, - { id: '2', linkedResource: 2, linkedResourceParent: 3, type: NodeType.LECTURE_UNIT } as NgxLearningPathNode, - ], - edges: [], - } as NgxLearningPathDTO; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockComponent(LearningPathNodeComponent), MockPipe(ArtemisTranslatePipe), MockDirective(NgbTooltip)], - declarations: [LearningPathComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathComponent); - comp = fixture.componentInstance; - learningPathService = TestBed.inject(LearningPathService); - getLearningPathNgxGraphStub = jest.spyOn(learningPathService, 'getLearningPathNgxGraph').mockImplementation(); - const ngxPathResponse: HttpResponse = new HttpResponse({ - body: ngxPath, - status: 200, - }); - getLearningPathNgxPathStub = jest.spyOn(learningPathService, 'getLearningPathNgxPath').mockReturnValue(of(ngxPathResponse)); - learningPathStorageService = TestBed.inject(LearningPathStorageService); - getRecommendationsStub = jest.spyOn(learningPathStorageService, 'getRecommendations').mockReturnValue([new ExerciseEntry(1), new LectureUnitEntry(3, 2)]); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load data on init', () => { - comp.learningPathId = 1; - fixture.detectChanges(); - expect(getLearningPathNgxGraphStub).not.toHaveBeenCalled(); - expect(getLearningPathNgxPathStub).toHaveBeenCalledExactlyOnceWith(1); - expect(getRecommendationsStub).toHaveBeenCalledExactlyOnceWith(1); - expect(comp.path).toEqual(ngxPath.nodes); - }); - - it.each([new ExerciseEntry(1), new LectureUnitEntry(3, 2)])('should highlight node', (entry) => { - comp.learningPathId = 1; - fixture.detectChanges(); - comp.highlightNode(entry); - if (entry instanceof LectureUnitEntry) { - expect(comp.highlightedNode).toEqual(ngxPath.nodes[1]); - } else { - expect(comp.highlightedNode).toEqual(ngxPath.nodes[0]); - } - }); - - it('should clear highlighting', () => { - comp.learningPathId = 1; - comp.highlightedNode = new NgxLearningPathNode(); - fixture.detectChanges(); - comp.clearHighlighting(); - expect(comp.highlightedNode).toBeUndefined(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/node-details/competency-node-details.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/node-details/competency-node-details.component.spec.ts deleted file mode 100644 index 22b70fb07834..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/node-details/competency-node-details.component.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../../test.module'; -import { MockComponent, MockPipe } from 'ng-mocks'; -import { of } from 'rxjs'; -import { HttpResponse } from '@angular/common/http'; -import { CompetencyNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/competency-node-details.component'; -import { Competency, CompetencyProgress, CompetencyTaxonomy } from 'app/entities/competency.model'; -import { CompetencyRingsComponent } from 'app/course/competencies/competency-rings/competency-rings.component'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltipMocksModule } from '../../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; -import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; - -describe('CompetencyNodeDetailsComponent', () => { - let fixture: ComponentFixture; - let comp: CompetencyNodeDetailsComponent; - let courseCompetencyService: CourseCompetencyService; - let findByIdStub: jest.SpyInstance; - let competency: Competency; - let competencyProgress: CompetencyProgress; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, NgbTooltipMocksModule], - declarations: [CompetencyNodeDetailsComponent, MockComponent(CompetencyRingsComponent), MockPipe(ArtemisTranslatePipe), MockPipe(HtmlForMarkdownPipe)], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(CompetencyNodeDetailsComponent); - comp = fixture.componentInstance; - competency = {}; - competency.id = 2; - competency.title = 'Some arbitrary title'; - competency.description = 'Some description'; - competency.taxonomy = CompetencyTaxonomy.ANALYZE; - competency.masteryThreshold = 50; - competencyProgress = new CompetencyProgress(); - competencyProgress.progress = 80; - competencyProgress.confidence = 70; - - courseCompetencyService = TestBed.inject(CourseCompetencyService); - findByIdStub = jest.spyOn(courseCompetencyService, 'findById').mockReturnValue(of(new HttpResponse({ body: competency }))); - comp.courseId = 1; - comp.competencyId = competency.id; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load competency on init if not present', () => { - fixture.detectChanges(); - expect(findByIdStub).toHaveBeenCalledOnce(); - expect(findByIdStub).toHaveBeenCalledWith(competency.id, 1); - expect(comp.competency).toEqual(competency); - }); - - it('should not load competency on init if already present', () => { - comp.competency = competency; - comp.competencyProgress = competencyProgress; - fixture.detectChanges(); - expect(findByIdStub).not.toHaveBeenCalled(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts deleted file mode 100644 index bf1140cc06fb..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/node-details/exercise-node-details.component.spec.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../../test.module'; -import { MockPipe } from 'ng-mocks'; -import { of } from 'rxjs'; -import { HttpResponse } from '@angular/common/http'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltipMocksModule } from '../../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { ExerciseNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/exercise-node-details.component'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { Exercise } from 'app/entities/exercise.model'; -import { TextExercise } from 'app/entities/text/text-exercise.model'; - -describe('ExerciseNodeDetailsComponent', () => { - let fixture: ComponentFixture; - let comp: ExerciseNodeDetailsComponent; - let exerciseService: ExerciseService; - let findStub: jest.SpyInstance; - let exercise: Exercise; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, NgbTooltipMocksModule], - declarations: [ExerciseNodeDetailsComponent, MockPipe(ArtemisTranslatePipe)], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(ExerciseNodeDetailsComponent); - comp = fixture.componentInstance; - exercise = new TextExercise(undefined, undefined); - exercise.id = 1; - exercise.title = 'Some arbitrary title'; - - exerciseService = TestBed.inject(ExerciseService); - findStub = jest.spyOn(exerciseService, 'find').mockReturnValue(of(new HttpResponse({ body: exercise }))); - comp.exerciseId = exercise.id; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load exercise on init if not present', () => { - fixture.detectChanges(); - expect(findStub).toHaveBeenCalledOnce(); - expect(findStub).toHaveBeenCalledWith(exercise.id); - expect(comp.exercise).toEqual(exercise); - }); - - it('should not load exercise on init if already present', () => { - comp.exercise = exercise; - fixture.detectChanges(); - expect(findStub).not.toHaveBeenCalled(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/graph/node-details/lecture-unit-node-details.component.spec.ts b/src/test/javascript/spec/component/learning-paths/graph/node-details/lecture-unit-node-details.component.spec.ts deleted file mode 100644 index 13f47328ff5d..000000000000 --- a/src/test/javascript/spec/component/learning-paths/graph/node-details/lecture-unit-node-details.component.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../../test.module'; -import { MockPipe } from 'ng-mocks'; -import { of } from 'rxjs'; -import { HttpResponse } from '@angular/common/http'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltipMocksModule } from '../../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { LectureUnitNodeDetailsComponent } from 'app/course/learning-paths/learning-path-graph/node-details/lecture-unit-node-details.component'; -import { LectureUnitForLearningPathNodeDetailsDTO, LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; - -describe('LectureUnitNodeDetailsComponent', () => { - let fixture: ComponentFixture; - let comp: LectureUnitNodeDetailsComponent; - let lectureUnitService: LectureUnitService; - let getLectureUnitForLearningPathNodeDetailsStub: jest.SpyInstance; - let lectureUnit: LectureUnitForLearningPathNodeDetailsDTO; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, NgbTooltipMocksModule], - declarations: [LectureUnitNodeDetailsComponent, MockPipe(ArtemisTranslatePipe)], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LectureUnitNodeDetailsComponent); - comp = fixture.componentInstance; - lectureUnit = new LectureUnitForLearningPathNodeDetailsDTO(); - lectureUnit.id = 1; - lectureUnit.name = 'Some arbitrary name'; - lectureUnit.type = LectureUnitType.TEXT; - - lectureUnitService = TestBed.inject(LectureUnitService); - getLectureUnitForLearningPathNodeDetailsStub = jest - .spyOn(lectureUnitService, 'getLectureUnitForLearningPathNodeDetails') - .mockReturnValue(of(new HttpResponse({ body: lectureUnit }))); - comp.lectureUnitId = lectureUnit.id; - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should load lecture unit on init if not present', () => { - fixture.detectChanges(); - expect(getLectureUnitForLearningPathNodeDetailsStub).toHaveBeenCalledOnce(); - expect(getLectureUnitForLearningPathNodeDetailsStub).toHaveBeenCalledWith(lectureUnit.id); - expect(comp.lectureUnit).toEqual(lectureUnit); - }); - - it('should not load lecture unit on init if already present', () => { - comp.lectureUnit = lectureUnit; - fixture.detectChanges(); - expect(getLectureUnitForLearningPathNodeDetailsStub).not.toHaveBeenCalled(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/management/learning-path-health-status-warning.component.spec.ts b/src/test/javascript/spec/component/learning-paths/management/learning-path-health-status-warning.component.spec.ts deleted file mode 100644 index f5396e32157d..000000000000 --- a/src/test/javascript/spec/component/learning-paths/management/learning-path-health-status-warning.component.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { MockPipe } from 'ng-mocks'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { LearningPathHealthStatusWarningComponent } from 'app/course/learning-paths/learning-path-management/learning-path-health-status-warning.component'; -import { HealthStatus } from 'app/entities/competency/learning-path-health.model'; -import { MockHasAnyAuthorityDirective } from '../../../helpers/mocks/directive/mock-has-any-authority.directive'; - -describe('LearningPathHealthStatusWarningComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathHealthStatusWarningComponent; - let getWarningTitleStub: jest.SpyInstance; - let getWarningBodyStub: jest.SpyInstance; - let getWarningActionStub: jest.SpyInstance; - let getWarningHintStub: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, NgbTooltipMocksModule], - declarations: [LearningPathHealthStatusWarningComponent, MockPipe(ArtemisTranslatePipe), MockHasAnyAuthorityDirective], - providers: [], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathHealthStatusWarningComponent); - comp = fixture.componentInstance; - getWarningTitleStub = jest.spyOn(comp, 'getWarningTitle'); - getWarningBodyStub = jest.spyOn(comp, 'getWarningBody'); - getWarningActionStub = jest.spyOn(comp, 'getWarningAction'); - getWarningHintStub = jest.spyOn(comp, 'getWarningHint'); - fixture.detectChanges(); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should create', () => { - expect(fixture).toBeTruthy(); - expect(comp).toBeTruthy(); - }); - - it.each([HealthStatus.MISSING, HealthStatus.NO_COMPETENCIES, HealthStatus.NO_RELATIONS])('should load title', (status: HealthStatus) => { - comp.status = status; - fixture.detectChanges(); - expect(getWarningTitleStub).toHaveBeenCalledWith(status); - }); - - it.each([HealthStatus.MISSING, HealthStatus.NO_COMPETENCIES, HealthStatus.NO_RELATIONS])('should load body', (status: HealthStatus) => { - comp.status = status; - fixture.detectChanges(); - expect(getWarningBodyStub).toHaveBeenCalledWith(status); - }); - - it.each([HealthStatus.MISSING, HealthStatus.NO_COMPETENCIES, HealthStatus.NO_RELATIONS])('should load action', (status: HealthStatus) => { - comp.status = status; - fixture.detectChanges(); - expect(getWarningActionStub).toHaveBeenCalledWith(status); - }); - - it.each([HealthStatus.MISSING, HealthStatus.NO_COMPETENCIES, HealthStatus.NO_RELATIONS])('should load hint', (status: HealthStatus) => { - comp.status = status; - fixture.detectChanges(); - expect(getWarningHintStub).toHaveBeenCalledWith(status); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/management/learning-path-management.component.spec.ts b/src/test/javascript/spec/component/learning-paths/management/learning-path-management.component.spec.ts deleted file mode 100644 index 884544d93695..000000000000 --- a/src/test/javascript/spec/component/learning-paths/management/learning-path-management.component.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { LearningPathManagementComponent, TableColumn } from 'app/course/learning-paths/learning-path-management/learning-path-management.component'; -import { LearningPathPagingService } from 'app/course/learning-paths/learning-path-paging.service'; -import { SortService } from 'app/shared/service/sort.service'; -import { SearchResult, SearchTermPageableSearch, SortingOrder } from 'app/shared/table/pageable-table'; -import { LearningPath, LearningPathInformationDTO } from 'app/entities/competency/learning-path.model'; -import { ArtemisTestModule } from '../../../test.module'; -import { MockComponent, MockDirective, MockPipe } from 'ng-mocks'; -import { ButtonComponent } from 'app/shared/components/button.component'; -import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; -import { SortByDirective } from 'app/shared/sort/sort-by.directive'; -import { SortDirective } from 'app/shared/sort/sort.directive'; -import { of, throwError } from 'rxjs'; -import { ActivatedRoute } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { HealthStatus, LearningPathHealthDTO } from 'app/entities/competency/learning-path-health.model'; -import { AlertService } from 'app/core/util/alert.service'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; - -describe('LearningPathManagementComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathManagementComponent; - let alertService: AlertService; - let alertServiceStub: jest.SpyInstance; - let pagingService: LearningPathPagingService; - let sortService: SortService; - let searchStub: jest.SpyInstance; - let sortByPropertyStub: jest.SpyInstance; - let searchResult: SearchResult; - let state: SearchTermPageableSearch; - let learningPath: LearningPath; - let learningPathService: LearningPathService; - let enableLearningPathsStub: jest.SpyInstance; - let generateMissingLearningPathsForCourseStub: jest.SpyInstance; - let getHealthStatusForCourseStub: jest.SpyInstance; - let health: LearningPathHealthDTO; - let courseId: number; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockComponent(NgbPagination)], - declarations: [ - LearningPathManagementComponent, - MockComponent(ButtonComponent), - MockDirective(SortByDirective), - MockDirective(SortDirective), - MockPipe(ArtemisTranslatePipe), - ], - providers: [ - { - provide: ActivatedRoute, - useValue: { - parent: { - params: of({ - courseId: 1, - }), - }, - }, - }, - ], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathManagementComponent); - comp = fixture.componentInstance; - alertService = TestBed.inject(AlertService); - alertServiceStub = jest.spyOn(alertService, 'error'); - pagingService = TestBed.inject(LearningPathPagingService); - sortService = TestBed.inject(SortService); - searchStub = jest.spyOn(pagingService, 'search'); - sortByPropertyStub = jest.spyOn(sortService, 'sortByProperty'); - learningPathService = TestBed.inject(LearningPathService); - enableLearningPathsStub = jest.spyOn(learningPathService, 'enableLearningPaths'); - generateMissingLearningPathsForCourseStub = jest.spyOn(learningPathService, 'generateMissingLearningPathsForCourse'); - getHealthStatusForCourseStub = jest.spyOn(learningPathService, 'getHealthStatusForCourse'); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - beforeEach(() => { - fixture.detectChanges(); - learningPath = new LearningPath(); - learningPath.id = 2; - courseId = 1; - searchResult = { numberOfPages: 3, resultsOnPage: [learningPath] }; - state = { - page: 1, - pageSize: 10, - searchTerm: 'initialSearchTerm', - sortingOrder: SortingOrder.DESCENDING, - sortedColumn: TableColumn.ID, - ...searchResult, - }; - searchStub.mockReturnValue(of(searchResult)); - enableLearningPathsStub.mockReturnValue(of(new HttpResponse())); - generateMissingLearningPathsForCourseStub.mockReturnValue(of(new HttpResponse())); - health = new LearningPathHealthDTO([]); - getHealthStatusForCourseStub.mockReturnValue(of(new HttpResponse({ body: health }))); - }); - - const setStateAndCallOnInit = (middleExpectation: () => void) => { - comp.state = { ...state }; - comp.ngOnInit(); - middleExpectation(); - expect(comp.content).toEqual(searchResult); - comp.sortRows(); - expect(sortByPropertyStub).toHaveBeenCalledWith(searchResult.resultsOnPage, comp.sortedColumn, comp.listSorting); - }; - - it('should load health status on init', fakeAsync(() => { - setStateAndCallOnInit(() => { - comp.listSorting = true; - tick(10); - expect(getHealthStatusForCourseStub).toHaveBeenCalledWith(courseId); - expect(comp.health).toEqual(health); - }); - })); - - it('should alert error if loading health status fails', fakeAsync(() => { - const error = { status: 404 }; - getHealthStatusForCourseStub.mockReturnValue(throwError(() => new HttpErrorResponse(error))); - fixture.detectChanges(); - comp.ngOnInit(); - expect(getHealthStatusForCourseStub).toHaveBeenCalledWith(courseId); - expect(alertServiceStub).toHaveBeenCalledOnce(); - })); - - it('should alert error if enable learning paths fails', fakeAsync(() => { - const error = { status: 404 }; - enableLearningPathsStub.mockReturnValue(throwError(() => new HttpErrorResponse(error))); - fixture.detectChanges(); - comp.ngOnInit(); - comp.enableLearningPaths(); - expect(enableLearningPathsStub).toHaveBeenCalledWith(courseId); - expect(alertServiceStub).toHaveBeenCalledOnce(); - })); - - it('should generate missing learning paths and load data', fakeAsync(() => { - const healthMissing = new LearningPathHealthDTO([HealthStatus.MISSING]); - getHealthStatusForCourseStub.mockReturnValueOnce(of(new HttpResponse({ body: healthMissing }))).mockReturnValueOnce(of(new HttpResponse({ body: health }))); - fixture.detectChanges(); - comp.ngOnInit(); - expect(comp.health).toEqual(healthMissing); - comp.generateMissing(); - expect(generateMissingLearningPathsForCourseStub).toHaveBeenCalledOnce(); - expect(generateMissingLearningPathsForCourseStub).toHaveBeenCalledWith(courseId); - expect(getHealthStatusForCourseStub).toHaveBeenCalledTimes(3); - expect(comp.health).toEqual(health); - })); - - it('should alert error if generate missing learning paths fails', fakeAsync(() => { - const error = { status: 404 }; - generateMissingLearningPathsForCourseStub.mockReturnValue(throwError(() => new HttpErrorResponse(error))); - fixture.detectChanges(); - comp.ngOnInit(); - comp.generateMissing(); - expect(generateMissingLearningPathsForCourseStub).toHaveBeenCalledWith(courseId); - expect(alertServiceStub).toHaveBeenCalledOnce(); - })); - - it('should set content to paging result on sort', fakeAsync(() => { - expect(comp.listSorting).toBeTrue(); - setStateAndCallOnInit(() => { - comp.listSorting = false; - tick(10); - expect(searchStub).toHaveBeenCalledWith({ ...state, sortingOrder: SortingOrder.DESCENDING }, { courseId }); - expect(comp.listSorting).toBeFalse(); - }); - })); - - it('should set content to paging result on pageChange', fakeAsync(() => { - expect(comp.page).toBe(1); - setStateAndCallOnInit(() => { - comp.onPageChange(5); - tick(10); - expect(searchStub).toHaveBeenCalledWith({ ...state, page: 5 }, { courseId }); - expect(comp.page).toBe(5); - }); - })); - - it('should set content to paging result on search', fakeAsync(() => { - expect(comp.searchTerm).toBe(''); - setStateAndCallOnInit(() => { - const givenSearchTerm = 'givenSearchTerm'; - comp.searchTerm = givenSearchTerm; - tick(10); - expect(searchStub).not.toHaveBeenCalled(); - tick(290); - expect(searchStub).toHaveBeenCalledWith({ ...state, searchTerm: givenSearchTerm }, { courseId }); - expect(comp.searchTerm).toEqual(givenSearchTerm); - }); - })); - - it('should set content to paging result on sortedColumn change', fakeAsync(() => { - expect(comp.sortedColumn).toEqual(TableColumn.ID); - setStateAndCallOnInit(() => { - comp.sortedColumn = TableColumn.USER_LOGIN; - tick(10); - expect(searchStub).toHaveBeenCalledWith({ ...state, sortedColumn: TableColumn.USER_LOGIN }, { courseId }); - expect(comp.sortedColumn).toEqual(TableColumn.USER_LOGIN); - }); - })); - - it('should return learning path id', () => { - expect(comp.trackId(0, { id: 2 })).toEqual(learningPath.id); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-modal.component.spec.ts b/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-modal.component.spec.ts deleted file mode 100644 index 8d1fecdb8152..000000000000 --- a/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-modal.component.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { MockComponent } from 'ng-mocks'; -import { LearningPathProgressModalComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-modal.component'; -import { LearningPathGraphComponent } from 'app/course/learning-paths/learning-path-graph/learning-path-graph.component'; -import { By } from '@angular/platform-browser'; -import { LearningPathProgressNavComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-nav.component'; -import { LearningPathInformationDTO, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { Router } from '@angular/router'; -import { MockRouter } from '../../../helpers/mocks/mock-router'; - -describe('LearningPathProgressModalComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathProgressModalComponent; - let activeModal: NgbActiveModal; - let closeStub: jest.SpyInstance; - const router = new MockRouter(); - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockComponent(LearningPathGraphComponent), MockComponent(LearningPathProgressNavComponent)], - declarations: [LearningPathProgressModalComponent], - providers: [{ provide: Router, useValue: router }], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathProgressModalComponent); - comp = fixture.componentInstance; - activeModal = TestBed.inject(NgbActiveModal); - closeStub = jest.spyOn(activeModal, 'close'); - fixture.detectChanges(); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - router.navigate.mockRestore(); - }); - - it('should display learning path graph if learning path is present', () => { - comp.courseId = 2; - comp.learningPath = { id: 1 } as LearningPathInformationDTO; - fixture.detectChanges(); - expect(fixture.debugElement.query(By.css('.graph')).nativeElement).toBeTruthy(); - }); - - it('should correctly close modal', () => { - comp.close(); - expect(closeStub).toHaveBeenCalledOnce(); - }); - - it.each([ - { id: '1', type: NodeType.COMPETENCY_START, linkedResource: 3 } as NgxLearningPathNode, - { id: '1', type: NodeType.COMPETENCY_END, linkedResource: 3 } as NgxLearningPathNode, - ])('should navigate on competency node clicked', (node) => { - comp.courseId = 2; - comp.learningPath = { id: 1 } as LearningPathInformationDTO; - fixture.detectChanges(); - comp.onNodeClicked(node); - expect(router.navigate).toHaveBeenCalledOnce(); - expect(router.navigate).toHaveBeenCalledWith(['courses', 2, 'competencies', 3]); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-nav.component.spec.ts b/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-nav.component.spec.ts deleted file mode 100644 index 877e9718b84e..000000000000 --- a/src/test/javascript/spec/component/learning-paths/management/learning-path-progress-nav.component.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../../../test.module'; -import { By } from '@angular/platform-browser'; -import { LearningPathProgressNavComponent } from 'app/course/learning-paths/progress-modal/learning-path-progress-nav.component'; -import { LearningPathInformationDTO } from 'app/entities/competency/learning-path.model'; -import { UserNameAndLoginDTO } from 'app/core/user/user.model'; -import { MockPipe } from 'ng-mocks'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; - -describe('LearningPathProgressNavComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathProgressNavComponent; - let onRefreshStub: jest.SpyInstance; - let onCenterViewStub: jest.SpyInstance; - let onCloseStub: jest.SpyInstance; - let learningPath: LearningPathInformationDTO; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, NgbTooltipMocksModule], - declarations: [LearningPathProgressNavComponent, MockPipe(ArtemisTranslatePipe)], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathProgressNavComponent); - comp = fixture.componentInstance; - onRefreshStub = jest.spyOn(comp.onRefresh, 'emit'); - onCenterViewStub = jest.spyOn(comp.onCenterView, 'emit'); - onCloseStub = jest.spyOn(comp.onClose, 'emit'); - learningPath = new LearningPathInformationDTO(); - learningPath.user = new UserNameAndLoginDTO(); - learningPath.user.name = 'some arbitrary name'; - learningPath.user.login = 'somearbitrarylogin'; - comp.learningPath = learningPath; - fixture.detectChanges(); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should create', () => { - expect(fixture).toBeTruthy(); - expect(comp).toBeTruthy(); - }); - - it('should emit refresh on click', () => { - const button = fixture.debugElement.query(By.css('#refresh-button')); - expect(button).not.toBeNull(); - - button.nativeElement.click(); - expect(onRefreshStub).toHaveBeenCalledOnce(); - }); - - it('should emit center view on click', () => { - const button = fixture.debugElement.query(By.css('#center-button')); - expect(button).not.toBeNull(); - - button.nativeElement.click(); - expect(onCenterViewStub).toHaveBeenCalledOnce(); - }); - - it('should emit close on click', () => { - const button = fixture.debugElement.query(By.css('#close-button')); - expect(button).not.toBeNull(); - - button.nativeElement.click(); - expect(onCloseStub).toHaveBeenCalledOnce(); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts b/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts deleted file mode 100644 index 9ead2c3a9994..000000000000 --- a/src/test/javascript/spec/component/learning-paths/participate/learning-path-container.component.spec.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockDirective, MockModule, MockPipe } from 'ng-mocks'; -import { ArtemisTestModule } from '../../../test.module'; -import { of, throwError } from 'rxjs'; -import { ActivatedRoute, RouterModule } from '@angular/router'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { LearningPathContainerComponent } from 'app/course/learning-paths/participate/learning-path-container.component'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; -import { LectureService } from 'app/lecture/lecture.service'; -import { Lecture } from 'app/entities/lecture.model'; -import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; -import { Exercise } from 'app/entities/exercise.model'; -import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { AttachmentUnit } from 'app/entities/lecture-unit/attachmentUnit.model'; -import { TextExercise } from 'app/entities/text/text-exercise.model'; -import { LearningPathLectureUnitViewComponent } from 'app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component'; -import { CourseExerciseDetailsComponent } from 'app/overview/exercise-details/course-exercise-details.component'; -import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry, StorageEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import { LearningPathComponent } from 'app/course/learning-paths/learning-path-graph/learning-path.component'; - -describe('LearningPathContainerComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathContainerComponent; - let learningPathService: LearningPathService; - let getLearningPathIdStub: jest.SpyInstance; - const learningPathId = 1337; - let lectureService: LectureService; - let lecture: Lecture; - const lectureId = 2; - let lectureUnit: LectureUnit; - const lectureUnitId = 3; - let findWithDetailsStub: jest.SpyInstance; - let exerciseService: ExerciseService; - let exercise: Exercise; - const exerciseId = 4; - let getExerciseDetailsStub: jest.SpyInstance; - let historyService: LearningPathStorageService; - let hasNextRecommendationStub: jest.SpyInstance; - let getNextRecommendationStub: jest.SpyInstance; - let hasPrevRecommendationStub: jest.SpyInstance; - let getPrevRecommendationStub: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(RouterModule), MockPipe(ArtemisTranslatePipe), MockDirective(NgbTooltip), LearningPathComponent], - declarations: [LearningPathContainerComponent], - providers: [ - { - provide: ActivatedRoute, - useValue: { - parent: { - parent: { - params: of({ - courseId: 1, - }), - }, - }, - }, - }, - ], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathContainerComponent); - comp = fixture.componentInstance; - learningPathService = TestBed.inject(LearningPathService); - getLearningPathIdStub = jest.spyOn(learningPathService, 'getLearningPathId').mockReturnValue(of(new HttpResponse({ body: learningPathId }))); - - lectureUnit = new AttachmentUnit(); - lectureUnit.id = lectureUnitId; - lecture = new Lecture(); - lecture.id = lectureId; - lecture.lectureUnits = [lectureUnit]; - lectureService = TestBed.inject(LectureService); - findWithDetailsStub = jest.spyOn(lectureService, 'findWithDetails').mockReturnValue(of(new HttpResponse({ body: lecture }))); - - exercise = new TextExercise(undefined, undefined); - exercise.id = exerciseId; - exerciseService = TestBed.inject(ExerciseService); - getExerciseDetailsStub = jest.spyOn(exerciseService, 'getExerciseDetails').mockReturnValue(of(new HttpResponse({ body: { exercise: exercise } }))); - - historyService = TestBed.inject(LearningPathStorageService); - hasNextRecommendationStub = jest.spyOn(historyService, 'hasNextRecommendation'); - getNextRecommendationStub = jest.spyOn(historyService, 'getNextRecommendation'); - hasPrevRecommendationStub = jest.spyOn(historyService, 'hasPrevRecommendation'); - getPrevRecommendationStub = jest.spyOn(historyService, 'getPrevRecommendation'); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should initialize', () => { - fixture.detectChanges(); - expect(comp.courseId).toBe(1); - expect(getLearningPathIdStub).toHaveBeenCalled(); - expect(getLearningPathIdStub).toHaveBeenCalledWith(1); - }); - - it('should generate learning path if not found', () => { - const generateLearningPathStub = jest.spyOn(learningPathService, 'generateLearningPath').mockReturnValue(of(new HttpResponse({ body: learningPathId }))); - getLearningPathIdStub.mockReturnValue(throwError(() => new HttpErrorResponse({ status: 404 }))); - fixture.detectChanges(); - expect(comp.courseId).toBe(1); - expect(getLearningPathIdStub).toHaveBeenCalled(); - expect(getLearningPathIdStub).toHaveBeenCalledWith(1); - expect(generateLearningPathStub).toHaveBeenCalled(); - expect(generateLearningPathStub).toHaveBeenCalledWith(1); - }); - - it('should not generate learning path for other error', () => { - const generateLearningPathStub = jest.spyOn(learningPathService, 'generateLearningPath').mockReturnValue(of(new HttpResponse({ body: learningPathId }))); - getLearningPathIdStub.mockReturnValue(throwError(() => new HttpErrorResponse({ status: 400 }))); - fixture.detectChanges(); - expect(comp.courseId).toBe(1); - expect(getLearningPathIdStub).toHaveBeenCalled(); - expect(getLearningPathIdStub).toHaveBeenCalledWith(1); - expect(generateLearningPathStub).not.toHaveBeenCalled(); - }); - - it('should not load previous task if no task selected', () => { - fixture.detectChanges(); - comp.onPrevTask(); - expect(getPrevRecommendationStub).not.toHaveBeenCalled(); - expect(findWithDetailsStub).not.toHaveBeenCalled(); - expect(getExerciseDetailsStub).not.toHaveBeenCalled(); - }); - - it.each([ - [new LectureUnitEntry(lectureId, lectureUnitId), false], - [new LectureUnitEntry(lectureId, lectureUnitId), true], - [new ExerciseEntry(exerciseId), false], - [new ExerciseEntry(exerciseId), true], - ])('should load entry', (entry: StorageEntry, next: boolean) => { - fixture.detectChanges(); - if (next) { - hasNextRecommendationStub.mockReturnValue(true); - getNextRecommendationStub.mockReturnValue(entry); - } else { - hasPrevRecommendationStub.mockReturnValue(true); - getPrevRecommendationStub.mockReturnValue(entry); - } - fixture.detectChanges(); - if (next) { - comp.onNextTask(); - } else { - comp.onPrevTask(); - } - - if (entry instanceof LectureUnitEntry) { - expect(findWithDetailsStub).toHaveBeenCalledExactlyOnceWith(lecture.id); - expect(getExerciseDetailsStub).not.toHaveBeenCalled(); - } else { - expect(findWithDetailsStub).not.toHaveBeenCalled(); - expect(getExerciseDetailsStub).toHaveBeenCalledExactlyOnceWith(exercise.id); - } - }); - - it('should set properties of lecture unit view on activate', () => { - fixture.detectChanges(); - comp.learningObjectId = lectureUnit.id!; - comp.lectureUnit = lectureUnit; - comp.lectureId = lecture.id; - comp.lecture = lecture; - fixture.detectChanges(); - const instance = { lecture: undefined, lectureUnit: undefined } as unknown as LearningPathLectureUnitViewComponent; - comp.setupLectureUnitView(instance); - expect(instance.lecture).toEqual(lecture); - expect(instance.lectureUnit).toEqual(lectureUnit); - }); - - it('should set properties of exercise view on activate', () => { - fixture.detectChanges(); - comp.exercise = exercise; - comp.learningObjectId = exercise.id!; - fixture.detectChanges(); - const instance = { - learningPathMode: false, - courseId: undefined, - exerciseId: undefined, - } as unknown as CourseExerciseDetailsComponent; - comp.setupExerciseView(instance); - expect(instance.learningPathMode).toBeTruthy(); - expect(instance.courseId).toBe(1); - expect(instance.exerciseId).toEqual(exercise.id); - }); - - it('should handle lecture unit node click', () => { - fixture.detectChanges(); - const node = { - id: 'some-id', - type: NodeType.LECTURE_UNIT, - linkedResource: 2, - linkedResourceParent: 3, - } as NgxLearningPathNode; - comp.onNodeClicked(node); - expect(comp.learningObjectId).toBe(node.linkedResource); - expect(comp.lectureId).toBe(node.linkedResourceParent); - expect(findWithDetailsStub).toHaveBeenCalledWith(node.linkedResourceParent); - }); - - it('should handle exercise node click', () => { - fixture.detectChanges(); - const node = { id: 'some-id', type: NodeType.EXERCISE, linkedResource: 2 } as NgxLearningPathNode; - comp.onNodeClicked(node); - expect(comp.learningObjectId).toBe(node.linkedResource); - expect(getExerciseDetailsStub).toHaveBeenCalledWith(node.linkedResource); - }); -}); diff --git a/src/test/javascript/spec/component/learning-paths/participate/learning-path-lecture-unit-view.component.spec.ts b/src/test/javascript/spec/component/learning-paths/participate/learning-path-lecture-unit-view.component.spec.ts deleted file mode 100644 index ad24cc2dd512..000000000000 --- a/src/test/javascript/spec/component/learning-paths/participate/learning-path-lecture-unit-view.component.spec.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockModule } from 'ng-mocks'; -import { ArtemisTestModule } from '../../../test.module'; -import { RouterModule } from '@angular/router'; -import { Lecture } from 'app/entities/lecture.model'; -import { LearningPathLectureUnitViewComponent, LectureUnitCompletionEvent } from 'app/course/learning-paths/participate/lecture-unit/learning-path-lecture-unit-view.component'; -import { AttachmentUnit } from 'app/entities/lecture-unit/attachmentUnit.model'; -import { ArtemisLectureUnitsModule } from 'app/overview/course-lectures/lecture-units.module'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; -import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; -import { OnlineUnit } from 'app/entities/lecture-unit/onlineUnit.model'; -import { Course, CourseInformationSharingConfiguration } from 'app/entities/course.model'; -import { AttachmentUnitComponent } from 'app/overview/course-lectures/attachment-unit/attachment-unit.component'; -import { VideoUnitComponent } from 'app/overview/course-lectures/video-unit/video-unit.component'; -import { OnlineUnitComponent } from 'app/overview/course-lectures/online-unit/online-unit.component'; -import { TextUnitComponent } from 'app/overview/course-lectures/text-unit/text-unit.component'; -import { MockLocalStorageService } from '../../../helpers/mocks/service/mock-local-storage.service'; -import { LocalStorageService } from 'ngx-webstorage'; -import { SafeResourceUrlPipe } from 'app/shared/pipes/safe-resource-url.pipe'; - -describe('LearningPathLectureUnitViewComponent', () => { - let fixture: ComponentFixture; - let comp: LearningPathLectureUnitViewComponent; - let lecture: Lecture; - let lectureUnitService: LectureUnitService; - let setCompletionStub: jest.SpyInstance; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule, MockModule(RouterModule), MockModule(ArtemisLectureUnitsModule)], - providers: [ - { provide: LocalStorageService, useClass: MockLocalStorageService }, - { provide: SafeResourceUrlPipe, useClass: SafeResourceUrlPipe }, - ], - declarations: [LearningPathLectureUnitViewComponent, AttachmentUnitComponent, VideoUnitComponent, TextUnitComponent, OnlineUnitComponent], - }) - .compileComponents() - .then(() => { - fixture = TestBed.createComponent(LearningPathLectureUnitViewComponent); - comp = fixture.componentInstance; - lecture = new Lecture(); - lecture.id = 1; - lectureUnitService = TestBed.inject(LectureUnitService); - setCompletionStub = jest.spyOn(lectureUnitService, 'setCompletion'); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it.each([ - { lectureUnit: new AttachmentUnit(), selector: 'jhi-attachment-unit' }, - // { lectureUnit: new VideoUnit(), selector: 'jhi-video-unit' }, --> component will be deleted soon anyhow - { lectureUnit: new TextUnit(), selector: 'jhi-text-unit' }, - { lectureUnit: new OnlineUnit(), selector: 'jhi-online-unit' }, - ])('should display lecture unit correctly', ({ lectureUnit, selector }) => { - lectureUnit.id = 3; - lecture.lectureUnits = [lectureUnit]; - comp.lecture = lecture; - comp.lectureUnit = lectureUnit; - fixture.detectChanges(); - const view = fixture.debugElement.nativeElement.querySelector(selector); - expect(view).toBeTruthy(); - }); - - it('should display no discussions when disabled', () => { - const attachment = new AttachmentUnit(); - attachment.id = 3; - lecture.lectureUnits = [attachment]; - comp.lecture = lecture; - comp.lectureUnit = attachment; - lecture.course = new Course(); - lecture.course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.DISABLED; - fixture.detectChanges(); - const outlet = fixture.debugElement.nativeElement.querySelector('router-outlet'); - expect(outlet).toBeFalsy(); - }); - - it('should display discussions when enabled', () => { - const attachment = new AttachmentUnit(); - attachment.id = 3; - lecture.lectureUnits = [attachment]; - comp.lecture = lecture; - comp.lectureUnit = attachment; - lecture.course = new Course(); - lecture.course.courseInformationSharingConfiguration = CourseInformationSharingConfiguration.COMMUNICATION_AND_MESSAGING; - fixture.detectChanges(); - const outlet = fixture.debugElement.nativeElement.querySelector('router-outlet'); - expect(outlet).toBeTruthy(); - }); - - it('should set lecture unit completion', () => { - const attachment = new AttachmentUnit(); - attachment.id = 3; - attachment.visibleToStudents = true; - attachment.completed = false; - lecture.lectureUnits = [attachment]; - comp.lecture = lecture; - comp.lectureUnit = attachment; - lecture.course = new Course(); - fixture.detectChanges(); - const event = { lectureUnit: attachment, completed: true } as LectureUnitCompletionEvent; - comp.completeLectureUnit(event); - expect(setCompletionStub).toHaveBeenCalledOnce(); - expect(setCompletionStub).toHaveBeenCalledWith(attachment.id, lecture.id, event.completed); - }); -}); diff --git a/src/test/javascript/spec/service/learning-path-paging.service.spec.ts b/src/test/javascript/spec/service/learning-path-paging.service.spec.ts deleted file mode 100644 index 81a97d15a266..000000000000 --- a/src/test/javascript/spec/service/learning-path-paging.service.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { MockHttpService } from '../helpers/mocks/service/mock-http.service'; -import { LearningPathPagingService } from 'app/course/learning-paths/learning-path-paging.service'; -import { SearchTermPageableSearch, SortingOrder } from 'app/shared/table/pageable-table'; -import { HttpClient, HttpParams } from '@angular/common/http'; -import { TableColumn } from 'app/course/learning-paths/learning-path-management/learning-path-management.component'; -import { ArtemisTestModule } from '../test.module'; -import { TestBed } from '@angular/core/testing'; - -describe('LearningPathPagingService', () => { - let learningPathPagingService: LearningPathPagingService; - let httpService: HttpClient; - let getStub: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - providers: [{ provide: HttpClient, useClass: MockHttpService }], - }) - .compileComponents() - .then(() => { - httpService = TestBed.inject(HttpClient); - learningPathPagingService = new LearningPathPagingService(httpService); - getStub = jest.spyOn(httpService, 'get'); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should send a request to the server to activate the user', () => { - const pageable = { - page: 1, - pageSize: 10, - searchTerm: 'initialSearchTerm', - sortingOrder: SortingOrder.DESCENDING, - sortedColumn: TableColumn.ID, - } as SearchTermPageableSearch; - learningPathPagingService.search(pageable, { courseId: 1 }).subscribe(); - const params = new HttpParams() - .set('pageSize', String(pageable.pageSize)) - .set('page', String(pageable.page)) - .set('sortingOrder', pageable.sortingOrder) - .set('searchTerm', pageable.searchTerm) - .set('sortedColumn', pageable.sortedColumn); - expect(getStub).toHaveBeenCalledOnce(); - expect(getStub).toHaveBeenCalledWith('api/courses/1/learning-paths', { params, observe: 'response' }); - }); -}); diff --git a/src/test/javascript/spec/service/learning-path-storage.service.spec.ts b/src/test/javascript/spec/service/learning-path-storage.service.spec.ts deleted file mode 100644 index daa3f9c4ec03..000000000000 --- a/src/test/javascript/spec/service/learning-path-storage.service.spec.ts +++ /dev/null @@ -1,189 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { ArtemisTestModule } from '../test.module'; -import { ExerciseEntry, LearningPathStorageService, LectureUnitEntry } from 'app/course/learning-paths/participate/learning-path-storage.service'; -import { NgxLearningPathDTO, NgxLearningPathEdge, NgxLearningPathNode, NodeType } from 'app/entities/competency/learning-path.model'; - -describe('LearningPathStorageService', () => { - let storageService: LearningPathStorageService; - let learningPathId1: number; - let learningPathId2: number; - let ngxPathEmpty: NgxLearningPathDTO; - let ngxPath1: NgxLearningPathDTO; - let ngxPath2: NgxLearningPathDTO; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - }) - .compileComponents() - .then(() => { - storageService = new LearningPathStorageService(); - learningPathId1 = 1; - learningPathId2 = 2; - - ngxPath1 = { - nodes: [ - { id: '1', type: NodeType.COMPETENCY_START } as NgxLearningPathNode, - { id: '2', type: NodeType.LECTURE_UNIT, linkedResource: 5, linkedResourceParent: 6 } as NgxLearningPathNode, - { id: '3', type: NodeType.EXERCISE, linkedResource: 7 } as NgxLearningPathNode, - { id: '4', type: NodeType.COMPETENCY_END } as NgxLearningPathNode, - ], - edges: [ - { id: '8', source: '1', target: '2' } as NgxLearningPathEdge, - { id: '9', source: '2', target: '3' } as NgxLearningPathEdge, - { id: '10', source: '3', target: '4' } as NgxLearningPathEdge, - ], - } as NgxLearningPathDTO; - - ngxPath2 = { - nodes: [ - { id: '11', type: NodeType.COMPETENCY_START } as NgxLearningPathNode, - { id: '12', type: NodeType.LECTURE_UNIT, linkedResource: 15, linkedResourceParent: 16 } as NgxLearningPathNode, - { id: '13', type: NodeType.EXERCISE, linkedResource: 17 } as NgxLearningPathNode, - { id: '14', type: NodeType.COMPETENCY_END } as NgxLearningPathNode, - ], - edges: [ - { id: '18', source: '11', target: '12' } as NgxLearningPathEdge, - { id: '19', source: '12', target: '13' } as NgxLearningPathEdge, - { id: '20', source: '13', target: '14' } as NgxLearningPathEdge, - ], - } as NgxLearningPathDTO; - - ngxPathEmpty = { nodes: [], edges: [] } as NgxLearningPathDTO; - }); - }); - - it('should have no recommendation for empty learning path', () => { - storageService.storeRecommendations(learningPathId1, ngxPathEmpty); - expect(storageService.hasPrevRecommendation(learningPathId1)).toBeFalsy(); - expect(storageService.getPrevRecommendation(learningPathId1)).toBeUndefined(); - - const arbitraryEntry = new ExerciseEntry(1337); - expect(storageService.hasPrevRecommendation(learningPathId1, arbitraryEntry)).toBeFalsy(); - expect(storageService.getPrevRecommendation(learningPathId1, arbitraryEntry)).toBeUndefined(); - - expect(storageService.hasNextRecommendation(learningPathId1)).toBeFalsy(); - expect(storageService.getNextRecommendation(learningPathId1)).toBeUndefined(); - - expect(storageService.hasNextRecommendation(learningPathId1, arbitraryEntry)).toBeFalsy(); - expect(storageService.getNextRecommendation(learningPathId1, arbitraryEntry)).toBeUndefined(); - }); - - it('should retrieve first entry as next recommendation for undefined entry', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - expect(storageService.hasNextRecommendation(learningPathId1)).toBeTruthy(); - const recommendation = storageService.getNextRecommendation(learningPathId1); - expect(recommendation).toBeInstanceOf(LectureUnitEntry); - const lectureUnitEntry = recommendation as LectureUnitEntry; - expect(lectureUnitEntry.lectureUnitId).toBe(5); - expect(lectureUnitEntry.lectureId).toBe(6); - }); - - it('should retrieve successor entry as next recommendation', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - const lectureUnitEntry = new LectureUnitEntry(6, 5); - expect(storageService.hasNextRecommendation(learningPathId1, lectureUnitEntry)).toBeTruthy(); - const recommendation = storageService.getNextRecommendation(learningPathId1, lectureUnitEntry); - expect(recommendation).toBeInstanceOf(ExerciseEntry); - const exerciseEntry = recommendation as ExerciseEntry; - expect(exerciseEntry.exerciseId).toBe(7); - }); - - it('should retrieve no entry as next recommendation for last entry', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - const exerciseEntry = new ExerciseEntry(7); - expect(storageService.hasNextRecommendation(learningPathId1, exerciseEntry)).toBeFalsy(); - expect(storageService.getNextRecommendation(learningPathId1, exerciseEntry)).toBeUndefined(); - }); - - it('should not retrieve entry as previous recommendation for undefined entry', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - expect(storageService.hasPrevRecommendation(learningPathId1)).toBeFalsy(); - expect(storageService.getPrevRecommendation(learningPathId1)).toBeUndefined(); - }); - - it('should not retrieve entry as previous recommendation for first entry', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - const lectureUnitEntry = new LectureUnitEntry(6, 5); - expect(storageService.hasPrevRecommendation(learningPathId1, lectureUnitEntry)).toBeFalsy(); - expect(storageService.getPrevRecommendation(learningPathId1, lectureUnitEntry)).toBeUndefined(); - }); - - it('should retrieve predecessor entry as next recommendation', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - const exerciseEntry = new ExerciseEntry(7); - expect(storageService.hasPrevRecommendation(learningPathId1, exerciseEntry)).toBeTruthy(); - const recommendation = storageService.getPrevRecommendation(learningPathId1, exerciseEntry); - expect(recommendation).toBeInstanceOf(LectureUnitEntry); - const lectureUnitEntry = recommendation as LectureUnitEntry; - expect(lectureUnitEntry.lectureUnitId).toBe(5); - expect(lectureUnitEntry.lectureId).toBe(6); - }); - - it('should handle multiple learning paths - next, entry undefined', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - storageService.storeRecommendations(learningPathId2, ngxPath2); - - expect(storageService.hasNextRecommendation(learningPathId1)).toBeTruthy(); - const recommendation1 = storageService.getNextRecommendation(learningPathId1); - expect(recommendation1).toBeInstanceOf(LectureUnitEntry); - const recommendedLectureUnitEntry1 = recommendation1 as LectureUnitEntry; - expect(recommendedLectureUnitEntry1.lectureUnitId).toBe(5); - expect(recommendedLectureUnitEntry1.lectureId).toBe(6); - expect(storageService.hasNextRecommendation(learningPathId2)).toBeTruthy(); - const recommendation2 = storageService.getNextRecommendation(learningPathId2); - expect(recommendation2).toBeInstanceOf(LectureUnitEntry); - const recommendedLectureUnitEntry2 = recommendation2 as LectureUnitEntry; - expect(recommendedLectureUnitEntry2.lectureUnitId).toBe(15); - expect(recommendedLectureUnitEntry2.lectureId).toBe(16); - }); - - it('should handle multiple learning paths - next, entry defined', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - storageService.storeRecommendations(learningPathId2, ngxPath2); - - const lectureUnitEntry1 = new LectureUnitEntry(6, 5); - const lectureUnitEntry2 = new LectureUnitEntry(16, 15); - expect(storageService.hasNextRecommendation(learningPathId1, lectureUnitEntry1)).toBeTruthy(); - const recommendation1 = storageService.getNextRecommendation(learningPathId1, lectureUnitEntry1); - expect(recommendation1).toBeInstanceOf(ExerciseEntry); - const exerciseEntry1 = recommendation1 as ExerciseEntry; - expect(exerciseEntry1.exerciseId).toBe(7); - expect(storageService.hasNextRecommendation(learningPathId1, lectureUnitEntry2)).toBeFalsy(); - expect(storageService.hasNextRecommendation(learningPathId2, lectureUnitEntry1)).toBeFalsy(); - expect(storageService.hasNextRecommendation(learningPathId2, lectureUnitEntry2)).toBeTruthy(); - const recommendation2 = storageService.getNextRecommendation(learningPathId2, lectureUnitEntry2); - expect(recommendation2).toBeInstanceOf(ExerciseEntry); - const exerciseEntry2 = recommendation2 as ExerciseEntry; - expect(exerciseEntry2.exerciseId).toBe(17); - }); - - it('should handle multiple learning paths - prev, entry undefined', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - storageService.storeRecommendations(learningPathId2, ngxPath2); - - expect(storageService.hasPrevRecommendation(learningPathId1)).toBeFalsy(); - expect(storageService.hasPrevRecommendation(learningPathId2)).toBeFalsy(); - }); - - it('should handle multiple learning paths - prev, entry defined', () => { - storageService.storeRecommendations(learningPathId1, ngxPath1); - storageService.storeRecommendations(learningPathId2, ngxPath2); - - const exerciseEntry1 = new ExerciseEntry(7); - const exerciseEntry2 = new ExerciseEntry(17); - expect(storageService.hasPrevRecommendation(learningPathId1, exerciseEntry1)).toBeTruthy(); - const recommendation1 = storageService.getPrevRecommendation(learningPathId1, exerciseEntry1); - expect(recommendation1).toBeInstanceOf(LectureUnitEntry); - const lectureUnitEntry1 = recommendation1 as LectureUnitEntry; - expect(lectureUnitEntry1.lectureUnitId).toBe(5); - expect(lectureUnitEntry1.lectureId).toBe(6); - expect(storageService.hasPrevRecommendation(learningPathId1, exerciseEntry2)).toBeFalsy(); - expect(storageService.hasPrevRecommendation(learningPathId2, exerciseEntry1)).toBeFalsy(); - expect(storageService.hasPrevRecommendation(learningPathId2, exerciseEntry2)).toBeTruthy(); - const recommendation2 = storageService.getPrevRecommendation(learningPathId2, exerciseEntry2); - expect(recommendation2).toBeInstanceOf(LectureUnitEntry); - const lectureUnitEntry2 = recommendation2 as LectureUnitEntry; - expect(lectureUnitEntry2.lectureUnitId).toBe(15); - expect(lectureUnitEntry2.lectureId).toBe(16); - }); -}); diff --git a/src/test/javascript/spec/service/learning-path/learning-path.service.spec.ts b/src/test/javascript/spec/service/learning-path/learning-path.service.spec.ts deleted file mode 100644 index 87232f66fdc9..000000000000 --- a/src/test/javascript/spec/service/learning-path/learning-path.service.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { MockHttpService } from '../../helpers/mocks/service/mock-http.service'; -import { LearningPathService } from 'app/course/learning-paths/learning-path.service'; -import { ArtemisTestModule } from '../../test.module'; -import { HttpClient } from '@angular/common/http'; -import { TestBed } from '@angular/core/testing'; -import { LearningPathStorageService } from 'app/course/learning-paths/participate/learning-path-storage.service'; - -describe('LearningPathService', () => { - let learningPathService: LearningPathService; - let storageService: LearningPathStorageService; - let httpService: HttpClient; - let putStub: jest.SpyInstance; - let postStub: jest.SpyInstance; - let getStub: jest.SpyInstance; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ArtemisTestModule], - providers: [{ provide: HttpClient, useClass: MockHttpService }], - }) - .compileComponents() - .then(() => { - httpService = TestBed.inject(HttpClient); - storageService = TestBed.inject(LearningPathStorageService); - learningPathService = new LearningPathService(httpService, storageService); - putStub = jest.spyOn(httpService, 'put'); - postStub = jest.spyOn(httpService, 'post'); - getStub = jest.spyOn(httpService, 'get'); - }); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it('should send a request to the server to enable learning paths for course', () => { - learningPathService.enableLearningPaths(1).subscribe(); - expect(putStub).toHaveBeenCalledExactlyOnceWith('api/courses/1/learning-paths/enable', null, { observe: 'response' }); - }); - - it('should send request to the server to generate missing learning paths for course', () => { - learningPathService.generateMissingLearningPathsForCourse(1).subscribe(); - expect(putStub).toHaveBeenCalledExactlyOnceWith('api/courses/1/learning-paths/generate-missing', null, { observe: 'response' }); - }); - - it('should send a request to the server to get health status of learning paths for course', () => { - learningPathService.getHealthStatusForCourse(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/courses/1/learning-path-health', { observe: 'response' }); - }); - - it('should send a request to the server to get learning path information', () => { - learningPathService.getLearningPath(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/learning-path/1', { observe: 'response' }); - }); - - it('should send a request to the server to get ngx graph representation of learning path', () => { - learningPathService.getLearningPathNgxGraph(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/learning-path/1/graph', { observe: 'response' }); - }); - - it('should send a request to the server to get ngx path representation of learning path', () => { - learningPathService.getLearningPathNgxPath(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/learning-path/1/path', { observe: 'response' }); - }); - - it('should send a request to the server to get learning path id of the current user in the course', () => { - learningPathService.getLearningPathId(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/courses/1/learning-path-id', { observe: 'response' }); - }); - - it('should send a request to the server to generate learning path for course', () => { - learningPathService.generateLearningPath(1).subscribe(); - expect(postStub).toHaveBeenCalledExactlyOnceWith('api/courses/1/learning-path', null, { observe: 'response' }); - }); - - it('should send a request to the server to get competency progress for learning path', () => { - learningPathService.getCompetencyProgressForLearningPath(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/learning-path/1/competency-progress', { observe: 'response' }); - }); - - it('should send a request to the server to get learning path navigation overview', () => { - learningPathService.getLearningPathNavigationOverview(1).subscribe(); - expect(getStub).toHaveBeenCalledExactlyOnceWith('api/learning-path/1/navigation-overview', { observe: 'response' }); - }); -}); From 4053946220c502a57bcaf1288572c4c77400e8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Tue, 29 Oct 2024 19:33:22 +0100 Subject: [PATCH 3/7] Lower test coverage thresholds --- jest.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jest.config.js b/jest.config.js index 79e40bdb3162..587aaf737988 100644 --- a/jest.config.js +++ b/jest.config.js @@ -102,10 +102,10 @@ module.exports = { coverageThreshold: { global: { // TODO: in the future, the following values should increase to at least 90% - statements: 87.52, - branches: 73.62, - functions: 82.12, - lines: 87.57, + statements: 87.46, + branches: 73.56, + functions: 87.52, + lines: 82.04, }, }, coverageReporters: ['clover', 'json', 'lcov', 'text-summary'], From 571b2d4fb54fced5f1b66c8ac56b902ec100d5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Tue, 29 Oct 2024 20:49:15 +0100 Subject: [PATCH 4/7] Fix coverage threshold order --- jest.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index 587aaf737988..6b821e64e158 100644 --- a/jest.config.js +++ b/jest.config.js @@ -104,8 +104,8 @@ module.exports = { // TODO: in the future, the following values should increase to at least 90% statements: 87.46, branches: 73.56, - functions: 87.52, - lines: 82.04, + functions: 82.04, + lines: 87.52, }, }, coverageReporters: ['clover', 'json', 'lcov', 'text-summary'], From 85f7705b9b81ff828359c1ca8fe9a87379b5c17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Wed, 30 Oct 2024 12:04:14 +0100 Subject: [PATCH 5/7] Decrease server coverage as well --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 5f9283d1f9ea..0e441e52543f 100644 --- a/build.gradle +++ b/build.gradle @@ -177,7 +177,7 @@ jacocoTestCoverageVerification { counter = "INSTRUCTION" value = "COVEREDRATIO" // TODO: in the future the following value should become higher than 0.92 - minimum = 0.894 + minimum = 0.892 } limit { counter = "CLASS" From d38aab5b71005eea6343f9a11591cbd9e885aa04 Mon Sep 17 00:00:00 2001 From: Johannes Wiest Date: Wed, 30 Oct 2024 13:44:33 +0100 Subject: [PATCH 6/7] remove comment --- src/main/webapp/app/overview/courses-routing.module.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/webapp/app/overview/courses-routing.module.ts b/src/main/webapp/app/overview/courses-routing.module.ts index 1307396134e0..3e835cc3d3d8 100644 --- a/src/main/webapp/app/overview/courses-routing.module.ts +++ b/src/main/webapp/app/overview/courses-routing.module.ts @@ -191,7 +191,6 @@ const routes: Routes = [ path: 'learning-path', loadComponent: () => import('app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component').then((c) => c.LearningPathStudentPageComponent), - // loadChildren: () => import('app/course/learning-paths/learning-paths.module').then((m) => m.ArtemisLearningPathsModule), data: { authorities: [Authority.USER], pageTitle: 'overview.learningPath', From 25713ac825b35cbd9b03fb08705ed48cb7b76eb0 Mon Sep 17 00:00:00 2001 From: Johannes Wiest Date: Wed, 30 Oct 2024 13:55:14 +0100 Subject: [PATCH 7/7] fix router behaviour for student page --- .../learning-path-student-page.component.ts | 2 +- .../pages/learning-path-student-page.component.spec.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts index 7857288daba1..7d83742015f0 100644 --- a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts +++ b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts @@ -29,7 +29,7 @@ export class LearningPathStudentPageComponent { readonly isLearningPathLoading = signal(false); readonly learningPath = signal(undefined); - readonly courseId = toSignal(this.activatedRoute.parent!.parent!.params.pipe(map((params) => Number(params.courseId))), { requireSync: true }); + readonly courseId = toSignal(this.activatedRoute.parent!.params.pipe(map((params) => Number(params.courseId))), { requireSync: true }); readonly currentLearningObject = this.learningPathNavigationService.currentLearningObject; readonly isLearningPathNavigationLoading = this.learningPathNavigationService.isLoading; diff --git a/src/test/javascript/spec/component/learning-paths/pages/learning-path-student-page.component.spec.ts b/src/test/javascript/spec/component/learning-paths/pages/learning-path-student-page.component.spec.ts index 2bebd4f5df76..3c4d22ecbf22 100644 --- a/src/test/javascript/spec/component/learning-paths/pages/learning-path-student-page.component.spec.ts +++ b/src/test/javascript/spec/component/learning-paths/pages/learning-path-student-page.component.spec.ts @@ -14,6 +14,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { MockAlertService } from '../../../helpers/mocks/service/mock-alert.service'; import { EntityNotFoundError } from 'app/course/learning-paths/exceptions/entity-not-found.error'; import { LearningPathDTO } from 'app/entities/competency/learning-path.model'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; describe('LearningPathStudentPageComponent', () => { let component: LearningPathStudentPageComponent; @@ -33,15 +34,14 @@ describe('LearningPathStudentPageComponent', () => { imports: [LearningPathStudentPageComponent], providers: [ provideHttpClient(), + provideHttpClientTesting(), { provide: ActivatedRoute, useValue: { parent: { - parent: { - params: of({ - courseId: courseId, - }), - }, + params: of({ + courseId: courseId, + }), }, }, },