Skip to content

Commit

Permalink
Added getAdvice method to AIService
Browse files Browse the repository at this point in the history
  • Loading branch information
Maryna-511750 committed Dec 10, 2024
1 parent 4df7833 commit 3b0afdd
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 12 deletions.
4 changes: 0 additions & 4 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,6 @@
<artifactId>jsoup</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
10 changes: 10 additions & 0 deletions dao/src/main/java/greencity/repository/HabitRepo.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,14 @@ public interface HabitRepo extends JpaRepository<Habit, Long>, JpaSpecificationE
+ "WHERE ha.habit_id =:habitId "
+ "AND ha.user_id =:userId", nativeQuery = true)
List<Long> findHabitAssignByHabitIdAndHabitOwnerId(@Param("habitId") Long habitId, @Param("userId") Long userId);

/**
* Finds a random habit that is not marked as deleted.
*
* @return a random {@link Habit} entity that is not deleted, or {@code null} if
* no such habit exists.
*/
@Query(nativeQuery = true,
value = "SELECT * FROM habits h WHERE h.is_deleted = false ORDER BY random() LIMIT 1")
Habit findRandomHabit();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ public class OpenAIRequest {
"respond in a personalized manner, concisely, and accurately. Use numbers to present approximate data. "
+ "Provide a forecast of this person's impact on the global environment, considering their habits "
+ "over the specified number of days:";
public static final String ADVICE = "answer by starting with the words \"since you don't have active habits\" "
+ "in the specified language. and then advertise the benefits of this habit:";
}
13 changes: 13 additions & 0 deletions service-api/src/main/java/greencity/dto/habit/ShortHabitDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package greencity.dto.habit;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Data
@Builder
@AllArgsConstructor
public class ShortHabitDto {
Long id;
String description;
}
11 changes: 10 additions & 1 deletion service-api/src/main/java/greencity/service/AIService.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ public interface AIService {
* @return The forecast as a string in the specified language.
*/
String getForecast(Long userId, String language);
}

/**
* Gets advice for a user based on their ID and the selected language.
*
* @param userId The ID of the user for whom the advice is being requested.
* @param language The preferred language for the forecast response.
* @return The advice as a string in the specified language.
*/
String getAdvice(Long userId, String language);
}
17 changes: 17 additions & 0 deletions service/src/main/java/greencity/mapping/ShortHabitDtoMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package greencity.mapping;

import greencity.dto.habit.ShortHabitDto;
import greencity.entity.Habit;
import org.modelmapper.AbstractConverter;
import org.springframework.stereotype.Component;

