+ nlp
This commit is contained in:
@@ -58,6 +58,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation("org.telegram:telegrambots:6.9.7.1")
|
implementation("org.telegram:telegrambots:6.9.7.1")
|
||||||
implementation("org.telegram:telegrambots-spring-boot-starter:6.9.7.1")
|
implementation("org.telegram:telegrambots-spring-boot-starter:6.9.7.1")
|
||||||
|
implementation("com.opencsv:opencsv:5.10")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package space.luminic.budgerapp
|
|||||||
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||||
import org.springframework.boot.runApplication
|
import org.springframework.boot.runApplication
|
||||||
import org.springframework.cache.annotation.EnableCaching
|
import org.springframework.cache.annotation.EnableCaching
|
||||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories
|
||||||
import org.springframework.scheduling.annotation.EnableAsync
|
import org.springframework.scheduling.annotation.EnableAsync
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling
|
import org.springframework.scheduling.annotation.EnableScheduling
|
||||||
|
import space.luminic.budgerapp.configs.NLPConfig
|
||||||
import space.luminic.budgerapp.configs.TelegramBotProperties
|
import space.luminic.budgerapp.configs.TelegramBotProperties
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -15,7 +17,8 @@ import java.util.*
|
|||||||
@EnableCaching
|
@EnableCaching
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableConfigurationProperties(TelegramBotProperties::class)
|
//@EnableConfigurationProperties([TelegramBotProperties::class,)
|
||||||
|
@ConfigurationPropertiesScan(basePackages = ["space.luminic.budgerapp"])
|
||||||
@EnableMongoRepositories(basePackages = ["space.luminic.budgerapp.repos"])
|
@EnableMongoRepositories(basePackages = ["space.luminic.budgerapp.repos"])
|
||||||
class BudgerAppApplication
|
class BudgerAppApplication
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
package space.luminic.budgerapp.configs
|
package space.luminic.budgerapp.configs
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
|
|
||||||
//@Configuration
|
|
||||||
//class CommonConfig {
|
//class CommonConfig {
|
||||||
// @Bean
|
// @Bean
|
||||||
// fun httpTraceRepository(): HttpTraceRepository {
|
// fun httpTraceRepository(): HttpTraceRepository {
|
||||||
// return InMemoryHttpTraceRepository()
|
// return InMemoryHttpTraceRepository()
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "nlp")
|
||||||
|
data class NLPConfig(
|
||||||
|
val address: String,
|
||||||
|
)
|
||||||
@@ -1,18 +1,25 @@
|
|||||||
package space.luminic.budgerapp.controllers
|
package space.luminic.budgerapp.controllers
|
||||||
|
|
||||||
|
import com.opencsv.CSVWriter
|
||||||
import kotlinx.coroutines.reactor.awaitSingle
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
|
import org.apache.commons.io.IOUtils.writer
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.http.HttpHeaders
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
import space.luminic.budgerapp.controllers.BudgetController.LimitValue
|
||||||
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
import space.luminic.budgerapp.controllers.dtos.BudgetCreationDTO
|
||||||
import space.luminic.budgerapp.models.*
|
import space.luminic.budgerapp.models.*
|
||||||
import space.luminic.budgerapp.services.*
|
import space.luminic.budgerapp.services.*
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/spaces")
|
@RequestMapping("/spaces")
|
||||||
class SpaceController(
|
class SpaceController(
|
||||||
@@ -172,6 +179,63 @@ class SpaceController(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{spaceId}/transactions/csv")
|
||||||
|
suspend fun getTransactionsCSV(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@RequestParam(value = "transaction_type") transactionType: String? = null,
|
||||||
|
@RequestParam(value = "category_type") categoryType: String? = null,
|
||||||
|
@RequestParam(value = "user_id") userId: String? = null,
|
||||||
|
@RequestParam(value = "is_child") isChild: Boolean? = null,
|
||||||
|
@RequestParam(value = "limit") limit: Int = 20000,
|
||||||
|
@RequestParam(value = "offset") offset: Int = 0
|
||||||
|
): ResponseEntity<Any> {
|
||||||
|
try {
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
val writer = CSVWriter(OutputStreamWriter(bos))
|
||||||
|
val CSVHeaders = arrayOf("id", "name", "category")
|
||||||
|
writer.writeNext(CSVHeaders)
|
||||||
|
financialService.getTransactions(
|
||||||
|
spaceId = spaceId,
|
||||||
|
transactionType = transactionType,
|
||||||
|
categoryType = categoryType,
|
||||||
|
userId = userId,
|
||||||
|
isChild = isChild,
|
||||||
|
limit = limit,
|
||||||
|
offset = offset
|
||||||
|
).awaitSingle().map {
|
||||||
|
val data = arrayOf(it.id, it.comment, it.category.name)
|
||||||
|
writer.writeNext(data)
|
||||||
|
}
|
||||||
|
writer.close()
|
||||||
|
|
||||||
|
val csvData = bos.toByteArray()
|
||||||
|
val headers = HttpHeaders()
|
||||||
|
headers.contentType = MediaType.parseMediaType("text/csv")
|
||||||
|
headers.setContentDispositionFormData("attachment", "pojos.csv")
|
||||||
|
|
||||||
|
return ResponseEntity(csvData, headers, HttpStatus.OK)
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
return ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{spaceId}/category-predict")
|
||||||
|
suspend fun getTransactionCategoryPredict(
|
||||||
|
@PathVariable spaceId: String,
|
||||||
|
@RequestParam comment: String
|
||||||
|
): List<Category> {
|
||||||
|
val user = authService.getSecurityUser()
|
||||||
|
spaceService.isValidRequest(spaceId, user)
|
||||||
|
return categoryService.getCategories(
|
||||||
|
"67af3c0f652da946a7dd9931",
|
||||||
|
"EXPENSE",
|
||||||
|
sortBy = "name",
|
||||||
|
direction = "ASC",
|
||||||
|
predict = comment
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/transactions/{id}")
|
@GetMapping("/{spaceId}/transactions/{id}")
|
||||||
suspend fun getTransaction(
|
suspend fun getTransaction(
|
||||||
@@ -181,6 +245,7 @@ class SpaceController(
|
|||||||
return financialService.getTransactionById(id)
|
return financialService.getTransactionById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PostMapping("/{spaceId}/transactions")
|
@PostMapping("/{spaceId}/transactions")
|
||||||
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
suspend fun createTransaction(@PathVariable spaceId: String, @RequestBody transaction: Transaction): Transaction {
|
||||||
val user = authService.getSecurityUser()
|
val user = authService.getSecurityUser()
|
||||||
@@ -220,7 +285,7 @@ class SpaceController(
|
|||||||
): List<Category> {
|
): List<Category> {
|
||||||
val user = authService.getSecurityUser()
|
val user = authService.getSecurityUser()
|
||||||
spaceService.isValidRequest(spaceId, user)
|
spaceService.isValidRequest(spaceId, user)
|
||||||
return categoryService.getCategories(spaceId, type, sortBy, direction).awaitSingleOrNull().orEmpty()
|
return categoryService.getCategories(spaceId, type, sortBy, direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/{spaceId}/categories/types")
|
@GetMapping("/{spaceId}/categories/types")
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package space.luminic.budgerapp.models
|
||||||
|
|
||||||
|
data class CategoryPrediction(val category: String, val weight: Double) {
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service
|
|||||||
import org.telegram.telegrambots.bots.TelegramLongPollingBot
|
import org.telegram.telegrambots.bots.TelegramLongPollingBot
|
||||||
import org.telegram.telegrambots.meta.api.methods.send.SendMessage
|
import org.telegram.telegrambots.meta.api.methods.send.SendMessage
|
||||||
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.DeleteMessage
|
||||||
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageReplyMarkup
|
||||||
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText
|
import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText
|
||||||
import org.telegram.telegrambots.meta.api.objects.Update
|
import org.telegram.telegrambots.meta.api.objects.Update
|
||||||
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup
|
import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup
|
||||||
@@ -35,9 +36,76 @@ class BotService(
|
|||||||
private val categoriesService: CategoryService,
|
private val categoriesService: CategoryService,
|
||||||
private val financialService: FinancialService,
|
private val financialService: FinancialService,
|
||||||
private val spaceService: SpaceService,
|
private val spaceService: SpaceService,
|
||||||
|
private val nlpService: NLPService
|
||||||
) : TelegramLongPollingBot(telegramBotProperties.token) {
|
) : TelegramLongPollingBot(telegramBotProperties.token) {
|
||||||
private val logger = LoggerFactory.getLogger(javaClass)
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
|
|
||||||
|
private suspend fun constructCategoriesButtons(
|
||||||
|
nlp: Boolean,
|
||||||
|
text: String? = null,
|
||||||
|
chatId: Long
|
||||||
|
): InlineKeyboardMarkup {
|
||||||
|
val categories =
|
||||||
|
categoriesService.getCategories(
|
||||||
|
"67af3c0f652da946a7dd9931",
|
||||||
|
"EXPENSE",
|
||||||
|
sortBy = "name",
|
||||||
|
direction = "ASC"
|
||||||
|
)
|
||||||
|
|
||||||
|
val keyboard = InlineKeyboardMarkup()
|
||||||
|
val buttonLines = mutableListOf<MutableList<InlineKeyboardButton>>()
|
||||||
|
val filteredCategories = mutableListOf<Category>()
|
||||||
|
if (nlp) {
|
||||||
|
if (text.isNullOrBlank()) {
|
||||||
|
throw TelegramBotException("Текст не может быть пустым", chatId)
|
||||||
|
}
|
||||||
|
val predictedCategories = nlpService.predictCategory(text, 0)
|
||||||
|
|
||||||
|
|
||||||
|
for (category in predictedCategories) {
|
||||||
|
filteredCategories.add(categories.first { it.name == category.category })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filteredCategories.addAll(categories)
|
||||||
|
}
|
||||||
|
filteredCategories.map { category ->
|
||||||
|
val btn = InlineKeyboardButton.builder().text("${category.icon} ${category.name}")
|
||||||
|
.callbackData("category_${category.id}").build()
|
||||||
|
|
||||||
|
if (category.name.length >= 15) {
|
||||||
|
// Если текст длинный, создаём отдельную строку для кнопки
|
||||||
|
buttonLines.add(mutableListOf(btn))
|
||||||
|
} else {
|
||||||
|
var isAdded = false
|
||||||
|
|
||||||
|
// Пытаемся добавить кнопку в существующую строку
|
||||||
|
for (line in buttonLines) {
|
||||||
|
if (line.size < 2 && (line.isEmpty() || line[0].text.length < 14)) {
|
||||||
|
line.add(btn)
|
||||||
|
isAdded = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если не нашли подходящую строку, создаём новую
|
||||||
|
if (!isAdded) {
|
||||||
|
buttonLines.add(mutableListOf(btn))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val allCatsBtn = InlineKeyboardButton.builder().text("Все категории").callbackData("all_cats").build()
|
||||||
|
val backButton = InlineKeyboardButton.builder().text("Отмена").callbackData("cancel").build()
|
||||||
|
|
||||||
|
buttonLines.add(mutableListOf(allCatsBtn))
|
||||||
|
buttonLines.add(mutableListOf(backButton))
|
||||||
|
keyboard.keyboard = buttonLines
|
||||||
|
return keyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun getBotUsername(): String {
|
override fun getBotUsername(): String {
|
||||||
return telegramBotProperties.username
|
return telegramBotProperties.username
|
||||||
}
|
}
|
||||||
@@ -85,6 +153,12 @@ class BotService(
|
|||||||
val deleteMsg = DeleteMessage(chatId, update.callbackQuery.message.messageId)
|
val deleteMsg = DeleteMessage(chatId, update.callbackQuery.message.messageId)
|
||||||
execute(deleteMsg)
|
execute(deleteMsg)
|
||||||
sendMessage(chatId, "Введите сумму и комментарий когда будете готовы.")
|
sendMessage(chatId, "Введите сумму и комментарий когда будете готовы.")
|
||||||
|
} else if (update.callbackQuery.data == "all_cats") {
|
||||||
|
val editMessageReplyMarkup = EditMessageReplyMarkup()
|
||||||
|
editMessageReplyMarkup.chatId = chatId
|
||||||
|
editMessageReplyMarkup.messageId = update.callbackQuery.message.messageId
|
||||||
|
editMessageReplyMarkup.replyMarkup = constructCategoriesButtons(false, chatId = chatId.toLong())
|
||||||
|
execute(editMessageReplyMarkup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,53 +223,12 @@ class BotService(
|
|||||||
comment += "$word "
|
comment += "$word "
|
||||||
}
|
}
|
||||||
|
|
||||||
val categories =
|
|
||||||
categoriesService.getCategories(
|
|
||||||
"67af3c0f652da946a7dd9931",
|
|
||||||
"EXPENSE",
|
|
||||||
sortBy = "name",
|
|
||||||
direction = "ASC"
|
|
||||||
)
|
|
||||||
.awaitSingle()
|
|
||||||
val keyboard = InlineKeyboardMarkup()
|
|
||||||
val buttonLines = mutableListOf<MutableList<InlineKeyboardButton>>()
|
|
||||||
categories.map { category ->
|
|
||||||
val btn = InlineKeyboardButton.builder().text("${category.icon} ${category.name}")
|
|
||||||
.callbackData("category_${category.id}").build()
|
|
||||||
|
|
||||||
if (category.name.length >= 15) {
|
|
||||||
// Если текст длинный, создаём отдельную строку для кнопки
|
|
||||||
buttonLines.add(mutableListOf(btn))
|
|
||||||
} else {
|
|
||||||
var isAdded = false
|
|
||||||
|
|
||||||
// Пытаемся добавить кнопку в существующую строку
|
|
||||||
for (line in buttonLines) {
|
|
||||||
if (line.size < 2 && (line.isEmpty() || line[0].text.length < 14)) {
|
|
||||||
line.add(btn)
|
|
||||||
isAdded = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если не нашли подходящую строку, создаём новую
|
|
||||||
if (!isAdded) {
|
|
||||||
buttonLines.add(mutableListOf(btn))
|
|
||||||
} else {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val backButton = InlineKeyboardButton.builder().text("Отмена").callbackData("cancel").build()
|
|
||||||
|
|
||||||
buttonLines.add(mutableListOf(backButton))
|
|
||||||
keyboard.keyboard = buttonLines
|
|
||||||
|
|
||||||
val message = SendMessage()
|
val message = SendMessage()
|
||||||
message.chatId = chatId
|
message.chatId = chatId
|
||||||
val msg = "Выберите категорию"
|
val msg = "Выберите категорию"
|
||||||
message.text = msg
|
message.text = msg
|
||||||
message.replyMarkup = keyboard
|
message.replyMarkup = constructCategoriesButtons(true, text, chatId.toLong())
|
||||||
val userState = BotUserState(user = user)
|
val userState = BotUserState(user = user)
|
||||||
val chatData =
|
val chatData =
|
||||||
userState.data.find { it.chatId == chatId } ?: ChatData(chatId, state = BotStates.WAIT_CATEGORY)
|
userState.data.find { it.chatId == chatId } ?: ChatData(chatId, state = BotStates.WAIT_CATEGORY)
|
||||||
@@ -229,7 +262,6 @@ class BotService(
|
|||||||
sortBy = "name",
|
sortBy = "name",
|
||||||
direction = "ASC"
|
direction = "ASC"
|
||||||
)
|
)
|
||||||
.awaitSingle()
|
|
||||||
.first { it.id == update.callbackQuery.data.split("_")[1] }
|
.first { it.id == update.callbackQuery.data.split("_")[1] }
|
||||||
val space = spaceService.getSpace("67af3c0f652da946a7dd9931")
|
val space = spaceService.getSpace("67af3c0f652da946a7dd9931")
|
||||||
val instantType = financialService.getTransactionTypes().first { it.code == "INSTANT" }
|
val instantType = financialService.getTransactionTypes().first { it.code == "INSTANT" }
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package space.luminic.budgerapp.services
|
|||||||
|
|
||||||
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
import kotlinx.coroutines.reactive.awaitFirstOrNull
|
||||||
import kotlinx.coroutines.reactive.awaitSingle
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import kotlinx.coroutines.reactor.awaitSingle
|
||||||
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
import kotlinx.coroutines.reactor.awaitSingleOrNull
|
||||||
import org.bson.Document
|
import org.bson.Document
|
||||||
import org.bson.types.ObjectId
|
import org.bson.types.ObjectId
|
||||||
@@ -28,7 +29,7 @@ class CategoryService(
|
|||||||
private val mongoTemplate: ReactiveMongoTemplate,
|
private val mongoTemplate: ReactiveMongoTemplate,
|
||||||
private val categoryMapper: CategoryMapper,
|
private val categoryMapper: CategoryMapper,
|
||||||
private val budgetRepo: BudgetRepo,
|
private val budgetRepo: BudgetRepo,
|
||||||
|
private val nlpService: NLPService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(javaClass)
|
private val logger = LoggerFactory.getLogger(javaClass)
|
||||||
@@ -67,14 +68,15 @@ class CategoryService(
|
|||||||
}.awaitFirstOrNull() ?: throw NotFoundException("Category not found")
|
}.awaitFirstOrNull() ?: throw NotFoundException("Category not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Cacheable(cacheNames = ["categories"])
|
||||||
fun getCategories(
|
suspend fun getCategories(
|
||||||
spaceId: String,
|
spaceId: String,
|
||||||
type: String? = null,
|
type: String? = null,
|
||||||
sortBy: String,
|
sortBy: String,
|
||||||
direction: String,
|
direction: String,
|
||||||
tagCode: String? = null
|
tagCode: String? = null,
|
||||||
): Mono<List<Category>> {
|
predict: String? = null
|
||||||
|
): MutableList<Category> {
|
||||||
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
val lookupSpaces = lookup("spaces", "space.\$id", "_id", "spaceDetails")
|
||||||
val unwindSpace = unwind("spaceDetails")
|
val unwindSpace = unwind("spaceDetails")
|
||||||
val matchCriteria = mutableListOf<Criteria>()
|
val matchCriteria = mutableListOf<Criteria>()
|
||||||
@@ -99,7 +101,7 @@ class CategoryService(
|
|||||||
).filterNotNull()
|
).filterNotNull()
|
||||||
|
|
||||||
val aggregation = newAggregation(aggregationBuilder)
|
val aggregation = newAggregation(aggregationBuilder)
|
||||||
return mongoTemplate.aggregate(
|
val categories = mongoTemplate.aggregate(
|
||||||
aggregation, "categories", Document::class.java
|
aggregation, "categories", Document::class.java
|
||||||
)
|
)
|
||||||
.collectList() // Преобразуем Flux<Transaction> в Mono<List<Transaction>>
|
.collectList() // Преобразуем Flux<Transaction> в Mono<List<Transaction>>
|
||||||
@@ -107,8 +109,19 @@ class CategoryService(
|
|||||||
docs.map { doc ->
|
docs.map { doc ->
|
||||||
categoryMapper.fromDocument(doc)
|
categoryMapper.fromDocument(doc)
|
||||||
}
|
}
|
||||||
|
}.awaitSingle().toMutableList()
|
||||||
|
|
||||||
|
val predictedCategories = mutableListOf<CategoryPrediction>()
|
||||||
|
if (!predict.isNullOrBlank()) {
|
||||||
|
predictedCategories.addAll(nlpService.predictCategory(predict, 1))
|
||||||
}
|
}
|
||||||
|
val filteredCategories = mutableListOf<Category>()
|
||||||
|
for (category in predictedCategories) {
|
||||||
|
categories.find { it.name == category.category }?.let { filteredCategories.add(it) }
|
||||||
}
|
}
|
||||||
|
return if (filteredCategories.isEmpty()) categories else filteredCategories
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Cacheable("categoryTypes")
|
@Cacheable("categoryTypes")
|
||||||
fun getCategoryTypes(): List<CategoryType> {
|
fun getCategoryTypes(): List<CategoryType> {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class FinancialService(
|
|||||||
private val categoryRepo: CategoryRepo,
|
private val categoryRepo: CategoryRepo,
|
||||||
val transactionsMapper: TransactionsMapper,
|
val transactionsMapper: TransactionsMapper,
|
||||||
val budgetMapper: BudgetMapper,
|
val budgetMapper: BudgetMapper,
|
||||||
private val subscriptionService: SubscriptionService
|
private val subscriptionService: SubscriptionService,
|
||||||
) {
|
) {
|
||||||
private val logger = LoggerFactory.getLogger(FinancialService::class.java)
|
private val logger = LoggerFactory.getLogger(FinancialService::class.java)
|
||||||
|
|
||||||
@@ -57,8 +57,14 @@ class FinancialService(
|
|||||||
transaction.space!!.id!!, budgetId = null, transaction.date, transaction.date
|
transaction.space!!.id!!, budgetId = null, transaction.date, transaction.date
|
||||||
)
|
)
|
||||||
if (transaction.category.type.code == "EXPENSE") {
|
if (transaction.category.type.code == "EXPENSE") {
|
||||||
val budgetCategory = budget.categories.firstOrNull { it.category.id == transaction.category.id }
|
var budgetCategory = budget.categories.firstOrNull { it.category.id == transaction.category.id }
|
||||||
?: throw NotFoundException("Budget category not found in the budget")
|
|
||||||
|
if (budgetCategory == null) {
|
||||||
|
budgetCategory = BudgetCategory(0.0, 0.0, 0.0, transaction.category)
|
||||||
|
budget.categories.add(budgetCategory)
|
||||||
|
budgetRepo.save(budget).awaitSingle()
|
||||||
|
|
||||||
|
}
|
||||||
if (transaction.category.type.code == "INCOME") {
|
if (transaction.category.type.code == "INCOME") {
|
||||||
budgetRepo.save(budget).awaitSingle()
|
budgetRepo.save(budget).awaitSingle()
|
||||||
}
|
}
|
||||||
@@ -845,6 +851,7 @@ class FinancialService(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||||
|
|
||||||
suspend fun createTransaction(space: Space, transaction: Transaction, user: User? = null): Transaction {
|
suspend fun createTransaction(space: Space, transaction: Transaction, user: User? = null): Transaction {
|
||||||
@@ -930,12 +937,15 @@ class FinancialService(
|
|||||||
"main" -> {
|
"main" -> {
|
||||||
"Пользователь ${author.username} изменил $transactionType транзакцию:\n$sb"
|
"Пользователь ${author.username} изменил $transactionType транзакцию:\n$sb"
|
||||||
}
|
}
|
||||||
|
|
||||||
"done_true" -> {
|
"done_true" -> {
|
||||||
"Пользователь ${author.username} выполнил ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
"Пользователь ${author.username} выполнил ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
"done_false" -> {
|
"done_false" -> {
|
||||||
"Пользователь ${author.username} отменил выполнение ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
"Пользователь ${author.username} отменил выполнение ${transaction.comment} с суммой ${transaction.amount.toInt()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> "Изменения не обнаружены, но что то точно изменилось"
|
else -> "Изменения не обнаружены, но что то точно изменилось"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package space.luminic.budgerapp.services
|
||||||
|
|
||||||
|
import kotlinx.coroutines.reactive.awaitSingle
|
||||||
|
import org.springframework.stereotype.Service
|
||||||
|
import org.springframework.web.reactive.function.client.WebClient
|
||||||
|
import space.luminic.budgerapp.configs.NLPConfig
|
||||||
|
import space.luminic.budgerapp.models.CategoryPrediction
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class NLPService(
|
||||||
|
private val nlpConfig: NLPConfig,
|
||||||
|
private val webClient: WebClient = WebClient.builder()
|
||||||
|
.baseUrl(nlpConfig.address)
|
||||||
|
// .defaultHeader("Authorization", "Bearer YOUR_API_KEY")
|
||||||
|
.build(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun predictCategory(category: String, cloud: Int): List<CategoryPrediction> {
|
||||||
|
val response = webClient.get().uri("/predict?req=$category&cloud=$cloud")
|
||||||
|
.retrieve()
|
||||||
|
.bodyToFlux(CategoryPrediction::class.java)
|
||||||
|
.collectList()
|
||||||
|
.awaitSingle()
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -140,7 +140,7 @@ class SpaceService(
|
|||||||
|
|
||||||
launch {
|
launch {
|
||||||
val categories =
|
val categories =
|
||||||
categoryService.getCategories(objectId.toString(), null, "name", "ASC").awaitFirstOrNull().orEmpty()
|
categoryService.getCategories(objectId.toString(), null, "name", "ASC")
|
||||||
categoryRepo.deleteAll(categories).awaitFirstOrNull()
|
categoryRepo.deleteAll(categories).awaitFirstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,7 +280,6 @@ class SpaceService(
|
|||||||
val existedTag = findTag(space, tagCode) ?: throw NoSuchElementException("Tag with code $tagCode not found")
|
val existedTag = findTag(space, tagCode) ?: throw NoSuchElementException("Tag with code $tagCode not found")
|
||||||
val categoriesWithTag =
|
val categoriesWithTag =
|
||||||
categoryService.getCategories(space.id!!, sortBy = "name", direction = "ASC", tagCode = existedTag.code)
|
categoryService.getCategories(space.id!!, sortBy = "name", direction = "ASC", tagCode = existedTag.code)
|
||||||
.awaitSingleOrNull().orEmpty()
|
|
||||||
categoriesWithTag.map { cat ->
|
categoriesWithTag.map { cat ->
|
||||||
cat.tags.removeIf { it.code == tagCode } // Изменяем список тегов
|
cat.tags.removeIf { it.code == tagCode } // Изменяем список тегов
|
||||||
cat
|
cat
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ spring.data.mongodb.uri=mongodb://budger-app:BA1q2w3e4r!@luminic.space:27017/bud
|
|||||||
management.endpoints.web.exposure.include=*
|
management.endpoints.web.exposure.include=*
|
||||||
management.endpoint.health.show-details=always
|
management.endpoint.health.show-details=always
|
||||||
telegram.bot.token=6972242509:AAGyXuL3T-BNE4XMoo_qvtaYxw_SuiS_dDs
|
telegram.bot.token=6972242509:AAGyXuL3T-BNE4XMoo_qvtaYxw_SuiS_dDs
|
||||||
|
nlp.address=http://127.0.0.1:8000
|
||||||
@@ -17,4 +17,4 @@ logging.level.org.springframework.data.mongodb.code = DEBUG
|
|||||||
|
|
||||||
|
|
||||||
telegram.bot.token = 6662300972:AAFXjk_h0AUCy4bORC12UcdXbYnh2QSVKAY
|
telegram.bot.token = 6662300972:AAFXjk_h0AUCy4bORC12UcdXbYnh2QSVKAY
|
||||||
|
nlp.address=https://nlp.luminic.space
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ server.port=8082
|
|||||||
#server.servlet.context-path=/api
|
#server.servlet.context-path=/api
|
||||||
spring.webflux.base-path=/api
|
spring.webflux.base-path=/api
|
||||||
|
|
||||||
spring.profiles.active=prod
|
spring.profiles.active=dev
|
||||||
spring.main.web-application-type=reactive
|
spring.main.web-application-type=reactive
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logging.level.org.springframework.web=INFO
|
logging.level.org.springframework.web=INFO
|
||||||
logging.level.org.springframework.data = INFO
|
logging.level.org.springframework.data = INFO
|
||||||
logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=INFO
|
logging.level.org.springframework.data.mongodb.core.ReactiveMongoTemplate=INFO
|
||||||
|
|||||||
Reference in New Issue
Block a user