From 2acb4f82cf729f7dfc3662635967979db826cf4b Mon Sep 17 00:00:00 2001 From: Sangaran Ramesch Date: Mon, 5 Jan 2026 17:10:32 +0100 Subject: [PATCH 1/5] add queries for averageSkillValue and skillValue --- .../controller/SkillLevelController.java | 10 +++ .../entity/SkillAverageValueEntity.java | 25 ++++++ .../SkillAverageValueRepository.java | 9 +++ .../service/SkillLevelService.java | 79 +++++++++++++++++++ .../resources/graphql/service/schema.graphqls | 12 +++ 5 files changed, 135 insertions(+) create mode 100644 src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java create mode 100644 src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java index e8d92c8..7e6205e 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java @@ -3,6 +3,7 @@ import de.unistuttgart.iste.meitrex.common.user_handling.GlobalPermissionAccessValidator; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.generated.dto.SkillLevels; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.service.SkillLevelService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -44,5 +45,14 @@ public List skillLevelForUserBySkillIds(@Argument final List return skilllevelService.getSkillLevelsForSkillIds(skillIds, userId); } + @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "averageSkillValuesBySkillIds") + public List averageSkillValuesBySkillIds(@Argument final List skillIds) { + return skilllevelService.getAverageSkillValuesForSkillIds(skillIds); + } + + @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "skillValueBySkillId") + public Float skillValueBySkillId(@Argument final UUID skillId, @ContextValue final LoggedInUser currentUser) { + return skilllevelService.getSkillValueForSkillId(skillId, currentUser.getId()); + } } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java new file mode 100644 index 0000000..8e60c04 --- /dev/null +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java @@ -0,0 +1,25 @@ +package de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.UUID; +/** * Database entity which represents for every skill the average skillValue in the course. */ + +@Entity(name = "SkillAverageValue") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SkillAverageValueEntity { + + @Id private UUID skillId; + + /** * Current average value of the skill level. Can range from 0 to 1. */ + @Column(nullable = false) private float averageValue; + + /** * Current participant count of the skill level. */ + @Column(nullable = false) private int participantCount; + +} \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java new file mode 100644 index 0000000..20bbb32 --- /dev/null +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java @@ -0,0 +1,9 @@ +package de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +@Repository +public interface SkillAverageValueRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java index 2118542..6341101 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java @@ -8,6 +8,7 @@ import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAbilityEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillLevelEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillsForCourse; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.mapper.SkillLevelMapper; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.AllSkillLevelsRepository; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.ItemDifficultyRepository; @@ -77,7 +78,85 @@ public List getSkillLevelsForCourse(UUID courseId, UUID userId) { */ public List getSkillLevelsForSkillIds(List skillIds, UUID userId) { return getSkillLevelEntitiesForSkillIds(skillIds, userId).stream().map(mapper::entityToDto).toList(); + } + + /** + * Returns the skill average values for given skillIds. + * + * @param skillIds List of skillIds + * @return List of SkillAverageValueEntity that represents the average values of the given skills + */ + public List getAverageSkillValuesForSkillIds(List skillIds) { + List skillAverageValues = new ArrayList<>(); + for(UUID skillId : skillIds) { + skillAverageValues.add(getAverageSkillValueForSkillId(skillId)); + } + return skillAverageValues; + } + + /** + * Returns the skill average value for a given skillId. + * + * @param skillId skillId of skillLevels + * @return SkillAverageValueEntity that represents the average value of a specific skill + */ + private SkillAverageValueEntity getAverageSkillValueForSkillId(UUID skillId) { + List skillLevels = skillLevelsRepository.findByIdSkillId(skillId); + List skillValues = new ArrayList<>(); + for(AllSkillLevelsEntity skillLevel : skillLevels) { + skillValues.add(getSkillValueForSkillLevel(skillLevel)); + } + float sum = 0f; + for(Float skillValue : skillValues) { + sum += skillValue; + } + SkillAverageValueEntity skillAverageValue = new SkillAverageValueEntity(); + skillAverageValue.setSkillId(skillId); + skillAverageValue.setParticipantCount(skillValues.size()); + if (skillValues.isEmpty()) { + skillAverageValue.setAverageValue(0f); + } + else { + skillAverageValue.setAverageValue(sum / skillValues.size()); + } + return skillAverageValue; + } + /** + * Returns the skill value for a given skillLevel by combining all 6 skillLevelEntities. + * + * @param skillLevel skillLevel of a skill + * @return A float value that represents the skill value of a skill + */ + private Float getSkillValueForSkillLevel(AllSkillLevelsEntity skillLevel) { + List values = List.of( + skillLevel.getRemember().getValue(), + skillLevel.getUnderstand().getValue(), + skillLevel.getApply().getValue(), + skillLevel.getAnalyze().getValue(), + skillLevel.getEvaluate().getValue(), + skillLevel.getCreate().getValue() + ); + List nonZeroValues = values.stream().filter(v -> v > 0f).toList(); + if (nonZeroValues.isEmpty()) { + return 0f; + } + else{ + float sum = nonZeroValues.stream().reduce(0f, Float::sum); + return sum / nonZeroValues.size(); + } + } + + /** + * return the skill value of the given user and the given skill + * + * @param skillId the ids of the skill + * @param userId the id of the user + * @return the skill value for the given user and skill + */ + public Float getSkillValueForSkillId(UUID skillId, UUID userId) { + List skillLevels = getSkillLevelEntitiesForSkillIds(List.of(skillId), userId); + return getSkillValueForSkillLevel(skillLevels.get(0)); } /** diff --git a/src/main/resources/graphql/service/schema.graphqls b/src/main/resources/graphql/service/schema.graphqls index dfe1e25..1423530 100644 --- a/src/main/resources/graphql/service/schema.graphqls +++ b/src/main/resources/graphql/service/schema.graphqls @@ -36,6 +36,9 @@ type Query { """ _internal_noauth_skillLevelForUserBySkillIds(skillIds: [UUID!]!, userId: UUID!): [SkillLevels!]! + _internal_noauth_averageSkillValuesBySkillIds(skillIds: [UUID!]!): [SkillAverageValue!]! + + _internal_noauth_skillValueBySkillId(skillId: UUID!): Float! } type Mutation { @@ -129,4 +132,13 @@ type SkillLevelLogItem { the probability of a correct response, that M-Elo predicts """ predictedCorrectness:Float! +} + +type SkillAverageValue { + + skillId: UUID! + + averageValue: Float! + + participantCount: Int! } \ No newline at end of file From 5a22351a346cb34bf4ddf6815a85959fe634ce27 Mon Sep 17 00:00:00 2001 From: Sangaran Ramesch Date: Tue, 6 Jan 2026 17:43:05 +0100 Subject: [PATCH 2/5] change averageValueType to StatType and adjust identifier to it --- .../controller/SkillLevelController.java | 9 ++--- ...ity.java => SkillAllUsersStatsEntity.java} | 8 ++-- ...java => SkillAllUsersStatsRepository.java} | 4 +- .../service/SkillLevelService.java | 38 ++++++++++--------- .../resources/graphql/service/schema.graphqls | 6 +-- 5 files changed, 33 insertions(+), 32 deletions(-) rename src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/{SkillAverageValueEntity.java => SkillAllUsersStatsEntity.java} (70%) rename src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/{SkillAverageValueRepository.java => SkillAllUsersStatsRepository.java} (68%) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java index 7e6205e..c2a174b 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java @@ -3,7 +3,7 @@ import de.unistuttgart.iste.meitrex.common.user_handling.GlobalPermissionAccessValidator; import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.generated.dto.SkillLevels; -import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAllUsersStatsEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.service.SkillLevelService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,7 +14,6 @@ import org.springframework.stereotype.Controller; import java.util.List; -import java.util.Set; import java.util.UUID; @Slf4j @@ -45,9 +44,9 @@ public List skillLevelForUserBySkillIds(@Argument final List return skilllevelService.getSkillLevelsForSkillIds(skillIds, userId); } - @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "averageSkillValuesBySkillIds") - public List averageSkillValuesBySkillIds(@Argument final List skillIds) { - return skilllevelService.getAverageSkillValuesForSkillIds(skillIds); + @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "skillsAllUsersStatsBySkillIds") + public List skillsAllUsersStatsBySkillIds(@Argument final List skillIds) { + return skilllevelService.getSkillsAllUsersStatsForSkillIds(skillIds); } @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "skillValueBySkillId") diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java similarity index 70% rename from src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java rename to src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java index 8e60c04..593925b 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAverageValueEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java @@ -7,17 +7,17 @@ import java.util.UUID; /** * Database entity which represents for every skill the average skillValue in the course. */ -@Entity(name = "SkillAverageValue") +@Entity(name = "SkillAllUsersStats") @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class SkillAverageValueEntity { +public class SkillAllUsersStatsEntity { @Id private UUID skillId; - /** * Current average value of the skill level. Can range from 0 to 1. */ - @Column(nullable = false) private float averageValue; + /** * Current sum of the skill values from all users in a course with this skill. */ + @Column(nullable = false) private float skillValueSum; /** * Current participant count of the skill level. */ @Column(nullable = false) private int participantCount; diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAllUsersStatsRepository.java similarity index 68% rename from src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java rename to src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAllUsersStatsRepository.java index 20bbb32..e25931b 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAverageValueRepository.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillAllUsersStatsRepository.java @@ -1,9 +1,9 @@ package de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository; -import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAllUsersStatsEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.UUID; @Repository -public interface SkillAverageValueRepository extends JpaRepository { +public interface SkillAllUsersStatsRepository extends JpaRepository { } \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java index 6341101..e6a3128 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java @@ -6,9 +6,9 @@ import de.unistuttgart.iste.meitrex.common.event.ItemResponse; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.AllSkillLevelsEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAbilityEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAllUsersStatsEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillLevelEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillsForCourse; -import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAverageValueEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.mapper.SkillLevelMapper; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.AllSkillLevelsRepository; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.ItemDifficultyRepository; @@ -81,45 +81,47 @@ public List getSkillLevelsForSkillIds(List skillIds, UUID use } /** - * Returns the skill average values for given skillIds. + * Returns the stats for the skills for all users from the course of the skills. * * @param skillIds List of skillIds - * @return List of SkillAverageValueEntity that represents the average values of the given skills + * @return List of SkillAllUsersStatsEntity that represents stats + * like skillValueSum and participantCount of the given skills */ - public List getAverageSkillValuesForSkillIds(List skillIds) { - List skillAverageValues = new ArrayList<>(); + public List getSkillsAllUsersStatsForSkillIds(List skillIds) { + List skillAllUsersStats = new ArrayList<>(); for(UUID skillId : skillIds) { - skillAverageValues.add(getAverageSkillValueForSkillId(skillId)); + skillAllUsersStats.add(getSkillAllUsersStatsForSkillId(skillId)); } - return skillAverageValues; + return skillAllUsersStats; } /** - * Returns the skill average value for a given skillId. + * Returns the skill's stats for a given skillId. * * @param skillId skillId of skillLevels - * @return SkillAverageValueEntity that represents the average value of a specific skill + * @return SkillValueAllUsersStatsEntity that represents stats + * like skillValue sum and participantCount of the given skills */ - private SkillAverageValueEntity getAverageSkillValueForSkillId(UUID skillId) { + private SkillAllUsersStatsEntity getSkillAllUsersStatsForSkillId(UUID skillId) { List skillLevels = skillLevelsRepository.findByIdSkillId(skillId); List skillValues = new ArrayList<>(); for(AllSkillLevelsEntity skillLevel : skillLevels) { skillValues.add(getSkillValueForSkillLevel(skillLevel)); } - float sum = 0f; + float skillValueSum = 0f; for(Float skillValue : skillValues) { - sum += skillValue; + skillValueSum += skillValue; } - SkillAverageValueEntity skillAverageValue = new SkillAverageValueEntity(); - skillAverageValue.setSkillId(skillId); - skillAverageValue.setParticipantCount(skillValues.size()); + SkillAllUsersStatsEntity skillAllUsersStats = new SkillAllUsersStatsEntity(); + skillAllUsersStats.setSkillId(skillId); + skillAllUsersStats.setParticipantCount(skillValues.size()); if (skillValues.isEmpty()) { - skillAverageValue.setAverageValue(0f); + skillAllUsersStats.setSkillValueSum(0f); } else { - skillAverageValue.setAverageValue(sum / skillValues.size()); + skillAllUsersStats.setSkillValueSum(skillValueSum); } - return skillAverageValue; + return skillAllUsersStats; } /** diff --git a/src/main/resources/graphql/service/schema.graphqls b/src/main/resources/graphql/service/schema.graphqls index 1423530..9fd1e6a 100644 --- a/src/main/resources/graphql/service/schema.graphqls +++ b/src/main/resources/graphql/service/schema.graphqls @@ -36,7 +36,7 @@ type Query { """ _internal_noauth_skillLevelForUserBySkillIds(skillIds: [UUID!]!, userId: UUID!): [SkillLevels!]! - _internal_noauth_averageSkillValuesBySkillIds(skillIds: [UUID!]!): [SkillAverageValue!]! + _internal_noauth_skillsAllUsersStatsBySkillIds(skillIds: [UUID!]!): [SkillAllUsersStats!]! _internal_noauth_skillValueBySkillId(skillId: UUID!): Float! } @@ -134,11 +134,11 @@ type SkillLevelLogItem { predictedCorrectness:Float! } -type SkillAverageValue { +type SkillAllUsersStats { skillId: UUID! - averageValue: Float! + skillValueSum: Float! participantCount: Int! } \ No newline at end of file From c68bb01b566dc8982ceebfc807260d757ea50f82 Mon Sep 17 00:00:00 2001 From: Sangaran Ramesch Date: Tue, 6 Jan 2026 23:34:21 +0100 Subject: [PATCH 3/5] adjust skillValue through skillValueEntity --- .../controller/SkillLevelController.java | 7 +++--- .../entity/SkillAllUsersStatsEntity.java | 2 +- .../persistence/entity/SkillValueEntity.java | 22 +++++++++++++++++++ .../repository/SkillValueRepository.java | 9 ++++++++ .../service/SkillLevelService.java | 22 ++++++++++++++----- .../resources/graphql/service/schema.graphqls | 8 ++++++- 6 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillValueEntity.java create mode 100644 src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillValueRepository.java diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java index c2a174b..1f2fa10 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/controller/SkillLevelController.java @@ -4,6 +4,7 @@ import de.unistuttgart.iste.meitrex.common.user_handling.LoggedInUser; import de.unistuttgart.iste.meitrex.generated.dto.SkillLevels; import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAllUsersStatsEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillValueEntity; import de.unistuttgart.iste.meitrex.skilllevel_service.service.SkillLevelService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -49,9 +50,9 @@ public List skillsAllUsersStatsBySkillIds(@Argument fi return skilllevelService.getSkillsAllUsersStatsForSkillIds(skillIds); } - @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "skillValueBySkillId") - public Float skillValueBySkillId(@Argument final UUID skillId, @ContextValue final LoggedInUser currentUser) { - return skilllevelService.getSkillValueForSkillId(skillId, currentUser.getId()); + @QueryMapping(name = INTERNAL_NOAUTH_PREFIX + "skillValuesBySkillIds") + public List skillValuesBySkillIds(@Argument final List skillIds, @ContextValue final LoggedInUser currentUser) { + return skilllevelService.getSkillValuesForSkillIds(skillIds, currentUser.getId()); } } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java index 593925b..cf11524 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillAllUsersStatsEntity.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.util.UUID; -/** * Database entity which represents for every skill the average skillValue in the course. */ +/** * Database entity which represents for every skill the skillStats for all users in the course. */ @Entity(name = "SkillAllUsersStats") @Data diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillValueEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillValueEntity.java new file mode 100644 index 0000000..6770f63 --- /dev/null +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/entity/SkillValueEntity.java @@ -0,0 +1,22 @@ +package de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import java.util.UUID; +/** * Database entity which represents for every skill the skillValue in the course. */ + +@Entity(name = "SkillValue") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SkillValueEntity { + + @Id private UUID skillId; + + /** * Current skill value. Can range from 0 to 1. */ + @Column(nullable = false) private float skillValue; + +} \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillValueRepository.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillValueRepository.java new file mode 100644 index 0000000..269746c --- /dev/null +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/persistence/repository/SkillValueRepository.java @@ -0,0 +1,9 @@ +package de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillValueEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +@Repository +public interface SkillValueRepository extends JpaRepository { +} diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java index e6a3128..3afca98 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java @@ -18,10 +18,13 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; + import org.springframework.stereotype.Service; import java.util.*; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillValueEntity; + @Service @Slf4j @RequiredArgsConstructor @@ -150,15 +153,22 @@ private Float getSkillValueForSkillLevel(AllSkillLevelsEntity skillLevel) { } /** - * return the skill value of the given user and the given skill + * return the skill values for the given user and skills * - * @param skillId the ids of the skill + * @param skillIds the ids of the skills * @param userId the id of the user - * @return the skill value for the given user and skill + * @return the skill values for the given user and skills */ - public Float getSkillValueForSkillId(UUID skillId, UUID userId) { - List skillLevels = getSkillLevelEntitiesForSkillIds(List.of(skillId), userId); - return getSkillValueForSkillLevel(skillLevels.get(0)); + public List getSkillValuesForSkillIds(List skillIds, UUID userId) { + List skillLevels = getSkillLevelEntitiesForSkillIds(skillIds, userId); + List skillValues = new ArrayList<>(); + for(AllSkillLevelsEntity skillLevel : skillLevels) { + SkillValueEntity entity = new SkillValueEntity(); + entity.setSkillId(skillLevel.getId().getSkillId()); + entity.setSkillValue(getSkillValueForSkillLevel(skillLevel)); + skillValues.add(entity); + } + return skillValues; } /** diff --git a/src/main/resources/graphql/service/schema.graphqls b/src/main/resources/graphql/service/schema.graphqls index 9fd1e6a..36f0bcf 100644 --- a/src/main/resources/graphql/service/schema.graphqls +++ b/src/main/resources/graphql/service/schema.graphqls @@ -38,7 +38,7 @@ type Query { _internal_noauth_skillsAllUsersStatsBySkillIds(skillIds: [UUID!]!): [SkillAllUsersStats!]! - _internal_noauth_skillValueBySkillId(skillId: UUID!): Float! + _internal_noauth_skillValuesBySkillIds(skillIds: [UUID!]!): [SkillValue!]! } type Mutation { @@ -134,6 +134,12 @@ type SkillLevelLogItem { predictedCorrectness:Float! } +type SkillValue { + skillId: UUID! + + skillValue: Float! +} + type SkillAllUsersStats { skillId: UUID! From d9b03b2a39420f713881841f99e299b3775a1630 Mon Sep 17 00:00:00 2001 From: Sangaran Ramesch Date: Thu, 15 Jan 2026 16:45:16 +0100 Subject: [PATCH 4/5] add tests for getSkillValuesForSkillIds() + getSkillsAllUsersStatsForSkillIds() --- .../service/SkillLevelUsersStatsTest.java | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/test/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelUsersStatsTest.java diff --git a/src/test/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelUsersStatsTest.java b/src/test/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelUsersStatsTest.java new file mode 100644 index 0000000..087210d --- /dev/null +++ b/src/test/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelUsersStatsTest.java @@ -0,0 +1,155 @@ +package de.unistuttgart.iste.meitrex.skilllevel_service.service; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import de.unistuttgart.iste.meitrex.skilllevel_service.service.calculation.SkillLevelCalculator; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.AllSkillLevelsEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillLevelEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillValueEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.entity.SkillAllUsersStatsEntity; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.mapper.SkillLevelMapper; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.AllSkillLevelsRepository; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.ItemDifficultyRepository; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.SkillAbilityRepository; +import de.unistuttgart.iste.meitrex.skilllevel_service.persistence.repository.SkillsForCourseRepository; +import de.unistuttgart.iste.meitrex.common.dapr.TopicPublisher; +import de.unistuttgart.iste.meitrex.skilllevel_service.service.SkillLevelService; + +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class SkillLevelUsersStatsTest { + + private final AllSkillLevelsRepository skillLevelsRepository = Mockito.mock(AllSkillLevelsRepository.class); + private final SkillLevelMapper skillLevelMapper = Mockito.mock(SkillLevelMapper.class); + private final SkillLevelCalculator skillLevelCalculator = Mockito.mock(SkillLevelCalculator.class); + private final SkillAbilityRepository skillAbilityRepository = Mockito.mock(SkillAbilityRepository.class); + private final ItemDifficultyRepository itemDifficultyRepository = Mockito.mock(ItemDifficultyRepository.class); + private final SkillsForCourseRepository skillsForCourseRepository = Mockito.mock(SkillsForCourseRepository.class); + private final TopicPublisher topicPublisher = Mockito.mock(TopicPublisher.class); + + private final SkillLevelService skillLevelService = new SkillLevelService( + skillLevelsRepository, + skillLevelMapper, + skillLevelCalculator, + skillAbilityRepository, + itemDifficultyRepository, + skillsForCourseRepository, + topicPublisher + ); + + @Test + void testGetSkillValuesForSkillIds() { + final UUID userId = UUID.randomUUID(); + final UUID skillId1 = UUID.randomUUID(); + final UUID skillId2 = UUID.randomUUID(); + final UUID skillId3 = UUID.randomUUID(); + final UUID skillId4 = UUID.randomUUID(); + List skillIds = List.of(skillId1, skillId2, skillId3, skillId4); + + AllSkillLevelsEntity skillLevel1 = createAllSkillLevelEntity(userId, skillId1, 1f, 1f, 1f, 1f, 1f, 1f); + AllSkillLevelsEntity skillLevel2 = createAllSkillLevelEntity(userId, skillId2, 0f, 0f, 0f, 0f, 0f, 0f); + AllSkillLevelsEntity skillLevel3 = createAllSkillLevelEntity(userId, skillId3, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f); + AllSkillLevelsEntity skillLevel4 = createAllSkillLevelEntity(userId, skillId4, 0f, 0f, 0f, 0.5f, 0f, 0f); + + List skillLevels = List.of(skillLevel1, skillLevel2, skillLevel3, skillLevel4); + + final List primaryKeys = skillIds.stream() + .map(x -> new AllSkillLevelsEntity.PrimaryKey(x, userId)) + .toList(); + when(skillLevelsRepository.findAllById(primaryKeys)) + .thenReturn(skillLevels); + + List skillValues = skillLevelService.getSkillValuesForSkillIds(skillIds, userId); + + assertThat(skillValues).hasSize(4); + assertThat(skillValues).extracting(SkillValueEntity::getSkillId, SkillValueEntity::getSkillValue) + .containsExactlyInAnyOrder( + tuple(skillId1, 1f), + tuple(skillId2, 0f), + tuple(skillId3, 0.55f), + tuple(skillId4, 0.5f) + ); + verify(skillLevelsRepository, times(1)).findAllById(primaryKeys); + } + + @Test + void testGetSkillsAllUsersStatsForSkillIds() { + final UUID user1Id = UUID.randomUUID(); + final UUID user2Id = UUID.randomUUID(); + final UUID user3Id = UUID.randomUUID(); + + final UUID skillId1 = UUID.randomUUID(); + final UUID skillId2 = UUID.randomUUID(); + final UUID skillId3 = UUID.randomUUID(); + List skillIds = List.of(skillId1, skillId2, skillId3); + + AllSkillLevelsEntity user1Skill1 = createAllSkillLevelEntity(user1Id, skillId1, 1f, 1f, 1f, 1f, 1f, 1f); + AllSkillLevelsEntity user2Skill1 = createAllSkillLevelEntity(user2Id, skillId1, 1f, 1f, 1f, 1f, 1f, 1f); + + AllSkillLevelsEntity user1Skill2 = createAllSkillLevelEntity(user1Id, skillId2, 0f, 0f, 0f, 0f, 0f, 0f); + AllSkillLevelsEntity user2Skill2 = createAllSkillLevelEntity(user2Id, skillId2, 0f, 0f, 0f, 0f, 0f, 0f); + + AllSkillLevelsEntity user1Skill3 = createAllSkillLevelEntity(user1Id, skillId3, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f); + AllSkillLevelsEntity user2Skill3 = createAllSkillLevelEntity(user2Id, skillId3, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1f); + AllSkillLevelsEntity user3Skill3 = createAllSkillLevelEntity(user3Id, skillId3, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f); + + when(skillLevelsRepository.findByIdSkillId(skillId1)) + .thenReturn(List.of(user1Skill1, user2Skill1)); + when(skillLevelsRepository.findByIdSkillId(skillId2)) + .thenReturn(List.of(user1Skill2, user2Skill2)); + when(skillLevelsRepository.findByIdSkillId(skillId3)) + .thenReturn(List.of(user1Skill3, user2Skill3, user3Skill3)); + + List skillAllUsersStats = skillLevelService.getSkillsAllUsersStatsForSkillIds(skillIds); + + assertThat(skillAllUsersStats).hasSize(3); + assertThat(skillAllUsersStats).extracting(SkillAllUsersStatsEntity::getSkillId, SkillAllUsersStatsEntity::getSkillValueSum, SkillAllUsersStatsEntity::getParticipantCount) + .containsExactlyInAnyOrder( + tuple(skillId1, 2f, 2), + tuple(skillId2, 0f, 2), + tuple(skillId3, 1.75f, 3) + ); + + verify(skillLevelsRepository, times(1)).findByIdSkillId(skillId1); + verify(skillLevelsRepository, times(1)).findByIdSkillId(skillId2); + verify(skillLevelsRepository, times(1)).findByIdSkillId(skillId3); + } + + /** + * Helper method to create a skill level entity with specified values + */ + private AllSkillLevelsEntity createAllSkillLevelEntity(UUID userId, UUID skillId, + float remember, float understand, float apply, + float analyze, float evaluate, float create) { + AllSkillLevelsEntity entity = new AllSkillLevelsEntity(); + AllSkillLevelsEntity.PrimaryKey pk = new AllSkillLevelsEntity.PrimaryKey(); + pk.setUserId(userId); + pk.setSkillId(skillId); + entity.setId(pk); + + entity.setRemember(createSkillLevelEntity(remember)); + entity.setUnderstand(createSkillLevelEntity(understand)); + entity.setApply(createSkillLevelEntity(apply)); + entity.setAnalyze(createSkillLevelEntity(analyze)); + entity.setEvaluate(createSkillLevelEntity(evaluate)); + entity.setCreate(createSkillLevelEntity(create)); + + return entity; + } + + /** + * Helper method to create a SkillLevelEntity with a specific value + */ + private SkillLevelEntity createSkillLevelEntity(float value) { + SkillLevelEntity entity = new SkillLevelEntity(value); + entity.setValue(value); + return entity; + } +} From 46cb69d5277baecbdb57477cbd74d5ca9c6421bf Mon Sep 17 00:00:00 2001 From: Sangaran Ramesch Date: Thu, 15 Jan 2026 19:46:15 +0100 Subject: [PATCH 5/5] style fix --- .../service/SkillLevelService.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java index 3afca98..5ed20d3 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/skilllevel_service/service/SkillLevelService.java @@ -92,7 +92,7 @@ public List getSkillLevelsForSkillIds(List skillIds, UUID use */ public List getSkillsAllUsersStatsForSkillIds(List skillIds) { List skillAllUsersStats = new ArrayList<>(); - for(UUID skillId : skillIds) { + for (UUID skillId : skillIds) { skillAllUsersStats.add(getSkillAllUsersStatsForSkillId(skillId)); } return skillAllUsersStats; @@ -108,11 +108,11 @@ public List getSkillsAllUsersStatsForSkillIds(List skillLevels = skillLevelsRepository.findByIdSkillId(skillId); List skillValues = new ArrayList<>(); - for(AllSkillLevelsEntity skillLevel : skillLevels) { + for (AllSkillLevelsEntity skillLevel : skillLevels) { skillValues.add(getSkillValueForSkillLevel(skillLevel)); } float skillValueSum = 0f; - for(Float skillValue : skillValues) { + for (Float skillValue : skillValues) { skillValueSum += skillValue; } SkillAllUsersStatsEntity skillAllUsersStats = new SkillAllUsersStatsEntity(); @@ -120,8 +120,7 @@ private SkillAllUsersStatsEntity getSkillAllUsersStatsForSkillId(UUID skillId) { skillAllUsersStats.setParticipantCount(skillValues.size()); if (skillValues.isEmpty()) { skillAllUsersStats.setSkillValueSum(0f); - } - else { + } else { skillAllUsersStats.setSkillValueSum(skillValueSum); } return skillAllUsersStats; @@ -145,8 +144,7 @@ private Float getSkillValueForSkillLevel(AllSkillLevelsEntity skillLevel) { List nonZeroValues = values.stream().filter(v -> v > 0f).toList(); if (nonZeroValues.isEmpty()) { return 0f; - } - else{ + } else { float sum = nonZeroValues.stream().reduce(0f, Float::sum); return sum / nonZeroValues.size(); } @@ -162,7 +160,7 @@ private Float getSkillValueForSkillLevel(AllSkillLevelsEntity skillLevel) { public List getSkillValuesForSkillIds(List skillIds, UUID userId) { List skillLevels = getSkillLevelEntitiesForSkillIds(skillIds, userId); List skillValues = new ArrayList<>(); - for(AllSkillLevelsEntity skillLevel : skillLevels) { + for (AllSkillLevelsEntity skillLevel : skillLevels) { SkillValueEntity entity = new SkillValueEntity(); entity.setSkillId(skillLevel.getId().getSkillId()); entity.setSkillValue(getSkillValueForSkillLevel(skillLevel));