Skip to content

Commit

Permalink
Merge pull request #309 from tipi-tapi/feature/307
Browse files Browse the repository at this point in the history
일기 프롬프트 생성을 위해 호출한 GPT History를 모든 일기가 갖도록 변경
  • Loading branch information
choihuk authored Mar 29, 2024
2 parents 07f9a83 + a103312 commit ea36437
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package tipitapi.drawmytoday.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

@Configuration
public class TransactionTemplateConfig {

@Bean
public TransactionTemplate writeTransactionTemplate(
PlatformTransactionManager transactionManager) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setReadOnly(false);
return transactionTemplate;
}

@Bean
public TransactionTemplate readTransactionTemplate(
PlatformTransactionManager transactionManager) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setReadOnly(true);
return transactionTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import java.util.stream.LongStream;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort.Direction;
Expand Down Expand Up @@ -60,4 +61,16 @@ public ResponseEntity<SuccessResponse<Page<GetDiaryAdminResponse>>> getDiaries(
withTest)
).asHttp(HttpStatus.OK);
}

@Operation(summary = "GPT Generator Content 추가")
@GetMapping("/add-gpt-generator-content")
public ResponseEntity<Integer> addGptGeneratorContent(
@AuthUser JwtTokenInfo jwtTokenInfo,
@RequestParam(value = "reputation", required = false, defaultValue = "1") Long reputation
) {
int addedCount = LongStream.range(0, reputation).
mapToObj(i -> adminService.addGptGeneratorContent(jwtTokenInfo.getUserId())).
reduce(0, Integer::sum);
return ResponseEntity.ok(addedCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package tipitapi.drawmytoday.domain.admin.dto;

import com.querydsl.core.annotations.QueryProjection;
import lombok.Getter;

@Getter
public class GetDiaryNoteAndPromptResponse {

private final Long promptId;
private String notes;
private final String prompt;

@QueryProjection
public GetDiaryNoteAndPromptResponse(Long promptId, String notes, String prompt) {
this.promptId = promptId;
this.notes = notes;
this.prompt = prompt;
}

public void updateNotes(String notes) {
this.notes = notes;
}

public String getGptPrompt() {
String[] promptTexts = prompt.split("Impressionist oil painting,");
return promptTexts[promptTexts.length - 1].trim();
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,106 @@
package tipitapi.drawmytoday.domain.admin.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryAdminResponse;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryNoteAndPromptResponse;
import tipitapi.drawmytoday.domain.diary.domain.Prompt;
import tipitapi.drawmytoday.domain.diary.domain.PromptGeneratorResult;
import tipitapi.drawmytoday.domain.diary.repository.PromptRepository;
import tipitapi.drawmytoday.domain.diary.service.AdminDiaryService;
import tipitapi.drawmytoday.domain.generator.api.gpt.domain.ChatCompletionsRole;
import tipitapi.drawmytoday.domain.generator.api.gpt.domain.Message;
import tipitapi.drawmytoday.domain.generator.api.gpt.dto.GptChatCompletionsRequest;
import tipitapi.drawmytoday.domain.generator.service.TranslateTextService;
import tipitapi.drawmytoday.domain.user.service.ValidateUserService;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AdminService {

private final ValidateUserService validateUserService;
private final AdminDiaryService adminDiaryService;
private final TranslateTextService translateTextService;
private final TransactionTemplate writeTransactionTemplate;
private final PromptRepository promptRepository;
private final ObjectMapper objectMapper;
@Value("${openai.gpt.chat_completions_prompt}")
private String gptChatCompletionsPrompt;

@Transactional(readOnly = true)
public Page<GetDiaryAdminResponse> getDiaries(Long userId, int size, int page,
Direction direction, Long emotionId, boolean withTest) {
validateUserService.validateAdminUserById(userId);
return adminDiaryService.getDiaries(size, page, direction, emotionId, withTest);
}

public int addGptGeneratorContent(Long userId) {
validateUserService.validateAdminUserById(userId);
List<GetDiaryNoteAndPromptResponse> responses = adminDiaryService.getDiaryNoteAndPrompt();
ExecutorService executor = Executors.newFixedThreadPool(10);
CountDownLatch latch = new CountDownLatch(responses.size());
for (int i = 0; i < responses.size(); i++) {
GetDiaryNoteAndPromptResponse response = responses.get(i);
int finalI = i;
executor.execute(() -> {
try {
String translatedNotes = translateTextService.translateAutoToEnglish(
response.getNotes());
response.updateNotes(translatedNotes);
} catch (Exception e) {
log.error("번역 API 예외가 발생했습니다.", e);
responses.remove(finalI);
} finally {
latch.countDown();
}
});
}

try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("작업이 중단되었습니다.", e);
throw new RuntimeException(e);
}

executor.shutdown();

if (responses.isEmpty()) {
throw new RuntimeException("번역할 데이터가 없거나 모두 실패했습니다.");
}

writeTransactionTemplate.executeWithoutResult(status -> {
for (GetDiaryNoteAndPromptResponse response : responses) {
Prompt prompt = promptRepository.findById(response.getPromptId()).get();
List<Message> messages = GptChatCompletionsRequest.createFirstMessage(
gptChatCompletionsPrompt, response.getNotes())
.getMessages();
messages.add(new Message(ChatCompletionsRole.assistant, response.getGptPrompt()));
String gptResponses;
try {
gptResponses = objectMapper.writeValueAsString(messages);
} catch (JsonProcessingException e) {
log.error("GPT Message를 JSON으로 변환하는데 실패했습니다.", e);
throw new RuntimeException(e);
}
PromptGeneratorResult result = PromptGeneratorResult.createGpt3Result(gptResponses);
prompt.updatePromptGeneratorResult(result);
}
});
return responses.size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,8 @@ public static Prompt create(String promptText) {
public void imageGeneratorSuccess() {
this.isSuccess = true;
}

public void updatePromptGeneratorResult(PromptGeneratorResult promptGeneratorResult) {
this.promptGeneratorResult = promptGeneratorResult;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryAdminResponse;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryNoteAndPromptResponse;
import tipitapi.drawmytoday.domain.diary.domain.Diary;
import tipitapi.drawmytoday.domain.diary.dto.GetMonthlyDiariesResponse;

Expand All @@ -20,4 +21,6 @@ Page<GetDiaryAdminResponse> getDiariesForMonitorAsPage(Pageable pageable,

List<GetMonthlyDiariesResponse> getMonthlyDiaries(Long userId, LocalDateTime startMonth,
LocalDateTime endMonth);

List<GetDiaryNoteAndPromptResponse> getDiaryNoteAndPrompt();
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.support.PageableExecutionUtils;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryAdminResponse;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryNoteAndPromptResponse;
import tipitapi.drawmytoday.domain.admin.dto.QGetDiaryAdminResponse;
import tipitapi.drawmytoday.domain.admin.dto.QGetDiaryNoteAndPromptResponse;
import tipitapi.drawmytoday.domain.diary.domain.Diary;
import tipitapi.drawmytoday.domain.diary.dto.GetMonthlyDiariesResponse;
import tipitapi.drawmytoday.domain.diary.dto.QGetMonthlyDiariesResponse;
Expand Down Expand Up @@ -86,4 +88,17 @@ public List<GetMonthlyDiariesResponse> getMonthlyDiaries(Long userId, LocalDateT
.groupBy(diary.diaryId)
.fetch();
}

@Override
public List<GetDiaryNoteAndPromptResponse> getDiaryNoteAndPrompt() {
return queryFactory.select(
new QGetDiaryNoteAndPromptResponse(prompt.promptId, diary.notes, prompt.promptText))
.from(prompt)
.join(image).on(prompt.promptId.eq(image.prompt.promptId))
.join(diary).on(image.diary.diaryId.eq(diary.diaryId))
.where(prompt.promptGeneratorResult.promptGeneratorContent.isNull())
.where(prompt.promptText.notLike("%, portrait"))
.limit(10L)
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package tipitapi.drawmytoday.domain.diary.service;

import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tipitapi.drawmytoday.common.utils.Encryptor;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryAdminResponse;
import tipitapi.drawmytoday.domain.admin.dto.GetDiaryNoteAndPromptResponse;
import tipitapi.drawmytoday.domain.diary.repository.DiaryRepository;
import tipitapi.drawmytoday.domain.r2.service.R2PreSignedService;

Expand All @@ -18,6 +22,7 @@ public class AdminDiaryService {

private final DiaryRepository diaryRepository;
private final R2PreSignedService r2PreSignedService;
private final Encryptor encryptor;
@Value("${presigned-image.expiration.admin-diaries}")
private int imageExpiration;

Expand All @@ -33,4 +38,13 @@ private GetDiaryAdminResponse generatePresignedURL(GetDiaryAdminResponse respons
r2PreSignedService.getCustomDomainUrl(response.getImageURL()));
return response;
}

public List<GetDiaryNoteAndPromptResponse> getDiaryNoteAndPrompt() {
return diaryRepository.getDiaryNoteAndPrompt()
.stream()
.map(response -> {
response.updateNotes(encryptor.decrypt(response.getNotes()));
return response;
}).collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package tipitapi.drawmytoday.domain.generator.api.google.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import tipitapi.drawmytoday.domain.generator.service.TranslateTextService;

@Slf4j
@Service
@RequiredArgsConstructor
public class GoogleTranslatorService implements TranslateTextService {

private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper;
private static final String GOOGLE_TRANSLATE_URL = "https://translate.googleapis.com/translate_a/single";

@Override
public String translateAutoToEnglish(String text) {
String translatedText = null;
try {
String jsonResult = restTemplate.getForObject(
GOOGLE_TRANSLATE_URL + "?client=gtx&sl=auto&tl=en&dt=t&dt=bd&dj=1&source=icon&q="
+ text, String.class);
JsonNode rootNode = objectMapper.readTree(jsonResult);
if (rootNode.get("sentences") != null) {
translatedText = rootNode.get("sentences").get(0).get("trans").asText();
} else {
translatedText = rootNode.get("dict").get(0).get("terms").get(0).asText();
}
} catch (RestClientException e) {
log.error("구글 번역 API 요청 실패: {}", text, e);
throw e;
} catch (JsonProcessingException e) {
log.error("구글 번역 API 응답 파싱 실패: {}", text, e);
throw new RuntimeException("구글 번역 API 응답 파싱 실패: " + text, e);
}
return translatedText;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package tipitapi.drawmytoday.domain.generator.service;

public interface TranslateTextService {

String translateAutoToEnglish(String text);
}

0 comments on commit ea36437

Please sign in to comment.