diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ActionController.java b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ActionController.java index 3b3e6616..b2db2d93 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ActionController.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ActionController.java @@ -1,12 +1,15 @@ package com.groupa1.resq.controller; +import com.groupa1.resq.auth.UserDetailsImpl; +import com.groupa1.resq.request.CreateCommentRequest; import com.groupa1.resq.request.CreateActionRequest; +import com.groupa1.resq.dto.ActionDto; import com.groupa1.resq.request.UpdateActionRequest; -import com.groupa1.resq.response.ActionResponse; import com.groupa1.resq.service.ActionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -27,43 +30,68 @@ public class ActionController { @PreAuthorize("hasRole('COORDINATOR')") @PostMapping("/createAction") - public ResponseEntity createAction(@RequestBody CreateActionRequest createActionRequest){ + public ResponseEntity createAction(@RequestBody CreateActionRequest createActionRequest){ return actionService.createAction(createActionRequest); } - @PreAuthorize("hasRole('RESPONDER') or hasRole('COORDINATOR')") - @GetMapping("/viewActions") - public ResponseEntity> viewActions(@RequestParam Long taskId) { - return actionService.viewActions( taskId); + @PreAuthorize("hasRole('COORDINATOR') or hasRole('RESPONDER')") + @GetMapping("/viewSingleAction") + public ResponseEntity viewSingleAction(@RequestParam Long actionId) { + return actionService.viewSingleAction(actionId); } @PreAuthorize("hasRole('COORDINATOR')") - @GetMapping("/updateAction") - public ResponseEntity updateAction(@RequestBody UpdateActionRequest updateActionRequest, @RequestParam Long actionId){ - return actionService.updateAction(actionId, updateActionRequest); + @PostMapping("/deleteAction") + public ResponseEntity deleteAction(@RequestParam Long actionId) { + return actionService.deleteAction(actionId); } @PreAuthorize("hasRole('COORDINATOR')") - @GetMapping("/deleteAction") - public ResponseEntity deleteAction(@RequestParam Long actionId, @RequestParam Long taskId){ - return actionService.deleteAction(actionId, taskId); + @PostMapping("/updateAction") + public ResponseEntity updateAction(@RequestBody + UpdateActionRequest updateActionRequest, @RequestParam Long actionId) { + return actionService.updateAction(updateActionRequest, actionId); } - @PreAuthorize("hasRole('COORDINATOR')") - @GetMapping("/viewActionByFilter") - public ResponseEntity> viewActionByFilter(@RequestParam(required = false) Long taskId, - @RequestParam(required = false) Long verifierId, - @RequestParam(required = false) Boolean isCompleted, - @RequestParam(required = false) Boolean isVerified, - @RequestParam(required = false) LocalDateTime dueDate){ - return actionService.viewActionByFilter(taskId, verifierId, isCompleted, isVerified, dueDate); + + + @PreAuthorize("hasRole('RESPONDER')") + @PostMapping("/completeAction") + public ResponseEntity completeAction(@RequestParam Long actionId, Authentication authentication){ + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + return actionService.completeAction(actionId, userId); + } + @PreAuthorize("hasRole('FACILITATOR')") + @PostMapping("/verifyAction") + public ResponseEntity verifyAction(@RequestParam Long actionId, Authentication authentication){ + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + return actionService.verifyAction(actionId, userId); } @PreAuthorize("hasRole('FACILITATOR')") - @GetMapping("/verifyAction") - public ResponseEntity verifyAction(@RequestParam Long actionId, @RequestParam Long verifierId){ - return actionService.verifyAction(actionId, verifierId); + @PostMapping("/commentAction") + public ResponseEntity verifyAction(@RequestBody + CreateCommentRequest commentActionRequest){ + return actionService.commentAction(commentActionRequest); + } + + @PreAuthorize("hasRole('COORDINATOR') or hasRole('FACILITATOR')") + @GetMapping("/filterAction") + public ResponseEntity> viewActionsByFilter(@RequestParam(required = false) Long verifierId, + @RequestParam(required = false) Boolean isCompleted, + @RequestParam(required = false) LocalDateTime latestDueDate, + @RequestParam(required = false) LocalDateTime earliestDueDate, + @RequestParam(required = false) Long taskId, + @RequestParam(required = false) Boolean isVerified){ + + return actionService.viewActionsByFilter(verifierId, isCompleted, latestDueDate, earliestDueDate, taskId, isVerified); } + + + + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/FeedbackController.java b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/FeedbackController.java new file mode 100644 index 00000000..96c0092f --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/FeedbackController.java @@ -0,0 +1,33 @@ +package com.groupa1.resq.controller; + +import com.groupa1.resq.request.CreateFeedbackRequest; +import com.groupa1.resq.service.FeedbackService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/feedback") +public class FeedbackController { + + @Autowired + private FeedbackService feedbackService; + + @PreAuthorize("hasRole('RESPONDER')") + @PostMapping("/giveFeedback") + public ResponseEntity giveFeedback(CreateFeedbackRequest feedback) { + return feedbackService.giveFeedback(feedback); + } + + + @PreAuthorize("hasRole('RESPONDER')") + @PostMapping("/deleteFeedback") + public ResponseEntity deleteFeedback(@RequestParam Long feedbackId) { + return feedbackService.deleteFeedback(feedbackId); + } + +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/NotificationController.java b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/NotificationController.java index d92c1dd8..2ab95b3a 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/NotificationController.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/NotificationController.java @@ -1,10 +1,8 @@ package com.groupa1.resq.controller; -import com.groupa1.resq.entity.Need; -import com.groupa1.resq.entity.Notification; -import com.groupa1.resq.entity.enums.ENotificationEntityType; import com.groupa1.resq.converter.NotificationConverter; import com.groupa1.resq.dto.NotificationDto; +import com.groupa1.resq.entity.enums.ENotificationEntityType; import com.groupa1.resq.service.NotificationService; import com.groupa1.resq.util.NotificationMessages; import lombok.extern.slf4j.Slf4j; @@ -43,7 +41,8 @@ public NotificationDto viewNotificationById(@RequestParam Long notificationId, @ @PostMapping("/sendNotification") @PreAuthorize("hasRole('ADMIN')") public void sendSystemNotification(@RequestParam String title, @RequestParam Long userId, - @RequestParam Long relatedEntityId, @RequestParam ENotificationEntityType notificationType) { + @RequestParam Long relatedEntityId, @RequestParam + ENotificationEntityType notificationType) { String body = String.format(NotificationMessages.SYSTEM_MESSAGE, userId, relatedEntityId); log.info("Sending notification with title: {}, body: {}, user id: {}, related entity id: {}, notification type: {}" , title, body, userId, relatedEntityId, notificationType); diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ResourceController.java b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ResourceController.java index ea06484a..524bfec5 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ResourceController.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/ResourceController.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.groupa1.resq.dto.ResourceDto; +import com.groupa1.resq.entity.enums.EResourceStatus; import com.groupa1.resq.request.CreateResourceRequest; import com.groupa1.resq.service.ResourceService; import lombok.extern.slf4j.Slf4j; @@ -53,6 +54,7 @@ public ResponseEntity deleteResource(@RequestParam Long resourceId) { } + @PreAuthorize("hasRole('COORDINATOR') or hasRole('VICTIM') or hasRole('RESPONDER')") @GetMapping("/filterByDistance") public ResponseEntity> filterByDistance(@RequestParam @@ -66,9 +68,10 @@ public ResponseEntity> filterByDistance(@RequestParam public ResponseEntity> filterByCategory(@RequestParam(required = false) String categoryTreeId, @RequestParam(required = false) BigDecimal longitude, @RequestParam(required = false) BigDecimal latitude, - @RequestParam(required = false) Long userId) { + @RequestParam(required = false) Long userId, + @RequestParam(required = false) EResourceStatus status){ log.info("Filtering resources by category"); - return resourceService.filterResource(latitude, longitude, categoryTreeId, userId); + return resourceService.filterResource(latitude, longitude, categoryTreeId, userId, status); } @PreAuthorize("hasRole('COORDINATOR') or hasRole('VICTIM') or hasRole('RESPONDER')") diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/TaskController.java b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/TaskController.java index 643e2a9b..6537463d 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/controller/TaskController.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/controller/TaskController.java @@ -1,22 +1,24 @@ package com.groupa1.resq.controller; +import com.groupa1.resq.auth.UserDetailsImpl; import com.groupa1.resq.entity.enums.EStatus; import com.groupa1.resq.entity.enums.EUrgency; +import com.groupa1.resq.request.AddResourceToTaskRequest; import com.groupa1.resq.request.CreateTaskRequest; +import com.groupa1.resq.dto.TaskDto; import com.groupa1.resq.request.UpdateTaskRequest; -import com.groupa1.resq.response.TaskResponse; import com.groupa1.resq.service.TaskService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; - import java.util.List; @RestController @@ -29,35 +31,98 @@ public class TaskController { @PreAuthorize("hasRole('COORDINATOR')") @PostMapping("/createTask") - public ResponseEntity createTask(@RequestBody CreateTaskRequest createTaskRequest) { + public ResponseEntity createTask(@RequestBody CreateTaskRequest createTaskRequest) { return taskService.createTask(createTaskRequest); } @PreAuthorize("hasRole('RESPONDER')") @PostMapping("/acceptTask") - public ResponseEntity acceptTask(@RequestParam Long taskId, @RequestParam Long userId) { + public ResponseEntity acceptTask(@RequestParam Long taskId, Authentication authentication){ + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); return taskService.acceptTask(taskId, userId); } + @PreAuthorize("hasRole('RESPONDER')") + @PostMapping("/declineTask") + public ResponseEntity declineTask(@RequestParam Long taskId, Authentication authentication){ + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + return taskService.declineTask(taskId, userId); + } + @PreAuthorize("hasRole('RESPONDER') or hasRole('COORDINATOR')") + @GetMapping("/viewSingleTask") + public ResponseEntity viewSingleTask(@RequestParam Long taskId) { + return taskService.viewSingleTask(taskId); + } + + @PreAuthorize("hasRole('RESPONDER') or hasRole('COORDINATOR')") @GetMapping("/viewTasks") - public ResponseEntity> viewAllTasks(@RequestParam Long userId) { - return taskService.viewAllTasks(userId); + public ResponseEntity> viewTasks(@RequestParam Long userId) { + return taskService.viewTasks(userId); + } + + @PreAuthorize("hasRole('COORDINATOR')") + @PostMapping("/deleteTask") + public ResponseEntity deleteTask(@RequestParam Long taskId) { + return taskService.deleteTask(taskId); } @PreAuthorize("hasRole('COORDINATOR')") @PostMapping("/updateTask") - public ResponseEntity updateTask(@RequestParam Long taskId, @RequestBody UpdateTaskRequest updateTaskRequest) { - return taskService.updateTask(taskId, updateTaskRequest); + public ResponseEntity updateTask(@RequestBody + UpdateTaskRequest updateTaskRequest, @RequestParam Long taskId){ + return taskService.updateTask(updateTaskRequest, taskId); + } + + @PreAuthorize("hasRole('COORDINATOR')") + @PostMapping("/assignTask") + public ResponseEntity assignTask(@RequestParam Long taskId, @RequestParam Long userId) { + return taskService.assignTask(taskId, userId); + } + + @PreAuthorize("hasRole('COORDINATOR')") + @PostMapping("/unassignTask") + public ResponseEntity unassignTask(@RequestParam Long taskId) { + return taskService.unassignTask(taskId); + } + + + @PreAuthorize("hasRole('RESPONDER')") + @PostMapping("/completeTask") + public ResponseEntity completeTask(@RequestParam Long taskId, Authentication authentication) { + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + return taskService.completeTask(taskId, userId); + } + + @PreAuthorize("hasRole('COORDINATOR')") + @PostMapping("/addResources") + public ResponseEntity addResources(@RequestBody AddResourceToTaskRequest addResourceToTaskRequest, Authentication authentication) { + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + log.info("Adding resources to task by coordinator with id: {}", userId); + return taskService.addResources(addResourceToTaskRequest); + } + + + @PreAuthorize("hasRole('COORDINATOR')") + @PostMapping("/removeResources") + public ResponseEntity removeResources(@RequestBody AddResourceToTaskRequest addResourceToTaskRequest, Authentication authentication) { + UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal(); + Long userId = userDetails.getId(); + log.info("Adding resources to task by coordinator with id: {}", userId); + return taskService.removeResources(addResourceToTaskRequest); } @PreAuthorize("hasRole('COORDINATOR')") @PostMapping("/viewTaskByFilter") - public ResponseEntity> viewTaskByFilter(@RequestParam Long assignerId, @RequestParam Long assigneeId, - @RequestParam EUrgency urgency, @RequestParam EStatus status) { - return taskService.viewTaskByFilter(assignerId, assigneeId, urgency, status); + public ResponseEntity> viewTaskByFilter(@RequestParam(required = false) Long assignerId, @RequestParam(required = false) Long assigneeId, + @RequestParam(required = false) EUrgency urgency, @RequestParam(required = false) EStatus status) { + return taskService.viewTasksByFilter(assignerId, assigneeId, urgency, status); } @@ -66,4 +131,12 @@ public ResponseEntity> viewTaskByFilter(@RequestParam Long as + + + + + + + + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ActionConverter.java b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ActionConverter.java new file mode 100644 index 00000000..5a441a72 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ActionConverter.java @@ -0,0 +1,28 @@ +package com.groupa1.resq.converter; + +import com.groupa1.resq.dto.ActionDto; +import com.groupa1.resq.entity.Action; +import org.springframework.stereotype.Service; + +@Service +public class ActionConverter { + + public ActionDto convertToDto(Action action){ + ActionDto actionDto = new ActionDto(); + actionDto.setId(action.getId()); + actionDto.setTaskId(action.getTask().getId()); + if (action.getVerifier() != null) { + actionDto.setVerifierId(action.getVerifier().getId()); + } + actionDto.setDescription(action.getDescription()); + actionDto.setCompleted(action.isCompleted()); + actionDto.setStartLatitude(action.getStartLatitude()); + actionDto.setStartLongitude(action.getStartLongitude()); + actionDto.setEndLatitude(action.getEndLatitude()); + actionDto.setEndLongitude(action.getEndLongitude()); + actionDto.setDueDate(action.getDueDate()); + actionDto.setCreatedDate(action.getCreatedAt()); + return actionDto; + + } +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/converter/FeedbackConverter.java b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/FeedbackConverter.java new file mode 100644 index 00000000..b3070019 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/FeedbackConverter.java @@ -0,0 +1,19 @@ +package com.groupa1.resq.converter; + +import com.groupa1.resq.dto.FeedbackDto; +import com.groupa1.resq.entity.Feedback; +import org.springframework.stereotype.Service; + +@Service +public class FeedbackConverter { + + public FeedbackDto convertToDto(Feedback feedback){ + FeedbackDto feedbackDto = new FeedbackDto(); + feedbackDto.setId(feedback.getId()); + feedbackDto.setTaskId(feedback.getTask().getId()); + feedbackDto.setUserId(feedback.getCreator().getId()); + feedbackDto.setMessage(feedback.getMessage()); + feedbackDto.setCreatedDate(feedback.getCreatedAt()); + return feedbackDto; + } +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ResourceConverter.java b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ResourceConverter.java index 3d914aa4..762a2997 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ResourceConverter.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/ResourceConverter.java @@ -27,6 +27,10 @@ public ResourceDto convertToDto(Resource resource){ resourceDto.setLatitude(resource.getLatitude()); resourceDto.setLongitude(resource.getLongitude()); resourceDto.setCreatedDate(resource.getCreatedAt()); + if(resource.getStatus() != null){ + resourceDto.setStatus(resource.getStatus()); + } + if (resource.getSize() != null) resourceDto.setSize(resource.getSize()); if (resource.getFile() != null){ diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/converter/TaskConverter.java b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/TaskConverter.java new file mode 100644 index 00000000..13c64c47 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/converter/TaskConverter.java @@ -0,0 +1,48 @@ +package com.groupa1.resq.converter; + +import com.groupa1.resq.dto.ActionDto; +import com.groupa1.resq.dto.FeedbackDto; +import com.groupa1.resq.dto.ResourceDto; +import com.groupa1.resq.dto.TaskDto; +import com.groupa1.resq.entity.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Set; + +@Service +public class TaskConverter { + + @Autowired + private ActionConverter actionConverter; + + @Autowired + private ResourceConverter resourceConverter; + + @Autowired + private FeedbackConverter feedbackConverter; + + + public TaskDto convertToDto(Task task){ + TaskDto taskDto = new TaskDto(); + taskDto.setId(task.getId()); + taskDto.setAssignee(task.getAssignee().getId()); + taskDto.setAssigner(task.getAssigner().getId()); + taskDto.setStatus(task.getStatus()); + Set taskActions = new HashSet<>(task.getActions().stream() + .map(action -> actionConverter.convertToDto(action)).toList()); + taskDto.setActions(taskActions); + taskDto.setDescription(task.getDescription()); + Set taskResources = new HashSet<>(task.getResources().stream() + .map(resource ->resourceConverter.convertToDto(resource)).toList()); + taskDto.setResources(taskResources); + Set taskFeedbacks = new HashSet<>(task.getFeedbacks().stream() + .map(feedback ->feedbackConverter.convertToDto(feedback)).toList()); + taskDto.setFeedbacks(taskFeedbacks); + taskDto.setUrgency(task.getUrgency()); + taskDto.setCreatedDate(task.getCreatedAt()); + return taskDto; + + } +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/response/ActionResponse.java b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ActionDto.java similarity index 81% rename from resq/backend/resq/src/main/java/com/groupa1/resq/response/ActionResponse.java rename to resq/backend/resq/src/main/java/com/groupa1/resq/dto/ActionDto.java index e7061f3a..fea75201 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/response/ActionResponse.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ActionDto.java @@ -1,4 +1,4 @@ -package com.groupa1.resq.response; +package com.groupa1.resq.dto; import lombok.Data; import lombok.experimental.Accessors; @@ -8,9 +8,9 @@ @Data @Accessors(chain = true) -public class ActionResponse { - private long id; - private long taskId; +public class ActionDto { + private Long id; + private Long taskId; private long verifierId; private String description; private boolean isCompleted; diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/response/FeedbackResponse.java b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/FeedbackDto.java similarity index 60% rename from resq/backend/resq/src/main/java/com/groupa1/resq/response/FeedbackResponse.java rename to resq/backend/resq/src/main/java/com/groupa1/resq/dto/FeedbackDto.java index 4b7493c0..73624965 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/response/FeedbackResponse.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/FeedbackDto.java @@ -1,15 +1,18 @@ -package com.groupa1.resq.response; +package com.groupa1.resq.dto; import lombok.Data; import lombok.experimental.Accessors; +import java.time.LocalDateTime; + @Data @Accessors(chain = true) -public class FeedbackResponse { +public class FeedbackDto { private long id; private long taskId; private long userId; private String message; + private LocalDateTime createdDate; diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ResourceDto.java b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ResourceDto.java index 2359ebee..867379f6 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ResourceDto.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/ResourceDto.java @@ -2,12 +2,16 @@ import com.groupa1.resq.entity.enums.EGender; import com.groupa1.resq.entity.enums.ESize; +import com.groupa1.resq.entity.enums.EResourceStatus; +import jakarta.persistence.Access; import lombok.Data; +import lombok.experimental.Accessors; import java.math.BigDecimal; import java.time.LocalDateTime; @Data +@Accessors(chain = true) public class ResourceDto { private Long id; private Long senderId; @@ -20,4 +24,5 @@ public class ResourceDto { private LocalDateTime createdDate; private ESize size; private FileDto file; + private EResourceStatus status; } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/response/TaskResponse.java b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/TaskDto.java similarity index 60% rename from resq/backend/resq/src/main/java/com/groupa1/resq/response/TaskResponse.java rename to resq/backend/resq/src/main/java/com/groupa1/resq/dto/TaskDto.java index a7a91608..5c62d1da 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/response/TaskResponse.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/dto/TaskDto.java @@ -1,23 +1,25 @@ -package com.groupa1.resq.response; +package com.groupa1.resq.dto; import com.groupa1.resq.entity.enums.EStatus; import com.groupa1.resq.entity.enums.EUrgency; import lombok.Data; import lombok.experimental.Accessors; +import java.time.LocalDateTime; import java.util.Set; @Data @Accessors(chain = true) -public class TaskResponse { +public class TaskDto { private long id; private long assignee; private long assigner; - private Set actions; + private Set actions; private String description; - private Set resources; - private Set feedbacks; + private Set resources; + private Set feedbacks; private EUrgency urgency; private EStatus status; + private LocalDateTime createdDate; } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Action.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Action.java index 6db223a8..3371f5e6 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Action.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Action.java @@ -8,6 +8,7 @@ import java.math.BigDecimal; import java.time.LocalDateTime; +import java.util.Set; @NoArgsConstructor @Entity @@ -38,5 +39,8 @@ public class Action extends BaseEntity { private BigDecimal endLatitude; private BigDecimal endLongitude; + @OneToMany(fetch = FetchType.LAZY, mappedBy="action") + private Set comments; + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Comment.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Comment.java new file mode 100644 index 00000000..b66e43d4 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Comment.java @@ -0,0 +1,30 @@ +package com.groupa1.resq.entity; + + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@Table(name = "COMMENT") +@Data +public class Comment extends BaseEntity{ + + @ManyToOne + @JoinColumn(name = "action_id") + private Action action; + + @ManyToOne + @JoinColumn(name = "creator_id") + private User verifier; + + @Lob + @Column(length = 3000) + private String comment; +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Resource.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Resource.java index 690106c3..b1f0c262 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Resource.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Resource.java @@ -1,6 +1,7 @@ package com.groupa1.resq.entity; import com.groupa1.resq.entity.enums.EGender; +import com.groupa1.resq.entity.enums.EResourceStatus; import com.groupa1.resq.entity.enums.ESize; import jakarta.persistence.*; import lombok.Data; @@ -26,6 +27,10 @@ public class Resource extends BaseEntity { @JoinColumn(name = "receiver_id") private User receiver; + @ManyToOne + @JoinColumn(name = "task_id") + private Task task; + private String categoryTreeId; private EGender gender; @@ -43,4 +48,5 @@ public class Resource extends BaseEntity { @JoinColumn(name = "file_id", referencedColumnName = "id") private File file; + private EResourceStatus status; } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Task.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Task.java index 3c762b35..9a30b83e 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Task.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/Task.java @@ -25,10 +25,10 @@ public class Task extends BaseEntity { @JoinColumn(name = "assigner_id") private User assigner; - @OneToMany(fetch = FetchType.LAZY, mappedBy="task") + @OneToMany(fetch = FetchType.LAZY, mappedBy="task", cascade = CascadeType.ALL) private Set actions; - @OneToMany(fetch = FetchType.LAZY) + @OneToMany(fetch = FetchType.LAZY, mappedBy = "task", cascade = CascadeType.ALL) private Set resources; @OneToMany(fetch = FetchType.LAZY, mappedBy="task") diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EResourceStatus.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EResourceStatus.java new file mode 100644 index 00000000..53b01585 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EResourceStatus.java @@ -0,0 +1,8 @@ +package com.groupa1.resq.entity.enums; + +public enum EResourceStatus { + IN_TASK, + DELIVERED, + CANCELLED, + AVAILABLE +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EStatus.java b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EStatus.java index 96045304..2a54f069 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EStatus.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/entity/enums/EStatus.java @@ -1,8 +1,10 @@ package com.groupa1.resq.entity.enums; public enum EStatus { - TODO, + TODO, // accepted IN_PROGRESS, DONE, - PENDING + PENDING, //responder assigned, but not yet accepted + DECLINED, + FREE // no responder assigned } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/exception/GeneralExceptionHandler.java b/resq/backend/resq/src/main/java/com/groupa1/resq/exception/GeneralExceptionHandler.java index fa0e4f3e..b41397ad 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/exception/GeneralExceptionHandler.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/exception/GeneralExceptionHandler.java @@ -14,13 +14,11 @@ public class GeneralExceptionHandler extends ResponseEntityExceptionHandler { // Each kind of exception should be registered here in general exception handler so that Spring handles them. @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity entityNotFoundException(EntityNotFoundException exception) { - log.error(exception.getMessage()); return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND); } @ExceptionHandler(NotOwnerException.class) public ResponseEntity nowOwnerException(NotOwnerException exception) { - log.error(exception.getMessage()); return new ResponseEntity<>(exception.getMessage(), HttpStatus.UNAUTHORIZED); } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/repository/ActionRepository.java b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/ActionRepository.java index df7292e2..05585882 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/repository/ActionRepository.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/ActionRepository.java @@ -9,4 +9,5 @@ public interface ActionRepository extends JpaRepository { List findAll(Specification specification); + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/repository/FeedbackRepository.java b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/FeedbackRepository.java new file mode 100644 index 00000000..3c29d46a --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/FeedbackRepository.java @@ -0,0 +1,9 @@ +package com.groupa1.resq.repository; + +import com.groupa1.resq.entity.Feedback; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface FeedbackRepository extends JpaRepository { +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/repository/TaskRepository.java b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/TaskRepository.java index ea592510..4d9e2359 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/repository/TaskRepository.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/repository/TaskRepository.java @@ -1,6 +1,5 @@ package com.groupa1.resq.repository; -import com.groupa1.resq.entity.Action; import com.groupa1.resq.entity.Task; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/AddResourceToTaskRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/AddResourceToTaskRequest.java new file mode 100644 index 00000000..afb72bea --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/AddResourceToTaskRequest.java @@ -0,0 +1,14 @@ +package com.groupa1.resq.request; + +import lombok.Data; + +import java.util.List; + +@Data +public class AddResourceToTaskRequest { + private Long taskId; + private Long receiverId; + private List resourceIds; + + +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateActionRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateActionRequest.java index 6b8157d6..408a7c1c 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateActionRequest.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateActionRequest.java @@ -5,6 +5,7 @@ import java.math.BigDecimal; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; @Data @@ -14,6 +15,7 @@ public class CreateActionRequest { private Long verifierId; private String description; private boolean isCompleted; + private boolean isVerified; private BigDecimal startLatitude; private BigDecimal startLongitude; @@ -22,5 +24,4 @@ public class CreateActionRequest { private LocalDateTime dueDate; - } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateCommentRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateCommentRequest.java new file mode 100644 index 00000000..0a551fb8 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateCommentRequest.java @@ -0,0 +1,10 @@ +package com.groupa1.resq.request; + +import lombok.Data; + +@Data +public class CreateCommentRequest { + private Long userId; + private Long actionId; + private String comment; +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateFeedbackRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateFeedbackRequest.java new file mode 100644 index 00000000..011626f3 --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateFeedbackRequest.java @@ -0,0 +1,10 @@ +package com.groupa1.resq.request; + +import lombok.Data; + +@Data +public class CreateFeedbackRequest { + private Long userId; + private Long taskId; + private String message; +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateResourceRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateResourceRequest.java index a8b3ffc1..bc1946f1 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateResourceRequest.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateResourceRequest.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.groupa1.resq.entity.enums.EGender; import com.groupa1.resq.entity.enums.ESize; +import com.groupa1.resq.entity.enums.EResourceStatus; import lombok.Data; import org.springframework.lang.Nullable; import org.springframework.web.multipart.MultipartFile; @@ -19,4 +20,5 @@ public class CreateResourceRequest { private BigDecimal longitude; private EGender gender; private ESize size; + private EResourceStatus status; // This field is only for resource update, default is AVAILABLE } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateTaskRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateTaskRequest.java index 14d89ecc..306493c4 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateTaskRequest.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/CreateTaskRequest.java @@ -8,6 +8,7 @@ import lombok.Data; import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.List; @JsonInclude(JsonInclude.Include.NON_NULL) @@ -16,33 +17,7 @@ public class CreateTaskRequest { private Long assignerId; private Long assigneeId; private String description; - private List actions; - private List resources; private EUrgency urgency; - private EStatus status; - - @Data - public static class Action { - private Long verifierId; - private String description; - private BigDecimal startLatitude; - private BigDecimal startLongitude; - private BigDecimal endLatitude; - private BigDecimal endLongitude; - } - - @Data - public static class Resource { - private Long senderId; - private String categoryTreeId; - private EGender gender; - private Integer quantity; - private BigDecimal latitude; - private BigDecimal longitude; - - - } - } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateActionRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateActionRequest.java index 9ebce324..e5af8728 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateActionRequest.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateActionRequest.java @@ -10,7 +10,6 @@ @Data @JsonInclude(JsonInclude.Include.NON_NULL) public class UpdateActionRequest { - private Long taskId; private Long verifierId; private String description; private boolean isCompleted; diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateTaskRequest.java b/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateTaskRequest.java index d70cdd07..f9f34251 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateTaskRequest.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/request/UpdateTaskRequest.java @@ -1,50 +1,12 @@ package com.groupa1.resq.request; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.groupa1.resq.entity.enums.EGender; import com.groupa1.resq.entity.enums.EStatus; import com.groupa1.resq.entity.enums.EUrgency; import lombok.Data; -import java.math.BigDecimal; -import java.util.List; - -@JsonInclude(JsonInclude.Include.NON_NULL) @Data public class UpdateTaskRequest { - private Long assignerId; - private Long assigneeId; private String description; - private List actions; - private List resources; private EUrgency urgency; private EStatus status; - - @Data - public static class Action { - private Long verifierId; - private String description; - private BigDecimal startLatitude; - private BigDecimal startLongitude; - private BigDecimal endLatitude; - private BigDecimal endLongitude; - private boolean isCompleted; - private boolean isVerified; - } - - @Data - public static class Resource { - private Long senderId; - private String categoryTreeId; - private EGender gender; - private Integer quantity; - private BigDecimal latitude; - private BigDecimal longitude; - - - } - - } - diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/service/ActionService.java b/resq/backend/resq/src/main/java/com/groupa1/resq/service/ActionService.java index 091cbd88..20d8a62f 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/service/ActionService.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/service/ActionService.java @@ -1,17 +1,24 @@ package com.groupa1.resq.service; -import com.fasterxml.jackson.databind.deser.std.NumberDeserializers; + +import com.groupa1.resq.converter.ActionConverter; import com.groupa1.resq.entity.Action; -import com.groupa1.resq.entity.Need; +import com.groupa1.resq.entity.Comment; import com.groupa1.resq.entity.Task; import com.groupa1.resq.entity.User; +import com.groupa1.resq.entity.enums.ENotificationEntityType; import com.groupa1.resq.exception.EntityNotFoundException; +import com.groupa1.resq.exception.NotOwnerException; import com.groupa1.resq.repository.ActionRepository; import com.groupa1.resq.repository.TaskRepository; import com.groupa1.resq.repository.UserRepository; +import com.groupa1.resq.request.CreateCommentRequest; import com.groupa1.resq.request.CreateActionRequest; import com.groupa1.resq.request.UpdateActionRequest; -import com.groupa1.resq.response.ActionResponse; +import com.groupa1.resq.dto.ActionDto; +import com.groupa1.resq.specification.ActionSpecifications; +import com.groupa1.resq.util.NotificationMessages; +import jakarta.transaction.Transactional; import com.groupa1.resq.specification.ActionSpecifications; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -38,8 +45,16 @@ public class ActionService { @Autowired private UserRepository userRepository; + @Autowired + private NotificationService notificationService; + + @Autowired + private ActionConverter actionConverter; + + - public ResponseEntity createAction(CreateActionRequest createActionRequest) { + + public ResponseEntity createAction(CreateActionRequest createActionRequest) { if (!taskRepository.existsById(createActionRequest.getTaskId())){ return ResponseEntity.badRequest().body("No task found"); } @@ -69,146 +84,164 @@ public ResponseEntity createAction(CreateActionRequest createActionReque taskRepository.save(task); actionEntity.setTask(task); + return ResponseEntity.ok(actionRepository.save(actionEntity).getId()); - actionRepository.save(actionEntity); - return ResponseEntity.ok("Action saved successfully!"); } - public ResponseEntity> viewActions(Long taskId){ - Optional task = taskRepository.findById(taskId); - List actionResponses = new ArrayList<>(); - if (task.isPresent()) { - Set actions = task.get().getActions(); - actions.forEach(action -> { - ActionResponse actionResponse = new ActionResponse(); - actionResponse.setId(action.getId()) - .setTaskId(action.getTask().getId()) - .setVerifierId(action.getVerifier().getId()) - .setDescription(action.getDescription()) - .setCompleted(action.isCompleted()) - .setStartLatitude(action.getStartLatitude()) - .setStartLongitude(action.getStartLongitude()) - .setEndLatitude(action.getEndLatitude()) - .setEndLongitude(action.getEndLongitude()) - .setDueDate(action.getDueDate()) - .setCreatedDate(action.getCreatedAt()); - actionResponses.add(actionResponse); - }); - return ResponseEntity.ok(actionResponses); - } else { - log.error("No task found with id: {}", taskId); - return null; +public ResponseEntity viewSingleAction(Long actionId){ + Action action = actionRepository.findById(actionId).orElse(null); + if (action == null){ + log.error("No action found with id: {}", actionId); + throw new EntityNotFoundException("No action found"); } + return ResponseEntity.ok(actionConverter.convertToDto(action)); } - public ResponseEntity updateAction(Long actionId, UpdateActionRequest updateActionRequest) { - Optional action = actionRepository.findById(actionId); - if (action.isPresent()) { - Action actionEntity = action.get(); - User verifier = userRepository.findById(updateActionRequest.getVerifierId()).orElseThrow(()-> new EntityNotFoundException("No user found")); - String description = updateActionRequest.getDescription(); - LocalDateTime dueDate = updateActionRequest.getDueDate(); - BigDecimal startLatitude = updateActionRequest.getStartLatitude(); - BigDecimal startLongitude =updateActionRequest.getStartLongitude(); - BigDecimal endLatitude = updateActionRequest.getEndLatitude(); - BigDecimal endLongitude = updateActionRequest.getEndLongitude(); - boolean isCompleted = updateActionRequest.isCompleted(); - boolean isVerified = updateActionRequest.isVerified(); - actionEntity.setVerifier(verifier); - actionEntity.setCompleted(isCompleted); - actionEntity.setVerified(isVerified); - actionEntity.setDescription(description); - actionEntity.setDueDate(dueDate); - actionEntity.setStartLatitude(startLatitude); - actionEntity.setStartLongitude(startLongitude); - actionEntity.setEndLatitude(endLatitude); - actionEntity.setEndLongitude(endLongitude); - actionRepository.save(actionEntity); - return ResponseEntity.ok("Action updated successfully!"); - } else { + @Transactional + public ResponseEntity deleteAction(Long actionId){ + Action action = actionRepository.findById(actionId).orElse(null); + if (action == null){ log.error("No action found with id: {}", actionId); - return null; + throw new EntityNotFoundException("No action found"); } + actionRepository.delete(action); + return ResponseEntity.ok("Action deleted successfully"); + } + + @Transactional + public ResponseEntity updateAction(UpdateActionRequest updateActionRequest, Long actionId){ + Action action = actionRepository.findById(actionId).orElseThrow(()-> new EntityNotFoundException("No action found")); + User verifier = userRepository.findById(updateActionRequest.getVerifierId()).orElseThrow(()-> new EntityNotFoundException("No user found")); + action.setVerifier(verifier); + action.setDescription(updateActionRequest.getDescription()); + action.setCompleted(updateActionRequest.isCompleted()); + action.setVerified(updateActionRequest.isVerified()); + action.setStartLatitude(updateActionRequest.getStartLatitude()); + action.setStartLongitude(updateActionRequest.getStartLongitude()); + action.setEndLatitude(updateActionRequest.getEndLatitude()); + action.setEndLongitude(updateActionRequest.getEndLongitude()); + action.setDueDate(updateActionRequest.getDueDate()); + + actionRepository.save(action); + return ResponseEntity.ok("Action updated successfully"); + } - public ResponseEntity deleteAction(Long actionId, Long taskId) { - Optional action = actionRepository.findById(actionId); - if (action.isPresent()) { - Action actionEntity = action.get(); - Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); - task.getActions().remove(actionEntity); - taskRepository.save(task); - actionRepository.deleteById(actionId); - return ResponseEntity.ok("Action deleted successfully!"); - } else { + // acted one completes the action + @Transactional + public ResponseEntity completeAction(Long actionId, Long userId){ + Action action = actionRepository.findById(actionId).orElse(null); + User user = userRepository.findById(userId).orElse(null); + if (action == null){ + log.error("No action found with id: {}", actionId); + throw new EntityNotFoundException("No action found"); + } + if (user == null){ + log.error("No user found with id: {}", userId); + throw new EntityNotFoundException("No user found"); + } + if (action.getTask().getAssignee().getId() != userId) { + log.error( + "User with id: {} is not the assignee for action with id: {}", + userId, actionId); + throw new NotOwnerException("User is not the assignee for action"); + } + action.setCompleted(true); + actionRepository.save(action); + + String bodyMessage = String.format(NotificationMessages.ACTION_WAITING_FOR_VERIFICATION, action.getId()); + notificationService.sendNotification("Request Created", bodyMessage, action.getVerifier().getId(), action.getId() , ENotificationEntityType.RESOURCE); + + + return ResponseEntity.ok("Action completed by responder"); + + + + } + + @Transactional + public ResponseEntity verifyAction(Long actionId, Long userId) { + Action action = actionRepository.findById(actionId).orElse(null); + User user = userRepository.findById(userId).orElse(null); + + if (action == null) { log.error("No action found with id: {}", actionId); - return null; + throw new EntityNotFoundException("No action found"); } + if (user == null) { + log.error("No user found with id: {}", userId); + throw new EntityNotFoundException("No user found"); + } + if (action.getVerifier() == null) { + log.error("No verifier found for action with id: {}", actionId); + throw new EntityNotFoundException("No verifier found for action"); + } + if (action.getVerifier().getId() != userId) { + log.error( + "User with id: {} is not the verifier for action with id: {}", + userId, actionId); + throw new NotOwnerException("User is not the verifier for action"); + } + action.setVerified(true); + actionRepository.save(action); + String bodyMessage = String.format(NotificationMessages.ACTION_VERIFIED, action.getId(), action.getVerifier().getId()); + notificationService.sendNotification("New Task Assigned", bodyMessage, user.getId(), action.getId(), ENotificationEntityType.TASK); + return ResponseEntity.ok("Action verified by facilitator"); } - public ResponseEntity> viewActionByFilter(Long taskId, Long verifierId, Boolean isCompleted, Boolean isVerified, LocalDateTime dueDate){ + public ResponseEntity commentAction( + CreateCommentRequest commentActionRequest) { + User user = userRepository.findById(commentActionRequest.getUserId()).orElse(null); + Action action = actionRepository.findById(commentActionRequest.getActionId()).orElse(null); + User verifier = action.getVerifier(); + if(action == null || user == null || verifier == null){ + log.error("Action, user, or action verifier is not found"); + throw new EntityNotFoundException("No action found"); + } + if (user.getId() != verifier.getId()){ + log.error("User with id: {} is not the verifier for action with id: {}", + user.getId(), action.getId()); + throw new NotOwnerException("User is not the verifier for action"); + } + Comment comment = new Comment(); + comment.setVerifier(user); + comment.setAction(action); + comment.setComment(commentActionRequest.getComment()); + action.getComments().add(comment); + return ResponseEntity.ok("Comment added successfully"); + + } + + public ResponseEntity> viewActionsByFilter(Long verifierId, Boolean isCompleted, LocalDateTime latestDueDate, LocalDateTime earliestDueDate, Long taskId, Boolean isVerified){ Specification spec = Specification.where(null); - if (taskId != null) { - spec = spec.and(ActionSpecifications.hasTaskId(taskId)); - } if (verifierId != null) { - spec = spec.and(ActionSpecifications.hasVerifierId(verifierId)); + spec = spec.and(ActionSpecifications.hasVerifier(verifierId)); } + if (isCompleted != null) { spec = spec.and(ActionSpecifications.hasCompleted(isCompleted)); } - if (isVerified != null) { - spec = spec.and(ActionSpecifications.hasVerified(isVerified)); - } - if (dueDate != null) { - spec = spec.and(ActionSpecifications.hasDueDate(dueDate)); - } - - List actionResponses = new ArrayList<>(); - List actions = actionRepository.findAll(spec); - actions.forEach(action -> { - ActionResponse actionResponse = new ActionResponse(); - actionResponse.setId(action.getId()) - .setTaskId(action.getTask().getId()) - .setVerifierId(action.getVerifier().getId()) - .setDescription(action.getDescription()) - .setCompleted(action.isCompleted()) - .setStartLatitude(action.getStartLatitude()) - .setStartLongitude(action.getStartLongitude()) - .setEndLatitude(action.getEndLatitude()) - .setEndLongitude(action.getEndLongitude()) - .setDueDate(action.getDueDate()) - .setCreatedDate(action.getCreatedAt()); - actionResponses.add(actionResponse); - }); - - return ResponseEntity.ok(actionResponses); - } - public ResponseEntity verifyAction(Long actionId, Long verifierId) { - Optional action = actionRepository.findById(actionId); - if (action.isPresent()) { - Action actionEntity = action.get(); - User verifier = userRepository.findById(verifierId).orElseThrow(() -> new EntityNotFoundException("No user found")); - if (actionEntity.getVerifier().getId() != verifier.getId()) { - return ResponseEntity.badRequest().body("User is not the verifier of the action"); - } else { - actionEntity.setVerified(true); - actionRepository.save(actionEntity); - return ResponseEntity.ok("Action verified successfully!"); - } - } else { - log.error("No action found with id: {}", actionId); - return null; + if (latestDueDate != null) { + spec = spec.and(ActionSpecifications.hasLatestDueDate(latestDueDate)); } - } - + if (earliestDueDate != null) { + spec = spec.and(ActionSpecifications.hasEarliestDueDate(earliestDueDate)); + } + if (taskId != null) { + spec = spec.and(ActionSpecifications.hasTaskId(taskId)); + } + if (isVerified != null){ + spec = spec.and(ActionSpecifications.hasVerified(isVerified)); + } + return ResponseEntity.ok(actionRepository.findAll(spec).stream().map(action -> actionConverter.convertToDto(action)).toList()); + } } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/service/FeedbackService.java b/resq/backend/resq/src/main/java/com/groupa1/resq/service/FeedbackService.java new file mode 100644 index 00000000..cf64745c --- /dev/null +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/service/FeedbackService.java @@ -0,0 +1,56 @@ +package com.groupa1.resq.service; + +import com.groupa1.resq.dto.FeedbackDto; +import com.groupa1.resq.entity.Feedback; +import com.groupa1.resq.entity.Task; +import com.groupa1.resq.entity.User; +import com.groupa1.resq.exception.EntityNotFoundException; +import com.groupa1.resq.repository.FeedbackRepository; +import com.groupa1.resq.repository.TaskRepository; +import com.groupa1.resq.repository.UserRepository; +import com.groupa1.resq.request.CreateFeedbackRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +@Service +public class FeedbackService { + + @Autowired + private TaskRepository taskRepository; + + + @Autowired + private UserRepository userRepository; + + @Autowired + private FeedbackRepository feedbackRepository; + + + + public ResponseEntity giveFeedback( + CreateFeedbackRequest feedbackRequest){ + Task task = taskRepository.findById(feedbackRequest.getTaskId()).orElseThrow(()-> new EntityNotFoundException("No task found")); + User user = userRepository.findById(feedbackRequest.getUserId()).orElseThrow(()-> new EntityNotFoundException("No user found")); + Feedback feedback = new Feedback(); + feedback.setCreator(user); + feedback.setMessage(feedbackRequest.getMessage()); + feedback.setTask(task); + task.getFeedbacks().add(feedback); + // TODO: send notification to coordinator + taskRepository.save(task); + return ResponseEntity.ok("Feedback saved successfully"); + } + public ResponseEntity deleteFeedback(Long feedbackId){ + Feedback feedback = feedbackRepository.findById(feedbackId).orElse(null); + if(feedback == null){ + return ResponseEntity.badRequest().body("No feedback found"); + } + feedbackRepository.delete(feedback); + return ResponseEntity.ok("Feedback deleted successfully"); + + } + + + +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/service/ResourceService.java b/resq/backend/resq/src/main/java/com/groupa1/resq/service/ResourceService.java index 765aa68c..bc1fdabe 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/service/ResourceService.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/service/ResourceService.java @@ -6,6 +6,7 @@ import com.groupa1.resq.entity.File; import com.groupa1.resq.entity.Resource; import com.groupa1.resq.entity.User; +import com.groupa1.resq.entity.enums.EResourceStatus; import com.groupa1.resq.exception.EntityNotFoundException; import com.groupa1.resq.repository.ResourceRepository; import com.groupa1.resq.request.CreateResourceRequest; @@ -66,6 +67,8 @@ public ResponseEntity createResource(CreateResourceRequest createResourc fileEntity.setResource(resource); resource.setFile(fileEntity); } + resource.setStatus(EResourceStatus.AVAILABLE); // default + resource.setGender(createResourceRequest.getGender()); Long resourceId = resourceRepository.save(resource).getId(); return ResponseEntity.ok(resourceId); } @@ -77,6 +80,7 @@ public ResponseEntity updateResource(CreateResourceRequest createResourc resource.setLongitude(createResourceRequest.getLongitude()); resource.setCategoryTreeId(createResourceRequest.getCategoryTreeId()); resource.setSize(createResourceRequest.getSize()); + resource.setStatus(createResourceRequest.getStatus()); resourceRepository.save(resource); return ResponseEntity.ok("Resource updated successfully"); } @@ -90,7 +94,7 @@ public ResponseEntity deleteResource(Long resourceId){ return ResponseEntity.ok("Resource deleted successfully"); } - public ResponseEntity> filterResource(BigDecimal latitude, BigDecimal longitude, String categoryTreeId, Long userId){ + public ResponseEntity> filterResource(BigDecimal latitude, BigDecimal longitude, String categoryTreeId, Long userId, EResourceStatus status){ Specification spec = Specification.where(null); if (longitude != null && latitude != null) { @@ -105,6 +109,9 @@ public ResponseEntity> filterResource(BigDecimal latitude, Big if (userId != null) { spec = spec.and(ResourceSpecifications.hasOwnerId(userId)); } + if (status != null){ + spec = spec.and(ResourceSpecifications.hasStatus(status)); + } return ResponseEntity.ok(resourceRepository.findAll(spec).stream().map(resource -> resourceConverter.convertToDto(resource)).toList()); } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/service/TaskService.java b/resq/backend/resq/src/main/java/com/groupa1/resq/service/TaskService.java index 560795c4..8352f5b3 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/service/TaskService.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/service/TaskService.java @@ -1,22 +1,26 @@ package com.groupa1.resq.service; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.groupa1.resq.converter.ActionConverter; +import com.groupa1.resq.converter.TaskConverter; +import com.groupa1.resq.dto.ResourceDto; import com.groupa1.resq.entity.Action; import com.groupa1.resq.entity.Resource; import com.groupa1.resq.entity.Task; import com.groupa1.resq.entity.User; -import com.groupa1.resq.entity.enums.EGender; import com.groupa1.resq.entity.enums.ENotificationEntityType; +import com.groupa1.resq.entity.enums.EResourceStatus; import com.groupa1.resq.entity.enums.EStatus; import com.groupa1.resq.entity.enums.EUrgency; +import com.groupa1.resq.exception.EntityNotFoundException; import com.groupa1.resq.repository.ActionRepository; +import com.groupa1.resq.repository.ResourceRepository; import com.groupa1.resq.repository.TaskRepository; import com.groupa1.resq.repository.UserRepository; +import com.groupa1.resq.request.AddResourceToTaskRequest; import com.groupa1.resq.request.CreateTaskRequest; +import com.groupa1.resq.dto.TaskDto; import com.groupa1.resq.request.UpdateTaskRequest; -import com.groupa1.resq.response.ActionResponse; -import com.groupa1.resq.response.FeedbackResponse; -import com.groupa1.resq.response.ResourceResponse; -import com.groupa1.resq.response.TaskResponse; import com.groupa1.resq.specification.TaskSpecifications; import com.groupa1.resq.util.NotificationMessages; import jakarta.transaction.Transactional; @@ -25,8 +29,7 @@ import org.springframework.data.jpa.domain.Specification; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; - -import java.math.BigDecimal; +import org.springframework.web.bind.annotation.RequestParam; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashSet; @@ -54,59 +57,31 @@ public class TaskService { @Autowired NotificationService notificationService; + + @Autowired + private TaskConverter taskConverter; + + @Autowired + private ResourceRepository resourceRepository; + @Transactional - public ResponseEntity createTask(CreateTaskRequest createTaskRequest) { + public ResponseEntity createTask(CreateTaskRequest createTaskRequest) { if (createTaskRequest.getAssigneeId() == null || createTaskRequest.getAssignerId() == null) { return ResponseEntity.badRequest().body("Assignee and assigner must be specified"); } - User assignee = userService.findById(createTaskRequest.getAssigneeId()); User assigner = userService.findById(createTaskRequest.getAssignerId()); + User assignee = userService.findById(createTaskRequest.getAssigneeId()); + +// if (!assignee.getRoles().contains("RESPONDER")){ +// return ResponseEntity.badRequest().body("Assignee must be a responder"); +// } List actionEntities = new ArrayList<>(); List resourceEntities = new ArrayList<>(); EUrgency urgency = createTaskRequest.getUrgency(); - EStatus status = EStatus.PENDING; // thought the default should be like pending + EStatus status = EStatus.PENDING; String description = createTaskRequest.getDescription(); - createTaskRequest.getActions().forEach(action -> { - Action actionEntity = new Action(); - User verifier = userService.findById(action.getVerifierId()); - String actionDescription = action.getDescription(); - BigDecimal startLatitude = action.getStartLatitude(); - BigDecimal startLongitude = action.getStartLongitude(); - BigDecimal endLatitude = action.getEndLatitude(); - BigDecimal endLongitude = action.getEndLongitude(); - actionEntity.setVerifier(verifier); - actionEntity.setDescription(actionDescription); - actionEntity.setCompleted(false); - actionEntity.setStartLatitude(startLatitude); - actionEntity.setStartLongitude(startLongitude); - actionEntity.setEndLatitude(endLatitude); - actionEntity.setEndLongitude(endLongitude); - actionEntity.setCreatedAt(LocalDateTime.now()); - actionEntities.add(actionEntity); - - }); - - createTaskRequest.getResources().forEach( resource -> { - Resource resourceEntity = new Resource(); - User owner = userService.findById(resource.getSenderId()); - String categoryTreeId = resource.getCategoryTreeId();// I think this should a different calculation - EGender gender = resource.getGender(); - Integer quantity = resource.getQuantity(); - BigDecimal latitude = resource.getLatitude(); - BigDecimal longitude = resource.getLongitude(); - resourceEntity.setSender(owner); - resourceEntity.setCategoryTreeId(categoryTreeId); - resourceEntity.setQuantity(quantity); - resourceEntity.setGender(gender); - resourceEntity.setLatitude(latitude); - resourceEntity.setLongitude(longitude); - resourceEntity.setCreatedAt(LocalDateTime.now()); - resourceEntities.add(resourceEntity); - } - ); - Task task = new Task(); task.setAssignee(assignee); task.setAssigner(assigner); @@ -116,17 +91,14 @@ public ResponseEntity createTask(CreateTaskRequest createTaskRequest) { task.setActions(new HashSet<>(actionEntities)); task.setResources(new HashSet<>(resourceEntities)); task.setCreatedAt(LocalDateTime.now()); - - Task savedTask = taskRepository.save(task); - actionEntities.forEach(action -> { - action.setTask(savedTask); - }); - actionRepository.saveAll(actionEntities); + task.setModifiedAt(LocalDateTime.now()); + actionRepository.saveAll(actionEntities); + Task savedTask = taskRepository.save(task); String bodyMessage = String.format(NotificationMessages.TASK_ASSIGNED, assigner.getId(), task.getId()); notificationService.sendNotification("New Task Assigned", bodyMessage, assignee.getId(), task.getId(), ENotificationEntityType.TASK); - return ResponseEntity.ok("Task saved successfully"); + return ResponseEntity.ok(savedTask.getId()); } @@ -134,6 +106,10 @@ public ResponseEntity createTask(CreateTaskRequest createTaskRequest) { public ResponseEntity acceptTask(Long taskId, Long userId) { Optional task = taskRepository.findById(taskId); if (task.isPresent()) { + if (task.get().getAssignee().getId() != userId){ + log.error("User is not the assignee of the task"); + return ResponseEntity.badRequest().body("User is not the assignee of the task"); + } Optional user = userRepository.findById(userId); if (user.isPresent()){ task.get().setAssignee(user.get()); @@ -151,63 +127,39 @@ public ResponseEntity acceptTask(Long taskId, Long userId) { } } - public ResponseEntity> viewAllTasks(Long userId) { + public ResponseEntity declineTask(Long taskId, Long userId){ + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + User user = userRepository.findById(userId).orElseThrow(()-> new EntityNotFoundException("No user found")); + if (task.getAssignee().getId() != user.getId()){ + log.error("User is not the assignee of the task"); + return ResponseEntity.badRequest().body("User is not the assignee of the task"); + } + task.setStatus(EStatus.DECLINED); + taskRepository.save(task); + return ResponseEntity.ok("Task declined"); + } + + public ResponseEntity viewSingleTask(Long taskId) { + Optional task = taskRepository.findById(taskId); + if (task.isPresent()) { + TaskDto taskResponse = taskConverter.convertToDto(task.get()); + return ResponseEntity.ok(taskResponse); + } else { + log.error("No task found with id: {}", taskId); + return ResponseEntity.notFound().build(); + } + } + + + + public ResponseEntity> viewTasks(Long userId) { Optional> tasks = taskRepository.findByAssignee(userId); - List taskResponses = new ArrayList<>(); + List taskResponses = new ArrayList<>(); if (tasks.isPresent()) { for (Task task : tasks.get()) { - TaskResponse taskResponse = new TaskResponse(); - taskResponse.setId(task.getId()) - .setAssignee(task.getAssignee().getId()) - .setAssigner(task.getAssigner().getId()); - Set actions = task.getActions(); - Set taskActions = new HashSet<>(); - actions.forEach( action -> { - ActionResponse actionResponse = new ActionResponse(); - actionResponse.setId(action.getId()) - .setTaskId(action.getTask().getId()) - .setVerifierId(action.getVerifier().getId()) - .setDescription(action.getDescription()) - .setCompleted(action.isCompleted()) - .setStartLatitude(action.getStartLatitude()) - .setStartLongitude(action.getStartLongitude()) - .setEndLatitude(action.getEndLatitude()) - .setEndLongitude(action.getEndLongitude()) - .setDueDate(action.getDueDate()); - taskActions.add(actionResponse); - }); - taskResponse.setActions(taskActions); - taskResponse.setDescription(task.getDescription()); - taskResponse.setUrgency(task.getUrgency()); - taskResponse.setStatus(task.getStatus()); - - Set taskResources = new HashSet<>(); - ResourceResponse resourceResponse = new ResourceResponse(); - task.getResources().forEach(resource -> { - - resourceResponse.setId(resource.getId()) - .setSenderId(resource.getSender().getId()) // may change to resource.getOwner().getId() - .setQuantity(resource.getQuantity()) - .setGender(resource.getGender()) - .setCategoryId(resource.getCategoryTreeId()) - .setLatitude(resource.getLatitude()) - .setLongitude(resource.getLongitude()); - taskResources.add(resourceResponse); - }); - Set taskFeedbacks = new HashSet<>(); - task.getFeedbacks().forEach(feedback -> { - FeedbackResponse feedbackResponse = new FeedbackResponse(); - feedbackResponse.setId(feedback.getId()) - .setTaskId(feedback.getTask().getId()) - .setUserId(feedback.getCreator().getId()) - .setMessage(feedback.getMessage()); - taskFeedbacks.add(feedbackResponse); - }); - taskResponse.setFeedbacks(taskFeedbacks); - taskResponse.setResources(taskResources); + TaskDto taskResponse = taskConverter.convertToDto(task); taskResponses.add(taskResponse); - } return ResponseEntity.ok(taskResponses); } else { @@ -216,148 +168,161 @@ public ResponseEntity> viewAllTasks(Long userId) { } } + @Transactional + public ResponseEntity deleteTask(@RequestParam Long taskId){ + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + taskRepository.delete(task); + return ResponseEntity.ok("Task deleted successfully"); + } - public ResponseEntity updateTask(Long taskId, UpdateTaskRequest updateTaskRequest) { - if (updateTaskRequest.getAssigneeId() == null || updateTaskRequest.getAssignerId() == null) { - return ResponseEntity.badRequest().body("Assignee and assigner must be specified"); + @Transactional + public ResponseEntity updateTask(UpdateTaskRequest updateTaskRequest, Long taskId) { + + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + task.setDescription(updateTaskRequest.getDescription() != null ? updateTaskRequest.getDescription() : + task.getDescription()); + task.setUrgency(updateTaskRequest.getUrgency() != null ? updateTaskRequest.getUrgency() : task.getUrgency()); + task.setStatus(updateTaskRequest.getStatus() != null ? updateTaskRequest.getStatus() : task.getStatus()); + taskRepository.save(task); + return ResponseEntity.ok("Task updated successfully"); + } + public ResponseEntity assignTask(Long taskId, Long userId){ + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + User user = userRepository.findById(userId).orElseThrow(()-> new EntityNotFoundException("No user found")); + if (task.getAssignee() != null){ + log.error("Task already assigned"); + return ResponseEntity.badRequest().body("Task already assigned"); } - Optional task = taskRepository.findById(taskId); - if (task.isPresent()) { - User assignee = userService.findById(updateTaskRequest.getAssigneeId()); - User assigner = userService.findById(updateTaskRequest.getAssignerId()); - List actionEntities = new ArrayList<>(); - List resourceEntities = new ArrayList<>(); - EUrgency urgency = updateTaskRequest.getUrgency(); - EStatus status = updateTaskRequest.getStatus(); - String description = updateTaskRequest.getDescription(); - updateTaskRequest.getActions().forEach(action -> { - Action actionEntity = new Action(); - actionEntity.setVerifier(userService.findById(action.getVerifierId())); - actionEntity.setDescription(action.getDescription()); - actionEntity.setCompleted(action.isCompleted()); - actionEntity.setVerified(action.isVerified()); - actionEntity.setStartLatitude(action.getStartLatitude()); - actionEntity.setStartLongitude(action.getStartLongitude()); - actionEntity.setEndLatitude(action.getEndLatitude()); - actionEntity.setEndLongitude(action.getEndLongitude()); - actionEntity.setCreatedAt(LocalDateTime.now()); - actionEntities.add(actionEntity); - }); - updateTaskRequest.getResources().forEach(resource -> { - Resource resourceEntity = new Resource(); - resourceEntity.setSender(userService.findById(resource.getSenderId())); - resourceEntity.setCategoryTreeId(resource.getCategoryTreeId()); - resourceEntity.setQuantity(resource.getQuantity()); - resourceEntity.setGender(resource.getGender()); - resourceEntity.setLatitude(resource.getLatitude()); - resourceEntity.setLongitude(resource.getLongitude()); - resourceEntity.setCreatedAt(LocalDateTime.now()); - resourceEntities.add(resourceEntity); - }); - Task taskEntity = task.get(); - taskEntity.setAssignee(assignee); - taskEntity.setAssigner(assigner); - taskEntity.setStatus(status); - taskEntity.setUrgency(urgency); - taskEntity.setDescription(description); - taskEntity.setActions(new HashSet<>(actionEntities)); - taskEntity.setResources(new HashSet<>(resourceEntities)); - taskEntity.setCreatedAt(LocalDateTime.now()); - - Task savedTask = taskRepository.save(taskEntity); - actionEntities.forEach(action -> { - action.setTask(savedTask); - }); - actionRepository.saveAll(actionEntities); - - String bodyMessage = String.format(NotificationMessages.TASK_UPDATED, assigner.getId(), savedTask.getId()); - notificationService.sendNotification("New Task Assigned", bodyMessage, assignee.getId(), savedTask.getId(), ENotificationEntityType.TASK); - - return ResponseEntity.ok("Task updated successfully"); - - }else{ - log.error("No task found with id: {}", taskId); - return ResponseEntity.badRequest().body("Task not found"); + task.setAssignee(user); + task.setStatus(EStatus.PENDING); + taskRepository.save(task); + User assigner = task.getAssigner(); + String bodyMessage = String.format(NotificationMessages.TASK_ASSIGNED, assigner.getId(), task.getId()); + notificationService.sendNotification("New Task Assigned", bodyMessage, user.getId(), task.getId(), ENotificationEntityType.TASK); + + return ResponseEntity.ok("Task assigned successfully"); + } + + public ResponseEntity unassignTask(Long taskId){ + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + User user = userRepository.findById(task.getAssignee().getId()).orElseThrow(()-> new EntityNotFoundException("No user found")); + + task.setAssignee(null); + task.setStatus(EStatus.FREE); + taskRepository.save(task); + User assigner = task.getAssigner(); + String bodyMessage = String.format(NotificationMessages.TASK_UNASSIGNED, assigner.getId(), task.getId()); + notificationService.sendNotification("You Are Unassigned", bodyMessage, user.getId(), task.getId(), ENotificationEntityType.TASK); + return ResponseEntity.ok("Task unassigned successfully"); + } + + @Transactional + public ResponseEntity completeTask(Long taskId, Long userId){ + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + User user = userRepository.findById(userId).orElseThrow(()-> new EntityNotFoundException("No user found")); + if (task.getAssignee().getId() != user.getId()){ + log.error("User is not the assignee of the task"); + return ResponseEntity.badRequest().body("User is not the assignee of the task"); + } + if (task.getStatus() != EStatus.TODO){ + log.error("Task is not in progress"); + return ResponseEntity.badRequest().body("Task is not in progress"); } + Set actions = task.getActions(); + actions.stream().filter(action -> !action.isCompleted()); + if (actions.size() > 0){ + log.error("Task is not completed, there are actions not completed"); + return ResponseEntity.badRequest().body("Actions are not done"); + } + + Set resources = task.getResources(); + resources.forEach( + resource -> { + resource.setStatus(EResourceStatus.DELIVERED); + resourceRepository.save(resource); + } + ); + + + task.setStatus(EStatus.DONE); + taskRepository.save(task); + + return ResponseEntity.ok("Task completed"); + } + + + @Transactional + public ResponseEntity addResources(AddResourceToTaskRequest addResourceToTaskRequest){ + Long taskId = addResourceToTaskRequest.getTaskId(); + List resources = addResourceToTaskRequest.getResourceIds(); + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + for (Long resourceId : resources){ + Resource resource = resourceRepository.findById(resourceId).orElseThrow(()-> new EntityNotFoundException("No resource found")); + User receiver = userRepository.findById(addResourceToTaskRequest.getReceiverId()).orElseThrow(()-> new EntityNotFoundException("No user found for receiver")); + if (resource.getStatus() != EResourceStatus.AVAILABLE){ + log.error("Resource is not available"); + return ResponseEntity.badRequest().body("Resource is not available"); + } + resource.setTask(task); + resource.setReceiver(receiver); + //notify receiver + String bodyMessage = String.format(NotificationMessages.RESOURCE_IS_BEING_SENT, resource.getId()); + notificationService.sendNotification("Resource is being sent", bodyMessage, receiver.getId(), resource.getId(), ENotificationEntityType.RESOURCE); + + resource.setStatus(EResourceStatus.IN_TASK); + resourceRepository.save(resource); + } + task.getResources().addAll(resourceRepository.findAllById(resources)); + taskRepository.save(task); + return ResponseEntity.ok("Resources added successfully to Task"); + } + + @Transactional + public ResponseEntity removeResources(AddResourceToTaskRequest addResourceToTaskRequest){ + Long taskId = addResourceToTaskRequest.getTaskId(); + List resources = addResourceToTaskRequest.getResourceIds(); + Task task = taskRepository.findById(taskId).orElseThrow(()-> new EntityNotFoundException("No task found")); + for (Long resourceId : resources){ + Resource resource = resourceRepository.findById(resourceId).orElseThrow(()-> new EntityNotFoundException("No resource found")); + resource.setTask(null); + resource.setReceiver(null); + resource.setStatus(EResourceStatus.AVAILABLE); + // TODO: notify receiver + resourceRepository.save(resource); + } + resourceRepository.findAllById(resources) + .forEach(resource -> task.getResources().remove(resource)); + taskRepository.save(task); + return ResponseEntity.ok("Resources removed successfully from Task"); + + } - public ResponseEntity> viewTaskByFilter(Long assignerId, Long assigneeId, EUrgency urgency, EStatus status){ + public ResponseEntity> viewTasksByFilter(Long assignerId, Long assigneeId, EUrgency urgency, EStatus status) { Specification spec = Specification.where(null); if (assignerId != null) { - spec = spec.and(TaskSpecifications.hasAssignerId(assignerId)); + spec = spec.and(TaskSpecifications.hasAssigner(assignerId)); } + if (assigneeId != null) { - spec = spec.and(TaskSpecifications.hasAssigneeId(assigneeId)); + spec = spec.and(TaskSpecifications.hasAssignee(assigneeId)); } - if (urgency != null) { + if (urgency != null){ spec = spec.and(TaskSpecifications.hasUrgency(urgency)); } - if (status != null) { + + if(status != null) { spec = spec.and(TaskSpecifications.hasStatus(status)); } - List taskResponses = new ArrayList<>(); - List tasks = taskRepository.findAll(spec); - - tasks.forEach(task -> { - TaskResponse taskResponse = new TaskResponse(); - taskResponse.setId(task.getId()) - .setAssignee(task.getAssignee().getId()) - .setAssigner(task.getAssigner().getId()) - .setDescription(task.getDescription()) - .setUrgency(task.getUrgency()) - .setStatus(task.getStatus()); - Set actions = task.getActions(); - Set taskActions = new HashSet<>(); - actions.forEach( action -> { - ActionResponse actionResponse = new ActionResponse(); - actionResponse.setId(action.getId()) - .setTaskId(action.getTask().getId()) - .setVerifierId(action.getVerifier().getId()) - .setDescription(action.getDescription()) - .setCompleted(action.isCompleted()) - .setStartLatitude(action.getStartLatitude()) - .setStartLongitude(action.getStartLongitude()) - .setEndLatitude(action.getEndLatitude()) - .setEndLongitude(action.getEndLongitude()) - .setDueDate(action.getDueDate()); - taskActions.add(actionResponse); - }); - taskResponse.setActions(taskActions); - Set resources = task.getResources(); - Set taskResources = new HashSet<>(); - resources.forEach( resource -> { - ResourceResponse resourceResponse = new ResourceResponse(); - resourceResponse.setId(resource.getId()) - .setSenderId(resource.getSender().getId()) - .setQuantity(resource.getQuantity()) - .setCategoryId(resource.getCategoryTreeId()) - .setGender(resource.getGender()) - .setLatitude(resource.getLatitude()) - .setLongitude(resource.getLongitude()); - }); - taskResponse.setResources(taskResources); - Set taskFeedbacks = new HashSet<>(); - task.getFeedbacks().forEach(feedback -> { - FeedbackResponse feedbackResponse = new FeedbackResponse(); - feedbackResponse.setId(feedback.getId()) - .setTaskId(feedback.getTask().getId()) - .setUserId(feedback.getCreator().getId()) - .setMessage(feedback.getMessage()); - taskFeedbacks.add(feedbackResponse); - }); - taskResponse.setFeedbacks(taskFeedbacks); - taskResponses.add(taskResponse); - }); - - return ResponseEntity.ok(taskResponses); - + return ResponseEntity.ok(taskRepository.findAll(spec).stream().map(task -> taskConverter.convertToDto(task)).toList()); } + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ActionSpecifications.java b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ActionSpecifications.java index 2bb8b4ac..1b70c448 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ActionSpecifications.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ActionSpecifications.java @@ -2,8 +2,10 @@ import com.groupa1.resq.entity.Action; import org.springframework.data.jpa.domain.Specification; + import java.time.LocalDateTime; + public class ActionSpecifications { public static Specification hasTaskId(Long taskId) { @@ -11,23 +13,28 @@ public static Specification hasTaskId(Long taskId) { criteriaBuilder.equal(root.get("task").get("id"), taskId); } - public static Specification hasVerifierId(Long verifierId) { + public static Specification hasVerifier(Long verifierId) { return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("verifier").get("id"), verifierId); } - public static Specification hasCompleted (boolean isCompleted) { + public static Specification hasCompleted(Boolean completed) { return (root, query, criteriaBuilder) -> - criteriaBuilder.equal(root.get("isCompleted"), isCompleted); + criteriaBuilder.equal(root.get("isCompleted"), completed); } + public static Specification hasLatestDueDate(LocalDateTime dueDate) { + return (root, query, criteriaBuilder) -> + criteriaBuilder.lessThanOrEqualTo(root.get("dueDate"), dueDate); + } public static Specification hasVerified (boolean isVerified) { return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("isVerified"), isVerified); } - - public static Specification hasDueDate (LocalDateTime dueDate) { + public static Specification hasEarliestDueDate(LocalDateTime dueDate) { return (root, query, criteriaBuilder) -> - criteriaBuilder.equal(root.get("dueDate"), dueDate); + criteriaBuilder.greaterThanOrEqualTo(root.get("dueDate"), dueDate); } -} \ No newline at end of file + + +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ResourceSpecifications.java b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ResourceSpecifications.java index f9fe7a7b..69d5d5bd 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ResourceSpecifications.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/ResourceSpecifications.java @@ -1,7 +1,7 @@ package com.groupa1.resq.specification; -import com.groupa1.resq.entity.Need; import com.groupa1.resq.entity.Resource; +import com.groupa1.resq.entity.enums.EResourceStatus; import org.springframework.data.jpa.domain.Specification; import java.math.BigDecimal; @@ -33,4 +33,8 @@ public static Specification isWithinRectangleScope(BigDecimal longitud ); } + public static Specification hasStatus(EResourceStatus status){ + return (root, query, builder) -> builder.equal(root.get("status"), status); + } + } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/TaskSpecifications.java b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/TaskSpecifications.java index e7d2b6bf..8b55c5fa 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/specification/TaskSpecifications.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/specification/TaskSpecifications.java @@ -7,16 +7,16 @@ public class TaskSpecifications { - public static Specification hasAssignerId(Long assignerId) { + public static Specification hasAssignee(Long assigneeId) { return (root, query, criteriaBuilder) -> - criteriaBuilder.equal(root.get("assigner").get("id"), assignerId); + criteriaBuilder.equal(root.get("assignee").get("id"), assigneeId); } - public static Specification hasAssigneeId(Long assigneeId) { + public static Specification hasAssigner(Long assignerId) { return (root, query, criteriaBuilder) -> - criteriaBuilder.equal(root.get("assignee").get("id"), assigneeId); + criteriaBuilder.equal(root.get("assigner").get("id"), + assignerId); } - public static Specification hasUrgency(EUrgency urgency) { return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("urgency"), urgency); @@ -26,4 +26,7 @@ public static Specification hasStatus(EStatus status) { return (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("status"), status); } -} \ No newline at end of file + + + +} diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/util/NotificationMessages.java b/resq/backend/resq/src/main/java/com/groupa1/resq/util/NotificationMessages.java index 2c5e679f..8822cd96 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/util/NotificationMessages.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/util/NotificationMessages.java @@ -1,7 +1,5 @@ package com.groupa1.resq.util; -import java.util.Locale; - public class NotificationMessages { public static final String REQUEST_CREATED_WITH_NEED = "Need #%s is included by %s in the Request #%s"; public static final String TASK_ASSIGNED = "Coordinator %s assigned Task #%s"; @@ -9,4 +7,6 @@ public class NotificationMessages { public static final String ACTION_WAITING_FOR_VERIFICATION = "Action #%s is waiting for verification"; public static final String SYSTEM_MESSAGE = "System Message from User #%s, related entity #%s"; public static final String TASK_UPDATED = "Task #%s is updated by %s"; + public static final String RESOURCE_IS_BEING_SENT = "Resource #%s is being for you to receive"; + public static final String TASK_UNASSIGNED = "Task #%s is unassigned by %s"; } diff --git a/resq/backend/resq/src/main/java/com/groupa1/resq/util/NullAwareBeanUtilsBean.java b/resq/backend/resq/src/main/java/com/groupa1/resq/util/NullAwareBeanUtilsBean.java index 10d5ef9a..f78d4b40 100644 --- a/resq/backend/resq/src/main/java/com/groupa1/resq/util/NullAwareBeanUtilsBean.java +++ b/resq/backend/resq/src/main/java/com/groupa1/resq/util/NullAwareBeanUtilsBean.java @@ -15,4 +15,5 @@ public void copyProperty(Object dest, String name, Object value) return; super.copyProperty(dest, name, value); } -} \ No newline at end of file + +} diff --git a/resq/backend/resq/src/test/java/com/groupa1/resq/service/ActionServiceTest.java b/resq/backend/resq/src/test/java/com/groupa1/resq/service/ActionServiceTest.java index dce6404c..fd4e55fa 100644 --- a/resq/backend/resq/src/test/java/com/groupa1/resq/service/ActionServiceTest.java +++ b/resq/backend/resq/src/test/java/com/groupa1/resq/service/ActionServiceTest.java @@ -8,7 +8,7 @@ import com.groupa1.resq.repository.TaskRepository; import com.groupa1.resq.repository.UserRepository; import com.groupa1.resq.request.CreateActionRequest; -import com.groupa1.resq.response.ActionResponse; +import com.groupa1.resq.dto.ActionDto; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -46,33 +46,8 @@ public class ActionServiceTest { - @Test - void testCreateAction_withNonExistentTask_shouldReturnBadRequest() { - // Given - CreateActionRequest createActionRequest = new CreateActionRequest(); - createActionRequest.setTaskId(1L); - createActionRequest.setVerifierId(1L); - createActionRequest.setDescription("Test Action"); - createActionRequest.setDueDate(LocalDateTime.now()); - createActionRequest.setStartLatitude(BigDecimal.valueOf(1.0)); - createActionRequest.setStartLongitude(BigDecimal.valueOf(2.0)); - createActionRequest.setEndLatitude(BigDecimal.valueOf(3.0)); - createActionRequest.setEndLongitude(BigDecimal.valueOf(4.0)); - - - - when(userRepository.findById(Mockito.anyLong())).thenReturn( - Optional.of(new User())); - // then - ResponseEntity response = - actionService.createAction(createActionRequest); - assertEquals("No task found", response.getBody()); - - - } - @Test void testViewActions_Success() { // Given @@ -101,7 +76,7 @@ void testViewActions_Success() { when(taskRepository.findById(Mockito.anyLong())).thenReturn(Optional.of(task)); - ResponseEntity> response = actionService.viewActions(taskId); + ResponseEntity> response = actionService.viewActions(taskId); // Then assertEquals(1, response.getBody().size()); diff --git a/resq/backend/resq/src/test/java/com/groupa1/resq/service/TaskServiceTest.java b/resq/backend/resq/src/test/java/com/groupa1/resq/service/TaskServiceTest.java index 64a00e24..02ed2d9d 100644 --- a/resq/backend/resq/src/test/java/com/groupa1/resq/service/TaskServiceTest.java +++ b/resq/backend/resq/src/test/java/com/groupa1/resq/service/TaskServiceTest.java @@ -39,72 +39,8 @@ public class TaskServiceTest { - @Test - void testCreateTask_withEmptyActions_withEmptyResources_shouldReturnSuccess() { - // Given - CreateTaskRequest createTaskRequest = new CreateTaskRequest(); - createTaskRequest.setActions(new ArrayList<>()); - createTaskRequest.setResources(new ArrayList<>()); - createTaskRequest.setAssigneeId(1L); - createTaskRequest.setAssignerId(2L); - createTaskRequest.setDescription("Test description"); - createTaskRequest.setUrgency(EUrgency.HIGH); - - - // when - when(userService.findById(createTaskRequest.getAssigneeId())).thenReturn(new User()); - when(userService.findById(createTaskRequest.getAssignerId())).thenReturn(new User()); - - - // then - ResponseEntity response = taskService.createTask(createTaskRequest); - assertEquals("Task saved successfully", response.getBody()); - } - - @Test - void testCreateTask_withNonSpecifiedAssigneeAssigner_shouldReturnBadRequest() { - // Given - CreateTaskRequest createTaskRequest = new CreateTaskRequest(); - createTaskRequest.setActions(new ArrayList<>()); - createTaskRequest.setResources(new ArrayList<>()); - createTaskRequest.setDescription("Test description"); - createTaskRequest.setUrgency(EUrgency.HIGH); - - // then - ResponseEntity response = taskService.createTask(createTaskRequest); - assertEquals("Assignee and assigner must be specified", - response.getBody()); - } - - @Test - void testCreateTask_withNonEmptyAction_withEmptyResources_shouldReturnSuccess() { - // Given - CreateTaskRequest createTaskRequest = new CreateTaskRequest(); - ArrayList actions = new ArrayList<>(); - actions.add(new CreateTaskRequest.Action()); - createTaskRequest.setActions(actions); - createTaskRequest.setResources(new ArrayList<>()); - createTaskRequest.setAssigneeId(1L); - createTaskRequest.setAssignerId(2L); - createTaskRequest.setDescription("Test description"); - createTaskRequest.setUrgency(EUrgency.HIGH); - - // when - when(userService.findById( - createTaskRequest.getAssigneeId())).thenReturn(new User()); - when(userService.findById( - createTaskRequest.getAssignerId())).thenReturn(new User()); - - // then - ResponseEntity response = - taskService.createTask(createTaskRequest); - assertEquals("Task saved successfully", response.getBody()); - - - } - @Test void testAcceptTask_withExistentTask_shouldReturnSuccess() {