diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/UserSubmissionsController.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/UserSubmissionsController.java new file mode 100644 index 0000000..bbd140b --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/controller/UserSubmissionsController.java @@ -0,0 +1,23 @@ +package nl.andrewlalis.gymboard_api.domains.api.controller; + +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.api.service.submission.UserSubmissionService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class UserSubmissionsController { + private final UserSubmissionService submissionService; + + public UserSubmissionsController(UserSubmissionService submissionService) { + this.submissionService = submissionService; + } + + @GetMapping(path = "/users/{userId}/recent-submissions") + public List getRecentSubmissions(@PathVariable String userId) { + return submissionService.getRecentSubmissions(userId); + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java index edc2ed0..5a97768 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/dto/SubmissionResponse.java @@ -15,7 +15,8 @@ public record SubmissionResponse( double rawWeight, String weightUnit, double metricWeight, - int reps + int reps, + boolean verified ) { public SubmissionResponse(Submission submission) { this( @@ -29,7 +30,8 @@ public record SubmissionResponse( submission.getRawWeight().doubleValue(), submission.getWeightUnit().name(), submission.getMetricWeight().doubleValue(), - submission.getReps() + submission.getReps(), + submission.isVerified() ); } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/WeightUnit.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/WeightUnit.java index 942dacf..9a68228 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/WeightUnit.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/WeightUnit.java @@ -19,4 +19,9 @@ public enum WeightUnit { BigDecimal metric = new BigDecimal("0.45359237"); return metric.multiply(pounds); } + + public static BigDecimal toKilograms(BigDecimal weight, WeightUnit unit) { + if (unit == POUNDS) return toKilograms(weight); + return weight; + } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java index dd0901d..3a98a48 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/model/submission/Submission.java @@ -53,9 +53,23 @@ public class Submission { @Column(nullable = false) private int reps; + @Column(nullable = false) + private boolean verified; + public Submission() {} - public Submission(String id, Gym gym, Exercise exercise, User user, LocalDateTime performedAt, String videoFileId, BigDecimal rawWeight, WeightUnit unit, BigDecimal metricWeight, int reps) { + public Submission( + String id, + Gym gym, + Exercise exercise, + User user, + LocalDateTime performedAt, + String videoFileId, + BigDecimal rawWeight, + WeightUnit unit, + BigDecimal metricWeight, + int reps + ) { this.id = id; this.gym = gym; this.exercise = exercise; @@ -66,6 +80,7 @@ public class Submission { this.weightUnit = unit; this.metricWeight = metricWeight; this.reps = reps; + this.verified = false; } public String getId() { @@ -111,4 +126,12 @@ public class Submission { public int getReps() { return reps; } + + public boolean isVerified() { + return verified; + } + + public void setVerified(boolean verified) { + this.verified = verified; + } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java index 6ec8c7f..85b74ab 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/GymService.java @@ -41,14 +41,16 @@ public class GymService { Gym gym = gymRepository.findByCompoundId(id) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); return submissionRepository.findAll((root, query, criteriaBuilder) -> { - query.orderBy(criteriaBuilder.desc(root.get("createdAt"))); + query.orderBy( + criteriaBuilder.desc(root.get("performedAt")), + criteriaBuilder.desc(root.get("createdAt")) + ); query.distinct(true); - - // TODO: Filter to only verified submissions. return PredicateBuilder.and(criteriaBuilder) .with(criteriaBuilder.equal(root.get("gym"), gym)) + .with(criteriaBuilder.isTrue(root.get("verified"))) .build(); - }, PageRequest.of(0, 10)) + }, PageRequest.of(0, 5)) .map(SubmissionResponse::new) .toList(); } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java index 891d981..c5566c7 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/LeaderboardService.java @@ -55,9 +55,10 @@ public class LeaderboardService { query.distinct(true); query.orderBy(criteriaBuilder.desc(root.get("metricWeight"))); - PredicateBuilder pb = PredicateBuilder.and(criteriaBuilder); + PredicateBuilder pb = PredicateBuilder.and(criteriaBuilder) + .with(criteriaBuilder.isTrue(root.get("verified"))); - cutoffTime.ifPresent(time -> pb.with(criteriaBuilder.greaterThan(root.get("createdAt"), time))); + cutoffTime.ifPresent(time -> pb.with(criteriaBuilder.greaterThan(root.get("performedAt"), time))); optionalExercise.ifPresent(exercise -> pb.with(criteriaBuilder.equal(root.get("exercise"), exercise))); if (!gyms.isEmpty()) { PredicateBuilder or = PredicateBuilder.or(criteriaBuilder); diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/UserSubmissionService.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/UserSubmissionService.java new file mode 100644 index 0000000..3416c92 --- /dev/null +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/api/service/submission/UserSubmissionService.java @@ -0,0 +1,44 @@ +package nl.andrewlalis.gymboard_api.domains.api.service.submission; + +import nl.andrewlalis.gymboard_api.domains.api.dao.submission.SubmissionRepository; +import nl.andrewlalis.gymboard_api.domains.api.dto.SubmissionResponse; +import nl.andrewlalis.gymboard_api.domains.auth.dao.UserRepository; +import nl.andrewlalis.gymboard_api.domains.auth.model.User; +import nl.andrewlalis.gymboard_api.util.PredicateBuilder; +import org.springframework.data.domain.PageRequest; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +@Service +public class UserSubmissionService { + private final UserRepository userRepository; + private final SubmissionRepository submissionRepository; + + public UserSubmissionService(UserRepository userRepository, SubmissionRepository submissionRepository) { + this.userRepository = userRepository; + this.submissionRepository = submissionRepository; + } + + @Transactional(readOnly = true) + public List getRecentSubmissions(String userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + if (user.getPreferences().isAccountPrivate()) { + throw new ResponseStatusException(HttpStatus.FORBIDDEN); + } + return submissionRepository.findAll((root, query, criteriaBuilder) -> { + query.orderBy( + criteriaBuilder.desc(root.get("performedAt")), + criteriaBuilder.desc(root.get("createdAt")) + ); + PredicateBuilder pb = PredicateBuilder.and(criteriaBuilder) + .with(criteriaBuilder.equal(root.get("user"), user)); + + return pb.build(); + }, PageRequest.of(0, 5)).map(SubmissionResponse::new).toList(); + } +} diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserResponse.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserResponse.java index 6995fef..0c70817 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserResponse.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/domains/auth/dto/UserResponse.java @@ -6,14 +6,16 @@ public record UserResponse( String id, boolean activated, String email, - String name + String name, + boolean accountPrivate ) { public UserResponse(User user) { this( user.getId(), user.isActivated(), user.getEmail(), - user.getName() + user.getName(), + user.getPreferences().isAccountPrivate() ); } } diff --git a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java index ab53843..4296512 100644 --- a/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java +++ b/gymboard-api/src/main/java/nl/andrewlalis/gymboard_api/util/sample_data/SampleSubmissionGenerator.java @@ -63,8 +63,9 @@ public class SampleSubmissionGenerator implements SampleDataGenerator { final LocalDateTime latestSubmission = LocalDateTime.now(); Random random = new Random(1); + List submissions = new ArrayList<>(count); for (int i = 0; i < count; i++) { - generateRandomSubmission( + submissions.add(generateRandomSubmission( gyms, users, exercises, @@ -72,11 +73,12 @@ public class SampleSubmissionGenerator implements SampleDataGenerator { earliestSubmission, latestSubmission, random - ); + )); } + submissionRepository.saveAll(submissions); } - private void generateRandomSubmission( + private Submission generateRandomSubmission( List gyms, List users, List exercises, @@ -94,18 +96,20 @@ public class SampleSubmissionGenerator implements SampleDataGenerator { rawWeight = metricWeight.multiply(new BigDecimal("2.2046226218")); } - submissionRepository.save(new Submission( - ulid.nextULID(), - randomChoice(gyms, random), - randomChoice(exercises, random), - randomChoice(users, random), - time, - randomChoice(videoIds, random), - rawWeight, - weightUnit, - metricWeight, - random.nextInt(13) - )); + var submission = new Submission( + ulid.nextULID(), + randomChoice(gyms, random), + randomChoice(exercises, random), + randomChoice(users, random), + time, + randomChoice(videoIds, random), + rawWeight, + weightUnit, + metricWeight, + random.nextInt(13) + 1 + ); + submission.setVerified(true); + return submission; } @Override diff --git a/gymboard-app/src/components/ExerciseSubmissionListItem.vue b/gymboard-app/src/components/ExerciseSubmissionListItem.vue index 2b3b201..7c39d5a 100644 --- a/gymboard-app/src/components/ExerciseSubmissionListItem.vue +++ b/gymboard-app/src/components/ExerciseSubmissionListItem.vue @@ -10,14 +10,13 @@ - {{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }} + {{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }} \ No newline at end of file + +