Skip to content

Commit

Permalink
Support account folders, whitespaces in accounts and categories names
Browse files Browse the repository at this point in the history
  • Loading branch information
isKONSTANTIN committed Aug 29, 2024
1 parent bff72c8 commit d9f7e52
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 91 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group = 'app.finwave.telegrambot'
version = '1.6.0'
version = '1.7.0'
mainClassName = group + ".Main"
archivesBaseName = 'FinWave-Bot'

Expand Down
2 changes: 1 addition & 1 deletion localhost-utils/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ services:
POSTGRES_USER: "finwavebot"
POSTGRES_PASSWORD: "change_me"
ports:
- "5433:5432"
- "5432:5432"
5 changes: 4 additions & 1 deletion src/main/java/app/finwave/telegrambot/scenes/MainScene.java
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,10 @@ public synchronized void update() {
protected ComposedMessage buildNewRequestView(TransactionApi.NewTransactionRequest newRequest) {
MessageBuilder builder = MessageBuilder.create("Подтвердите новую транзакцию: ").gap();

builder.line(EmojiList.ACCOUNT + " Счет: " + state.getAccountsMap().get(newRequest.accountId()).name());
AccountApi.AccountEntry account = state.getAccountsMap().get(newRequest.accountId());
AccountFolderApi.FolderEntry folder = state.getAccountFoldersMap().get(account.folderId());

builder.line(EmojiList.ACCOUNT + " Счет: " + account.name() + " (" + folder.name() + ")");
builder.line(EmojiList.TAG + " Тег: " + state.getTransactionCategoriesMap().get(newRequest.categoryId()).name());
builder.line(EmojiList.WARNING + " Сумма: " + state.formatAmount(newRequest.delta(), newRequest.accountId(), true, preferencesRecord.getHideAmounts()));

Expand Down
171 changes: 83 additions & 88 deletions src/main/java/app/finwave/telegrambot/utils/ActionParser.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package app.finwave.telegrambot.utils;

import app.finwave.api.AccountApi;
import app.finwave.api.NoteApi;
import app.finwave.api.TransactionApi;
import app.finwave.api.TransactionCategoryApi;
import app.finwave.api.*;
import app.finwave.api.tools.IRequest;
import app.finwave.tat.utils.Pair;
import org.apache.commons.text.similarity.JaccardSimilarity;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ActionParser {
protected ClientState state;
Expand All @@ -21,7 +21,68 @@ public class ActionParser {
public ActionParser(ClientState state) {
this.state = state;
}


protected Pair<AccountApi.AccountEntry, Integer> findAccount(List<String> words, int deltaIndex, long preferredAccountId) {
AccountApi.AccountEntry targetAccount = null;

List<AccountApi.AccountEntry> accounts = state.getAccounts();
Map<Long, AccountFolderApi.FolderEntry> folderMap = state.getAccountFoldersMap();

double maxAccountSimilarity = -1;
double accountSim;
int accountAdditionalWords = 0;

for (var account : accounts) {
AccountFolderApi.FolderEntry folder = folderMap.get(account.folderId());
int additionalWords = folder.name().trim().split(" ").length +
account.name().trim().split(" ").length + 1;

String closetWords = String.join(" ", words.subList(
Math.max(0, deltaIndex - additionalWords),
Math.min(words.size(), deltaIndex + additionalWords + 1)
)).toLowerCase();

accountSim = similarity.apply(closetWords, (folder.name() + " " + account.name()).toLowerCase());

if (account.accountId() == preferredAccountId)
accountSim *= 1.2;

if (accountSim > maxAccountSimilarity) {
maxAccountSimilarity = accountSim;
targetAccount = account;
accountAdditionalWords = additionalWords;
}
}

return Pair.of(targetAccount, accountAdditionalWords);
}

protected TransactionCategoryApi.CategoryEntry findCategory(ArrayList<String> words, int deltaIndex, int deltaSig, int accountAdditionalWords) {
TransactionCategoryApi.CategoryEntry targetCategory = null;

List<TransactionCategoryApi.CategoryEntry> categories = state.getTransactionCategories().stream().filter((t) -> t.type() * deltaSig >= 0).toList();
double maxCategorySimilarity = -1;
double categorySim;

for (var category : categories) {
int additionalWords = accountAdditionalWords + category.name().trim().split(" ").length;

String closetWords = String.join(" ", words.subList(
Math.max(0, deltaIndex - additionalWords),
Math.min(words.size(), deltaIndex + additionalWords + 1)
)).toLowerCase();

categorySim = similarity.apply(closetWords, category.name().toLowerCase());

if (categorySim > maxCategorySimilarity) {
maxCategorySimilarity = categorySim;
targetCategory = category;
}
}

return targetCategory;
}

public IRequest<?> parse(String clientRequest, long preferredAccountId) {
if (clientRequest == null || clientRequest.isBlank())
return null;
Expand Down Expand Up @@ -54,100 +115,34 @@ public IRequest<?> parse(String clientRequest, long preferredAccountId) {
deltaIndex++;
}

if (delta == null)
if (delta == null || words.isEmpty())
return null;

ArrayList<String> closetWords = new ArrayList<>(words.subList(
Math.max(0, deltaIndex - 2),
Math.min(words.size(), deltaIndex + 3)
));

String categoryWord = null;
String accountWord = null;

double maxCategorySimilarity = -1;
double maxAccountSimilarity = -1;
AccountApi.AccountEntry targetAccount;
int additionalWords = 0;

double categorySim = -1;
double accountSim = -1;

int deltaSig = delta.signum();

List<TransactionCategoryApi.CategoryEntry> categories = state.getTransactionCategories().stream().filter((t) -> t.type() * deltaSig >= 0).toList();
List<AccountApi.AccountEntry> accounts = state.getAccounts();

TransactionCategoryApi.CategoryEntry targetCategory = null;
AccountApi.AccountEntry targetAccount = null;

for (String word : closetWords) {
for (var account : accounts) {
accountSim = similarity.apply(word.toLowerCase(), account.name().toLowerCase());

boolean startWith = account.name().toLowerCase().startsWith(word.toLowerCase());

if (startWith)
accountSim *= 1.2;

if ((startWith || accountSim >= 0.5) && accountSim > maxAccountSimilarity) {
maxAccountSimilarity = accountSim;
targetAccount = account;
accountWord = word;
}

if (accountSim >= 1)
break;
}

if (accountSim >= 1) break;
}

if (targetAccount == null || (preferredAccountId != -1 && accountSim < 0.95))
if (words.size() == 1) {
targetAccount = state.getAccountsMap().get(preferredAccountId);
}else {
Pair<AccountApi.AccountEntry, Integer> found = findAccount(words, deltaIndex, preferredAccountId);
targetAccount = found.first();
additionalWords = found.second();
}

if (targetAccount == null)
return null;

if (accountWord != null)
closetWords.remove(accountWord);

for (String word : closetWords) {
for (var category : categories) {
categorySim = similarity.apply(word.toLowerCase(), category.name().toLowerCase());

boolean startWith = category.name().toLowerCase().startsWith(word.toLowerCase());

if (startWith)
categorySim *= 1.2;

if ((startWith || categorySim >= 0.5) && categorySim > maxCategorySimilarity) {
maxCategorySimilarity = categorySim;
targetCategory = category;
categoryWord = word;
}

if (categorySim >= 1)
break;
}

if (categorySim >= 1) break;
}
TransactionCategoryApi.CategoryEntry targetCategory = findCategory(words, deltaIndex, delta.signum(), additionalWords);

if (targetCategory == null)
return null;

words.remove(categoryWord);
words.remove(accountWord);

String description = null;

if (!words.isEmpty()) {
StringBuilder builder = new StringBuilder();
words.forEach((w) -> builder.append(w).append(" "));
description = builder.toString().trim();
}

OffsetDateTime time = OffsetDateTime.now();

return new TransactionApi.NewTransactionRequest(targetCategory.categoryId(), targetAccount.accountId(), time, delta, description);
return new TransactionApi.NewTransactionRequest(
targetCategory.categoryId(),
targetAccount.accountId(),
OffsetDateTime.now(),
delta,
String.join(" ", words) + " (TG)"
);
}
}

0 comments on commit d9f7e52

Please sign in to comment.