Skip to content

Commit

Permalink
statistics analysis supports filtering tags
Browse files Browse the repository at this point in the history
  • Loading branch information
mayswind committed Dec 8, 2024
1 parent 9f6446c commit db94282
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 23 deletions.
28 changes: 26 additions & 2 deletions pkg/api/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,20 @@ func (a *TransactionsApi) TransactionStatisticsHandler(c *core.WebContext) (any,
return nil, errs.ErrClientTimezoneOffsetInvalid
}

var allTagIds []int64
noTags := statisticReq.TagIds == "none"

if !noTags {
allTagIds, err = a.getTagIds(statisticReq.TagIds)

if err != nil {
log.Warnf(c, "[transactions.TransactionStatisticsHandler] get transaction tag ids error, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
}

uid := c.GetCurrentUid()
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime, utcOffset, statisticReq.UseTransactionTimezone)
totalAmounts, err := a.transactions.GetAccountsAndCategoriesTotalIncomeAndExpense(c, uid, statisticReq.StartTime, statisticReq.EndTime, allTagIds, noTags, statisticReq.TagFilterType, utcOffset, statisticReq.UseTransactionTimezone)

if err != nil {
log.Errorf(c, "[transactions.TransactionStatisticsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
Expand Down Expand Up @@ -350,8 +362,20 @@ func (a *TransactionsApi) TransactionStatisticsTrendsHandler(c *core.WebContext)
return nil, errs.Or(err, errs.ErrOperationFailed)
}

var allTagIds []int64
noTags := statisticTrendsReq.TagIds == "none"

if !noTags {
allTagIds, err = a.getTagIds(statisticTrendsReq.TagIds)

if err != nil {
log.Warnf(c, "[transactions.TransactionStatisticsTrendsHandler] get transaction tag ids error, because %s", err.Error())
return nil, errs.Or(err, errs.ErrOperationFailed)
}
}

uid := c.GetCurrentUid()
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, utcOffset, statisticTrendsReq.UseTransactionTimezone)
allMonthlyTotalAmounts, err := a.transactions.GetAccountsAndCategoriesMonthlyIncomeAndExpense(c, uid, startYear, startMonth, endYear, endMonth, allTagIds, noTags, statisticTrendsReq.TagFilterType, utcOffset, statisticTrendsReq.UseTransactionTimezone)

if err != nil {
log.Errorf(c, "[transactions.TransactionStatisticsTrendsHandler] failed to get accounts and categories total income and expense for user \"uid:%d\", because %s", uid, err.Error())
Expand Down
12 changes: 8 additions & 4 deletions pkg/models/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,19 @@ type TransactionListInMonthByPageRequest struct {

// TransactionStatisticRequest represents all parameters of transaction statistic request
type TransactionStatisticRequest struct {
StartTime int64 `form:"start_time" binding:"min=0"`
EndTime int64 `form:"end_time" binding:"min=0"`
UseTransactionTimezone bool `form:"use_transaction_timezone"`
StartTime int64 `form:"start_time" binding:"min=0"`
EndTime int64 `form:"end_time" binding:"min=0"`
TagIds string `form:"tag_ids"`
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
UseTransactionTimezone bool `form:"use_transaction_timezone"`
}

// TransactionStatisticTrendsRequest represents all parameters of transaction statistic trends request
type TransactionStatisticTrendsRequest struct {
YearMonthRangeRequest
UseTransactionTimezone bool `form:"use_transaction_timezone"`
TagIds string `form:"tag_ids"`
TagFilterType TransactionTagFilterType `form:"tag_filter_type" binding:"min=0,max=3"`
UseTransactionTimezone bool `form:"use_transaction_timezone"`
}

// TransactionAmountsRequest represents all parameters of transaction amounts request
Expand Down
14 changes: 10 additions & 4 deletions pkg/services/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ func (s *TransactionService) GetAccountsTotalIncomeAndExpense(c core.Context, ui
}

// GetAccountsAndCategoriesTotalIncomeAndExpense returns the every accounts and categories total income and expense amount by specific date range
func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c core.Context, uid int64, startUnixTime int64, endUnixTime int64, utcOffset int16, useTransactionTimezone bool) ([]*models.Transaction, error) {
func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c core.Context, uid int64, startUnixTime int64, endUnixTime int64, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, utcOffset int16, useTransactionTimezone bool) ([]*models.Transaction, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
Expand Down Expand Up @@ -1336,7 +1336,10 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
finalConditionParams = append(finalConditionParams, maxTransactionTime)
}

err := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...).Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)
sess := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)

err := sess.Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)

