From 9c6e893638e81381571cc3fd1ac32a5bcb92f341 Mon Sep 17 00:00:00 2001 From: petra kohler <41048546+smuefsmuef@users.noreply.github.com> Date: Mon, 22 Jul 2024 14:10:56 +0200 Subject: [PATCH] Dear 120 survey datamodel * DEAR-115: add datamodel for team * DEAR-115: fix user service tests * DEAR-115: add create team endpoint * DEAR-115: add join team endpoint * DEAR-120 add swagger-api to allowed requests * DEAR-120 add init sqls and testdata * DEAR-120 adjust timestamp, add entities * DEAR-120 add all entities * DEAR-120 add all repos * DEAR-120 add overall happiness score service & controller * DEAR-120 remove comment * DEAR-120 checkstyle * DEAR-120 adjust table names * DEAR-120 rename service, controller, add emotionSurveyRepo * DEAR-120 set userids testdata * DEAR-120 submit happiness survey * DEAR-120 set userids testdata * DEAR-120 get overallhappiness * DEAR-120 add workkinds * DEAR-120 add workkinds, add logger * DEAR-120 add workkind controller * DEAR-120 refactor * DEAR-120 add emotion api, checkstyle * DEAR-120 submit emotion survey * DEAR-120 update emotion_survey_data * DEAR-120 improve get dashbaord data * DEAR-120 add unit tests * DEAR-120 add exceptions, unit tests * DEAR-120 add happinessScore most voted, unit tests * DEAR-120 get happiness average score for workkind * DEAR-120 fix checkstyle * DEAR-120 review * DEAR-120 update query to use dto * DEAR-120 wip review * DEAR-120 resolve conflicts * DEAR-120 resolve conflicts * DEAR-120 clean up workkinds --------- Co-authored-by: Nick Baur <79518844+baurnick@users.noreply.github.com> --- database/01_init.sql | 53 ++++- database/02_test_data.sql | 62 +++++- .../deardevbackend/config/SecurityConfig.java | 1 + .../controller/DashboardController.java | 53 +++++ .../controller/EmotionController.java | 25 +++ .../controller/WorkKindController.java | 32 +++ .../fhnw/deardevbackend/dto/DashboardDTO.java | 16 ++ .../dto/MostVotedWorkKindDTO.java | 15 ++ .../dto/SubmitEmotionSurveyDTO.java | 9 + .../dto/SubmitHappinessSurveyDTO.java | 9 + .../dto/SubmitWorkKindSurveyDTO.java | 10 + .../fhnw/deardevbackend/entities/Emotion.java | 22 ++ .../entities/EmotionSurvey.java | 33 +++ .../entities/HappinessSurvey.java | 33 +++ .../deardevbackend/entities/WorkKind.java | 25 +++ .../entities/WorkKindSurvey.java | 33 +++ .../mapper/DashboardMapper.java | 18 ++ .../mapper/SubmitEmotionSurveyMapper.java | 17 ++ .../mapper/SubmitHappinessSurveyMapper.java | 17 ++ .../mapper/SubmitWorkKindSurveyMapper.java | 18 ++ .../mapper/TeamWithMembersMapper.java | 2 +- .../repositories/EmotionRepository.java | 11 + .../repositories/EmotionSurveyRepository.java | 10 + .../HappinessSurveyRepository.java | 23 ++ .../repositories/TeamMemberRepository.java | 3 + .../repositories/WorkKindRepository.java | 17 ++ .../WorkKindSurveyRepository.java | 27 +++ .../services/DashboardService.java | 126 +++++++++++ .../services/EmotionService.java | 21 ++ .../deardevbackend/services/TeamService.java | 4 + .../services/WorkKindService.java | 26 +++ src/main/resources/application.properties | 5 +- .../services/DashboardServiceTest.java | 210 ++++++++++++++++++ .../services/EmotionServiceTest.java | 53 +++++ .../services/WorkKindServiceTest.java | 64 ++++++ 35 files changed, 1088 insertions(+), 15 deletions(-) create mode 100644 src/main/java/ch/fhnw/deardevbackend/controller/DashboardController.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/controller/EmotionController.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/controller/WorkKindController.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/dto/DashboardDTO.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/dto/MostVotedWorkKindDTO.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/dto/SubmitEmotionSurveyDTO.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/dto/SubmitHappinessSurveyDTO.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/dto/SubmitWorkKindSurveyDTO.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/entities/Emotion.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/entities/EmotionSurvey.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/entities/HappinessSurvey.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/entities/WorkKind.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/entities/WorkKindSurvey.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/mapper/DashboardMapper.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/mapper/SubmitEmotionSurveyMapper.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/mapper/SubmitHappinessSurveyMapper.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/mapper/SubmitWorkKindSurveyMapper.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/repositories/EmotionRepository.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/repositories/EmotionSurveyRepository.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/repositories/HappinessSurveyRepository.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindRepository.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindSurveyRepository.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/services/DashboardService.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/services/EmotionService.java create mode 100644 src/main/java/ch/fhnw/deardevbackend/services/WorkKindService.java create mode 100644 src/test/java/ch/fhnw/deardevbackend/services/DashboardServiceTest.java create mode 100644 src/test/java/ch/fhnw/deardevbackend/services/EmotionServiceTest.java create mode 100644 src/test/java/ch/fhnw/deardevbackend/services/WorkKindServiceTest.java diff --git a/database/01_init.sql b/database/01_init.sql index 880ab7a..4450714 100644 --- a/database/01_init.sql +++ b/database/01_init.sql @@ -81,4 +81,55 @@ CREATE TABLE team_config -- Add constraints to the tables ALTER TABLE team - ADD CONSTRAINT fk_team_team_config FOREIGN KEY (config_id) REFERENCES team_config (id); \ No newline at end of file + ADD CONSTRAINT fk_team_team_config FOREIGN KEY (config_id) REFERENCES team_config (id); + +-- Create tables for DEAR-120-SURVEY DATA +------------------------------------------------------------------------------------------------------------------------ + +CREATE TABLE work_kind +( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + team_id INTEGER, + + CONSTRAINT fk_team FOREIGN KEY (team_id) REFERENCES team (id) +); + +CREATE TABLE emotion +( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +CREATE TABLE happiness_survey +( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + submitted TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + score INTEGER NOT NULL, + + CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id) +); + +CREATE TABLE work_kind_survey +( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + submitted TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + score INTEGER NOT NULL, + work_kind_id INTEGER NOT NULL, + + CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id), + CONSTRAINT fk_work_kind FOREIGN KEY (work_kind_id) REFERENCES work_kind (id) +); + +CREATE TABLE emotion_survey +( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + submitted TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + emotion_id INTEGER NOT NULL, + + CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users (id), + CONSTRAINT fk_emotion FOREIGN KEY (emotion_id) REFERENCES emotion (id) +); diff --git a/database/02_test_data.sql b/database/02_test_data.sql index d62f2dd..b16a052 100644 --- a/database/02_test_data.sql +++ b/database/02_test_data.sql @@ -1,15 +1,55 @@ -INSERT INTO public.users (id, name, email, "emailVerified", image, username) -VALUES (1, 'Hans Müller', 'hans@test.com', null, null, 'Hansi'); +INSERT INTO users (id, name, email, "emailVerified", image, username) +VALUES (100001, 'Hans Müller', 'hans@test.com', null, null, 'Hansi'), + (100002, 'Berta Roberts', 'berti@test.com', null, null, 'Berti'); -INSERT INTO public.team (id, name, current_sprint_id, config_id, code, created_by, created_at, active) -VALUES (1, 'Team Alpha', null, null, 'ALF1', 1, '2024-07-13 15:19:11.164000 +00:00', true); -INSERT INTO public.team (id, name, current_sprint_id, config_id, code, created_by, created_at, active) -VALUES (2, 'Team Beta', null, null, 'BET2', 1, '2024-07-13 15:20:22.162000 +00:00', true); +INSERT INTO team (id, name, current_sprint_id, config_id, code, created_by, created_at, active) +VALUES (998, 'Team Alpha', null, null, 'ALF1', 100001, '2024-07-13 15:19:11.164000 +00:00', true), + (999, 'Team Beta', null, null, 'BET2', 100001, '2024-07-13 15:20:22.162000 +00:00', true); -INSERT INTO public.team_member (id, user_id, team_id, role, active) -VALUES (1, 1, 1, 'ADMIN', true); -INSERT INTO public.team_member (id, user_id, team_id, role, active) -VALUES (2, 1, 2, 'MEMBER', true); +INSERT INTO team_member (id, user_id, team_id, role, active) +VALUES (990, 100001, 998, 'ADMIN', true); +INSERT INTO team_member (id, user_id, team_id, role, active) +VALUES (999, 100002, 999, 'MEMBER', true); -INSERT INTO public.team_config (id, work_kinds) +INSERT INTO team_config (id, work_kinds) VALUES (1, '{CODING,MEETING}'); + +INSERT INTO work_kind (name) +VALUES ('Coding'), + ('Meeting'), + ('Daily Standup'), + ('Planning'), + ('Review'), + ('Retrospective'); +INSERT INTO work_kind (name, team_id) +VALUES ('Team Huddle', 998), + ('Bowling', 999); + +INSERT INTO emotion (name) +VALUES ('Angry'), + ('Bored'), + ('Busy'), + ('Disappointed'), + ('Energetic'), + ('Exhausted'), + ('Frustrated'), + ('Lonely'), + ('Motivated'), + ('Nervous'), + ('Overwhelmed'), + ('Pessimistic'), + ('Relaxed'), + ('Sick'), + ('Stressed'); + +INSERT INTO happiness_survey (user_id, score) +VALUES (100001, 4), + (100001, 2), + (100001, 3), + (100002, 1); +INSERT INTO work_kind_survey (user_id, score, work_kind_id) +VALUES (100001, 6, 1), + (100002, 7, 2); +INSERT INTO emotion_survey (user_id, emotion_id) +VALUES (100001, 1), + (100002, 2); diff --git a/src/main/java/ch/fhnw/deardevbackend/config/SecurityConfig.java b/src/main/java/ch/fhnw/deardevbackend/config/SecurityConfig.java index 1e9ad43..96c417e 100644 --- a/src/main/java/ch/fhnw/deardevbackend/config/SecurityConfig.java +++ b/src/main/java/ch/fhnw/deardevbackend/config/SecurityConfig.java @@ -28,6 +28,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authz -> authz .requestMatchers("/auth/token").permitAll() + .requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll() .anyRequest().authenticated()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .formLogin(AbstractHttpConfigurer::disable) diff --git a/src/main/java/ch/fhnw/deardevbackend/controller/DashboardController.java b/src/main/java/ch/fhnw/deardevbackend/controller/DashboardController.java new file mode 100644 index 0000000..279f6d1 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/controller/DashboardController.java @@ -0,0 +1,53 @@ +package ch.fhnw.deardevbackend.controller; + +import ch.fhnw.deardevbackend.dto.DashboardDTO; +import ch.fhnw.deardevbackend.dto.SubmitEmotionSurveyDTO; +import ch.fhnw.deardevbackend.dto.SubmitHappinessSurveyDTO; +import ch.fhnw.deardevbackend.dto.SubmitWorkKindSurveyDTO; +import ch.fhnw.deardevbackend.entities.EmotionSurvey; +import ch.fhnw.deardevbackend.entities.HappinessSurvey; +import ch.fhnw.deardevbackend.entities.WorkKindSurvey; +import ch.fhnw.deardevbackend.services.DashboardService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + + +@RestController +@RequestMapping("/v1/dashboard") +public class DashboardController { + @Autowired + private DashboardService dashboardService; + + // todo ensure only logged in user can post his entries + + @GetMapping("/{userId}") + public ResponseEntity getDashboardDataByUserId(@PathVariable Integer userId) { + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + return ResponseEntity.ok().body(dashboardDTO); + } + + @GetMapping("/happiness/average/{userId}") + public ResponseEntity getAverageScoreByUserId(@PathVariable Integer userId) { + Integer averageScore = dashboardService.getAverageScoreByUserId(userId); + return ResponseEntity.ok().body(averageScore); + } + + @PostMapping("/survey/happiness") + public ResponseEntity submitHappinessSurvey(@RequestBody SubmitHappinessSurveyDTO request) { + HappinessSurvey data = dashboardService.saveHappinessSurvey(request); + return ResponseEntity.ok().body(data); + } + + @PostMapping("survey/workkind") + public ResponseEntity submitWorkKindSurvey(@RequestBody SubmitWorkKindSurveyDTO request) { + WorkKindSurvey data = dashboardService.saveWorkKindSurvey(request); + return ResponseEntity.ok().body(data); + } + + @PostMapping("survey/emotion") + public ResponseEntity submitEmotionSurvey(@RequestBody SubmitEmotionSurveyDTO request) { + EmotionSurvey data = dashboardService.saveEmotionSurvey(request); + return ResponseEntity.ok().body(data); + } +} diff --git a/src/main/java/ch/fhnw/deardevbackend/controller/EmotionController.java b/src/main/java/ch/fhnw/deardevbackend/controller/EmotionController.java new file mode 100644 index 0000000..4853145 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/controller/EmotionController.java @@ -0,0 +1,25 @@ +package ch.fhnw.deardevbackend.controller; + +import ch.fhnw.deardevbackend.entities.Emotion; +import ch.fhnw.deardevbackend.services.EmotionService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/v1/emotions") +public class EmotionController { + + @Autowired + private EmotionService emotionService; + + @GetMapping() + public ResponseEntity> getAllEmotions() { + List emotions = emotionService.getEmotions(); + return ResponseEntity.ok(emotions); + } +} diff --git a/src/main/java/ch/fhnw/deardevbackend/controller/WorkKindController.java b/src/main/java/ch/fhnw/deardevbackend/controller/WorkKindController.java new file mode 100644 index 0000000..46200a5 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/controller/WorkKindController.java @@ -0,0 +1,32 @@ +package ch.fhnw.deardevbackend.controller; + +import ch.fhnw.deardevbackend.entities.WorkKind; +import ch.fhnw.deardevbackend.services.WorkKindService; +import ch.fhnw.deardevbackend.services.TeamService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/v1/workkinds") +public class WorkKindController { + + @Autowired + private WorkKindService workKindService; + + @Autowired + private TeamService teamService; + + + @GetMapping("/user/{userId}") + public ResponseEntity> getAllWorkKindsByUserAndTeam(@PathVariable Integer userId) { + List teamIds = teamService.getTeamIdsForUser(userId); + List workKinds = workKindService.getWorkKindsForTeams(teamIds); + return ResponseEntity.ok(workKinds); + } +} diff --git a/src/main/java/ch/fhnw/deardevbackend/dto/DashboardDTO.java b/src/main/java/ch/fhnw/deardevbackend/dto/DashboardDTO.java new file mode 100644 index 0000000..43fc2d6 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/dto/DashboardDTO.java @@ -0,0 +1,16 @@ +package ch.fhnw.deardevbackend.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DashboardDTO { + private MostVotedWorkKindDTO mostVotedWorkKind; + private Integer averageScore; + + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/dto/MostVotedWorkKindDTO.java b/src/main/java/ch/fhnw/deardevbackend/dto/MostVotedWorkKindDTO.java new file mode 100644 index 0000000..526eb53 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/dto/MostVotedWorkKindDTO.java @@ -0,0 +1,15 @@ +package ch.fhnw.deardevbackend.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class MostVotedWorkKindDTO { + private Integer workKindId; + private String workKindName; + private Integer voteCount; + private Integer happinessScore; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/dto/SubmitEmotionSurveyDTO.java b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitEmotionSurveyDTO.java new file mode 100644 index 0000000..d70dc61 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitEmotionSurveyDTO.java @@ -0,0 +1,9 @@ +package ch.fhnw.deardevbackend.dto; + +import lombok.Data; + +@Data +public class SubmitEmotionSurveyDTO { + private Integer userId; + private Integer emotionId; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/dto/SubmitHappinessSurveyDTO.java b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitHappinessSurveyDTO.java new file mode 100644 index 0000000..b1e7ddf --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitHappinessSurveyDTO.java @@ -0,0 +1,9 @@ +package ch.fhnw.deardevbackend.dto; + +import lombok.Data; + +@Data +public class SubmitHappinessSurveyDTO { + private Integer userId; + private Integer score; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/dto/SubmitWorkKindSurveyDTO.java b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitWorkKindSurveyDTO.java new file mode 100644 index 0000000..b657927 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/dto/SubmitWorkKindSurveyDTO.java @@ -0,0 +1,10 @@ +package ch.fhnw.deardevbackend.dto; + +import lombok.Data; + +@Data +public class SubmitWorkKindSurveyDTO { + private Integer userId; + private Integer score; + private Integer workKindId; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/entities/Emotion.java b/src/main/java/ch/fhnw/deardevbackend/entities/Emotion.java new file mode 100644 index 0000000..d51e063 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/entities/Emotion.java @@ -0,0 +1,22 @@ +package ch.fhnw.deardevbackend.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "emotion") +@Builder +@Data +public class Emotion { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "name") + private String name; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/entities/EmotionSurvey.java b/src/main/java/ch/fhnw/deardevbackend/entities/EmotionSurvey.java new file mode 100644 index 0000000..e6699d6 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/entities/EmotionSurvey.java @@ -0,0 +1,33 @@ +package ch.fhnw.deardevbackend.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "emotion_survey") +@Builder +@Data +public class EmotionSurvey { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "user_id") + private Integer userId; + + @Column(name = "submitted", columnDefinition = "TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = false) + private String submitted; + + @Column(name = "emotion_id") + private Integer emotionId; + + + + + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/entities/HappinessSurvey.java b/src/main/java/ch/fhnw/deardevbackend/entities/HappinessSurvey.java new file mode 100644 index 0000000..28da9b3 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/entities/HappinessSurvey.java @@ -0,0 +1,33 @@ +package ch.fhnw.deardevbackend.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "happiness_survey") +@Builder +@Data +public class HappinessSurvey { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "user_id") + private Integer userId; + + @Column(name = "submitted", columnDefinition = "TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = false) + private String submitted; + + @Column(name = "score") + private Integer score; + + + + + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/entities/WorkKind.java b/src/main/java/ch/fhnw/deardevbackend/entities/WorkKind.java new file mode 100644 index 0000000..fc1b5fa --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/entities/WorkKind.java @@ -0,0 +1,25 @@ +package ch.fhnw.deardevbackend.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "work_kind") +@Builder +@Data +public class WorkKind { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "name") + private String name; + + @Column(name = "team_id") + private Integer teamId; +} diff --git a/src/main/java/ch/fhnw/deardevbackend/entities/WorkKindSurvey.java b/src/main/java/ch/fhnw/deardevbackend/entities/WorkKindSurvey.java new file mode 100644 index 0000000..c47b874 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/entities/WorkKindSurvey.java @@ -0,0 +1,33 @@ +package ch.fhnw.deardevbackend.entities; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "work_kind_survey") +@Builder +@Data +public class WorkKindSurvey { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "user_id") + private Integer userId; + + @Column(name = "submitted", columnDefinition = "TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP", insertable = false, updatable = false) + private String submitted; + + @Column(name = "score") + private Integer score; + + @Column(name = "work_kind_id") + private Integer workKindId; + + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/mapper/DashboardMapper.java b/src/main/java/ch/fhnw/deardevbackend/mapper/DashboardMapper.java new file mode 100644 index 0000000..ccae6f8 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/mapper/DashboardMapper.java @@ -0,0 +1,18 @@ +package ch.fhnw.deardevbackend.mapper; + +import ch.fhnw.deardevbackend.dto.DashboardDTO; +import ch.fhnw.deardevbackend.dto.MostVotedWorkKindDTO; +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface DashboardMapper { + DashboardMapper INSTANCE = Mappers.getMapper(DashboardMapper.class); + + default DashboardDTO toDashboardDTO(Integer workKindId, String workKindName, Integer voteCount, Integer averageScore, Integer happinessScore) { + MostVotedWorkKindDTO mostVotedWorkKindDTO = new MostVotedWorkKindDTO(workKindId, workKindName, voteCount, happinessScore); + + return new DashboardDTO(mostVotedWorkKindDTO, averageScore); + } +} diff --git a/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitEmotionSurveyMapper.java b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitEmotionSurveyMapper.java new file mode 100644 index 0000000..a673c5d --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitEmotionSurveyMapper.java @@ -0,0 +1,17 @@ +package ch.fhnw.deardevbackend.mapper; + +import ch.fhnw.deardevbackend.dto.SubmitEmotionSurveyDTO; +import ch.fhnw.deardevbackend.entities.EmotionSurvey; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface SubmitEmotionSurveyMapper { + SubmitEmotionSurveyMapper INSTANCE = Mappers.getMapper(SubmitEmotionSurveyMapper.class); + + @Mapping(target = "submitted", expression = "java(java.time.OffsetDateTime.now().toString())") + EmotionSurvey toEmotionSurvey(SubmitEmotionSurveyDTO dto); +} + diff --git a/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitHappinessSurveyMapper.java b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitHappinessSurveyMapper.java new file mode 100644 index 0000000..c1944a6 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitHappinessSurveyMapper.java @@ -0,0 +1,17 @@ +package ch.fhnw.deardevbackend.mapper; +import ch.fhnw.deardevbackend.dto.SubmitHappinessSurveyDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; +import ch.fhnw.deardevbackend.entities.HappinessSurvey; + + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface SubmitHappinessSurveyMapper { + SubmitHappinessSurveyMapper INSTANCE = Mappers.getMapper(SubmitHappinessSurveyMapper.class); + + @Mapping(target = "submitted", expression = "java(java.time.OffsetDateTime.now().toString())") + HappinessSurvey toHappinessSurvey(SubmitHappinessSurveyDTO dto); +} + diff --git a/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitWorkKindSurveyMapper.java b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitWorkKindSurveyMapper.java new file mode 100644 index 0000000..e0230ee --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/mapper/SubmitWorkKindSurveyMapper.java @@ -0,0 +1,18 @@ +package ch.fhnw.deardevbackend.mapper; + +import ch.fhnw.deardevbackend.dto.SubmitWorkKindSurveyDTO; +import ch.fhnw.deardevbackend.entities.WorkKindSurvey; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) +public interface SubmitWorkKindSurveyMapper { + SubmitWorkKindSurveyMapper INSTANCE = Mappers.getMapper(SubmitWorkKindSurveyMapper.class); + + @Mapping(target = "submitted", expression = "java(java.time.OffsetDateTime.now().toString())") + WorkKindSurvey toWorkKindSurvey(SubmitWorkKindSurveyDTO dto); +} + diff --git a/src/main/java/ch/fhnw/deardevbackend/mapper/TeamWithMembersMapper.java b/src/main/java/ch/fhnw/deardevbackend/mapper/TeamWithMembersMapper.java index c91155e..ae35002 100644 --- a/src/main/java/ch/fhnw/deardevbackend/mapper/TeamWithMembersMapper.java +++ b/src/main/java/ch/fhnw/deardevbackend/mapper/TeamWithMembersMapper.java @@ -15,6 +15,6 @@ public interface TeamWithMembersMapper { @Mapping(target = "team", source = "team") @Mapping(target = "members", source = "members") - @Mapping(target = " isAdmin", source = "isAdmin") + @Mapping(target = "isAdmin", source = "isAdmin") TeamWithMembersDTO toDTO(Team team, List members, Boolean isAdmin); } diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionRepository.java new file mode 100644 index 0000000..b3f031e --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionRepository.java @@ -0,0 +1,11 @@ +package ch.fhnw.deardevbackend.repositories; + +import ch.fhnw.deardevbackend.entities.Emotion; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface EmotionRepository extends JpaRepository { + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionSurveyRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionSurveyRepository.java new file mode 100644 index 0000000..6d3dd98 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/EmotionSurveyRepository.java @@ -0,0 +1,10 @@ +package ch.fhnw.deardevbackend.repositories; + +import ch.fhnw.deardevbackend.entities.EmotionSurvey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EmotionSurveyRepository extends JpaRepository { + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/HappinessSurveyRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/HappinessSurveyRepository.java new file mode 100644 index 0000000..2806fbb --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/HappinessSurveyRepository.java @@ -0,0 +1,23 @@ +package ch.fhnw.deardevbackend.repositories; + +import ch.fhnw.deardevbackend.entities.HappinessSurvey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Repository +public interface HappinessSurveyRepository extends JpaRepository { + + @Query("SELECT o FROM HappinessSurvey o WHERE o.userId = :userId") + List findByUserId(@Param("userId") Integer userId); + + @Query("SELECT AVG(o.score) FROM HappinessSurvey o WHERE o.userId = :userId") + Double findAverageScoreByUserId(@Param("userId") Integer userId); + + @Query("SELECT CAST(s.submitted AS date) AS day, AVG(s.score) AS dailyAverage FROM HappinessSurvey s WHERE s.userId = :userId GROUP BY day") + List findDailyAveragesByUserId(Integer userId); +} diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/TeamMemberRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/TeamMemberRepository.java index 35b8c6e..72c236f 100644 --- a/src/main/java/ch/fhnw/deardevbackend/repositories/TeamMemberRepository.java +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/TeamMemberRepository.java @@ -14,4 +14,7 @@ public interface TeamMemberRepository extends JpaRepository Boolean userIsInTeam(@Param("userId") Integer userId); List findByUserId(Integer userId); + + @Query("SELECT tm.teamId FROM TeamMember tm WHERE tm.userId = :userId AND tm.active = true") + List findTeamIdByUserId(@Param("userId") Integer userId); } diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindRepository.java new file mode 100644 index 0000000..fb5d04a --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindRepository.java @@ -0,0 +1,17 @@ +package ch.fhnw.deardevbackend.repositories; + +import ch.fhnw.deardevbackend.entities.WorkKind; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + + +@Repository +public interface WorkKindRepository extends JpaRepository { + + @Query("SELECT w FROM WorkKind w WHERE w.teamId IS NULL OR w.teamId IN (:teamIds)") + List findByTeamIdsOrNoTeam(@Param("teamIds") List teamIds); +} diff --git a/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindSurveyRepository.java b/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindSurveyRepository.java new file mode 100644 index 0000000..217ebce --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/repositories/WorkKindSurveyRepository.java @@ -0,0 +1,27 @@ +package ch.fhnw.deardevbackend.repositories; + +import ch.fhnw.deardevbackend.entities.WorkKindSurvey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + + +@Repository +public interface WorkKindSurveyRepository extends JpaRepository { + + @Query("SELECT w.workKindId, COUNT(w.workKindId) as voteCount " + + "FROM WorkKindSurvey w " + + "WHERE w.userId = :userId " + + "GROUP BY w.workKindId " + + "ORDER BY voteCount DESC") + List findWorkKindCountByUserId(Integer userId); + + @Query("SELECT AVG(w.score) " + + "FROM WorkKindSurvey w " + + "WHERE w.workKindId = :workKindId AND w.userId = :userId") + Optional findAverageHappinessScoreByWorkKindIdAndUserId(Integer workKindId, Integer userId); + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/services/DashboardService.java b/src/main/java/ch/fhnw/deardevbackend/services/DashboardService.java new file mode 100644 index 0000000..518a846 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/services/DashboardService.java @@ -0,0 +1,126 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.controller.exceptions.YappiException; +import ch.fhnw.deardevbackend.dto.*; +import ch.fhnw.deardevbackend.entities.EmotionSurvey; +import ch.fhnw.deardevbackend.entities.HappinessSurvey; +import ch.fhnw.deardevbackend.entities.WorkKind; +import ch.fhnw.deardevbackend.entities.WorkKindSurvey; +import ch.fhnw.deardevbackend.mapper.DashboardMapper; +import ch.fhnw.deardevbackend.mapper.SubmitEmotionSurveyMapper; +import ch.fhnw.deardevbackend.mapper.SubmitHappinessSurveyMapper; +import ch.fhnw.deardevbackend.mapper.SubmitWorkKindSurveyMapper; +import ch.fhnw.deardevbackend.repositories.*; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class DashboardService { + + @Autowired + private HappinessSurveyRepository happinessSurveyRepository; + + @Autowired + private WorkKindSurveyRepository workKindSurveyRepository; + + @Autowired + private EmotionSurveyRepository emotionSurveyRepository; + + @Autowired + private WorkKindRepository workKindRepository; + + @Autowired + private SubmitHappinessSurveyMapper submitHappinessSurveyMapper; + + @Autowired + private SubmitWorkKindSurveyMapper submitWorkKindSurveyMapper; + + @Autowired + private SubmitEmotionSurveyMapper submitEmotionSurveyMapper; + + @Transactional + public HappinessSurvey saveHappinessSurvey(SubmitHappinessSurveyDTO dto) { + try { + HappinessSurvey survey = submitHappinessSurveyMapper.toHappinessSurvey(dto); + return happinessSurveyRepository.save(survey); + } catch (DataIntegrityViolationException ex) { + throw new YappiException("Error saving dappiness data"); + } + } + + @Transactional + public WorkKindSurvey saveWorkKindSurvey(SubmitWorkKindSurveyDTO dto) { + try { + WorkKindSurvey survey = submitWorkKindSurveyMapper.toWorkKindSurvey(dto); + return workKindSurveyRepository.save(survey); + } catch (DataIntegrityViolationException ex) { + throw new YappiException("Error saving worktype data"); + } + } + + @Transactional + public EmotionSurvey saveEmotionSurvey(SubmitEmotionSurveyDTO dto) { // todo rename!!!!!!!!!! + try { + EmotionSurvey survey = submitEmotionSurveyMapper.toEmotionSurvey(dto); + return emotionSurveyRepository.save(survey); + } catch (DataIntegrityViolationException ex) { + throw new YappiException("Error saving emotion data"); + } + } + + @Transactional(readOnly = true) + public Integer getAverageScoreByUserId(Integer userId) { + try { + List dailyAverages = happinessSurveyRepository.findDailyAveragesByUserId(userId); + + if (dailyAverages.isEmpty()) { + return null; + } + + double total = 0; + for (Object[] dailyAverage : dailyAverages) { + total += (Double) dailyAverage[1]; + } + + double overallAverage = total / dailyAverages.size(); + return (int) Math.round(overallAverage); + } catch (Exception ex) { + throw new YappiException("Error calculating average score for user ID: " + userId); + } + } + + @Transactional(readOnly = true) + public DashboardDTO getDashboardDataByUserId(Integer userId) { + try { + + Integer averageScore = getAverageScoreByUserId(userId); + + List results = workKindSurveyRepository.findWorkKindCountByUserId(userId); + + if (results.isEmpty()) { + return DashboardMapper.INSTANCE.toDashboardDTO(null, null, null, averageScore, null); + } + + Object[] result = results.get(0); + int workKindId = (int) result[0]; + long voteCount = (long) result[1]; + String workKindName = workKindRepository.findById(workKindId) + .map(WorkKind::getName) + .orElse("Unknown"); + + Integer happinessScore = workKindSurveyRepository.findAverageHappinessScoreByWorkKindIdAndUserId(workKindId, userId) + .orElse(null); + + return DashboardMapper.INSTANCE.toDashboardDTO(workKindId, workKindName, (int) voteCount, averageScore, happinessScore); + } catch (Exception ex) { + throw new YappiException("Error fetching dashboard data for user ID: " + userId); + } + } + +} diff --git a/src/main/java/ch/fhnw/deardevbackend/services/EmotionService.java b/src/main/java/ch/fhnw/deardevbackend/services/EmotionService.java new file mode 100644 index 0000000..8fdaaf2 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/services/EmotionService.java @@ -0,0 +1,21 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.entities.Emotion; +import ch.fhnw.deardevbackend.repositories.EmotionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class EmotionService { + + @Autowired + private EmotionRepository emotionRepository; + + public List getEmotions() { + return emotionRepository.findAll(); + } +} diff --git a/src/main/java/ch/fhnw/deardevbackend/services/TeamService.java b/src/main/java/ch/fhnw/deardevbackend/services/TeamService.java index 061fb07..da70469 100644 --- a/src/main/java/ch/fhnw/deardevbackend/services/TeamService.java +++ b/src/main/java/ch/fhnw/deardevbackend/services/TeamService.java @@ -91,4 +91,8 @@ private String generateUniqueTeamCode() { } while (teamRepository.existsByCode(code)); return code; } + + public List getTeamIdsForUser(int userId) { + return teamMemberRepository.findTeamIdByUserId(userId); + } } diff --git a/src/main/java/ch/fhnw/deardevbackend/services/WorkKindService.java b/src/main/java/ch/fhnw/deardevbackend/services/WorkKindService.java new file mode 100644 index 0000000..fcfe786 --- /dev/null +++ b/src/main/java/ch/fhnw/deardevbackend/services/WorkKindService.java @@ -0,0 +1,26 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.entities.WorkKind; +import ch.fhnw.deardevbackend.repositories.WorkKindRepository; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class WorkKindService { + + private static final Logger logger = LoggerFactory.getLogger(WorkKindService.class); + + @Autowired + private final WorkKindRepository workKindRepository; + + public List getWorkKindsForTeams(List teamIds) { + logger.info("Fetching WorkKinds for teamIds: {}", teamIds); + return workKindRepository.findByTeamIdsOrNoTeam(teamIds); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 3ebf6da..9afa883 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -12,9 +12,10 @@ spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false ## Load environment specific properties spring.config.import=optional:file:.env[.properties] # Customize the OpenAPI path -springdoc.api-docs.path=/api-docs +springdoc.api-docs.path=/v3/api-docs springdoc.swagger-ui.path=/swagger-ui.html # JWT Configuration jwt.secret=${JWT_SECRET} #Logging Configuration -logging.level.root=WARN \ No newline at end of file +logging.level.root=WARN +logging.level.ch.fhnw.deardevbackend.services=INFO diff --git a/src/test/java/ch/fhnw/deardevbackend/services/DashboardServiceTest.java b/src/test/java/ch/fhnw/deardevbackend/services/DashboardServiceTest.java new file mode 100644 index 0000000..bf916b7 --- /dev/null +++ b/src/test/java/ch/fhnw/deardevbackend/services/DashboardServiceTest.java @@ -0,0 +1,210 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.controller.exceptions.YappiException; +import ch.fhnw.deardevbackend.dto.DashboardDTO; +import ch.fhnw.deardevbackend.entities.WorkKind; +import ch.fhnw.deardevbackend.repositories.HappinessSurveyRepository; +import ch.fhnw.deardevbackend.repositories.WorkKindRepository; +import ch.fhnw.deardevbackend.repositories.WorkKindSurveyRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +class DashboardServiceTest { + + @Mock + private HappinessSurveyRepository happinessSurveyRepository; + + @Mock + private WorkKindSurveyRepository workKindSurveyRepository; + + @Mock + private WorkKindRepository workKindRepository; + + @InjectMocks + private DashboardService dashboardService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + + @Test + void getAverageScoreByUserId_NoSurveys() { + int userId = 1; + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(Collections.emptyList()); + + Integer averageScore = dashboardService.getAverageScoreByUserId(userId); + assertNull(averageScore); + } + + @Test + void getAverageScoreByUserId_WithSurveys() { + int userId = 1; + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(List.of( + new Object[]{1, 5.0}, + new Object[]{2, 0.0}, + new Object[]{1, 15.0}, + new Object[]{2, 15.4} + )); + + Integer averageScore = dashboardService.getAverageScoreByUserId(userId); + assertEquals(9, averageScore); + } + + @Test + void getDashboardDataByUserId_NoSurveys() { + int userId = 1; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenReturn(Collections.emptyList()); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(Collections.emptyList()); + + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindId()); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindName()); + assertNull(dashboardDTO.getMostVotedWorkKind().getVoteCount()); + assertNull(dashboardDTO.getAverageScore()); + } + + @Test + void getDashboardDataByUserId_WithSurveys() { + int userId = 1; + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(List.of( + new Object[]{1, 5.0}, + new Object[]{2, 7.2} + )); + + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenReturn(List.of( + new Object[]{1, 5L}, + new Object[]{5, 7L} + )); + + when(workKindRepository.findById(1)).thenReturn(Optional.of(new WorkKind(55, "Development", null))); + when(workKindSurveyRepository.findAverageHappinessScoreByWorkKindIdAndUserId(1, 1)).thenReturn(Optional.of(10)); + + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + assertEquals(1, dashboardDTO.getMostVotedWorkKind().getWorkKindId()); + assertEquals("Development", dashboardDTO.getMostVotedWorkKind().getWorkKindName()); + assertEquals(5, dashboardDTO.getMostVotedWorkKind().getVoteCount()); + assertEquals(6, dashboardDTO.getAverageScore()); + assertEquals(10, dashboardDTO.getMostVotedWorkKind().getHappinessScore()); + } + + @Test + void getDashboardDataByUserId_WithWorkKindSurveysOnly() { + int userId = 1; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenReturn(List.of( + new Object[]{1, 5L}, + new Object[]{2, 3L}, + new Object[]{4, 2L}, + new Object[]{1, 8L} + )); + when(workKindRepository.findById(1)).thenReturn(Optional.of(new WorkKind(1, "Development", null))); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(List.of()); + when(workKindSurveyRepository.findAverageHappinessScoreByWorkKindIdAndUserId(1, 1)).thenReturn(Optional.of(10)); + + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + assertEquals(1, dashboardDTO.getMostVotedWorkKind().getWorkKindId()); + assertEquals("Development", dashboardDTO.getMostVotedWorkKind().getWorkKindName()); + assertEquals(5, dashboardDTO.getMostVotedWorkKind().getVoteCount()); + assertNull(dashboardDTO.getAverageScore()); + assertEquals(10, dashboardDTO.getMostVotedWorkKind().getHappinessScore()); + } + + @Test + void getDashboardDataByUserId_WithHappinessSurveysOnly() { + int userId = 1; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenReturn(List.of()); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(List.of( + new Object[]{1, 5.0}, + new Object[]{2, 10.0} + )); + + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindId()); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindName()); + assertNull(dashboardDTO.getMostVotedWorkKind().getVoteCount()); + assertEquals(8, dashboardDTO.getAverageScore()); + } + + @Test + void getDashboardDataByUserId_InvalidUserId() { + int userId = -1; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenReturn(List.of()); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenReturn(List.of()); + + DashboardDTO dashboardDTO = dashboardService.getDashboardDataByUserId(userId); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindId()); + assertNull(dashboardDTO.getMostVotedWorkKind().getWorkKindName()); + assertNull(dashboardDTO.getMostVotedWorkKind().getVoteCount()); + assertNull(dashboardDTO.getAverageScore()); + assertNull(dashboardDTO.getMostVotedWorkKind().getHappinessScore()); + } + + @Test + void getDashboardDataByUserId_MultipleUsers() { + int userId1 = 1; + int userId2 = 2; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId1)).thenReturn(List.of( + new Object[]{1, 8L}, + new Object[]{2, 3L}, + new Object[]{4, 2L} + )); + when(workKindRepository.findById(1)).thenReturn(Optional.of(new WorkKind(1, "Development", null))); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId1)).thenReturn(List.of( + new Object[]{1, 5.0}, + new Object[]{2, 10.0} + )); + when(workKindSurveyRepository.findAverageHappinessScoreByWorkKindIdAndUserId(1, 1)).thenReturn(Optional.of(10)); + when(workKindSurveyRepository.findWorkKindCountByUserId(userId2)).thenReturn(List.of( + new Object[]{2, 7L}, + new Object[]{7, 3L}, + new Object[]{3, 2L} + )); + when(workKindRepository.findById(2)).thenReturn(Optional.of(new WorkKind(2, "Testing", null))); + when(happinessSurveyRepository.findDailyAveragesByUserId(userId2)).thenReturn(List.of( + new Object[]{1, 3.0}, + new Object[]{2, 7.0} + )); + when(workKindSurveyRepository.findAverageHappinessScoreByWorkKindIdAndUserId(2, 2)).thenReturn(Optional.of(9)); + + DashboardDTO dashboardDTO1 = dashboardService.getDashboardDataByUserId(userId1); + assertEquals(1, dashboardDTO1.getMostVotedWorkKind().getWorkKindId()); + assertEquals("Development", dashboardDTO1.getMostVotedWorkKind().getWorkKindName()); + assertEquals(8, dashboardDTO1.getMostVotedWorkKind().getVoteCount()); + assertEquals(8, dashboardDTO1.getAverageScore()); + assertEquals(10, dashboardDTO1.getMostVotedWorkKind().getHappinessScore()); + + DashboardDTO dashboardDTO2 = dashboardService.getDashboardDataByUserId(userId2); + assertEquals(2, dashboardDTO2.getMostVotedWorkKind().getWorkKindId()); + assertEquals("Testing", dashboardDTO2.getMostVotedWorkKind().getWorkKindName()); + assertEquals(7, dashboardDTO2.getMostVotedWorkKind().getVoteCount()); + assertEquals(5, dashboardDTO2.getAverageScore()); + assertEquals(9, dashboardDTO2.getMostVotedWorkKind().getHappinessScore()); + } + + @Test + void getAverageScoreByUserId_Exception() { + int userId = 1; + when(happinessSurveyRepository.findDailyAveragesByUserId(userId)).thenThrow(new RuntimeException("Database error")); + + YappiException exception = assertThrows(YappiException.class, () -> dashboardService.getAverageScoreByUserId(userId)); + assertEquals("Error calculating average score for user ID: 1", exception.getMessage()); + } + + @Test + void getDashboardDataByUserId_Exception() { + int userId = 1; + when(workKindSurveyRepository.findWorkKindCountByUserId(userId)).thenThrow(new RuntimeException("Database error")); + + YappiException exception = assertThrows(YappiException.class, () -> dashboardService.getDashboardDataByUserId(userId)); + assertEquals("Error fetching dashboard data for user ID: 1", exception.getMessage()); + } +} diff --git a/src/test/java/ch/fhnw/deardevbackend/services/EmotionServiceTest.java b/src/test/java/ch/fhnw/deardevbackend/services/EmotionServiceTest.java new file mode 100644 index 0000000..2429786 --- /dev/null +++ b/src/test/java/ch/fhnw/deardevbackend/services/EmotionServiceTest.java @@ -0,0 +1,53 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.entities.Emotion; +import ch.fhnw.deardevbackend.repositories.EmotionRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +public class EmotionServiceTest { + + @Mock + private EmotionRepository emotionRepository; + + @InjectMocks + private EmotionService emotionService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void getEmotions() { + List emotions = Arrays.asList( + new Emotion(1, "Happy"), + new Emotion(2, "Sad"), + new Emotion(3, "Angry") + ); + when(emotionRepository.findAll()).thenReturn(emotions); + + List result = emotionService.getEmotions(); + + assertEquals(3, result.size()); + assertEquals("Happy", result.get(0).getName()); + assertEquals("Sad", result.get(1).getName()); + assertEquals("Angry", result.get(2).getName()); + } + + @Test + void getEmotions_EmptyList() { + when(emotionRepository.findAll()).thenReturn(List.of()); + + List result = emotionService.getEmotions(); + + assertEquals(0, result.size()); + } +} diff --git a/src/test/java/ch/fhnw/deardevbackend/services/WorkKindServiceTest.java b/src/test/java/ch/fhnw/deardevbackend/services/WorkKindServiceTest.java new file mode 100644 index 0000000..1861c5c --- /dev/null +++ b/src/test/java/ch/fhnw/deardevbackend/services/WorkKindServiceTest.java @@ -0,0 +1,64 @@ +package ch.fhnw.deardevbackend.services; + +import ch.fhnw.deardevbackend.entities.WorkKind; +import ch.fhnw.deardevbackend.repositories.WorkKindRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import java.util.Arrays; +import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class WorkKindServiceTest { + + @Mock + private WorkKindRepository workKindRepository; + + @InjectMocks + private WorkKindService workKindService; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void getWorkKindsForTeams() { + List teamIds = Arrays.asList(1, 2, 3); + List workKinds = Arrays.asList( + new WorkKind(1, "Development", 1), + new WorkKind(2, "Testing", 2), + new WorkKind(3, "Management", null) + ); + when(workKindRepository.findByTeamIdsOrNoTeam(teamIds)).thenReturn(workKinds); + + List result = workKindService.getWorkKindsForTeams(teamIds); + + assertEquals(3, result.size()); + assertEquals("Development", result.get(0).getName()); + assertEquals("Testing", result.get(1).getName()); + assertEquals("Management", result.get(2).getName()); + } + + @Test + void getWorkKindsForTeams_EmptyList() { + List teamIds = Arrays.asList(1, 2, 3); + when(workKindRepository.findByTeamIdsOrNoTeam(teamIds)).thenReturn(List.of()); + + List result = workKindService.getWorkKindsForTeams(teamIds); + + assertEquals(0, result.size()); + } + + @Test + void getWorkKindsForTeams_NullList() { + when(workKindRepository.findByTeamIdsOrNoTeam(null)).thenReturn(List.of()); + + List result = workKindService.getWorkKindsForTeams(null); + + assertEquals(0, result.size()); + } +}