Loading movie-recommender-microservice/README.md +2 −0 Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ Returns best rated movie in given genre. Returns recommended movie according to given movie id. ### `GET /api/v1/movie-recommender/recommended-movies-according-to-rating-in-same-genre/{id}` Returns recommended movies according to rating in same genre. ## Testing You can use swagger with url /api/v1/movie-recommender/swagger-ui/index.html to test the endpoints. Loading movie-recommender-microservice/src/main/java/cz/muni/fi/iamdb/rest/MovieRecommenderController.java +7 −0 Original line number Diff line number Diff line Loading @@ -33,4 +33,11 @@ public class MovieRecommenderController { public Optional<MovieBasicViewDto> getBestRatedMovieInGenre(@PathVariable UUID id) { return movieRecommenderService.getBestRatedMovieInGenre(id); } @GetMapping("/recommended-movies-according-to-rating-in-same-genre/{id}") @Operation(summary = "Find recommended movies according to the rating in the same genre as the movie with the given id") @ApiResponse(responseCode = "200", description = "Movie found") public List<MovieBasicViewDto> getRecommendedMoviesAccordingToRatingInSameGenre(@PathVariable UUID id) { return movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); } } movie-recommender-microservice/src/main/java/cz/muni/fi/iamdb/service/MovieRecommenderService.java +54 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,42 @@ public class MovieRecommenderService { .collect(Collectors.toList()); } public List<MovieBasicViewDto> getRecommendedMoviesAccordingToRatingInSameGenre(UUID movieId) { ResponseEntity<MovieBasicViewDto> givenMoviesResponse = restTemplate.getForEntity( MOVIES_URL + "/" + movieId, MovieBasicViewDto.class ); var givenMovie = givenMoviesResponse.getBody(); if (givenMovie == null) { return Collections.emptyList(); } var genreId = givenMovie.genres()[0].getId(); ResponseEntity<MovieBasicViewDto[]> moviesWithGivenGenreResponse = restTemplate.getForEntity( MOVIES_URL + "?genreId=" + genreId, MovieBasicViewDto[].class ); var moviesWithGivenGenre = moviesWithGivenGenreResponse.getBody(); if (moviesWithGivenGenre == null || moviesWithGivenGenre.length == 0) { return Collections.emptyList(); } Map<UUID, Double> movieRatings = Arrays.stream(moviesWithGivenGenre) .collect(Collectors.toMap(MovieBasicViewDto::id, this::calculateMovieRating)); return Arrays.stream(moviesWithGivenGenre) .map(movie -> new MovieRating(movie, movieRatings.getOrDefault(movie.id(), 0.0))) .sorted() .limit(5) .map(movieRating -> movieRating.movie) .collect(Collectors.toList()); } public Optional<MovieBasicViewDto> getBestRatedMovieInGenre(UUID genreId) { ResponseEntity<MovieBasicViewDto[]> moviesWithGivenGenreResponse = restTemplate.exchange( MOVIES_URL + "?genreId=" + genreId, Loading Loading @@ -140,5 +176,23 @@ public class MovieRecommenderService { return intersection / union; } private double calculateMovieRating(MovieBasicViewDto movie){ ResponseEntity<RatingDto[]> ratingsResponse = restTemplate.getForEntity( RATINGS_URL + "/movie/" + movie.id(), RatingDto[].class ); var ratings = ratingsResponse.getBody(); if (ratings == null || ratings.length == 0) { return 0; } return Arrays.stream(ratings) .mapToDouble(RatingDto::getScore) .average() .orElse(0); } } movie-recommender-microservice/src/test/java/cz/muni/fi/iamdb/rest/MovieRecommenderRestControllerTest.java +42 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ import java.util.Optional; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(MockitoExtension.class) public class MovieRecommenderRestControllerTest { Loading Loading @@ -94,4 +93,46 @@ public class MovieRecommenderRestControllerTest { // assert assertThat(response).containsExactly(movie1, movie2); } @Test void getBestRatedMovieInGenre_noMovies_returnsEmptyOptional() { // setup UUID id = UUID.randomUUID(); Mockito.when(movieRecommenderService.getBestRatedMovieInGenre(id)).thenReturn(Optional.empty()); // act var response = movieRecommenderController.getBestRatedMovieInGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noMovies_returnsEmptyList() { // setup UUID id = UUID.randomUUID(); Mockito.when(movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id)).thenReturn(Collections.emptyList()); // act var response = movieRecommenderController.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_singleMovie_returnsThatMovie() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); Mockito.when(movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id)).thenReturn(Collections.singletonList(movie)); // act var response = movieRecommenderController.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).containsExactly(movie); } } movie-recommender-microservice/src/test/java/cz/muni/fi/iamdb/service/MovieRecommenderServiceTest.java +83 −0 Original line number Diff line number Diff line package cz.muni.fi.iamdb.service; import cz.muni.fi.iamdb.api.RatingDto; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; Loading @@ -20,6 +21,9 @@ public class MovieRecommenderServiceTest { @Value("${app.urls.movies}") private String MOVIES_URL; @Value("${app.urls.ratings}") private String RATINGS_URL; @Mock private RestTemplate restTemplate; Loading Loading @@ -60,6 +64,85 @@ public class MovieRecommenderServiceTest { // assert assertThat(response).isEmpty(); } @Test void getRecommendedMovies_noGivenMovie() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(UUID.randomUUID(), "title", 1, 1, new GenreDto[] {genre}); Mockito.when(restTemplate.getForEntity(MOVIES_URL, MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[] {movie})); // act var response = movieRecommenderService.getRecommendedMovies(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noGivenMovie() { // setup UUID id = UUID.randomUUID(); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(null)); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noMoviesWithGivenGenre() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setId(UUID.randomUUID()); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(movie)); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "?genreId=" + genre.getId(), MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[0])); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); UUID id_2 = UUID.randomUUID(); GenreDto genre_2 = new GenreDto(); genre_2.setName("Action"); var movie_2 = new MovieBasicViewDto(id_2, "title2", 2, 2, new GenreDto[] {genre_2}); RatingDto rating = new RatingDto(); rating.setScore(5.0); rating.setMovieId(id_2); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(movie)); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "?genreId=" + genre.getId(), MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[] {movie_2})); Mockito.when(restTemplate.getForEntity(RATINGS_URL + "/movie/" + id_2, RatingDto[].class)) .thenReturn(ResponseEntity.ok(new RatingDto[] {rating})); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).containsExactly(movie_2); } } Loading
movie-recommender-microservice/README.md +2 −0 Original line number Diff line number Diff line Loading @@ -29,7 +29,9 @@ Returns best rated movie in given genre. Returns recommended movie according to given movie id. ### `GET /api/v1/movie-recommender/recommended-movies-according-to-rating-in-same-genre/{id}` Returns recommended movies according to rating in same genre. ## Testing You can use swagger with url /api/v1/movie-recommender/swagger-ui/index.html to test the endpoints. Loading
movie-recommender-microservice/src/main/java/cz/muni/fi/iamdb/rest/MovieRecommenderController.java +7 −0 Original line number Diff line number Diff line Loading @@ -33,4 +33,11 @@ public class MovieRecommenderController { public Optional<MovieBasicViewDto> getBestRatedMovieInGenre(@PathVariable UUID id) { return movieRecommenderService.getBestRatedMovieInGenre(id); } @GetMapping("/recommended-movies-according-to-rating-in-same-genre/{id}") @Operation(summary = "Find recommended movies according to the rating in the same genre as the movie with the given id") @ApiResponse(responseCode = "200", description = "Movie found") public List<MovieBasicViewDto> getRecommendedMoviesAccordingToRatingInSameGenre(@PathVariable UUID id) { return movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); } }
movie-recommender-microservice/src/main/java/cz/muni/fi/iamdb/service/MovieRecommenderService.java +54 −0 Original line number Diff line number Diff line Loading @@ -65,6 +65,42 @@ public class MovieRecommenderService { .collect(Collectors.toList()); } public List<MovieBasicViewDto> getRecommendedMoviesAccordingToRatingInSameGenre(UUID movieId) { ResponseEntity<MovieBasicViewDto> givenMoviesResponse = restTemplate.getForEntity( MOVIES_URL + "/" + movieId, MovieBasicViewDto.class ); var givenMovie = givenMoviesResponse.getBody(); if (givenMovie == null) { return Collections.emptyList(); } var genreId = givenMovie.genres()[0].getId(); ResponseEntity<MovieBasicViewDto[]> moviesWithGivenGenreResponse = restTemplate.getForEntity( MOVIES_URL + "?genreId=" + genreId, MovieBasicViewDto[].class ); var moviesWithGivenGenre = moviesWithGivenGenreResponse.getBody(); if (moviesWithGivenGenre == null || moviesWithGivenGenre.length == 0) { return Collections.emptyList(); } Map<UUID, Double> movieRatings = Arrays.stream(moviesWithGivenGenre) .collect(Collectors.toMap(MovieBasicViewDto::id, this::calculateMovieRating)); return Arrays.stream(moviesWithGivenGenre) .map(movie -> new MovieRating(movie, movieRatings.getOrDefault(movie.id(), 0.0))) .sorted() .limit(5) .map(movieRating -> movieRating.movie) .collect(Collectors.toList()); } public Optional<MovieBasicViewDto> getBestRatedMovieInGenre(UUID genreId) { ResponseEntity<MovieBasicViewDto[]> moviesWithGivenGenreResponse = restTemplate.exchange( MOVIES_URL + "?genreId=" + genreId, Loading Loading @@ -140,5 +176,23 @@ public class MovieRecommenderService { return intersection / union; } private double calculateMovieRating(MovieBasicViewDto movie){ ResponseEntity<RatingDto[]> ratingsResponse = restTemplate.getForEntity( RATINGS_URL + "/movie/" + movie.id(), RatingDto[].class ); var ratings = ratingsResponse.getBody(); if (ratings == null || ratings.length == 0) { return 0; } return Arrays.stream(ratings) .mapToDouble(RatingDto::getScore) .average() .orElse(0); } }
movie-recommender-microservice/src/test/java/cz/muni/fi/iamdb/rest/MovieRecommenderRestControllerTest.java +42 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ import java.util.Optional; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(MockitoExtension.class) public class MovieRecommenderRestControllerTest { Loading Loading @@ -94,4 +93,46 @@ public class MovieRecommenderRestControllerTest { // assert assertThat(response).containsExactly(movie1, movie2); } @Test void getBestRatedMovieInGenre_noMovies_returnsEmptyOptional() { // setup UUID id = UUID.randomUUID(); Mockito.when(movieRecommenderService.getBestRatedMovieInGenre(id)).thenReturn(Optional.empty()); // act var response = movieRecommenderController.getBestRatedMovieInGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noMovies_returnsEmptyList() { // setup UUID id = UUID.randomUUID(); Mockito.when(movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id)).thenReturn(Collections.emptyList()); // act var response = movieRecommenderController.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_singleMovie_returnsThatMovie() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); Mockito.when(movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id)).thenReturn(Collections.singletonList(movie)); // act var response = movieRecommenderController.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).containsExactly(movie); } }
movie-recommender-microservice/src/test/java/cz/muni/fi/iamdb/service/MovieRecommenderServiceTest.java +83 −0 Original line number Diff line number Diff line package cz.muni.fi.iamdb.service; import cz.muni.fi.iamdb.api.RatingDto; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; Loading @@ -20,6 +21,9 @@ public class MovieRecommenderServiceTest { @Value("${app.urls.movies}") private String MOVIES_URL; @Value("${app.urls.ratings}") private String RATINGS_URL; @Mock private RestTemplate restTemplate; Loading Loading @@ -60,6 +64,85 @@ public class MovieRecommenderServiceTest { // assert assertThat(response).isEmpty(); } @Test void getRecommendedMovies_noGivenMovie() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(UUID.randomUUID(), "title", 1, 1, new GenreDto[] {genre}); Mockito.when(restTemplate.getForEntity(MOVIES_URL, MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[] {movie})); // act var response = movieRecommenderService.getRecommendedMovies(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noGivenMovie() { // setup UUID id = UUID.randomUUID(); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(null)); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre_noMoviesWithGivenGenre() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setId(UUID.randomUUID()); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(movie)); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "?genreId=" + genre.getId(), MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[0])); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).isEmpty(); } @Test void getRecommendedMoviesAccordingToRatingInSameGenre() { // setup UUID id = UUID.randomUUID(); GenreDto genre = new GenreDto(); genre.setName("Action"); var movie = new MovieBasicViewDto(id, "title", 1, 1, new GenreDto[] {genre}); UUID id_2 = UUID.randomUUID(); GenreDto genre_2 = new GenreDto(); genre_2.setName("Action"); var movie_2 = new MovieBasicViewDto(id_2, "title2", 2, 2, new GenreDto[] {genre_2}); RatingDto rating = new RatingDto(); rating.setScore(5.0); rating.setMovieId(id_2); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "/" + id, MovieBasicViewDto.class)).thenReturn(ResponseEntity.ok(movie)); Mockito.when(restTemplate.getForEntity(MOVIES_URL + "?genreId=" + genre.getId(), MovieBasicViewDto[].class)).thenReturn(ResponseEntity.ok(new MovieBasicViewDto[] {movie_2})); Mockito.when(restTemplate.getForEntity(RATINGS_URL + "/movie/" + id_2, RatingDto[].class)) .thenReturn(ResponseEntity.ok(new RatingDto[] {rating})); // act var response = movieRecommenderService.getRecommendedMoviesAccordingToRatingInSameGenre(id); // assert assertThat(response).containsExactly(movie_2); } }