if err != nil {
return nil, err
Expand Down Expand Up @@ -1394,7 +1397,7 @@ func (s *TransactionService) GetAccountsAndCategoriesTotalIncomeAndExpense(c cor
}

// GetAccountsAndCategoriesMonthlyIncomeAndExpense returns the every accounts monthly income and expense amount by specific date range
func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c core.Context, uid int64, startYear int32, startMonth int32, endYear int32, endMonth int32, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c core.Context, uid int64, startYear int32, startMonth int32, endYear int32, endMonth int32, tagIds []int64, noTags bool, tagFilterType models.TransactionTagFilterType, utcOffset int16, useTransactionTimezone bool) (map[int32][]*models.Transaction, error) {
if uid <= 0 {
return nil, errs.ErrUserIdInvalid
}
Expand Down Expand Up @@ -1447,7 +1450,10 @@ func (s *TransactionService) GetAccountsAndCategoriesMonthlyIncomeAndExpense(c c
finalConditionParams = append(finalConditionParams, maxTransactionTime)
}

err := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...).Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)
sess := s.UserDataDB(uid).NewSession(c).Select("category_id, account_id, transaction_time, timezone_utc_offset, amount").Where(finalCondition, finalConditionParams...)
sess = s.appendFilterTagIdsConditionToQuery(sess, uid, maxTransactionTime, minTransactionTime, tagIds, noTags, tagFilterType)

err := sess.Limit(pageCountForLoadTransactionAmounts, 0).OrderBy("transaction_time desc").Find(&transactions)

if err != nil {
return nil, err
Expand Down
20 changes: 18 additions & 2 deletions src/lib/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ export default {
keyword = encodeURIComponent(keyword);
return axios.get(`v1/transactions/list/by_month.json?year=${year}&month=${month}&type=${type}&category_ids=${categoryIds}&account_ids=${accountIds}&tag_ids=${tagIds}&tag_filter_type=${tagFilterType}&amount_filter=${amountFilter}&keyword=${keyword}&trim_account=true&trim_category=true&trim_tag=true`);
},
getTransactionStatistics: ({ startTime, endTime, useTransactionTimezone }) => {
getTransactionStatistics: ({ startTime, endTime, useTransactionTimezone, tagIds, tagFilterType }) => {
const queryParams = [];

if (startTime) {
Expand All @@ -320,9 +320,17 @@ export default {
queryParams.push(`end_time=${endTime}`);
}

if (tagIds) {
queryParams.push(`tag_ids=${tagIds}`);
}

if (tagFilterType) {
queryParams.push(`tag_filter_type=${tagFilterType}`);
}

return axios.get(`v1/transactions/statistics.json?use_transaction_timezone=${useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
},
getTransactionStatisticsTrends: ({ startYearMonth, endYearMonth, useTransactionTimezone }) => {
getTransactionStatisticsTrends: ({ startYearMonth, endYearMonth, useTransactionTimezone, tagIds, tagFilterType }) => {
const queryParams = [];

if (startYearMonth) {
Expand All @@ -333,6 +341,14 @@ export default {
queryParams.push(`end_year_month=${endYearMonth}`);
}

if (tagIds) {
queryParams.push(`tag_ids=${tagIds}`);
}

if (tagFilterType) {
queryParams.push(`tag_filter_type=${tagFilterType}`);
}

return axios.get(`v1/transactions/statistics/trends.json?use_transaction_timezone=${useTransactionTimezone}` + (queryParams.length ? '&' + queryParams.join('&') : ''));
},
getTransactionAmounts: ({ useTransactionTimezone, today, thisWeek, thisMonth, thisYear, lastMonth, monthBeforeLastMonth, monthBeforeLast2Months, monthBeforeLast3Months, monthBeforeLast4Months, monthBeforeLast5Months, monthBeforeLast6Months, monthBeforeLast7Months, monthBeforeLast8Months, monthBeforeLast9Months, monthBeforeLast10Months }) => {
Expand Down
2 changes: 2 additions & 0 deletions src/router/desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ const router = createRouter({
initEndTime: route.query.endTime,
initFilterAccountIds: route.query.filterAccountIds,
initFilterCategoryIds: route.query.filterCategoryIds,
initTagIds: route.query.tagIds,
initTagFilterType: route.query.tagFilterType,
initSortingType: route.query.sortingType,
initTrendDateAggregationType: route.query.trendDateAggregationType
})
Expand Down
50 changes: 49 additions & 1 deletion src/stores/statistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useTransactionCategoriesStore } from './transactionCategory.js';
import { useExchangeRatesStore } from './exchangeRates.js';

import datetimeConstants from '@/consts/datetime.js';
import transactionConstants from '@/consts/transaction.js';
import statisticsConstants from '@/consts/statistics.js';
import categoryConstants from '@/consts/category.js';
import iconConstants from '@/consts/icon.js';
Expand All @@ -16,6 +17,7 @@ import logger from '@/lib/logger.js';
import {
isEquals,
isNumber,
isString,
isObject,
isInteger,
isYearMonth,
Expand Down Expand Up @@ -283,7 +285,9 @@ export const useStatisticsStore = defineStore('statistics', {
trendChartStartYearMonth: '',
trendChartEndYearMonth: '',
filterAccountIds: {},
filterCategoryIds: {}
filterCategoryIds: {},
tagIds: '',
tagFilterType: transactionConstants.defaultTransactionTagFilterType.type
},
transactionCategoryStatisticsData: {},
transactionCategoryTrendsData: {},
Expand Down Expand Up @@ -560,6 +564,8 @@ export const useStatisticsStore = defineStore('statistics', {
this.transactionStatisticsFilter.trendChartEndYearMonth = '';
this.transactionStatisticsFilter.filterAccountIds = {};
this.transactionStatisticsFilter.filterCategoryIds = {};
this.transactionStatisticsFilter.tagIds = '';
this.transactionStatisticsFilter.tagFilterType = transactionConstants.defaultTransactionTagFilterType.type;
this.transactionCategoryStatisticsData = {};
this.transactionCategoryTrendsData = {};
this.transactionStatisticsStateInvalid = true;
Expand Down Expand Up @@ -679,6 +685,18 @@ export const useStatisticsStore = defineStore('statistics', {
this.transactionStatisticsFilter.filterCategoryIds = settingsStore.appSettings.statistics.defaultTransactionCategoryFilter || {};
}

if (filter && isString(filter.tagIds)) {
this.transactionStatisticsFilter.tagIds = filter.tagIds;
} else {
this.transactionStatisticsFilter.tagIds = '';
}

if (filter && isInteger(filter.tagFilterType)) {
this.transactionStatisticsFilter.tagFilterType = filter.tagFilterType;
} else {
this.transactionStatisticsFilter.tagFilterType = transactionConstants.defaultTransactionTagFilterType.type;
}

if (filter && isInteger(filter.sortingType)) {
this.transactionStatisticsFilter.sortingType = filter.sortingType;
} else {
Expand Down Expand Up @@ -747,6 +765,16 @@ export const useStatisticsStore = defineStore('statistics', {
changed = true;
}

if (filter && isString(filter.tagIds) && this.transactionStatisticsFilter.tagIds !== filter.tagIds) {
this.transactionStatisticsFilter.tagIds = filter.tagIds;
changed = true;
}

if (filter && isInteger(filter.tagFilterType) && this.transactionStatisticsFilter.tagFilterType !== filter.tagFilterType) {
this.transactionStatisticsFilter.tagFilterType = filter.tagFilterType;
changed = true;
}

if (filter && isInteger(filter.sortingType) && this.transactionStatisticsFilter.sortingType !== filter.sortingType) {
this.transactionStatisticsFilter.sortingType = filter.sortingType;
changed = true;
Expand Down Expand Up @@ -798,6 +826,14 @@ export const useStatisticsStore = defineStore('statistics', {
}
}

if (this.transactionStatisticsFilter.tagIds) {
querys.push('tagIds=' + this.transactionStatisticsFilter.tagIds);
}

if (this.transactionStatisticsFilter.tagFilterType) {
querys.push('tagFilterType=' + this.transactionStatisticsFilter.tagFilterType);
}

querys.push('sortingType=' + this.transactionStatisticsFilter.sortingType);

return querys.join('&');
Expand Down Expand Up @@ -847,6 +883,14 @@ export const useStatisticsStore = defineStore('statistics', {
}
}

if (this.transactionStatisticsFilter.tagIds) {
querys.push('tagIds=' + this.transactionStatisticsFilter.tagIds);
}

if (this.transactionStatisticsFilter.tagFilterType) {
querys.push('tagFilterType=' + this.transactionStatisticsFilter.tagFilterType);
}

if (analysisType === statisticsConstants.allAnalysisTypes.CategoricalAnalysis
&& this.transactionStatisticsFilter.chartDataType !== statisticsConstants.allChartDataTypes.AccountTotalAssets.type
&& this.transactionStatisticsFilter.chartDataType !== statisticsConstants.allChartDataTypes.AccountTotalLiabilities.type) {
Expand All @@ -872,6 +916,8 @@ export const useStatisticsStore = defineStore('statistics', {
services.getTransactionStatistics({
startTime: self.transactionStatisticsFilter.categoricalChartStartTime,
endTime: self.transactionStatisticsFilter.categoricalChartEndTime,
tagIds: self.transactionStatisticsFilter.tagIds,
tagFilterType: self.transactionStatisticsFilter.tagFilterType,
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType
}).then(response => {
const data = response.data;
Expand Down Expand Up @@ -914,6 +960,8 @@ export const useStatisticsStore = defineStore('statistics', {
services.getTransactionStatisticsTrends({
startYearMonth: self.transactionStatisticsFilter.trendChartStartYearMonth,
endYearMonth: self.transactionStatisticsFilter.trendChartEndYearMonth,
tagIds: self.transactionStatisticsFilter.tagIds,
tagFilterType: self.transactionStatisticsFilter.tagFilterType,
useTransactionTimezone: settingsStore.appSettings.statistics.defaultTimezoneType
}).then(response => {
const data = response.data;
Expand Down
Loading

0 comments on commit db94282

Please sign in to comment.