@Component
public class ShortHabitDtoMapper extends AbstractConverter<Habit, ShortHabitDto> {
@Override
protected ShortHabitDto convert(Habit habit) {
return ShortHabitDto.builder()
.id(habit.getId())
.description(habit.getHabitTranslations().getFirst().getName())
.build();
}
}
14 changes: 14 additions & 0 deletions service/src/main/java/greencity/service/AIServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import greencity.constant.OpenAIRequest;
import greencity.dto.habit.DurationHabitDto;
import greencity.dto.habit.ShortHabitDto;
import greencity.entity.Habit;
import greencity.entity.HabitAssign;
import greencity.repository.HabitAssignRepo;
import greencity.repository.HabitRepo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
Expand All @@ -16,13 +19,24 @@
public class AIServiceImpl implements AIService {
private final OpenAIService openAIService;
private final HabitAssignRepo habitAssignRepo;
private final HabitRepo habitRepo;
private final ModelMapper modelMapper;

@Override
public String getForecast(Long userId, String language) {
List<HabitAssign> habitAssigns = habitAssignRepo.findAllByUserId(userId);
if (habitAssigns.isEmpty()) {
return getAdvice(userId, language);
}
List<DurationHabitDto> durationHabitDtos = habitAssigns.stream()
.map(habitAssign -> modelMapper.map(habitAssign, DurationHabitDto.class)).toList();
return openAIService.makeRequest(language + OpenAIRequest.FORECAST + durationHabitDtos);
}

@Override
public String getAdvice(Long userId, String language) {
Habit habit = habitRepo.findRandomHabit();
ShortHabitDto shortHabitDto = modelMapper.map(habit, ShortHabitDto.class);
return openAIService.makeRequest(language + OpenAIRequest.ADVICE + shortHabitDto);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package greencity.mapping;

import greencity.dto.habit.ShortHabitDto;
import greencity.entity.Habit;
import greencity.entity.HabitTranslation;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;

class ShortHabitDtoMapperTest {

private ShortHabitDtoMapper mapper;
private final Long id = 1L;
private final String habitTranslationName = "Test Habit";
private final HabitTranslation habitTranslation = HabitTranslation.builder().name(habitTranslationName).build();

private final Habit habit = Habit.builder()
.id(id)
.habitTranslations(Collections.singletonList(habitTranslation))
.build();

@BeforeEach
void setUp() {
mapper = new ShortHabitDtoMapper();
}

@Test
void convert_ShouldMapHabitToShortHabitDto() {
ShortHabitDto result = mapper.convert(habit);

assertEquals(id, result.getId());
assertEquals(habitTranslationName, result.getDescription());
}
}
75 changes: 68 additions & 7 deletions service/src/test/java/greencity/service/AIServiceImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

import greencity.constant.OpenAIRequest;
import greencity.dto.habit.DurationHabitDto;
import greencity.dto.habit.ShortHabitDto;
import greencity.entity.Habit;
import greencity.entity.HabitAssign;
import greencity.entity.HabitTranslation;
import greencity.enums.HabitAssignStatus;
import greencity.repository.HabitAssignRepo;
import greencity.repository.HabitRepo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.modelmapper.ModelMapper;
import java.util.Collections;
import java.util.List;
import static greencity.ModelUtils.getHabitAssign;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
Expand All @@ -25,40 +30,96 @@ class AIServiceImplTest {
@Mock
private HabitAssignRepo habitAssignRepo;
@Mock
private HabitRepo habitRepo;
@Mock
private ModelMapper modelMapper;
@InjectMocks
private AIServiceImpl aiServiceImpl;
private HabitAssign habitAssign = getHabitAssign(HabitAssignStatus.INPROGRESS);
private DurationHabitDto durationHabitDto = new DurationHabitDto("", 0L);
private Long userId = 1L;
private Long id = 1L;
private String language = "en";
private final String habitTranslationName = "Test Habit";
private final HabitTranslation habitTranslation = HabitTranslation.builder().name(habitTranslationName).build();

private final Habit habit = Habit.builder()
.id(id)
.habitTranslations(Collections.singletonList(habitTranslation))
.build();
private final ShortHabitDto shortHabitDto = new ShortHabitDto(id, habitTranslationName);

@Test
void getForecast_ReturnsResponse_FromOpenAIService() {
when(habitAssignRepo.findAllByUserId(userId)).thenReturn(List.of(habitAssign));
when(habitAssignRepo.findAllByUserId(id)).thenReturn(List.of(habitAssign));
when(modelMapper.map(habitAssign, DurationHabitDto.class)).thenReturn(durationHabitDto);
when(openAIService.makeRequest("en" + OpenAIRequest.FORECAST + List.of(durationHabitDto)))
.thenReturn("Forecast Response");

String result = aiServiceImpl.getForecast(userId, language);
String result = aiServiceImpl.getForecast(id, language);

assertThat(result).isEqualTo("Forecast Response");
verify(habitAssignRepo).findAllByUserId(userId);
verify(habitAssignRepo).findAllByUserId(id);
verify(modelMapper).map(habitAssign, DurationHabitDto.class);
verify(openAIService).makeRequest("en" + OpenAIRequest.FORECAST + List.of(durationHabitDto));
}

@Test
void getForecast_ThrowsException_WhenOpenAIServiceFails() {
when(habitAssignRepo.findAllByUserId(userId)).thenReturn(List.of(habitAssign));
when(habitAssignRepo.findAllByUserId(id)).thenReturn(List.of(habitAssign));
when(modelMapper.map(habitAssign, DurationHabitDto.class)).thenReturn(durationHabitDto);
when(openAIService.makeRequest("en" + OpenAIRequest.FORECAST + List.of(durationHabitDto)))
.thenThrow(new RuntimeException("OpenAI Service Failed"));

assertThrows(RuntimeException.class, () -> aiServiceImpl.getForecast(userId, language));
assertThrows(RuntimeException.class, () -> aiServiceImpl.getForecast(id, language));

verify(habitAssignRepo).findAllByUserId(userId);
verify(habitAssignRepo).findAllByUserId(id);
verify(modelMapper).map(habitAssign, DurationHabitDto.class);
verify(openAIService).makeRequest("en" + OpenAIRequest.FORECAST + List.of(durationHabitDto));
}

@Test
void getForecast_CallsGetAdvice_WhenHabitAssignsIsEmpty() {
when(habitAssignRepo.findAllByUserId(id)).thenReturn(Collections.emptyList());
when(habitRepo.findRandomHabit()).thenReturn(habit);
when(modelMapper.map(habit, ShortHabitDto.class)).thenReturn(shortHabitDto);
when(openAIService.makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto))
.thenReturn("Advice Response");

String result = aiServiceImpl.getForecast(id, language);

assertThat(result).isEqualTo("Advice Response");
verify(habitAssignRepo).findAllByUserId(id);
verify(habitRepo).findRandomHabit();
verify(modelMapper).map(habit, ShortHabitDto.class);
verify(openAIService).makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto);
}

@Test
void getAdvice_ReturnsResponse_FromOpenAIService() {
when(habitRepo.findRandomHabit()).thenReturn(habit);
when(modelMapper.map(habit, ShortHabitDto.class)).thenReturn(shortHabitDto);
when(openAIService.makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto))
.thenReturn("Advice Response");

String result = aiServiceImpl.getAdvice(id, language);

assertThat(result).isEqualTo("Advice Response");
verify(habitRepo).findRandomHabit();
verify(modelMapper).map(habit, ShortHabitDto.class);
verify(openAIService).makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto);
}

@Test
void getAdvice_ThrowsException_WhenOpenAIServiceFails() {
when(habitRepo.findRandomHabit()).thenReturn(habit);
when(modelMapper.map(habit, ShortHabitDto.class)).thenReturn(shortHabitDto);
when(openAIService.makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto))
.thenThrow(new RuntimeException("OpenAI Service Failed"));

assertThrows(RuntimeException.class, () -> aiServiceImpl.getAdvice(id, language));

verify(habitRepo).findRandomHabit();
verify(modelMapper).map(habit, ShortHabitDto.class);
verify(openAIService).makeRequest("en" + OpenAIRequest.ADVICE + shortHabitDto);
}
}

0 comments on commit 3b0afdd

Please sign in to comment.