analytics added
This commit is contained in:
@@ -32,6 +32,9 @@ public class KapitalParserService implements ParserService {
|
||||
@Autowired
|
||||
private MarketItemRepository marketItemRepository;
|
||||
|
||||
@Autowired
|
||||
private OllamaAnalyticsService analyticsService;
|
||||
|
||||
@Value("${rss.kapital.url}")
|
||||
private String rssFeedUrl;
|
||||
|
||||
@@ -79,6 +82,13 @@ public class KapitalParserService implements ParserService {
|
||||
try {
|
||||
MarketItem marketItem = processRssEntry(entry);
|
||||
if (marketItem != null) {
|
||||
try {
|
||||
MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
|
||||
marketItem.setAnalytics(analytics);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Analytics step failed for Kapital '{}': {}", marketItem.getTitle(),
|
||||
e.getMessage());
|
||||
}
|
||||
MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
|
||||
if (savedItem != null) {
|
||||
savedItems.add(savedItem);
|
||||
|
||||
@@ -32,6 +32,9 @@ public class KursivParserService implements ParserService {
|
||||
@Autowired
|
||||
private MarketItemRepository marketItemRepository;
|
||||
|
||||
@Autowired
|
||||
private OllamaAnalyticsService analyticsService;
|
||||
|
||||
@Value("${rss.feed.url}")
|
||||
private String rssFeedUrl;
|
||||
|
||||
@@ -79,6 +82,13 @@ public class KursivParserService implements ParserService {
|
||||
try {
|
||||
MarketItem marketItem = processRssEntry(entry);
|
||||
if (marketItem != null) {
|
||||
try {
|
||||
MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
|
||||
marketItem.setAnalytics(analytics);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Analytics step failed for entry '{}': {}", marketItem.getTitle(),
|
||||
e.getMessage());
|
||||
}
|
||||
MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
|
||||
if (savedItem != null) {
|
||||
savedItems.add(savedItem);
|
||||
|
||||
@@ -32,6 +32,9 @@ public class LsmParserService implements ParserService {
|
||||
@Autowired
|
||||
private MarketItemRepository marketItemRepository;
|
||||
|
||||
@Autowired
|
||||
private OllamaAnalyticsService analyticsService;
|
||||
|
||||
@Value("${rss.lsm.url}")
|
||||
private String rssFeedUrl;
|
||||
|
||||
@@ -79,6 +82,13 @@ public class LsmParserService implements ParserService {
|
||||
try {
|
||||
MarketItem marketItem = processRssEntry(entry);
|
||||
if (marketItem != null) {
|
||||
try {
|
||||
MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
|
||||
marketItem.setAnalytics(analytics);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Analytics step failed for LSM '{}': {}", marketItem.getTitle(),
|
||||
e.getMessage());
|
||||
}
|
||||
MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
|
||||
if (savedItem != null) {
|
||||
savedItems.add(savedItem);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package kz.konturai.parser.service;
|
||||
|
||||
import kz.konturai.parser.model.MarketItem;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class OllamaAnalyticsService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OllamaAnalyticsService.class);
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
@Value("${ollama.model:gemma3:1b}")
|
||||
private String modelName;
|
||||
|
||||
public OllamaAnalyticsService(@Value("${ollama.host:http://185.35.223.45:11434}") String ollamaHost) {
|
||||
this.webClient = WebClient.builder()
|
||||
.baseUrl(ollamaHost)
|
||||
.build();
|
||||
}
|
||||
|
||||
public MarketItem.Analytics analyzeText(String text) {
|
||||
MarketItem.Analytics analytics = new MarketItem.Analytics();
|
||||
|
||||
try {
|
||||
String summary = generate(text,
|
||||
"Напиши краткое саммари следующей новостной статьи на русском языке. Ответ должен содержать только саммари из 3-4 предложений, без лишних вступлений. Статья: ");
|
||||
analytics.setSummary(summary);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to get summary from Ollama: {}", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
String tagsCsv = generate(text,
|
||||
"Извлеки 5-7 ключевых слов или тегов из текста новостной статьи. В ответе дай только список тегов через запятую, без нумерации и заголовков. Статья: ");
|
||||
String[] tags = tagsCsv == null ? new String[] {} : tagsCsv.replace("\n", " ").split("\\s*,\\s*");
|
||||
analytics.setTags(tags);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to get tags from Ollama: {}", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
String sentiment = generate(text,
|
||||
"Определи тональность текста новостной статьи. В ответе дай только одно слово латиницей: positive, negative или neutral. Статья: ");
|
||||
if (sentiment != null) {
|
||||
sentiment = sentiment.trim().toLowerCase();
|
||||
}
|
||||
analytics.setSentiment(sentiment);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to get sentiment from Ollama: {}", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
String entitiesJson = generate(text,
|
||||
"Извлеки из текста имена людей, названия компаний и географические локации. В ответе дай только JSON объект следующей структуры: {\\\"persons\\\": [], \\\"companies\\\": [], \\\"locations\\\": []}. Статья: ");
|
||||
Map<String, Object> entities = new HashMap<>();
|
||||
if (entitiesJson != null && entitiesJson.trim().startsWith("{")) {
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
entities = mapper.readValue(entitiesJson, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
} catch (Exception parseEx) {
|
||||
entities.put("raw", entitiesJson);
|
||||
}
|
||||
}
|
||||
analytics.setEntities(entities);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to get entities from Ollama: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return analytics;
|
||||
}
|
||||
|
||||
private String generate(String text, String instructionPrefix) {
|
||||
String prompt = instructionPrefix + text;
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("model", modelName);
|
||||
requestBody.put("prompt", prompt);
|
||||
requestBody.put("stream", false);
|
||||
|
||||
try {
|
||||
Map<String, Object> response = this.webClient.post()
|
||||
.uri("/api/generate")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.bodyValue(requestBody)
|
||||
.retrieve()
|
||||
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
})
|
||||
.onErrorResume(err -> {
|
||||
logger.warn("Ollama request failed: {}", err.getMessage());
|
||||
return Mono.empty();
|
||||
})
|
||||
.block();
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object res = response.get("response");
|
||||
return res == null ? null : String.valueOf(res);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error calling Ollama generate: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,9 @@ public class RbcParserService implements ParserService {
|
||||
@Autowired
|
||||
private MarketItemRepository marketItemRepository;
|
||||
|
||||
@Autowired
|
||||
private OllamaAnalyticsService analyticsService;
|
||||
|
||||
@Value("${rss.rbc.url}")
|
||||
private String rssFeedUrl;
|
||||
|
||||
@@ -79,6 +82,13 @@ public class RbcParserService implements ParserService {
|
||||
try {
|
||||
MarketItem marketItem = processRssEntry(entry);
|
||||
if (marketItem != null) {
|
||||
try {
|
||||
MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
|
||||
marketItem.setAnalytics(analytics);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Analytics step failed for RBC '{}': {}", marketItem.getTitle(),
|
||||
e.getMessage());
|
||||
}
|
||||
MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
|
||||
if (savedItem != null) {
|
||||
savedItems.add(savedItem);
|
||||
|
||||
@@ -32,6 +32,9 @@ public class VedomostiParserService implements ParserService {
|
||||
@Autowired
|
||||
private MarketItemRepository marketItemRepository;
|
||||
|
||||
@Autowired
|
||||
private OllamaAnalyticsService analyticsService;
|
||||
|
||||
@Value("${rss.vedomosti.url}")
|
||||
private String rssFeedUrl;
|
||||
|
||||
@@ -79,6 +82,13 @@ public class VedomostiParserService implements ParserService {
|
||||
try {
|
||||
MarketItem marketItem = processRssEntry(entry);
|
||||
if (marketItem != null) {
|
||||
try {
|
||||
MarketItem.Analytics analytics = analyticsService.analyzeText(marketItem.getRawText());
|
||||
marketItem.setAnalytics(analytics);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Analytics step failed for Vedomosti '{}': {}", marketItem.getTitle(),
|
||||
e.getMessage());
|
||||
}
|
||||
MarketItem savedItem = saveOrUpdateMarketItem(marketItem);
|
||||
if (savedItem != null) {
|
||||
savedItems.add(savedItem);
|
||||
|
||||
@@ -37,3 +37,8 @@ logging.level.kz.konturai.parser.service.VedomostiParserService=INFO
|
||||
logging.level.org.springframework.scheduling=DEBUG
|
||||
logging.level.org.springframework.data.mongodb=INFO
|
||||
logging.level.com.mongodb=WARN
|
||||
|
||||
# Ollama AI analytics configuration
|
||||
ollama.host=http://185.35.223.45:11434
|
||||
ollama.model=gemma3:1b
|
||||
logging.level.kz.konturai.parser.service.OllamaAnalyticsService=INFO
|
||||
|
||||
Reference in New Issue
Block a user