made code deployable
parent
97afa9b437
commit
2c39be4302
|
@ -0,0 +1,18 @@
|
||||||
|
# Use specific platform to ensure compatibility with Linux (x86_64)
|
||||||
|
FROM --platform=linux/amd64 eclipse-temurin:17-jdk-alpine AS base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Build Stage
|
||||||
|
FROM base AS build
|
||||||
|
COPY .mvn/ .mvn
|
||||||
|
COPY mvnw pom.xml ./
|
||||||
|
RUN chmod +x mvnw # Ensure mvnw is executable
|
||||||
|
RUN ./mvnw dependency:resolve
|
||||||
|
|
||||||
|
COPY ./src ./src
|
||||||
|
RUN ./mvnw package -DskipTests
|
||||||
|
|
||||||
|
# Final Stage
|
||||||
|
FROM base AS final
|
||||||
|
COPY --from=build app/target/email-classifier-1.0.0.jar app.jar
|
||||||
|
ENTRYPOINT ["java", "-jar", "app.jar"]
|
|
@ -1,28 +1,30 @@
|
||||||
package com.email.classifier;
|
//package com.email.classifier;
|
||||||
|
//
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
import java.io.IOException;
|
//
|
||||||
|
//import java.io.IOException;
|
||||||
@Slf4j
|
//
|
||||||
@Service
|
//@Slf4j
|
||||||
@RequiredArgsConstructor
|
//@Service
|
||||||
public class AIProcessingService {
|
////@RequiredArgsConstructor
|
||||||
|
//public class AIProcessingService {
|
||||||
private final OpenAIClient openAIClient;
|
//
|
||||||
|
// @Autowired
|
||||||
public String processEmail(String emailBody) {
|
// private OpenAIClient openAIClient;
|
||||||
log.info("Sending email content to OpenAI for classification...");
|
//
|
||||||
String aiResponse = null;
|
// public String processEmail(String emailBody) {
|
||||||
try {
|
//// log.info("Sending email content to OpenAI for classification...");
|
||||||
aiResponse = openAIClient.sendRequest(emailBody);
|
// String aiResponse = null;
|
||||||
} catch (IOException e) {
|
// try {
|
||||||
throw new RuntimeException(e);
|
// aiResponse = openAIClient.sendRequest(emailBody);
|
||||||
}
|
// } catch (IOException e) {
|
||||||
|
// throw new RuntimeException(e);
|
||||||
log.info("AI Classification Response: {}", aiResponse);
|
// }
|
||||||
return aiResponse;
|
//
|
||||||
}
|
//// log.info("AI Classification Response: {}", aiResponse);
|
||||||
}
|
// return aiResponse;
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,50 +1,50 @@
|
||||||
package com.email.classifier;
|
//package com.email.classifier;
|
||||||
|
//
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.mail.Email;
|
//import org.apache.commons.mail.Email;
|
||||||
import org.apache.commons.mail.EmailException;
|
//import org.apache.commons.mail.EmailException;
|
||||||
import org.apache.commons.mail.SimpleEmail;
|
//import org.apache.commons.mail.SimpleEmail;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
|
//
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service
|
//@Service
|
||||||
public class CommonsEmailService {
|
//public class CommonsEmailService {
|
||||||
|
//
|
||||||
@Value("${spring.mail.host}")
|
// @Value("${spring.mail.host}")
|
||||||
private String smtpHost;
|
// private String smtpHost;
|
||||||
|
//
|
||||||
@Value("${spring.mail.username}")
|
// @Value("${spring.mail.username}")
|
||||||
private String senderEmail;
|
// private String senderEmail;
|
||||||
|
//
|
||||||
@Value("${spring.mail.password}")
|
// @Value("${spring.mail.password}")
|
||||||
private String smtpPassword;
|
// private String smtpPassword;
|
||||||
|
//
|
||||||
@Value("${spring.mail.port}")
|
// @Value("${spring.mail.port}")
|
||||||
private int smtpPort;
|
// private int smtpPort;
|
||||||
|
//
|
||||||
public void sendEmail(String toEmail, String subject, String ticketUrl) {
|
// public void sendEmail(String toEmail, String subject, String ticketUrl) {
|
||||||
try {
|
// try {
|
||||||
Email email = new SimpleEmail();
|
// Email email = new SimpleEmail();
|
||||||
email.setHostName(smtpHost);
|
// email.setHostName(smtpHost);
|
||||||
email.setSmtpPort(smtpPort);
|
// email.setSmtpPort(smtpPort);
|
||||||
email.setAuthentication(senderEmail, smtpPassword);
|
// email.setAuthentication(senderEmail, smtpPassword);
|
||||||
email.setSSLOnConnect(true);
|
// email.setSSLOnConnect(true);
|
||||||
email.setFrom(senderEmail);
|
// email.setFrom(senderEmail);
|
||||||
email.setSubject("Support Ticket Created for " + subject);
|
// email.setSubject("Support Ticket Created for " + subject);
|
||||||
email.setMsg(getBody(subject, ticketUrl));
|
// email.setMsg(getBody(subject, ticketUrl));
|
||||||
email.addTo(toEmail);
|
// email.addTo(toEmail);
|
||||||
email.send();
|
// email.send();
|
||||||
|
//
|
||||||
log.info("Email sent successfully to {}", toEmail);
|
//// log.info("Email sent successfully to {}", toEmail);
|
||||||
} catch (EmailException e) {
|
// } catch (EmailException e) {
|
||||||
log.error("Error sending email via Apache Commons Email", e);
|
//// log.error("Error sending email via Apache Commons Email", e);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private String getBody(String subject, String ticketUrl) {
|
// private String getBody(String subject, String ticketUrl) {
|
||||||
return "Dear User,\n\nYour support request has been logged in Jira.\n" +
|
// return "Dear User,\n\nYour support request has been logged in Jira.\n" +
|
||||||
"\nThis is in regard with your email with subject: " + subject +
|
// "\nThis is in regard with your email with subject: " + subject +
|
||||||
"\n\nYou can track the issue at: " + ticketUrl + "\n\nBest regards,\nSupport Team";
|
// "\n\nYou can track the issue at: " + ticketUrl + "\n\nBest regards,\nSupport Team";
|
||||||
}
|
// }
|
||||||
}
|
//}
|
|
@ -1,45 +1,49 @@
|
||||||
package com.email.classifier;
|
//package com.email.classifier;
|
||||||
|
//
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
//import org.springframework.stereotype.Service;
|
||||||
import java.util.Map;
|
//
|
||||||
|
//import java.util.Map;
|
||||||
@Slf4j
|
//
|
||||||
@Service
|
//@Slf4j
|
||||||
@RequiredArgsConstructor
|
//@Service
|
||||||
public class EmailProcessingService {
|
////@RequiredArgsConstructor
|
||||||
|
//public class EmailProcessingService {
|
||||||
private final AIProcessingService aiProcessingService;
|
//
|
||||||
private final JiraService jiraService;
|
// @Autowired
|
||||||
|
// private AIProcessingService aiProcessingService;
|
||||||
public void processEmail(String emailBody) {
|
//
|
||||||
log.info("Processing email...");
|
// @Autowired
|
||||||
|
// private JiraService jiraService;
|
||||||
// Step 1: Send email content to AI for classification
|
//
|
||||||
String aiResponse = aiProcessingService.processEmail(emailBody);
|
// public void processEmail(String emailBody) {
|
||||||
Map<String, String> extractedData = extractKeyDetails(aiResponse);
|
//// log.info("Processing email...");
|
||||||
|
//
|
||||||
String requestType = extractedData.get("requestType");
|
// // Step 1: Send email content to AI for classification
|
||||||
String subRequestType = extractedData.get("subRequestType");
|
// String aiResponse = aiProcessingService.processEmail(emailBody);
|
||||||
|
// Map<String, String> extractedData = extractKeyDetails(aiResponse);
|
||||||
log.info("Email classified as: {} -> {}", requestType, subRequestType);
|
//
|
||||||
|
// String requestType = extractedData.get("requestType");
|
||||||
// Step 2: Create Jira ticket based on classification
|
// String subRequestType = extractedData.get("subRequestType");
|
||||||
String summary = "New Service Request: " + requestType;
|
//
|
||||||
String description = "Details: " + emailBody + "\n\nAI Classification: " + aiResponse;
|
//// log.info("Email classified as: {} -> {}", requestType, subRequestType);
|
||||||
|
//
|
||||||
// jiraService.createJiraTicket(summary, description);
|
// // Step 2: Create Jira ticket based on classification
|
||||||
|
// String summary = "New Service Request: " + requestType;
|
||||||
log.info("Jira ticket created successfully.");
|
// String description = "Details: " + emailBody + "\n\nAI Classification: " + aiResponse;
|
||||||
}
|
//
|
||||||
|
//// jiraService.createJiraTicket(summary, description);
|
||||||
private Map<String, String> extractKeyDetails(String aiResponse) {
|
//
|
||||||
// Simulating AI response parsing
|
//// log.info("Jira ticket created successfully.");
|
||||||
return Map.of(
|
// }
|
||||||
"requestType", "Loan Modification",
|
//
|
||||||
"subRequestType", "Interest Rate Change"
|
// private Map<String, String> extractKeyDetails(String aiResponse) {
|
||||||
);
|
// // Simulating AI response parsing
|
||||||
}
|
// return Map.of(
|
||||||
}
|
// "requestType", "Loan Modification",
|
||||||
|
// "subRequestType", "Interest Rate Change"
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,151 +1,151 @@
|
||||||
package com.email.classifier;
|
//package com.email.classifier;
|
||||||
|
//
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
//import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
//import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
|
//
|
||||||
import javax.mail.*;
|
//import javax.mail.*;
|
||||||
import javax.mail.internet.MimeMultipart;
|
//import javax.mail.internet.MimeMultipart;
|
||||||
import javax.mail.search.FlagTerm;
|
//import javax.mail.search.FlagTerm;
|
||||||
import java.util.Map;
|
//import java.util.Map;
|
||||||
import java.util.Properties;
|
//import java.util.Properties;
|
||||||
|
//
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service
|
//@Service
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
public class GmailIMAPService {
|
//public class GmailIMAPService {
|
||||||
|
//
|
||||||
|
//
|
||||||
@Value("${spring.mail.password}")
|
// @Value("${spring.mail.password}")
|
||||||
private String gmailAppPassword;
|
// private String gmailAppPassword;
|
||||||
|
//
|
||||||
@Value("${spring.mail.username}")
|
// @Value("${spring.mail.username}")
|
||||||
private String gmailUserName;
|
// private String gmailUserName;
|
||||||
|
//
|
||||||
private static final String HOST = "imap.gmail.com";
|
// private static final String HOST = "imap.gmail.com";
|
||||||
private static final String LABEL_NAME = "email-classification"; // Folder name for the label
|
// private static final String LABEL_NAME = "email-classification"; // Folder name for the label
|
||||||
|
//
|
||||||
private final AIProcessingService aiProcessingService;
|
// private final AIProcessingService aiProcessingService;
|
||||||
private final JiraService jiraService;
|
// private final JiraService jiraService;
|
||||||
private final CommonsEmailService commonsEmailService;
|
// private final CommonsEmailService commonsEmailService;
|
||||||
|
//
|
||||||
// @Scheduled(fixedRate = 60000) // Check every 60 seconds
|
//// @Scheduled(fixedRate = 60000) // Check every 60 seconds
|
||||||
public void fetchUnreadEmails() {
|
// public void fetchUnreadEmails() {
|
||||||
log.info("Checking for new unread emails in label '{}'", LABEL_NAME);
|
// log.info("Checking for new unread emails in label '{}'", LABEL_NAME);
|
||||||
Properties properties = new Properties();
|
// Properties properties = new Properties();
|
||||||
properties.put("mail.imap.host", HOST);
|
// properties.put("mail.imap.host", HOST);
|
||||||
properties.put("mail.imap.port", "993");
|
// properties.put("mail.imap.port", "993");
|
||||||
properties.put("mail.imap.starttls.enable", "true");
|
// properties.put("mail.imap.starttls.enable", "true");
|
||||||
|
//
|
||||||
try {
|
// try {
|
||||||
Session session = Session.getDefaultInstance(properties);
|
// Session session = Session.getDefaultInstance(properties);
|
||||||
Store store = session.getStore("imaps");
|
// Store store = session.getStore("imaps");
|
||||||
store.connect(HOST, gmailUserName, gmailAppPassword);
|
// store.connect(HOST, gmailUserName, gmailAppPassword);
|
||||||
|
//
|
||||||
Folder labelFolder = store.getFolder(LABEL_NAME);
|
// Folder labelFolder = store.getFolder(LABEL_NAME);
|
||||||
labelFolder.open(Folder.READ_WRITE);
|
// labelFolder.open(Folder.READ_WRITE);
|
||||||
|
//
|
||||||
Message[] messages = labelFolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); // Fetch unread emails
|
// Message[] messages = labelFolder.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false)); // Fetch unread emails
|
||||||
log.info("Found {} unread emails in '{}'", messages.length, LABEL_NAME);
|
// log.info("Found {} unread emails in '{}'", messages.length, LABEL_NAME);
|
||||||
|
//
|
||||||
for (Message message : messages) {
|
// for (Message message : messages) {
|
||||||
processEmail(message);
|
// processEmail(message);
|
||||||
message.setFlag(Flags.Flag.SEEN, true); // Mark email as read
|
// message.setFlag(Flags.Flag.SEEN, true); // Mark email as read
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
labelFolder.close(false);
|
// labelFolder.close(false);
|
||||||
store.close();
|
// store.close();
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
log.error("Error fetching emails from '{}': ", LABEL_NAME, e);
|
// log.error("Error fetching emails from '{}': ", LABEL_NAME, e);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private void processEmail(Message message) throws Exception {
|
// private void processEmail(Message message) throws Exception {
|
||||||
log.info("Processing email from: {}", message.getFrom()[0]);
|
// log.info("Processing email from: {}", message.getFrom()[0]);
|
||||||
log.info("Subject: {}", message.getSubject());
|
// log.info("Subject: {}", message.getSubject());
|
||||||
|
//
|
||||||
String content = getTextFromMessage(message);
|
// String content = getTextFromMessage(message);
|
||||||
log.info("Email Body: {}", content);
|
// log.info("Email Body: {}", content);
|
||||||
|
//
|
||||||
// Send email text to OpenAI for classification
|
// // Send email text to OpenAI for classification
|
||||||
String classification = aiProcessingService.processEmail(content);
|
// String classification = aiProcessingService.processEmail(content);
|
||||||
log.info("AI Classification: {}", classification);
|
// log.info("AI Classification: {}", classification);
|
||||||
Map<String, String> extractedData = extractCategoryFromAIResponse(classification);
|
// Map<String, String> extractedData = extractCategoryFromAIResponse(classification);
|
||||||
String category = extractedData.getOrDefault("category", "Unknown");
|
// String category = extractedData.getOrDefault("category", "Unknown");
|
||||||
String subCategory = extractedData.getOrDefault("subCategory", "Unknown");
|
// String subCategory = extractedData.getOrDefault("subCategory", "Unknown");
|
||||||
|
//
|
||||||
log.info("Extracted Category: {} | Subcategory: {}", category, subCategory);
|
// log.info("Extracted Category: {} | Subcategory: {}", category, subCategory);
|
||||||
|
//
|
||||||
String reporterEmail = message.getFrom()[0].toString();
|
// String reporterEmail = message.getFrom()[0].toString();
|
||||||
String ticketUrl = jiraService.createJiraTicket(message.getSubject(), content, category, subCategory, reporterEmail);
|
// String ticketUrl = jiraService.createJiraTicket(message.getSubject(), content, category, subCategory, reporterEmail);
|
||||||
|
//
|
||||||
if (ticketUrl != null) {
|
// if (ticketUrl != null) {
|
||||||
commonsEmailService.sendEmail(reporterEmail, message.getSubject(), ticketUrl);
|
// commonsEmailService.sendEmail(reporterEmail, message.getSubject(), ticketUrl);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
log.info("Jira ticket created and auto-reply sent for email: {}", message.getSubject());
|
// log.info("Jira ticket created and auto-reply sent for email: {}", message.getSubject());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private Map<String, String> extractCategoryFromAIResponse(String aiResponse) {
|
// private Map<String, String> extractCategoryFromAIResponse(String aiResponse) {
|
||||||
try {
|
// try {
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
// ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
//
|
||||||
Map<String, String> responseMap = objectMapper.readValue(aiResponse, new TypeReference<Map<String, String>>() {});
|
// Map<String, String> responseMap = objectMapper.readValue(aiResponse, new TypeReference<Map<String, String>>() {});
|
||||||
|
//
|
||||||
String category = responseMap.getOrDefault("category", "Unknown");
|
// String category = responseMap.getOrDefault("category", "Unknown");
|
||||||
String subCategory = responseMap.getOrDefault("subCategory", "Unknown");
|
// String subCategory = responseMap.getOrDefault("subCategory", "Unknown");
|
||||||
|
//
|
||||||
log.info("Extracted Category: {} | Subcategory: {}", category, subCategory);
|
// log.info("Extracted Category: {} | Subcategory: {}", category, subCategory);
|
||||||
return Map.of("category", category, "subCategory", subCategory);
|
// return Map.of("category", category, "subCategory", subCategory);
|
||||||
|
//
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
log.error("Error extracting category from AI response", e);
|
// log.error("Error extracting category from AI response", e);
|
||||||
return Map.of("category", "Unknown", "subCategory", "Unknown");
|
// return Map.of("category", "Unknown", "subCategory", "Unknown");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private String getTextFromMessage(Message message) throws Exception {
|
// private String getTextFromMessage(Message message) throws Exception {
|
||||||
if (message.isMimeType("text/plain")) {
|
// if (message.isMimeType("text/plain")) {
|
||||||
return message.getContent().toString();
|
// return message.getContent().toString();
|
||||||
} else if (message.isMimeType("multipart/*")) {
|
// } else if (message.isMimeType("multipart/*")) {
|
||||||
MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
|
// MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
|
||||||
return getTextFromMimeMultipart(mimeMultipart);
|
// return getTextFromMimeMultipart(mimeMultipart);
|
||||||
}
|
// }
|
||||||
return "";
|
// return "";
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public void listFolders() {
|
// public void listFolders() {
|
||||||
try {
|
// try {
|
||||||
Properties properties = new Properties();
|
// Properties properties = new Properties();
|
||||||
properties.put("mail.imap.ssl.enable", "true");
|
// properties.put("mail.imap.ssl.enable", "true");
|
||||||
|
//
|
||||||
Session session = Session.getInstance(properties);
|
// Session session = Session.getInstance(properties);
|
||||||
Store store = session.getStore("imaps");
|
// Store store = session.getStore("imaps");
|
||||||
store.connect("imap.gmail.com", gmailUserName, gmailAppPassword);
|
// store.connect("imap.gmail.com", gmailUserName, gmailAppPassword);
|
||||||
|
//
|
||||||
Folder[] folders = store.getDefaultFolder().list();
|
// Folder[] folders = store.getDefaultFolder().list();
|
||||||
for (Folder folder : folders) {
|
// for (Folder folder : folders) {
|
||||||
System.out.println("Found folder: " + folder.getFullName());
|
// System.out.println("Found folder: " + folder.getFullName());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
store.close();
|
// store.close();
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
e.printStackTrace();
|
// e.printStackTrace();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
private String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws Exception {
|
// private String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws Exception {
|
||||||
StringBuilder result = new StringBuilder();
|
// StringBuilder result = new StringBuilder();
|
||||||
for (int i = 0; i < mimeMultipart.getCount(); i++) {
|
// for (int i = 0; i < mimeMultipart.getCount(); i++) {
|
||||||
BodyPart bodyPart = mimeMultipart.getBodyPart(i);
|
// BodyPart bodyPart = mimeMultipart.getBodyPart(i);
|
||||||
if (bodyPart.isMimeType("text/plain")) {
|
// if (bodyPart.isMimeType("text/plain")) {
|
||||||
result.append(bodyPart.getContent());
|
// result.append(bodyPart.getContent());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return result.toString();
|
// return result.toString();
|
||||||
}
|
// }
|
||||||
}
|
//}
|
|
@ -1,60 +1,60 @@
|
||||||
package com.email.classifier;
|
//package com.email.classifier;
|
||||||
|
//
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.*;
|
//import org.springframework.http.*;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
//import org.springframework.web.client.RestTemplate;
|
||||||
|
//
|
||||||
import java.util.List;
|
//import java.util.List;
|
||||||
import java.util.Map;
|
//import java.util.Map;
|
||||||
|
//
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service
|
//@Service
|
||||||
public class JiraService {
|
//public class JiraService {
|
||||||
|
//
|
||||||
@Value("${jira.token}")
|
// @Value("${jira.token}")
|
||||||
private String jiraToken;
|
// private String jiraToken;
|
||||||
|
//
|
||||||
@Value("${jira.user}")
|
// @Value("${jira.user}")
|
||||||
private String jiraUser;
|
// private String jiraUser;
|
||||||
|
//
|
||||||
@Value("${jira.host}")
|
// @Value("${jira.host}")
|
||||||
private String jiraHost;
|
// private String jiraHost;
|
||||||
|
//
|
||||||
private final RestTemplate restTemplate = new RestTemplate();
|
// private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
//
|
||||||
public String createJiraTicket(String subject, String emailBody, String category, String subCategory, String reporterEmail) {
|
// public String createJiraTicket(String subject, String emailBody, String category, String subCategory, String reporterEmail) {
|
||||||
try {
|
// try {
|
||||||
HttpHeaders headers = new HttpHeaders();
|
// HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
// headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
headers.setBasicAuth(jiraUser, jiraToken);
|
// headers.setBasicAuth(jiraUser, jiraToken);
|
||||||
|
//
|
||||||
Map<String, Object> request = Map.of(
|
// Map<String, Object> request = Map.of(
|
||||||
"fields", Map.of(
|
// "fields", Map.of(
|
||||||
"project", Map.of("key", "SCRUM"), // Replace with your Jira project key
|
// "project", Map.of("key", "SCRUM"), // Replace with your Jira project key
|
||||||
"summary", "[Support Request] " + subject,
|
// "summary", "[Support Request] " + subject,
|
||||||
"description", String.format(
|
// "description", String.format(
|
||||||
"**Category:** %s\n**Subcategory:** %s\n**Reported By:** %s\n\n**Issue Details:**\n%s",
|
// "**Category:** %s\n**Subcategory:** %s\n**Reported By:** %s\n\n**Issue Details:**\n%s",
|
||||||
category, subCategory, reporterEmail, emailBody
|
// category, subCategory, reporterEmail, emailBody
|
||||||
),
|
// ),
|
||||||
"issuetype", Map.of("name", "Task"), // Change to "Bug" or "Incident" if needed
|
// "issuetype", Map.of("name", "Task"), // Change to "Bug" or "Incident" if needed
|
||||||
"labels", List.of(category.replace(" ", "_"), subCategory.replace(" ", "_")),
|
// "labels", List.of(category.replace(" ", "_"), subCategory.replace(" ", "_")),
|
||||||
"reporter", Map.of("accountId", "712020:2cf94bb8-d7c4-4557-8511-0bae7381f521")
|
// "reporter", Map.of("accountId", "712020:2cf94bb8-d7c4-4557-8511-0bae7381f521")
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
|
// HttpEntity<Map<String, Object>> entity = new HttpEntity<>(request, headers);
|
||||||
ResponseEntity<Map> response = restTemplate.postForEntity(jiraHost, entity, Map.class);
|
// ResponseEntity<Map> response = restTemplate.postForEntity(jiraHost, entity, Map.class);
|
||||||
String ticketId = response.getBody().get("key").toString();
|
// String ticketId = response.getBody().get("key").toString();
|
||||||
String ticketUrl = jiraHost.replace("/rest/api/2/issue", "/browse/") + ticketId;
|
// String ticketUrl = jiraHost.replace("/rest/api/2/issue", "/browse/") + ticketId;
|
||||||
|
//
|
||||||
log.info("Jira ticket created successfully: {}", ticketId);
|
// log.info("Jira ticket created successfully: {}", ticketId);
|
||||||
return ticketUrl;
|
// return ticketUrl;
|
||||||
|
//
|
||||||
} catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
log.error("Error creating Jira ticket", e);
|
// log.error("Error creating Jira ticket", e);
|
||||||
}
|
// }
|
||||||
return "";
|
// return "";
|
||||||
}
|
// }
|
||||||
}
|
//}
|
|
@ -12,4 +12,20 @@ public class LLMQueryResponse {
|
||||||
|
|
||||||
private Boolean validSql;
|
private Boolean validSql;
|
||||||
private String sql;
|
private String sql;
|
||||||
|
|
||||||
|
public Boolean getValidSql() {
|
||||||
|
return validSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValidSql(Boolean validSql) {
|
||||||
|
this.validSql = validSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSql() {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSql(String sql) {
|
||||||
|
this.sql = sql;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,11 @@ import java.util.*;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class OpenAIClient {
|
public class OpenAIClient {
|
||||||
@Qualifier("openAiClient") // if you used a named bean
|
|
||||||
private final WebClient webClient;
|
@Qualifier("openAiClient")
|
||||||
|
@Autowired
|
||||||
|
private WebClient webClient;
|
||||||
|
|
||||||
@Value("${spring.ai.openai.api-key}")
|
@Value("${spring.ai.openai.api-key}")
|
||||||
private String openAiApiKey;
|
private String openAiApiKey;
|
||||||
|
@ -31,7 +32,9 @@ public class OpenAIClient {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RestTemplate restTemplate;
|
private RestTemplate restTemplate;
|
||||||
private final ObjectMapper objectMapper;
|
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
public static final Map<String, List<String>> CATEGORY_MAP = new HashMap<>();
|
public static final Map<String, List<String>> CATEGORY_MAP = new HashMap<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -63,7 +66,7 @@ public class OpenAIClient {
|
||||||
Map.of("role", "user", "content", "Please categorize among these mentioned category and subCategory: " + CATEGORY_MAP.toString())),
|
Map.of("role", "user", "content", "Please categorize among these mentioned category and subCategory: " + CATEGORY_MAP.toString())),
|
||||||
"max_tokens", 500);
|
"max_tokens", 500);
|
||||||
String requestBody = objectMapper.writeValueAsString(request);
|
String requestBody = objectMapper.writeValueAsString(request);
|
||||||
log.info("Sending request to OpenAI: {}", requestBody); // ✅ Log the request JSON for debugging
|
// log.info("Sending request to OpenAI: {}", requestBody); // ✅ Log the request JSON for debugging
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setBearerAuth(openAiApiKey);
|
headers.setBearerAuth(openAiApiKey);
|
||||||
|
@ -76,10 +79,10 @@ public class OpenAIClient {
|
||||||
|
|
||||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||||
JsonNode jsonResponse = objectMapper.readTree(responseEntity.getBody());
|
JsonNode jsonResponse = objectMapper.readTree(responseEntity.getBody());
|
||||||
log.info(responseEntity.getBody());
|
// log.info(responseEntity.getBody());
|
||||||
return jsonResponse.get("choices").get(0).get("message").get("content").asText();
|
return jsonResponse.get("choices").get(0).get("message").get("content").asText();
|
||||||
} else {
|
} else {
|
||||||
log.error("OpenAI API Error: {}", responseEntity.getBody());
|
// log.error("OpenAI API Error: {}", responseEntity.getBody());
|
||||||
return "Error extracting text from image.";
|
return "Error extracting text from image.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.email.classifier;
|
package com.email.classifier;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
@ -9,10 +10,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/query")
|
@RequestMapping("/api/query")
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
public class QueryController {
|
public class QueryController {
|
||||||
|
|
||||||
private final QueryService queryService;
|
@Autowired
|
||||||
|
private QueryService queryService;
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<QueryResponse> query(@RequestBody QueryRequest request) {
|
public ResponseEntity<QueryResponse> query(@RequestBody QueryRequest request) {
|
||||||
|
|
|
@ -2,7 +2,15 @@ package com.email.classifier;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
//@Data
|
||||||
public class QueryRequest {
|
public class QueryRequest {
|
||||||
private String question;
|
private String question;
|
||||||
|
|
||||||
|
public String getQuestion() {
|
||||||
|
return question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuestion(String question) {
|
||||||
|
this.question = question;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -17,4 +17,79 @@ public class QueryResponse {
|
||||||
private String generatedSql;
|
private String generatedSql;
|
||||||
private int records_count;
|
private int records_count;
|
||||||
private List<Map<String, Object>> result;
|
private List<Map<String, Object>> result;
|
||||||
|
|
||||||
|
private QueryResponse(Builder builder) {
|
||||||
|
this.message = builder.message;
|
||||||
|
this.generatedSql = builder.generatedSql;
|
||||||
|
this.records_count = builder.records_count;
|
||||||
|
this.result = builder.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private String message;
|
||||||
|
private String generatedSql;
|
||||||
|
private int records_count;
|
||||||
|
private List<Map<String, Object>> result;
|
||||||
|
|
||||||
|
public Builder message(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder generatedSql(String generatedSql) {
|
||||||
|
this.generatedSql = generatedSql;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder records_count(int records_count) {
|
||||||
|
this.records_count = records_count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder result(List<Map<String, Object>> result) {
|
||||||
|
this.result = result;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QueryResponse build() {
|
||||||
|
return new QueryResponse(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGeneratedSql() {
|
||||||
|
return generatedSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGeneratedSql(String generatedSql) {
|
||||||
|
this.generatedSql = generatedSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRecords_count() {
|
||||||
|
return records_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecords_count(int records_count) {
|
||||||
|
this.records_count = records_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Map<String, Object>> getResult() {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResult(List<Map<String, Object>> result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@ -14,14 +15,18 @@ import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
public class QueryService {
|
public class QueryService {
|
||||||
|
|
||||||
private final OpenAIClient openAIClient;
|
@Autowired
|
||||||
private final SchemaExtractor schemaExtractor;
|
private OpenAIClient openAIClient;
|
||||||
private final JdbcTemplate jdbcTemplate;
|
@Autowired
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private SchemaExtractor schemaExtractor;
|
||||||
|
@Autowired
|
||||||
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
private static final int MAX_REPAIR_ATTEMPTS = 3;
|
private static final int MAX_REPAIR_ATTEMPTS = 3;
|
||||||
|
|
||||||
|
@ -35,7 +40,7 @@ public class QueryService {
|
||||||
String lastException = null;
|
String lastException = null;
|
||||||
|
|
||||||
for (int attempt = 1; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {
|
for (int attempt = 1; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {
|
||||||
log.info("Attempt {} to generate SQL for question: {}", attempt, question);
|
// log.info("Attempt {} to generate SQL for question: {}", attempt, question);
|
||||||
|
|
||||||
// Get SQL from LLM (first attempt = direct, retries = repair)
|
// Get SQL from LLM (first attempt = direct, retries = repair)
|
||||||
if (attempt == 1) {
|
if (attempt == 1) {
|
||||||
|
@ -52,7 +57,8 @@ public class QueryService {
|
||||||
llmQueryResponse = objectMapper.readValue(sanitizedJson, LLMQueryResponse.class);
|
llmQueryResponse = objectMapper.readValue(sanitizedJson, LLMQueryResponse.class);
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
String errorId = UUID.randomUUID().toString();
|
String errorId = UUID.randomUUID().toString();
|
||||||
log.error("Error id: " + errorId + " Failed to parse LLM response: {}", e.getMessage());
|
// log.error("Error id: " + errorId + " Failed to parse LLM response: {}", e.getMessage());
|
||||||
|
|
||||||
return QueryResponse.builder()
|
return QueryResponse.builder()
|
||||||
.message("Unable to process request, please try again later or contact support! Error Id: " + errorId)
|
.message("Unable to process request, please try again later or contact support! Error Id: " + errorId)
|
||||||
.records_count(0)
|
.records_count(0)
|
||||||
|
@ -78,13 +84,13 @@ public class QueryService {
|
||||||
StringWriter sw = new StringWriter();
|
StringWriter sw = new StringWriter();
|
||||||
e.printStackTrace(new PrintWriter(sw));
|
e.printStackTrace(new PrintWriter(sw));
|
||||||
lastException = sw.toString();
|
lastException = sw.toString();
|
||||||
log.error("SQL execution failed at attempt {}: {}", attempt, e.getMessage());
|
// log.error("SQL execution failed at attempt {}: {}", attempt, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String errorId = UUID.randomUUID().toString();
|
String errorId = UUID.randomUUID().toString();
|
||||||
log.error("Error id: " + errorId + " Failed to parse LLM response: {}", lastException);
|
// log.error("Error id: " + errorId + " Failed to parse LLM response: {}", lastException);
|
||||||
return QueryResponse.builder()
|
return QueryResponse.builder()
|
||||||
.message("Unable to process request, please try again later or contact support! Error Id: " + errorId)
|
.message("Unable to process request, please try again later or contact support! Error Id: " + errorId)
|
||||||
.records_count(0)
|
.records_count(0)
|
||||||
|
|
|
@ -1,89 +1,10 @@
|
||||||
//package com.email.classifier;
|
|
||||||
//
|
|
||||||
//import jakarta.annotation.PostConstruct;
|
|
||||||
//import lombok.RequiredArgsConstructor;
|
|
||||||
//import org.springframework.stereotype.Component;
|
|
||||||
//
|
|
||||||
//import javax.sql.DataSource;
|
|
||||||
//import java.sql.*;
|
|
||||||
//import java.util.*;
|
|
||||||
//
|
|
||||||
//@Component
|
|
||||||
//@RequiredArgsConstructor
|
|
||||||
//public class SchemaExtractor {
|
|
||||||
//
|
|
||||||
// private final DataSource dataSource;
|
|
||||||
//
|
|
||||||
// private final Map<String, List<String>> schemaMap = new HashMap<>();
|
|
||||||
// private final Map<String, List<String>> foreignKeyMap = new HashMap<>();
|
|
||||||
//
|
|
||||||
// public Set<String> getAllTableNames() {
|
|
||||||
// return schemaMap.keySet();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public String getRequiredSchemaData(List<String> tableNames) {
|
|
||||||
// StringBuilder schemaBuilder = new StringBuilder();
|
|
||||||
//
|
|
||||||
// tableNames.forEach(table -> {
|
|
||||||
// schemaBuilder.append("Table: ").append(table).append("\nColumns: ");
|
|
||||||
// List<String> columns = schemaMap.getOrDefault(table, new ArrayList<>());
|
|
||||||
// schemaBuilder.append(String.join(", ", columns)).append("\n");
|
|
||||||
//
|
|
||||||
// List<String> fks = foreignKeyMap.getOrDefault(table, new ArrayList<>());
|
|
||||||
// if (!fks.isEmpty()) {
|
|
||||||
// schemaBuilder.append("Foreign Keys: ").append(String.join(", ", fks)).append("\n");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// schemaBuilder.append("\n");
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// return schemaBuilder.toString();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @PostConstruct
|
|
||||||
// public void extractSchema() {
|
|
||||||
// try (Connection conn = dataSource.getConnection()) {
|
|
||||||
// DatabaseMetaData metaData = conn.getMetaData();
|
|
||||||
//
|
|
||||||
// // ✅ Extract Tables & Columns
|
|
||||||
// ResultSet tables = metaData.getTables(conn.getCatalog(), null, "%", new String[]{"TABLE"});
|
|
||||||
// while (tables.next()) {
|
|
||||||
// String tableName = tables.getString("TABLE_NAME");
|
|
||||||
//
|
|
||||||
// ResultSet columns = metaData.getColumns(conn.getCatalog(), null, tableName, "%");
|
|
||||||
// List<String> columnNames = new ArrayList<>();
|
|
||||||
// while (columns.next()) {
|
|
||||||
// columnNames.add(columns.getString("COLUMN_NAME"));
|
|
||||||
// }
|
|
||||||
// schemaMap.put(tableName, columnNames);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // ✅ Extract Foreign Keys
|
|
||||||
// for (String tableName : schemaMap.keySet()) {
|
|
||||||
// ResultSet fks = metaData.getImportedKeys(conn.getCatalog(), null, tableName);
|
|
||||||
// List<String> fkList = new ArrayList<>();
|
|
||||||
// while (fks.next()) {
|
|
||||||
// String fkColumn = fks.getString("FKCOLUMN_NAME");
|
|
||||||
// String pkTable = fks.getString("PKTABLE_NAME");
|
|
||||||
// String pkColumn = fks.getString("PKCOLUMN_NAME");
|
|
||||||
// fkList.add(fkColumn + " -> " + pkTable + "." + pkColumn);
|
|
||||||
// }
|
|
||||||
// foreignKeyMap.put(tableName, fkList);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// } catch (SQLException e) {
|
|
||||||
// throw new RuntimeException("Error extracting schema", e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
package com.email.classifier;
|
package com.email.classifier;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
@ -91,11 +12,12 @@ import java.sql.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
public class SchemaExtractor {
|
public class SchemaExtractor {
|
||||||
|
|
||||||
private final DataSource dataSource;
|
@Autowired
|
||||||
|
private DataSource dataSource;
|
||||||
|
|
||||||
private final Map<String, TableSchema> schemaMap = new HashMap<>();
|
private final Map<String, TableSchema> schemaMap = new HashMap<>();
|
||||||
|
|
||||||
|
@ -170,7 +92,7 @@ public class SchemaExtractor {
|
||||||
schemaMap.put(tableName, tableSchema);
|
schemaMap.put(tableName, tableSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("✅ Schema extracted for {} tables/views", schemaMap.size());
|
// log.info("✅ Schema extracted for {} tables/views", schemaMap.size());
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new RuntimeException("Error extracting schema", e);
|
throw new RuntimeException("Error extracting schema", e);
|
||||||
|
@ -179,7 +101,7 @@ public class SchemaExtractor {
|
||||||
|
|
||||||
// ---------- Inner Classes ----------
|
// ---------- Inner Classes ----------
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class TableSchema {
|
public static class TableSchema {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String type; // TABLE or VIEW
|
private final String type; // TABLE or VIEW
|
||||||
|
@ -188,6 +110,35 @@ public class SchemaExtractor {
|
||||||
private final Map<String, String> foreignKeys = new LinkedHashMap<>();
|
private final Map<String, String> foreignKeys = new LinkedHashMap<>();
|
||||||
private final Map<String, List<String>> indexes = new LinkedHashMap<>();
|
private final Map<String, List<String>> indexes = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public TableSchema(String name, String type) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ColumnSchema> getColumns() {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPrimaryKeys() {
|
||||||
|
return primaryKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getForeignKeys() {
|
||||||
|
return foreignKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<String>> getIndexes() {
|
||||||
|
return indexes;
|
||||||
|
}
|
||||||
|
|
||||||
public String toPrettyString() {
|
public String toPrettyString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(type).append(": ").append(name).append("\n");
|
sb.append(type).append(": ").append(name).append("\n");
|
||||||
|
@ -220,6 +171,29 @@ public class SchemaExtractor {
|
||||||
private final int size;
|
private final int size;
|
||||||
private final boolean nullable;
|
private final boolean nullable;
|
||||||
|
|
||||||
|
public ColumnSchema(String name, String type, int size, boolean nullable) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.size = size;
|
||||||
|
this.nullable = nullable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNullable() {
|
||||||
|
return nullable;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + " (" + type + (size > 0 ? "(" + size + ")" : "") + (nullable ? ", NULL" : ", NOT NULL") + ")";
|
return name + " (" + type + (size > 0 ? "(" + size + ")" : "") + (nullable ? ", NULL" : ", NOT NULL") + ")";
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
package com.email.classifier.controller;
|
//package com.email.classifier.controller;
|
||||||
|
//
|
||||||
import com.email.classifier.service.OpenAiVisionService;
|
//import com.email.classifier.service.OpenAiVisionService;
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.ResponseEntity;
|
//import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
//import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
//import org.springframework.web.multipart.MultipartFile;
|
||||||
|
//
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
|
//
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@RestController
|
//@RestController
|
||||||
@RequestMapping("/api/v1/images")
|
//@RequestMapping("/api/v1/images")
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
public class ImageUploadController {
|
//public class ImageUploadController {
|
||||||
|
//
|
||||||
private final OpenAiVisionService openAiVisionService;
|
// private final OpenAiVisionService openAiVisionService;
|
||||||
|
//
|
||||||
@PostMapping("/extract-text")
|
// @PostMapping("/extract-text")
|
||||||
public ResponseEntity<String> extractTextFromImage(@RequestParam("file") MultipartFile file) {
|
// public ResponseEntity<String> extractTextFromImage(@RequestParam("file") MultipartFile file) {
|
||||||
try {
|
// try {
|
||||||
log.info("Processing image: {}", file.getOriginalFilename());
|
// log.info("Processing image: {}", file.getOriginalFilename());
|
||||||
String extractedText = openAiVisionService.extractTextFromImage(file);
|
// String extractedText = openAiVisionService.extractTextFromImage(file);
|
||||||
return ResponseEntity.ok(extractedText);
|
// return ResponseEntity.ok(extractedText);
|
||||||
} catch (IOException e) {
|
// } catch (IOException e) {
|
||||||
log.error("Failed to process image", e);
|
// log.error("Failed to process image", e);
|
||||||
return ResponseEntity.status(500).body("Error processing image.");
|
// return ResponseEntity.status(500).body("Error processing image.");
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
@ -1,70 +1,70 @@
|
||||||
package com.email.classifier.service;
|
//package com.email.classifier.service;
|
||||||
|
//
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
//import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
//import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
//import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
//import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
//import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.http.*;
|
//import org.springframework.http.*;
|
||||||
import org.springframework.stereotype.Service;
|
//import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
//import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
//import org.springframework.web.multipart.MultipartFile;
|
||||||
|
//
|
||||||
import java.io.IOException;
|
//import java.io.IOException;
|
||||||
import java.util.Collections;
|
//import java.util.Collections;
|
||||||
|
//
|
||||||
@Slf4j
|
//@Slf4j
|
||||||
@Service
|
//@Service
|
||||||
@RequiredArgsConstructor
|
//@RequiredArgsConstructor
|
||||||
public class OpenAiVisionService {
|
//public class OpenAiVisionService {
|
||||||
|
//
|
||||||
@Value("${spring.ai.openai.api-key}")
|
// @Value("${spring.ai.openai.api-key}")
|
||||||
private String openAiApiKey;
|
// private String openAiApiKey;
|
||||||
|
//
|
||||||
@Autowired
|
// @Autowired
|
||||||
private RestTemplate restTemplate;
|
// private RestTemplate restTemplate;
|
||||||
private final ObjectMapper objectMapper;
|
// private final ObjectMapper objectMapper;
|
||||||
|
//
|
||||||
private static final String OPENAI_VISION_URL = "https://api.openai.com/v1/chat/completions";
|
// private static final String OPENAI_VISION_URL = "https://api.openai.com/v1/chat/completions";
|
||||||
|
//
|
||||||
/**
|
// /**
|
||||||
* Extracts text from an uploaded image using OpenAI Vision API.
|
// * Extracts text from an uploaded image using OpenAI Vision API.
|
||||||
*/
|
// */
|
||||||
public String extractTextFromImage(MultipartFile file) throws IOException {
|
// public String extractTextFromImage(MultipartFile file) throws IOException {
|
||||||
// byte[] imageBytes = file.getBytes();
|
//// byte[] imageBytes = file.getBytes();
|
||||||
// String base64Image = java.util.Base64.getEncoder().encodeToString(imageBytes);
|
//// String base64Image = java.util.Base64.getEncoder().encodeToString(imageBytes);
|
||||||
String base64Image = "";
|
// String base64Image = "";
|
||||||
// JSON payload for OpenAI Vision API
|
// // JSON payload for OpenAI Vision API
|
||||||
String requestBody = """
|
// String requestBody = """
|
||||||
{
|
// {
|
||||||
"model": "gpt-4.5-preview",
|
// "model": "gpt-4.5-preview",
|
||||||
"messages": [{
|
// "messages": [{
|
||||||
"role": "user",
|
// "role": "user",
|
||||||
"content": [
|
// "content": [
|
||||||
{ "type": "text", "text": "Extract the text from the image, and output just that" }
|
// { "type": "text", "text": "Extract the text from the image, and output just that" }
|
||||||
]
|
// ]
|
||||||
}],
|
// }],
|
||||||
"max_tokens": 500
|
// "max_tokens": 500
|
||||||
}""";
|
// }""";
|
||||||
|
//
|
||||||
HttpHeaders headers = new HttpHeaders();
|
// HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setBearerAuth(openAiApiKey);
|
// headers.setBearerAuth(openAiApiKey);
|
||||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
// headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
//
|
||||||
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
|
// HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
|
||||||
|
//
|
||||||
// Send request to OpenAI
|
// // Send request to OpenAI
|
||||||
ResponseEntity<String> responseEntity = restTemplate.exchange(OPENAI_VISION_URL, HttpMethod.POST, requestEntity, String.class);
|
// ResponseEntity<String> responseEntity = restTemplate.exchange(OPENAI_VISION_URL, HttpMethod.POST, requestEntity, String.class);
|
||||||
|
//
|
||||||
if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
// if (responseEntity.getStatusCode() == HttpStatus.OK) {
|
||||||
JsonNode jsonResponse = objectMapper.readTree(responseEntity.getBody());
|
// JsonNode jsonResponse = objectMapper.readTree(responseEntity.getBody());
|
||||||
log.info(responseEntity.getBody());
|
// log.info(responseEntity.getBody());
|
||||||
return jsonResponse.get("choices").get(0).get("message").get("content").asText();
|
// return jsonResponse.get("choices").get(0).get("message").get("content").asText();
|
||||||
} else {
|
// } else {
|
||||||
log.error("OpenAI API Error: {}", responseEntity.getBody());
|
// log.error("OpenAI API Error: {}", responseEntity.getBody());
|
||||||
return "Error extracting text from image.";
|
// return "Error extracting text from image.";
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
package com.email.classifier;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
|
|
||||||
@SpringBootTest
|
|
||||||
class ApplicationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void contextLoads() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue