Modernize Feedback service
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package com.retailstore.feedback;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
|
||||
/**
|
||||
* Thread pool executor that allows for pausing and resuming tasks
|
||||
*/
|
||||
public class PausableThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
private boolean isPaused;
|
||||
private final ReentrantLock pauseLock = new ReentrantLock();
|
||||
private final Condition unpaused = pauseLock.newCondition();
|
||||
|
||||
public PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
|
||||
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingDeque<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void beforeExecute(Thread t, Runnable r) {
|
||||
super.beforeExecute(t, r);
|
||||
pauseLock.lock();
|
||||
try {
|
||||
while (isPaused) {
|
||||
unpaused.await();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
t.interrupt();
|
||||
} finally {
|
||||
pauseLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
pauseLock.lock();
|
||||
try {
|
||||
isPaused = true;
|
||||
} finally {
|
||||
pauseLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
pauseLock.lock();
|
||||
try {
|
||||
isPaused = false;
|
||||
unpaused.signalAll();
|
||||
} finally {
|
||||
pauseLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.retailstore.feedback;
|
||||
|
||||
import com.retailstore.feedback.model.FeedbackEntry;
|
||||
import com.retailstore.feedback.model.FeedbackEntry.FeedbackEntryBuilder;
|
||||
import edu.stanford.nlp.pipeline.CoreDocument;
|
||||
import edu.stanford.nlp.pipeline.CoreSentence;
|
||||
import edu.stanford.nlp.pipeline.StanfordCoreNLP;
|
||||
@@ -117,9 +119,8 @@ public class SentimentAnalyzer {
|
||||
if (feedbackEntry != null) {
|
||||
// Analyze sentiment
|
||||
log.info("Analyzing sentiment for entry #{}", feedbackEntry.getId());
|
||||
String sentiment = analyzeSentiment(feedbackEntry.comment);
|
||||
feedbackEntry.setSentiment(sentiment);
|
||||
|
||||
String sentiment = analyzeSentiment(feedbackEntry.getComment());
|
||||
feedbackEntry = feedbackEntry.withSentiment(sentiment);
|
||||
feedbackEntries.add(feedbackEntry);
|
||||
} else {
|
||||
log.error("Failed to parse entry at position {}", i);
|
||||
@@ -136,7 +137,7 @@ public class SentimentAnalyzer {
|
||||
* @return FeedbackEntry object
|
||||
*/
|
||||
private FeedbackEntry parseEntry(String entryText) {
|
||||
FeedbackEntry entry = new FeedbackEntry();
|
||||
FeedbackEntryBuilder entryBuilder = FeedbackEntry.builder();
|
||||
|
||||
try {
|
||||
// Split into lines for easier parsing
|
||||
@@ -150,7 +151,7 @@ public class SentimentAnalyzer {
|
||||
if (line.startsWith("Feedback #")) {
|
||||
Matcher numberMatcher = FEEDBACK_NUMBER_PATTERN.matcher(line);
|
||||
if (numberMatcher.find()) {
|
||||
entry.setId(Integer.parseInt(numberMatcher.group(1)));
|
||||
entryBuilder.id(Integer.parseInt(numberMatcher.group(1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +159,7 @@ public class SentimentAnalyzer {
|
||||
else if (line.startsWith("Customer:")) {
|
||||
Matcher customerMatcher = CUSTOMER_PATTERN.matcher(line);
|
||||
if (customerMatcher.find()) {
|
||||
entry.setCustomer(customerMatcher.group(1).trim());
|
||||
entryBuilder.customer(customerMatcher.group(1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,7 +167,7 @@ public class SentimentAnalyzer {
|
||||
else if (line.startsWith("Department:")) {
|
||||
Matcher departmentMatcher = DEPARTMENT_PATTERN.matcher(line);
|
||||
if (departmentMatcher.find()) {
|
||||
entry.setDepartment(departmentMatcher.group(1).trim());
|
||||
entryBuilder.department(departmentMatcher.group(1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +175,7 @@ public class SentimentAnalyzer {
|
||||
else if (line.startsWith("Date:")) {
|
||||
Matcher dateMatcher = DATE_PATTERN.matcher(line);
|
||||
if (dateMatcher.find()) {
|
||||
entry.setDate(dateMatcher.group(1).trim());
|
||||
entryBuilder.date(dateMatcher.group(1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +183,7 @@ public class SentimentAnalyzer {
|
||||
else if (line.startsWith("Comment:")) {
|
||||
Matcher commentMatcher = COMMENT_PATTERN.matcher(line);
|
||||
if (commentMatcher.find()) {
|
||||
entry.setComment(commentMatcher.group(1).trim());
|
||||
entryBuilder.comment(commentMatcher.group(1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +191,8 @@ public class SentimentAnalyzer {
|
||||
}
|
||||
|
||||
// Validate that we have all required fields
|
||||
if (entry.getId() > 0 && entry.getComment() != null) {
|
||||
FeedbackEntry entry = entryBuilder.build();
|
||||
if (entry.validate()) {
|
||||
return entry;
|
||||
} else {
|
||||
log.error("Invalid entry - missing ID or comment");
|
||||
@@ -212,6 +214,7 @@ public class SentimentAnalyzer {
|
||||
* @return Sentiment (VERY_POSITIVE, POSITIVE, NEUTRAL, NEGATIVE, VERY_NEGATIVE)
|
||||
*/
|
||||
public String analyzeSentiment(String comment) {
|
||||
|
||||
// Create a document from the comment
|
||||
CoreDocument doc = new CoreDocument(comment);
|
||||
|
||||
@@ -300,44 +303,4 @@ public class SentimentAnalyzer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class representing a feedback entry with sentiment analysis
|
||||
*/
|
||||
public static class FeedbackEntry {
|
||||
private int id;
|
||||
private String customer;
|
||||
private String department;
|
||||
private String date;
|
||||
private String comment;
|
||||
private String sentiment;
|
||||
|
||||
// Default values to avoid null
|
||||
public FeedbackEntry() {
|
||||
this.customer = "";
|
||||
this.department = "";
|
||||
this.date = "";
|
||||
this.comment = "";
|
||||
this.sentiment = "";
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public int getId() { return id; }
|
||||
public void setId(int id) { this.id = id; }
|
||||
|
||||
public String getCustomer() { return customer; }
|
||||
public void setCustomer(String customer) { this.customer = customer; }
|
||||
|
||||
public String getDepartment() { return department; }
|
||||
public void setDepartment(String department) { this.department = department; }
|
||||
|
||||
public String getDate() { return date; }
|
||||
public void setDate(String date) { this.date = date; }
|
||||
|
||||
public String getComment() { return comment; }
|
||||
public void setComment(String comment) { this.comment = comment; }
|
||||
|
||||
public String getSentiment() { return sentiment; }
|
||||
public void setSentiment(String sentiment) { this.sentiment = sentiment; }
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,11 @@ import java.util.List;
|
||||
@Controller
|
||||
public class FeedbackController {
|
||||
|
||||
@Autowired
|
||||
private FeedbackService feedbackService;
|
||||
private final FeedbackService feedbackService;
|
||||
|
||||
public FeedbackController(FeedbackService feedbackService) {
|
||||
this.feedbackService = feedbackService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the dashboard page.
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
package com.retailstore.feedback.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* Represents feedback enhanced with AI-generated category and actionable insights.
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
public class EnhancedFeedback extends FeedbackEntry {
|
||||
private String category;
|
||||
private String actionableInsight;
|
||||
|
||||
// Default constructor
|
||||
public EnhancedFeedback() {
|
||||
super();
|
||||
}
|
||||
|
||||
// Constructor based on FeedbackEntry
|
||||
public EnhancedFeedback(FeedbackEntry entry) {
|
||||
super(entry.getId(), entry.getCustomer(), entry.getDepartment(),
|
||||
entry.getDate(), entry.getComment(), entry.getSentiment());
|
||||
}
|
||||
|
||||
// Getters and setters for additional fields
|
||||
public String getCategory() { return category; }
|
||||
public void setCategory(String category) { this.category = category; }
|
||||
|
||||
public String getActionableInsight() { return actionableInsight; }
|
||||
public void setActionableInsight(String actionableInsight) {
|
||||
this.actionableInsight = actionableInsight;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,34 @@
|
||||
package com.retailstore.feedback.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.With;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* Represents a feedback entry with sentiment analysis.
|
||||
*/
|
||||
@Getter
|
||||
@SuperBuilder
|
||||
@AllArgsConstructor
|
||||
public class FeedbackEntry {
|
||||
|
||||
private int id;
|
||||
private String customer;
|
||||
private String department;
|
||||
private String date;
|
||||
private String comment;
|
||||
private String sentiment;
|
||||
|
||||
// Default constructor
|
||||
public FeedbackEntry() {}
|
||||
@Builder.Default
|
||||
private String customer = "";
|
||||
@Builder.Default
|
||||
private String department = "";
|
||||
@Builder.Default
|
||||
private String date = "";
|
||||
@Builder.Default
|
||||
private String comment = "";
|
||||
@With
|
||||
@Builder.Default
|
||||
private String sentiment = "";
|
||||
|
||||
// Constructor with parameters
|
||||
public FeedbackEntry(int id, String customer, String department,
|
||||
String date, String comment, String sentiment) {
|
||||
this.id = id;
|
||||
this.customer = customer;
|
||||
this.department = department;
|
||||
this.date = date;
|
||||
this.comment = comment;
|
||||
this.sentiment = sentiment;
|
||||
public boolean validate() {
|
||||
return id > 0 && comment != null;
|
||||
}
|
||||
|
||||
// Getters and setters
|
||||
public int getId() { return id; }
|
||||
public void setId(int id) { this.id = id; }
|
||||
|
||||
public String getCustomer() { return customer; }
|
||||
public void setCustomer(String customer) { this.customer = customer; }
|
||||
|
||||
public String getDepartment() { return department; }
|
||||
public void setDepartment(String department) { this.department = department; }
|
||||
|
||||
public String getDate() { return date; }
|
||||
public void setDate(String date) { this.date = date; }
|
||||
|
||||
public String getComment() { return comment; }
|
||||
public void setComment(String comment) { this.comment = comment; }
|
||||
|
||||
public String getSentiment() { return sentiment; }
|
||||
public void setSentiment(String sentiment) { this.sentiment = sentiment; }
|
||||
}
|
||||
@@ -1,44 +1,21 @@
|
||||
package com.retailstore.feedback.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a summary of feedback data for the dashboard.
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
public class FeedbackSummary {
|
||||
private int totalFeedback;
|
||||
private Map<String, Integer> sentimentCounts;
|
||||
private Map<String, Integer> categoryCounts;
|
||||
private Map<String, Integer> departmentCounts;
|
||||
private List<EnhancedFeedback> recentFeedback;
|
||||
|
||||
// Default constructor
|
||||
public FeedbackSummary() {}
|
||||
|
||||
// Getters and setters
|
||||
public int getTotalFeedback() { return totalFeedback; }
|
||||
public void setTotalFeedback(int totalFeedback) {
|
||||
this.totalFeedback = totalFeedback;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getSentimentCounts() { return sentimentCounts; }
|
||||
public void setSentimentCounts(Map<String, Integer> sentimentCounts) {
|
||||
this.sentimentCounts = sentimentCounts;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getCategoryCounts() { return categoryCounts; }
|
||||
public void setCategoryCounts(Map<String, Integer> categoryCounts) {
|
||||
this.categoryCounts = categoryCounts;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getDepartmentCounts() { return departmentCounts; }
|
||||
public void setDepartmentCounts(Map<String, Integer> departmentCounts) {
|
||||
this.departmentCounts = departmentCounts;
|
||||
}
|
||||
|
||||
public List<EnhancedFeedback> getRecentFeedback() { return recentFeedback; }
|
||||
public void setRecentFeedback(List<EnhancedFeedback> recentFeedback) {
|
||||
this.recentFeedback = recentFeedback;
|
||||
}
|
||||
private final int totalFeedback;
|
||||
private final Map<String, Integer> sentimentCounts;
|
||||
private final Map<String, Integer> categoryCounts;
|
||||
private final Map<String, Integer> departmentCounts;
|
||||
private final List<EnhancedFeedback> recentFeedback;
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import com.retailstore.feedback.model.FeedbackEntry;
|
||||
import com.retailstore.feedback.model.EnhancedFeedback;
|
||||
import com.retailstore.feedback.model.FeedbackSummary;
|
||||
|
||||
import com.retailstore.feedback.model.FeedbackSummary.FeedbackSummaryBuilder;
|
||||
import org.springframework.boot.context.event.ApplicationStartedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@@ -12,6 +15,7 @@ import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -82,7 +86,7 @@ public class FeedbackService {
|
||||
}
|
||||
|
||||
// Process each line
|
||||
if (line.trim().isEmpty() && entryText.length() > 0) {
|
||||
if (line.trim().isEmpty() && !entryText.isEmpty()) {
|
||||
// We've reached the end of an entry
|
||||
FeedbackEntry entry = parseFeedbackEntry(entryText.toString());
|
||||
if (entry != null) {
|
||||
@@ -95,7 +99,7 @@ public class FeedbackService {
|
||||
}
|
||||
|
||||
// Process the last entry if there is one
|
||||
if (entryText.length() > 0) {
|
||||
if (!entryText.isEmpty()) {
|
||||
FeedbackEntry entry = parseFeedbackEntry(entryText.toString());
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
@@ -113,12 +117,12 @@ public class FeedbackService {
|
||||
* @return FeedbackEntry object or null if parsing fails
|
||||
*/
|
||||
private FeedbackEntry parseFeedbackEntry(String text) {
|
||||
FeedbackEntry entry = new FeedbackEntry();
|
||||
FeedbackEntry.FeedbackEntryBuilder<?, ?> entryBuilder = FeedbackEntry.builder();
|
||||
|
||||
// Extract feedback ID
|
||||
Matcher idMatcher = FEEDBACK_PATTERN.matcher(text);
|
||||
if (idMatcher.find()) {
|
||||
entry.setId(Integer.parseInt(idMatcher.group(1)));
|
||||
entryBuilder.id(Integer.parseInt(idMatcher.group(1)));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -126,34 +130,34 @@ public class FeedbackService {
|
||||
// Extract customer
|
||||
Matcher customerMatcher = CUSTOMER_PATTERN.matcher(text);
|
||||
if (customerMatcher.find()) {
|
||||
entry.setCustomer(customerMatcher.group(1));
|
||||
entryBuilder.customer(customerMatcher.group(1));
|
||||
}
|
||||
|
||||
// Extract department
|
||||
Matcher departmentMatcher = DEPARTMENT_PATTERN.matcher(text);
|
||||
if (departmentMatcher.find()) {
|
||||
entry.setDepartment(departmentMatcher.group(1));
|
||||
entryBuilder.department(departmentMatcher.group(1));
|
||||
}
|
||||
|
||||
// Extract date
|
||||
Matcher dateMatcher = DATE_PATTERN.matcher(text);
|
||||
if (dateMatcher.find()) {
|
||||
entry.setDate(dateMatcher.group(1));
|
||||
entryBuilder.date(dateMatcher.group(1));
|
||||
}
|
||||
|
||||
// Extract comment
|
||||
Matcher commentMatcher = COMMENT_PATTERN.matcher(text);
|
||||
if (commentMatcher.find()) {
|
||||
entry.setComment(commentMatcher.group(1));
|
||||
entryBuilder.comment(commentMatcher.group(1));
|
||||
}
|
||||
|
||||
// Extract sentiment
|
||||
Matcher sentimentMatcher = SENTIMENT_PATTERN.matcher(text);
|
||||
if (sentimentMatcher.find()) {
|
||||
entry.setSentiment(sentimentMatcher.group(1));
|
||||
entryBuilder.sentiment(sentimentMatcher.group(1));
|
||||
}
|
||||
|
||||
return entry;
|
||||
return entryBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,14 +166,16 @@ public class FeedbackService {
|
||||
* @return List of EnhancedFeedback objects
|
||||
* @throws IOException If an I/O error occurs
|
||||
*/
|
||||
public synchronized List<EnhancedFeedback> getEnhancedFeedback() throws IOException {
|
||||
@EventListener(ApplicationStartedEvent.class)
|
||||
public List<EnhancedFeedback> getEnhancedFeedback() throws IOException {
|
||||
// Return cached data if available
|
||||
if (enhancedFeedbackCache != null) {
|
||||
return enhancedFeedbackCache;
|
||||
}
|
||||
enhancedFeedbackCache = Collections.emptyList();
|
||||
|
||||
List<FeedbackEntry> entries = readFeedbackData();
|
||||
List<EnhancedFeedback> enhancedEntries = new ArrayList<>();
|
||||
List<FeedbackEntry> entries = readFeedbackData();
|
||||
|
||||
for (FeedbackEntry entry : entries) {
|
||||
EnhancedFeedback enhancedEntry = enhanceFeedback(entry);
|
||||
@@ -189,14 +195,20 @@ public class FeedbackService {
|
||||
* @return EnhancedFeedback with AI-generated category and actionable insight
|
||||
*/
|
||||
private EnhancedFeedback enhanceFeedback(FeedbackEntry entry) {
|
||||
EnhancedFeedback enhancedEntry = new EnhancedFeedback(entry);
|
||||
EnhancedFeedback.EnhancedFeedbackBuilder<?, ?> enhancedEntryBuilder = EnhancedFeedback.builder()
|
||||
.id(entry.getId())
|
||||
.date(entry.getDate())
|
||||
.comment(entry.getComment())
|
||||
.customer(entry.getCustomer())
|
||||
.department(entry.getDepartment())
|
||||
.sentiment(entry.getSentiment());
|
||||
|
||||
// Create a prompt for Gemini
|
||||
String prompt = PROMPT.formatted(entry.getComment(), entry.getDepartment(), entry.getSentiment());
|
||||
|
||||
try {
|
||||
// Call Gemini API and parse the response
|
||||
String response = geminiService.generateContent(prompt);
|
||||
String response = geminiService.generateContent(prompt).get(3, TimeUnit.SECONDS);
|
||||
|
||||
// Parse JSON response
|
||||
// This is a simple parsing approach - for production, use a proper JSON parser
|
||||
@@ -206,27 +218,27 @@ public class FeedbackService {
|
||||
Pattern categoryPattern = Pattern.compile("\"category\"\\s*:\\s*\"([^\"]+)\"");
|
||||
Matcher categoryMatcher = categoryPattern.matcher(jsonResponse);
|
||||
if (categoryMatcher.find()) {
|
||||
enhancedEntry.setCategory(categoryMatcher.group(1));
|
||||
enhancedEntryBuilder.category(categoryMatcher.group(1));
|
||||
} else {
|
||||
enhancedEntry.setCategory("Uncategorized");
|
||||
enhancedEntryBuilder.category("Uncategorized");
|
||||
}
|
||||
|
||||
// Extract actionable insight
|
||||
Pattern insightPattern = Pattern.compile("\"actionableInsight\"\\s*:\\s*\"([^\"]+)\"");
|
||||
Matcher insightMatcher = insightPattern.matcher(jsonResponse);
|
||||
if (insightMatcher.find()) {
|
||||
enhancedEntry.setActionableInsight(insightMatcher.group(1));
|
||||
enhancedEntryBuilder.actionableInsight(insightMatcher.group(1));
|
||||
} else {
|
||||
enhancedEntry.setActionableInsight("No specific action recommended.");
|
||||
enhancedEntryBuilder.actionableInsight("No specific action recommended.");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
// Handle API errors gracefully
|
||||
enhancedEntry.setCategory("Error in processing");
|
||||
enhancedEntry.setActionableInsight("Could not generate insight due to API error: " + e.getMessage());
|
||||
enhancedEntryBuilder.category("Error in processing");
|
||||
enhancedEntryBuilder.actionableInsight("Could not generate insight due to API error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return enhancedEntry;
|
||||
return enhancedEntryBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,10 +249,8 @@ public class FeedbackService {
|
||||
*/
|
||||
public FeedbackSummary generateFeedbackSummary() throws IOException {
|
||||
List<EnhancedFeedback> allFeedback = getEnhancedFeedback();
|
||||
FeedbackSummary summary = new FeedbackSummary();
|
||||
|
||||
// Set total feedback count
|
||||
summary.setTotalFeedback(allFeedback.size());
|
||||
FeedbackSummaryBuilder summaryBuilder = FeedbackSummary.builder()
|
||||
.totalFeedback(allFeedback.size());
|
||||
|
||||
// Count sentiments
|
||||
Map<String, Integer> sentimentCounts = new HashMap<>();
|
||||
@@ -248,7 +258,7 @@ public class FeedbackService {
|
||||
String sentiment = feedback.getSentiment();
|
||||
sentimentCounts.put(sentiment, sentimentCounts.getOrDefault(sentiment, 0) + 1);
|
||||
}
|
||||
summary.setSentimentCounts(sentimentCounts);
|
||||
summaryBuilder.sentimentCounts(sentimentCounts);
|
||||
|
||||
// Count categories
|
||||
Map<String, Integer> categoryCounts = new HashMap<>();
|
||||
@@ -256,7 +266,7 @@ public class FeedbackService {
|
||||
String category = feedback.getCategory();
|
||||
categoryCounts.put(category, categoryCounts.getOrDefault(category, 0) + 1);
|
||||
}
|
||||
summary.setCategoryCounts(categoryCounts);
|
||||
summaryBuilder.categoryCounts(categoryCounts);
|
||||
|
||||
// Count departments
|
||||
Map<String, Integer> departmentCounts = new HashMap<>();
|
||||
@@ -264,15 +274,15 @@ public class FeedbackService {
|
||||
String department = feedback.getDepartment();
|
||||
departmentCounts.put(department, departmentCounts.getOrDefault(department, 0) + 1);
|
||||
}
|
||||
summary.setDepartmentCounts(departmentCounts);
|
||||
summaryBuilder.departmentCounts(departmentCounts);
|
||||
|
||||
// Get recent feedback (last 5 entries)
|
||||
List<EnhancedFeedback> recentFeedback = allFeedback.stream()
|
||||
.sorted(Comparator.comparing(EnhancedFeedback::getId).reversed())
|
||||
.limit(5)
|
||||
.collect(Collectors.toList());
|
||||
summary.setRecentFeedback(recentFeedback);
|
||||
summaryBuilder.recentFeedback(recentFeedback);
|
||||
|
||||
return summary;
|
||||
return summaryBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.retailstore.feedback.PausableThreadPoolExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
@@ -16,18 +18,23 @@ import org.springframework.http.*;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* Service for interacting with Google's Gemini AI using REST API
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class GeminiService {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final String serviceUri;
|
||||
|
||||
private static final PausableThreadPoolExecutor promptExecution = new PausableThreadPoolExecutor(1,1,30, TimeUnit.SECONDS);
|
||||
private static final ScheduledExecutorService scheduleExecution = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
public GeminiService(@Value("${gemini.api.url}") String url, @Value("${gemini.api.key}") String apiKey) {
|
||||
this.objectMapper = new JsonMapper();
|
||||
serviceUri = UriComponentsBuilder
|
||||
@@ -45,7 +52,11 @@ public class GeminiService {
|
||||
* @param prompt The prompt to send to Gemini
|
||||
* @return The response from Gemini
|
||||
*/
|
||||
public String generateContent(String prompt) {
|
||||
public Future<String> generateContent(String prompt) {
|
||||
return promptExecution.submit(() -> executePrompt(prompt));
|
||||
}
|
||||
|
||||
private String executePrompt(String prompt) {
|
||||
try {
|
||||
// Create the request body using Jackson
|
||||
ObjectNode requestBody = objectMapper.createObjectNode();
|
||||
@@ -91,18 +102,11 @@ public class GeminiService {
|
||||
return "No response from Gemini";
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error calling Gemini API: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
log.error("Error calling Gemini API: {}", e.getMessage(), e);
|
||||
log.info("Pausing Gemini API calls for 10 seconds...");
|
||||
promptExecution.pause();
|
||||
scheduleExecution.schedule(promptExecution::resume, 10, TimeUnit.SECONDS);
|
||||
return "Error: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method to verify API connection
|
||||
*/
|
||||
public void testConnection() {
|
||||
String testPrompt = "Say 'Hello, World!' if you can hear me.";
|
||||
String response = generateContent(testPrompt);
|
||||
System.out.println("Gemini API Test Response: " + response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user