Skip to content

Commit

Permalink
Merge branch 'develop' into feature#551
Browse files Browse the repository at this point in the history
  • Loading branch information
MarLea11 authored Jul 17, 2024
2 parents feaf110 + 2efd4dc commit d0d6018
Show file tree
Hide file tree
Showing 22 changed files with 250 additions and 39 deletions.
4 changes: 4 additions & 0 deletions itachallenge-challenge/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ dependencies {
implementation 'commons-validator:commons-validator:1.7'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

implementation 'org.slf4j:slf4j-api:1.7.32'
implementation 'org.slf4j:slf4j-simple:1.7.32'


testImplementation 'org.springframework.boot:spring-boot-starter-test:3.0.6'
testImplementation 'org.springframework:spring-test:5.3.13'
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,11 @@ public Mono<ResponseEntity<Map<String, String>>> patchResourcesById(@PathVariabl
responses = {
@ApiResponse(responseCode = "200", content = {@Content(schema = @Schema(implementation = ChallengeDto.class), mediaType = "application/json")})
})
public Flux<ChallengeDto> getAllChallenges
(@RequestParam(defaultValue = DEFAULT_OFFSET) @ValidGenericPattern(message = INVALID_PARAM) String offset,
@RequestParam(defaultValue = DEFAULT_LIMIT) @ValidGenericPattern(pattern = LIMIT, message = INVALID_PARAM) String
limit) {
return challengeService.getAllChallenges((Integer.parseInt(offset)), Integer.parseInt(limit));

public Mono<GenericResultDto<ChallengeDto>> getAllChallenges(
@RequestParam(defaultValue = DEFAULT_OFFSET) @ValidGenericPattern(message = INVALID_PARAM) String offset,
@RequestParam(defaultValue = DEFAULT_LIMIT) @ValidGenericPattern(pattern = LIMIT, message = INVALID_PARAM) String limit) {
return challengeService.getAllChallenges(Integer.parseInt(offset), Integer.parseInt(limit));
}

@GetMapping("/challenges/")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ public class GenericResultDto<T> {

public GenericResultDto() {}

public GenericResultDto(int offset, int limit, int count, T[] results) {
this.offset = offset;
this.limit = limit;
this.count = count;
this.results = results;
}


public void setInfo(int offset, int limit, int count, T[] results) {
this.offset = offset;
this.limit = limit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public interface ChallengeRepository extends ReactiveSortingRepository<Challenge
@Query(value = "{}", fields = "{'testingValues':0}")
Flux<ChallengeDocument> findAllByUuidNotNullExcludingTestingValues();
Flux<ChallengeDocument> findAllByResourcesContaining(UUID idResource);
Mono<Long> count();
Mono<Void> deleteByUuid(UUID uuid);
Mono<ChallengeDocument> save(ChallengeDocument challenge);
Flux<ChallengeDocument> saveAll(Flux<ChallengeDocument> challengeDocumentFlux);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,15 @@
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;


import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;





@Service
public class ChallengeServiceImp implements IChallengeService {

Expand Down Expand Up @@ -149,9 +154,22 @@ public Mono<GenericResultDto<LanguageDto>> getAllLanguages() {
}

@Override
public Flux<ChallengeDto> getAllChallenges(int offset, int limit) {
public Mono<GenericResultDto<ChallengeDto>> getAllChallenges(int offset, int limit) {

return challengeConverter.convertDocumentFluxToDtoFlux(challengeRepository.findAllByUuidNotNullExcludingTestingValues().skip(offset).take(limit) , ChallengeDto.class);
Mono<Long> countMono = challengeRepository.count();
Flux<ChallengeDto> challengeDtoFlux = challengeConverter.convertDocumentFluxToDtoFlux(
challengeRepository.findAllByUuidNotNullExcludingTestingValues()
.skip(offset)
.take(limit),
ChallengeDto.class);

return countMono.zipWith(challengeDtoFlux.collectList(), (totalCount, challenges) -> {
ChallengeDto[] challengeArray = challenges.toArray(new ChallengeDto[0]);
return new GenericResultDto<>(offset, limit, totalCount.intValue(), challengeArray);
}).onErrorResume(e -> {
// Manejo de errores, por ejemplo, devolver un GenericResultDto vacío o un error específico.
return Mono.just(new GenericResultDto<>(offset, limit, 0, new ChallengeDto[0]));
});
}

public Mono<GenericResultDto<SolutionDto>> getSolutions(String idChallenge, String idLanguage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IChallengeService {
Mono<GenericResultDto<LanguageDto>> getAllLanguages();
Mono<GenericResultDto<SolutionDto>> getSolutions(String idChallenge, String idLanguage);
Mono<SolutionDto> addSolution(SolutionDto solutionDto);
Flux<ChallengeDto> getAllChallenges(int offset, int limit);
Mono<GenericResultDto<ChallengeDto>> getAllChallenges(int offset, int limit);
Mono<GenericResultDto<ChallengeDto>> getChallengesByLanguageOrDifficulty(Optional<String> idLanguage, Optional<String> level, int offset, int limit);
Mono<GenericResultDto<ChallengeDto>> getRelatedChallenges(String id, int offset, int limit);
Mono<Map<String, Object>> getTestingParamsByChallengeIdAndLanguageId(String idChallenge, String idLanguage);
Expand Down
2 changes: 1 addition & 1 deletion itachallenge-challenge/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
spring:
application:
name: itachallenge-challenge
version: 2.0.1-RELEASE
version: 2.0.2-RELEASE
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,16 @@ void getAllChallenges_ValidPageParameters_ChallengesReturned() {
ChallengeDto challengeDto2 = new ChallengeDto();
ChallengeDto challengeDto3 = new ChallengeDto();
ChallengeDto[] expectedChallenges = {challengeDto1, challengeDto2, challengeDto3};
Flux<ChallengeDto> expectedChallengesFlux = Flux.just(expectedChallenges);
GenericResultDto<ChallengeDto> expectedResult = new GenericResultDto<>();
expectedResult.setInfo(0, 3, 3, expectedChallenges);

Mono<GenericResultDto<ChallengeDto>> expectedResultMono = Mono.just(expectedResult);

String offset = "0";
String limit = "3";

when(challengeService.getAllChallenges(Integer.parseInt(offset), Integer.parseInt(limit)))
.thenReturn(expectedChallengesFlux);
.thenReturn(expectedResultMono);

// Act & Assert
webTestClient.get()
Expand All @@ -174,13 +177,17 @@ void getAllChallenges_NullPageParameters_ChallengesReturned() {
ChallengeDto challengeDto1 = new ChallengeDto();
ChallengeDto challengeDto2 = new ChallengeDto();
ChallengeDto[] expectedChallenges = {challengeDto1, challengeDto2};
Flux<ChallengeDto> expectedChallengesFlux = Flux.just(expectedChallenges);
GenericResultDto<ChallengeDto> expectedResult = new GenericResultDto<>();
expectedResult.setInfo(0, 2, 2, expectedChallenges);

Mono<GenericResultDto<ChallengeDto>> expectedResultMono = Mono.just(expectedResult);


String offset = "0";
String limit = "2";

when(challengeService.getAllChallenges(Integer.parseInt(offset), Integer.parseInt(limit)))
.thenReturn(expectedChallengesFlux);
.thenReturn(expectedResultMono);

// Act & Assert
webTestClient.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ void getChallengesByPages_NullPageParameters_ChallengesReturned() {
.expectStatus().isOk()
.expectBodyList(ChallengeDto.class)
.contains(new ChallengeDto[]{})
.hasSize(2);
.hasSize(1);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.itachallenge.challenge.repository.ChallengeRepository;
import com.itachallenge.challenge.repository.LanguageRepository;
import com.itachallenge.challenge.repository.SolutionRepository;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
Expand Down Expand Up @@ -206,43 +207,33 @@ void getAllChallenges_ChallengesExist_ChallengesReturned() {
challenge1.setUuid(UUID.randomUUID());
ChallengeDocument challenge2 = new ChallengeDocument();
challenge2.setUuid(UUID.randomUUID());
ChallengeDocument challenge3 = new ChallengeDocument();
challenge3.setUuid(UUID.randomUUID());
ChallengeDocument challenge4 = new ChallengeDocument();
challenge4.setUuid(UUID.randomUUID());

// Simulate a set of ChallengeDto
ChallengeDto challengeDto1 = new ChallengeDto();
ChallengeDto challengeDto2 = new ChallengeDto();
ChallengeDto challengeDto3 = new ChallengeDto();
ChallengeDto challengeDto4 = new ChallengeDto();

when(challengeRepository.findAllByUuidNotNullExcludingTestingValues())
.thenReturn(Flux.just(challenge1, challenge2, challenge3, challenge4));
when(challengeConverter.convertDocumentFluxToDtoFlux(any(), any())).thenReturn(Flux.just(challengeDto1, challengeDto2, challengeDto3, challengeDto4));

.thenReturn(Flux.just(challenge1, challenge2));
when(challengeConverter.convertDocumentFluxToDtoFlux(any(), any())).thenReturn(Flux.just(challengeDto1, challengeDto2));
when(challengeRepository.count()).thenReturn(Mono.just(100L));
// Act
Flux<ChallengeDto> result = challengeService.getAllChallenges(offset, limit);
Mono<GenericResultDto<ChallengeDto>> result = challengeService.getAllChallenges(offset, limit);

// Assert
verify(challengeRepository).findAllByUuidNotNullExcludingTestingValues();
verify(challengeConverter).convertDocumentFluxToDtoFlux(any(), any());

StepVerifier.create(result)
.expectSubscription()
.expectNextCount(4)
.expectComplete()
.verify();

StepVerifier.create(result.skip(offset).take(limit))
.expectSubscription()
.expectNext(challengeDto2, challengeDto3)
.expectComplete()
.verify();

StepVerifier.create(challengeRepository.findAllByUuidNotNullExcludingTestingValues().skip(offset).take(limit))
StepVerifier.create(result)
.expectSubscription()
.expectNextCount(2)
.assertNext(resultDto -> {
Assertions.assertEquals(100, resultDto.getCount());
Assertions.assertEquals(offset, resultDto.getOffset());
Assertions.assertEquals(limit, resultDto.getLimit());
Assertions.assertEquals(2, resultDto.getResults().length);
Assertions.assertEquals(challengeDto1, resultDto.getResults()[0]);
Assertions.assertEquals(challengeDto2, resultDto.getResults()[1]);
})
.expectComplete()
.verify();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,28 @@ public Mono<ResponseEntity<Map<String, Long>>> getBookmarkCountByIdChallenge(
.map(count -> ResponseEntity.ok(Collections.singletonMap("bookmarked", count)));
}

@GetMapping(path = "/statistics/percent/{idChallenge}")
@Operation(
summary = "Percentage for a challenge idChallenge when users challengeUserStatus is not empty(started and ended in solutionUser) .",
responses = {
@ApiResponse(responseCode = "200", content = {@Content(schema = @Schema(implementation = Float.class),
mediaType = "application/json")}),
@ApiResponse(responseCode = "400", description = "Something went wrong",
content = {@Content(schema = @Schema())}),
@ApiResponse(responseCode = "404", description = "Challenge not found",
content = {@Content(schema = @Schema())})
}
)
public Mono<ResponseEntity<ChallengeUserPercentageStatisticDto>> challengeUserPercentageStatistic(
@PathVariable("idChallenge")
@GenericUUIDValid(message = "Invalid UUID for challenge")
String idChallenge) {

return serviceChallengeStatistics.getChallengeUsersPercentage(UUID.fromString(idChallenge))
.map(percentage -> new ChallengeUserPercentageStatisticDto(UUID.fromString(idChallenge), percentage))
.map(ResponseEntity::ok);
}

@PutMapping("/bookmark")
@Operation(
summary = "Mark or create a bookmark",
Expand Down Expand Up @@ -152,6 +174,7 @@ public Mono<ResponseEntity<Map<String, String>>> getVersion() {
return Mono.just(ResponseEntity.ok(response));
}


@GetMapping(path = "/{idUser}/challenges")
@Operation(
summary = "Retrieves all user challenges solutions and their status.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.itachallenge.user.dtos;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.UUID;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class ChallengeUserPercentageStatisticDto {
@JsonProperty(value = "id_challenge", index = 0)
private UUID challengeId;

@JsonProperty(index = 2)
private Float percentage;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.itachallenge.user.exception;

public class ChallengeNotFoundException extends RuntimeException {

public ChallengeNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,9 @@ public ResponseEntity<ErrorResponseDto> handleUnmodifiableSolutionException(Unmo
return ResponseEntity.status(HttpStatus.CONFLICT).body(new ErrorResponseDto(e.getMessage()));
}

@ExceptionHandler(ChallengeNotFoundException.class)
public ResponseEntity<ErrorResponseDto> handleChallengeNotFoundException(ChallengeNotFoundException e) {
return ResponseEntity.status(HttpStatus.OK).body(new ErrorResponseDto(e.getMessage()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public interface IUserSolutionRepository extends ReactiveMongoRepository<UserSol
Flux<UserSolutionDocument> findByBookmarked(Boolean bookmarked);
Flux<UserSolutionDocument> findByScore(int score);
Flux<UserSolutionDocument> findByStatus(ChallengeStatus status);
Flux<UserSolutionDocument> findByChallengeIdAndStatus(UUID challengeId, ChallengeStatus status);
Mono<Boolean> existsByUuid(UUID uuid);
Mono<Long> countByChallengeIdAndBookmarked(UUID challengeId, boolean isBookmarked);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@
public interface IServiceChallengeStatistics {
Mono<List<ChallengeStatisticsDto>> getChallengeStatistics(List<UUID> challengeIds);
Mono<Long> getBookmarkCountByIdChallenge(UUID idChallenge);

Mono<Float> getChallengeUsersPercentage(UUID idChallenge);
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package com.itachallenge.user.service;

import com.itachallenge.user.document.UserSolutionDocument;
import com.itachallenge.user.dtos.ChallengeStatisticsDto;
import com.itachallenge.user.dtos.UserSolutionDto;
import com.itachallenge.user.enums.ChallengeStatus;
import com.itachallenge.user.repository.IUserSolutionRepository;
import com.itachallenge.user.exception.ChallengeNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class ServiceChallengeStatistics implements IServiceChallengeStatistics {
Expand All @@ -18,6 +24,8 @@ public class ServiceChallengeStatistics implements IServiceChallengeStatistics {
//region ATTRIBUTES
SecureRandom random = new SecureRandom();

private static final String CHALLENGE_NOT_FOUND_ERROR = "Challenge with id %s not found";

//endregion ATTRIBUTES


Expand Down Expand Up @@ -59,5 +67,44 @@ public Mono<Long> getBookmarkCountByIdChallenge(UUID idChallenge) {
}

//endregion METHODS
@Override
public Mono<Float> getChallengeUsersPercentage(UUID idChallenge) {



Mono<Long> startedChallengesCount = userSolutionRepository.findByChallengeIdAndStatus(idChallenge, ChallengeStatus.STARTED).count();

Mono<Long> endedChallengesCount = userSolutionRepository.findByChallengeIdAndStatus(idChallenge, ChallengeStatus.ENDED).count();

Mono<Long> allChallenges = userSolutionRepository.findByChallengeId(idChallenge).count();


Mono<Float> percentage = startedChallengesCount.zipWith(endedChallengesCount, (value1, value2) -> value1 + value2)
.flatMap(sum -> allChallenges.flatMap(value3 -> {
if (value3 == 0) {
return Mono.error(new ChallengeNotFoundException("Challenge's id " + idChallenge + " is not found"));
}
return Mono.just((sum * 100f) / value3);
}));

return percentage;

}

private Flux<UserSolutionDocument> getUserSolutions() {
return userSolutionRepository.findAll();
}


private Flux<UserSolutionDocument> getUserSolutionsChallenge(List<UserSolutionDocument> userSolutions, UUID challengeId) {
List<UserSolutionDocument> userSolutionsChallenge = userSolutions.stream()
.filter(us -> challengeId.equals(us.getChallengeId()))
.collect(Collectors.toList());

if (userSolutionsChallenge.isEmpty()) {
return Flux.error(new ChallengeNotFoundException(String.format(CHALLENGE_NOT_FOUND_ERROR, challengeId)));
}

return Flux.fromIterable(userSolutionsChallenge);
}
}
Loading

0 comments on commit d0d6018

Please sign in to comment.