diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/controller/goal/GoalController.java b/src/main/java/no/ntnu/idi/stud/savingsapp/controller/goal/GoalController.java index b2bd0b25c666b1273d246938cbc669a579eab157..ca2aaea62cef4572bcec15ef0265ece0d3a485f5 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/controller/goal/GoalController.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/controller/goal/GoalController.java @@ -7,10 +7,12 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import no.ntnu.idi.stud.savingsapp.dto.goal.ChallengeDTO; import no.ntnu.idi.stud.savingsapp.dto.goal.MarkChallengeDTO; import no.ntnu.idi.stud.savingsapp.dto.goal.CreateGoalDTO; import no.ntnu.idi.stud.savingsapp.dto.goal.GoalDTO; import no.ntnu.idi.stud.savingsapp.exception.ExceptionResponse; +import no.ntnu.idi.stud.savingsapp.model.goal.Challenge; import no.ntnu.idi.stud.savingsapp.model.goal.Goal; import no.ntnu.idi.stud.savingsapp.security.AuthIdentity; import no.ntnu.idi.stud.savingsapp.service.ChallengeService; @@ -133,10 +135,18 @@ public class GoalController { } @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<GoalDTO> getGoal(@RequestParam Long id) { + public ResponseEntity<GoalDTO> getGoal(@PathVariable Long id) { Goal goal = goalService.getGoal(id); GoalDTO goalDTO = modelMapper.map(goal, GoalDTO.class); log.info("[GoalController:getGoal] goal: {}", goalDTO); return ResponseEntity.ok(goalDTO); } + + @PatchMapping(value = "/challenge/{id}", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity<ChallengeDTO> regenerateChallenge(@AuthenticationPrincipal AuthIdentity identity, + @PathVariable Long id) { + Challenge challenge = challengeService.regenerateChallenge(identity.getId(), id); + ChallengeDTO challengeDTO = modelMapper.map(challenge, ChallengeDTO.class); + return ResponseEntity.ok(challengeDTO); + } } diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/dto/goal/ChallengeTemplateDTO.java b/src/main/java/no/ntnu/idi/stud/savingsapp/dto/goal/ChallengeTemplateDTO.java index ff6d36bc45c1aad86c14b61a34e397d28d7b0296..463a98b2c06d49ef1b54741727907f18d0008593 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/dto/goal/ChallengeTemplateDTO.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/dto/goal/ChallengeTemplateDTO.java @@ -9,6 +9,7 @@ import java.math.BigDecimal; public final class ChallengeTemplateDTO { private long id; + private String templateName; private String text; private BigDecimal amount; private ChallengeType type; diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/dto/user/UserDTO.java b/src/main/java/no/ntnu/idi/stud/savingsapp/dto/user/UserDTO.java index e3d4db3c79aa078cd5fd8f78119b256ef5c5ae45..2487b82b8d8f8c5edf22fc642d0491e682e8c7d5 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/dto/user/UserDTO.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/dto/user/UserDTO.java @@ -16,8 +16,8 @@ public class UserDTO { private Timestamp createdAt; private String role; private String subscriptionLevel; - private BankAccountResponseDTO checkingAccountBBAN; - private BankAccountResponseDTO savingsAccountBBAN; + private BankAccountResponseDTO checkingAccount; + private BankAccountResponseDTO savingsAccount; private PointDTO point; private StreakDTO streak; } diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/model/goal/ChallengeTemplate.java b/src/main/java/no/ntnu/idi/stud/savingsapp/model/goal/ChallengeTemplate.java index f2279af8e0a09370796e35a2027fea10e56a7d53..b71a09c987ffe42f5375713fcdec4a273ac08081 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/model/goal/ChallengeTemplate.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/model/goal/ChallengeTemplate.java @@ -35,6 +35,10 @@ public class ChallengeTemplate { @Column(name = "challenge_text", nullable = false) private String text; + @NonNull + @Column(name = "challenge_name", nullable = false) + private String challengeName; + @Column(name = "challenge_amount", nullable = false) private BigDecimal amount; diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/service/ChallengeService.java b/src/main/java/no/ntnu/idi/stud/savingsapp/service/ChallengeService.java index 8a1b0c11d66bceeb11d3882ad42804234f10bf56..0c02092a2192761a7665d3d6cd584cfcb51bda4a 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/service/ChallengeService.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/service/ChallengeService.java @@ -48,4 +48,15 @@ public interface ChallengeService { * reflect the new target savings the user aims to achieve. */ void updateSavingAmount(long userId, long id, BigDecimal amount); + + /** + * Replaces that challenge in a given goal identified by the challenge ID. + * This method is useful for letting the user change out challenges they know + * they will not be able to do + * + * @param userId the ID of the user who sends the request + * @param challengeId The ID of the challenge that will be replaced + * @return The updated goal containing the new challenge + */ + Challenge regenerateChallenge(long userId, long challengeId); } diff --git a/src/main/java/no/ntnu/idi/stud/savingsapp/service/impl/ChallengeServiceImpl.java b/src/main/java/no/ntnu/idi/stud/savingsapp/service/impl/ChallengeServiceImpl.java index e5977f932eba00474b7305aa6a5a6116a70b036b..1f71131484219a7767a2a56fa9bcbe003388911f 100644 --- a/src/main/java/no/ntnu/idi/stud/savingsapp/service/impl/ChallengeServiceImpl.java +++ b/src/main/java/no/ntnu/idi/stud/savingsapp/service/impl/ChallengeServiceImpl.java @@ -4,13 +4,16 @@ import no.ntnu.idi.stud.savingsapp.exception.goal.ChallengeNotFoundException; import no.ntnu.idi.stud.savingsapp.exception.goal.GoalNotFoundException; import no.ntnu.idi.stud.savingsapp.exception.goal.InvalidChallengeDayException; import no.ntnu.idi.stud.savingsapp.exception.user.PermissionDeniedException; +import no.ntnu.idi.stud.savingsapp.exception.user.UserNotFoundException; import no.ntnu.idi.stud.savingsapp.model.configuration.ChallengeType; import no.ntnu.idi.stud.savingsapp.model.goal.Challenge; import no.ntnu.idi.stud.savingsapp.model.goal.ChallengeTemplate; import no.ntnu.idi.stud.savingsapp.model.goal.Goal; import no.ntnu.idi.stud.savingsapp.model.goal.Progress; import no.ntnu.idi.stud.savingsapp.model.user.User; +import no.ntnu.idi.stud.savingsapp.repository.ChallengeTemplateRepository; import no.ntnu.idi.stud.savingsapp.repository.GoalRepository; +import no.ntnu.idi.stud.savingsapp.repository.UserRepository; import no.ntnu.idi.stud.savingsapp.service.ChallengeService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -33,6 +36,10 @@ public class ChallengeServiceImpl implements ChallengeService { @Autowired private GoalRepository goalRepository; + @Autowired + private ChallengeTemplateRepository challengeTemplateRepository; + @Autowired + private UserRepository userRepository; /** * Generates a list of challenges for a given goal based on the user's preferences and goal's target date. @@ -49,10 +56,14 @@ public class ChallengeServiceImpl implements ChallengeService { t1.setAmount(BigDecimal.valueOf(40)); t1.setText("Spar {unit_amount} kr hver gang du kjøper kaffe, totalt {checkDays} ganger over " + "{totalDays} dager. Dette gir deg en total besparelse på {total_amount} kr."); + t1.setChallengeName("Spar på kaffe"); ChallengeTemplate t2 = new ChallengeTemplate(); t2.setChallengeType(ChallengeType.EAT_PACKED_LUNCH); - t2.setAmount(BigDecimal.valueOf(100)); - t2.setText("Spar {amount} kr ved å ta med deg matpakke"); + t2.setAmount(BigDecimal.valueOf(30)); + t2.setText("Spar {unit_amount} kr per gang du tar med deg hjemmelaget matpakke, " + + "totalt {checkDays} ganger over " + + "{totalDays} dager. Dette gir deg en total besparelse på {total_amount} kr."); + t2.setChallengeName("Spar på mat"); List<ChallengeTemplate> templates = Arrays.asList(t1, t2); //templateRepository.findAllByChallengeTypeIn(user.getConfiguration().getChallengeTypes()); @@ -64,6 +75,8 @@ public class ChallengeServiceImpl implements ChallengeService { List<Challenge> challenges = new ArrayList<>(); int i = 0; + int savedSoFar = 0; + while (remainingDays > 0) { int totalDays = Math.min(random.nextInt(23) + 7, remainingDays); int checkDays = user.getConfiguration().getCommitment().getCheckDays(totalDays); @@ -71,10 +84,12 @@ public class ChallengeServiceImpl implements ChallengeService { Challenge challenge = new Challenge(); challenge.setTemplate(template); challenge.setPotentialAmount(template.getAmount()); - challenge.setPoints(totalDays * 10); + challenge.setPoints(checkDays * 10); challenge.setCheckDays(checkDays); challenge.setTotalDays(totalDays); + savedSoFar += template.getAmount().intValue() * checkDays; + if (challenges.isEmpty()) { challenge.setStartDate(goal.getCreatedAt()); } else { @@ -93,6 +108,11 @@ public class ChallengeServiceImpl implements ChallengeService { } challenges.add(challenge); + + if(goal.getTargetAmount() < savedSoFar) { + break; + } + remainingDays -= totalDays; } return challenges; @@ -184,4 +204,54 @@ public class ChallengeServiceImpl implements ChallengeService { throw new ChallengeNotFoundException(); } } + + + /** + * Replaces that challenge in a given goal identified by the challenge ID. + * This method is useful for letting the user change out challenges they know + * they will not be able to do + * + * @param userId the ID of the user who sends the request + * @param challengeId The ID of the challenge that will be replaced + * @return The updated goal containing the new challenge + */ + public Challenge regenerateChallenge(long userId, long challengeId) { + Optional<User> userOptional = userRepository.findById(userId); + if (userOptional.isPresent()) { + User user = userOptional.get(); + + Goal goal = getGoalByChallengeId(challengeId); + if (goal.getUser().getId() != userId) { + throw new PermissionDeniedException(); + } + + Challenge challenge = goal.getChallenges().stream().filter(c -> c.getId() == challengeId).findFirst().orElse(null); + if (challenge == null) { + throw new ChallengeNotFoundException(); + } + + List<ChallengeType> challengeTypes = user.getConfiguration() + .getChallengeTypes(); + List<ChallengeTemplate> challengeTemplates = challengeTemplateRepository + .findAllByChallengeTypeIn(challengeTypes); + + int randomTemplateIndex = random.nextInt(challengeTemplates.size()); + + ChallengeTemplate newChallengeTemplate = challengeTemplates.get(randomTemplateIndex); + + int totalDays = challenge.getTotalDays(); + int checkDays = user.getConfiguration().getCommitment().getCheckDays(totalDays); + + + challenge.setPotentialAmount(newChallengeTemplate.getAmount()); + challenge.setCheckDays(checkDays); + challenge.setPoints(checkDays * 10); + challenge.setTemplate(newChallengeTemplate); + + goalRepository.save(goal); + return challenge; + } else { + throw new UserNotFoundException(); + } + } }