Added user submission improvements.
This commit is contained in:
parent
0663296052
commit
6b7b38595e
|
@ -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<SubmissionResponse> getRecentSubmissions(@PathVariable String userId) {
|
||||
return submissionService.getRecentSubmissions(userId);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<SubmissionResponse> 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();
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,8 +63,9 @@ public class SampleSubmissionGenerator implements SampleDataGenerator {
|
|||
final LocalDateTime latestSubmission = LocalDateTime.now();
|
||||
|
||||
Random random = new Random(1);
|
||||
List<Submission> 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<Gym> gyms,
|
||||
List<User> users,
|
||||
List<Exercise> exercises,
|
||||
|
@ -94,7 +96,7 @@ public class SampleSubmissionGenerator implements SampleDataGenerator {
|
|||
rawWeight = metricWeight.multiply(new BigDecimal("2.2046226218"));
|
||||
}
|
||||
|
||||
submissionRepository.save(new Submission(
|
||||
var submission = new Submission(
|
||||
ulid.nextULID(),
|
||||
randomChoice(gyms, random),
|
||||
randomChoice(exercises, random),
|
||||
|
@ -104,8 +106,10 @@ public class SampleSubmissionGenerator implements SampleDataGenerator {
|
|||
rawWeight,
|
||||
weightUnit,
|
||||
metricWeight,
|
||||
random.nextInt(13)
|
||||
));
|
||||
random.nextInt(13) + 1
|
||||
);
|
||||
submission.setVerified(true);
|
||||
return submission;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,14 +10,13 @@
|
|||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side top>
|
||||
{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
{{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ExerciseSubmission, WeightUnitUtil } from 'src/api/main/submission';
|
||||
import { getFileUrl } from 'src/api/cdn';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
<template>
|
||||
<q-page>
|
||||
<StandardCenteredPage v-if="submission">
|
||||
<video
|
||||
class="submission-video"
|
||||
:src="getFileUrl(submission.videoFileId)"
|
||||
loop
|
||||
controls
|
||||
autopictureinpicture
|
||||
preload="metadata"
|
||||
autoplay
|
||||
/>
|
||||
<h3>
|
||||
{{ submission.rawWeight }} {{ WeightUnitUtil.toAbbreviation(submission.weightUnit) }}
|
||||
{{ submission.exercise.displayName }}
|
||||
|
@ -9,17 +18,8 @@
|
|||
<p>by <router-link :to="'/users/' + submission.user.id">{{ submission.user.name }}</router-link></p>
|
||||
<p>At <router-link :to="getGymRoute(submission.gym)">{{ submission.gym.displayName }}</router-link></p>
|
||||
<p>
|
||||
{{ submission.createdAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
{{ submission.performedAt.setLocale($i18n.locale).toLocaleString(DateTime.DATETIME_MED) }}
|
||||
</p>
|
||||
<video
|
||||
:src="getFileUrl(submission.videoFileId)"
|
||||
width="600"
|
||||
loop
|
||||
controls
|
||||
autopictureinpicture
|
||||
preload="metadata"
|
||||
autoplay
|
||||
/>
|
||||
</StandardCenteredPage>
|
||||
</q-page>
|
||||
</template>
|
||||
|
@ -49,3 +49,10 @@ onMounted(async () => {
|
|||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.submission-video {
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue