Added submissions.csv to sample data, and improved concurrency.

This commit is contained in:
Andrew Lalis 2023-01-26 14:07:41 +01:00
parent 3908c2becd
commit d1aa06dd95
7 changed files with 65 additions and 27 deletions

View File

@ -45,7 +45,8 @@
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <!-- TODO: Change this to "test" once the SampleDataLoader is refactored to the tests. -->
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,2 +1,4 @@
nl,groningen,Groningen nl,groningen,Groningen
us,tampa,Tampa nl,amsterdam,Amsterdam
us,tampa,Tampa
us,new-york-city,New York City
1 nl groningen Groningen
2 us nl tampa amsterdam Tampa Amsterdam
3 us tampa Tampa
4 us new-york-city New York City

View File

@ -0,0 +1,2 @@
barbell-overhead-press,60,KG,1,Andrew Lalis,nl_groningen_trainmore-munnekeholm,sample_video_ohp.mp4
incline-dumbbell-bicep-curl,14,KG,10,Andrew Lalis,nl_groningen_trainmore-munnekeholm,sample_video_curl.mp4
1 barbell-overhead-press 60 KG 1 Andrew Lalis nl_groningen_trainmore-munnekeholm sample_video_ohp.mp4
2 incline-dumbbell-bicep-curl 14 KG 10 Andrew Lalis nl_groningen_trainmore-munnekeholm sample_video_curl.mp4

View File

@ -1,25 +1,11 @@
package nl.andrewlalis.gymboard_api.config; package nl.andrewlalis.gymboard_api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration @Configuration
@EnableAsync @EnableAsync
@EnableScheduling @EnableScheduling
public class AsyncConfig { public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(3);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("gymboard-api-");
executor.initialize();
return executor;
}
} }

View File

@ -1,18 +1,25 @@
package nl.andrewlalis.gymboard_api.model; package nl.andrewlalis.gymboard_api.model;
import nl.andrewlalis.gymboard_api.controller.dto.CompoundGymId;
import nl.andrewlalis.gymboard_api.controller.dto.ExerciseSubmissionPayload;
import nl.andrewlalis.gymboard_api.dao.CityRepository; import nl.andrewlalis.gymboard_api.dao.CityRepository;
import nl.andrewlalis.gymboard_api.dao.CountryRepository; import nl.andrewlalis.gymboard_api.dao.CountryRepository;
import nl.andrewlalis.gymboard_api.dao.GymRepository; import nl.andrewlalis.gymboard_api.dao.GymRepository;
import nl.andrewlalis.gymboard_api.dao.exercise.ExerciseRepository; import nl.andrewlalis.gymboard_api.dao.exercise.ExerciseRepository;
import nl.andrewlalis.gymboard_api.model.exercise.Exercise; import nl.andrewlalis.gymboard_api.model.exercise.Exercise;
import nl.andrewlalis.gymboard_api.model.exercise.ExerciseSubmission;
import nl.andrewlalis.gymboard_api.service.ExerciseSubmissionService;
import nl.andrewlalis.gymboard_api.service.UploadService;
import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord; import org.apache.commons.csv.CSVRecord;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
@ -31,17 +38,21 @@ public class SampleDataLoader implements ApplicationListener<ContextRefreshedEve
private final CityRepository cityRepository; private final CityRepository cityRepository;
private final GymRepository gymRepository; private final GymRepository gymRepository;
private final ExerciseRepository exerciseRepository; private final ExerciseRepository exerciseRepository;
private final ExerciseSubmissionService submissionService;
private final UploadService uploadService;
public SampleDataLoader( public SampleDataLoader(
CountryRepository countryRepository, CountryRepository countryRepository,
CityRepository cityRepository, CityRepository cityRepository,
GymRepository gymRepository, GymRepository gymRepository,
ExerciseRepository exerciseRepository ExerciseRepository exerciseRepository,
) { ExerciseSubmissionService submissionService, UploadService uploadService) {
this.countryRepository = countryRepository; this.countryRepository = countryRepository;
this.cityRepository = cityRepository; this.cityRepository = cityRepository;
this.gymRepository = gymRepository; this.gymRepository = gymRepository;
this.exerciseRepository = exerciseRepository; this.exerciseRepository = exerciseRepository;
this.submissionService = submissionService;
this.uploadService = uploadService;
} }
@Override @Override
@ -84,10 +95,40 @@ public class SampleDataLoader implements ApplicationListener<ContextRefreshedEve
record.get(7) record.get(7)
)); ));
}); });
loadCsv("submissions", record -> {
var exercise = exerciseRepository.findById(record.get(0)).orElseThrow();
BigDecimal weight = new BigDecimal(record.get(1));
ExerciseSubmission.WeightUnit unit = ExerciseSubmission.WeightUnit.valueOf(record.get(2).toUpperCase());
int reps = Integer.parseInt(record.get(3));
String name = record.get(4);
CompoundGymId gymId = CompoundGymId.parse(record.get(5));
String videoFilename = record.get(6);
try {
var uploadResp = uploadService.handleSubmissionUpload(gymId, new MockMultipartFile(
videoFilename,
videoFilename,
"video/mp4",
Files.readAllBytes(Path.of("sample_data", videoFilename))
));
submissionService.createSubmission(gymId, new ExerciseSubmissionPayload(
name,
exercise.getShortName(),
weight.floatValue(),
unit.name(),
reps,
uploadResp.id()
));
} catch (IOException e) {
e.printStackTrace();
}
});
} }
private void loadCsv(String csvName, Consumer<CSVRecord> recordConsumer) throws IOException { private void loadCsv(String csvName, Consumer<CSVRecord> recordConsumer) throws IOException {
var reader = new FileReader("sample_data/" + csvName + ".csv"); String path = "sample_data/" + csvName + ".csv";
log.info("Loading data from {}...", path);
var reader = new FileReader(path);
for (var record : CSVFormat.DEFAULT.parse(reader)) { for (var record : CSVFormat.DEFAULT.parse(reader)) {
recordConsumer.accept(record); recordConsumer.accept(record);
} }

View File

@ -18,7 +18,6 @@ import nl.andrewlalis.gymboard_api.model.exercise.ExerciseSubmissionVideoFile;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -33,6 +32,7 @@ import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -49,19 +49,23 @@ public class ExerciseSubmissionService {
private final ExerciseSubmissionRepository exerciseSubmissionRepository; private final ExerciseSubmissionRepository exerciseSubmissionRepository;
private final ExerciseSubmissionTempFileRepository tempFileRepository; private final ExerciseSubmissionTempFileRepository tempFileRepository;
private final ExerciseSubmissionVideoFileRepository submissionVideoFileRepository; private final ExerciseSubmissionVideoFileRepository submissionVideoFileRepository;
private final Executor taskExecutor;
public ExerciseSubmissionService(GymRepository gymRepository, public ExerciseSubmissionService(GymRepository gymRepository,
StoredFileRepository fileRepository, StoredFileRepository fileRepository,
ExerciseRepository exerciseRepository, ExerciseRepository exerciseRepository,
ExerciseSubmissionRepository exerciseSubmissionRepository, ExerciseSubmissionRepository exerciseSubmissionRepository,
ExerciseSubmissionTempFileRepository tempFileRepository, ExerciseSubmissionTempFileRepository tempFileRepository,
ExerciseSubmissionVideoFileRepository submissionVideoFileRepository) { ExerciseSubmissionVideoFileRepository submissionVideoFileRepository,
Executor taskExecutor
) {
this.gymRepository = gymRepository; this.gymRepository = gymRepository;
this.fileRepository = fileRepository; this.fileRepository = fileRepository;
this.exerciseRepository = exerciseRepository; this.exerciseRepository = exerciseRepository;
this.exerciseSubmissionRepository = exerciseSubmissionRepository; this.exerciseSubmissionRepository = exerciseSubmissionRepository;
this.tempFileRepository = tempFileRepository; this.tempFileRepository = tempFileRepository;
this.submissionVideoFileRepository = submissionVideoFileRepository; this.submissionVideoFileRepository = submissionVideoFileRepository;
this.taskExecutor = taskExecutor;
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
@ -132,7 +136,7 @@ public class ExerciseSubmissionService {
public void processWaitingSubmissions() { public void processWaitingSubmissions() {
List<ExerciseSubmission> waitingSubmissions = exerciseSubmissionRepository.findAllByStatus(ExerciseSubmission.Status.WAITING); List<ExerciseSubmission> waitingSubmissions = exerciseSubmissionRepository.findAllByStatus(ExerciseSubmission.Status.WAITING);
for (var submission : waitingSubmissions) { for (var submission : waitingSubmissions) {
processSubmission(submission.getId()); taskExecutor.execute(() -> processSubmission(submission.getId()));
} }
} }
@ -146,8 +150,7 @@ public class ExerciseSubmissionService {
* </p> * </p>
* @param submissionId The submission's id. * @param submissionId The submission's id.
*/ */
@Async private void processSubmission(long submissionId) {
public void processSubmission(long submissionId) {
log.info("Starting processing of submission {}.", submissionId); log.info("Starting processing of submission {}.", submissionId);
// First try and fetch the submission. // First try and fetch the submission.
Optional<ExerciseSubmission> optionalSubmission = exerciseSubmissionRepository.findById(submissionId); Optional<ExerciseSubmission> optionalSubmission = exerciseSubmissionRepository.findById(submissionId);

View File

@ -5,3 +5,6 @@ spring.datasource.url=jdbc:postgresql://localhost:5432/gymboard-api-dev
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false spring.jpa.show-sql=false
spring.task.execution.pool.core-size=3
spring.task.execution.pool.max-size=10