Browse Source

Task/harmonize filter parsing using groupBy() (#7211)

* Harmonize filter parsing using groupBy()

* Update changelog
pull/7212/head^2
Thomas Kaul 2 weeks ago
committed by GitHub
parent
commit
6df2f4e07f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 9
      apps/api/src/app/account-balance/account-balance.service.ts
  3. 4
      apps/api/src/app/account/account.service.ts
  4. 45
      apps/api/src/app/activities/activities.service.ts
  5. 27
      apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts
  6. 4
      apps/api/src/app/export/export.service.ts
  7. 51
      apps/api/src/app/portfolio/portfolio.service.ts
  8. 38
      libs/ui/src/lib/services/data.service.ts

1
CHANGELOG.md

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Improved the parsing of integer query parameters (`skip` and `take`) in the `GET api/v1/asset-profiles` endpoint - Improved the parsing of integer query parameters (`skip` and `take`) in the `GET api/v1/asset-profiles` endpoint
- Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/market-data/markets` endpoint - Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/market-data/markets` endpoint
- Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/symbol/:dataSource/:symbol` endpoint - Improved the parsing of the integer query parameter (`includeHistoricalData`) in the `GET api/v1/symbol/:dataSource/:symbol` endpoint
- Harmonized the filter parsing using `groupBy` across various services
- Improved the language localization by translating various tooltips across the application - Improved the language localization by translating various tooltips across the application
- Improved the language localization for Ukrainian (`uk`) - Improved the language localization for Ukrainian (`uk`)
- Upgraded `yahoo-finance2` from version `3.14.3` to `3.15.4` - Upgraded `yahoo-finance2` from version `3.14.3` to `3.15.4`

9
apps/api/src/app/account-balance/account-balance.service.ts

@ -15,6 +15,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { AccountBalance, Prisma } from '@prisma/client'; import { AccountBalance, Prisma } from '@prisma/client';
import { Big } from 'big.js'; import { Big } from 'big.js';
import { format, parseISO } from 'date-fns'; import { format, parseISO } from 'date-fns';
import { groupBy } from 'lodash';
@Injectable() @Injectable()
export class AccountBalanceService { export class AccountBalanceService {
@ -144,12 +145,12 @@ export class AccountBalanceService {
}): Promise<AccountBalancesResponse> { }): Promise<AccountBalancesResponse> {
const where: Prisma.AccountBalanceWhereInput = { userId }; const where: Prisma.AccountBalanceWhereInput = { userId };
const accountFilter = filters?.find(({ type }) => { const { ACCOUNT: [filterByAccount] = [] } = groupBy(filters, ({ type }) => {
return type === 'ACCOUNT'; return type;
}); });
if (accountFilter) { if (filterByAccount) {
where.accountId = accountFilter.id; where.accountId = filterByAccount.id;
} }
if (withExcludedAccounts === false) { if (withExcludedAccounts === false) {

4
apps/api/src/app/account/account.service.ts

@ -187,11 +187,11 @@ export class AccountService {
where.isExcluded = false; where.isExcluded = false;
} }
const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => { const { ACCOUNT: filtersByAccount = [] } = groupBy(filters, ({ type }) => {
return type; return type;
}); });
if (filtersByAccount?.length > 0) { if (filtersByAccount.length > 0) {
where.id = { where.id = {
in: filtersByAccount.map(({ id }) => { in: filtersByAccount.map(({ id }) => {
return id; return id;

45
apps/api/src/app/activities/activities.service.ts

@ -556,26 +556,17 @@ export class ActivitiesService {
} }
const { const {
ACCOUNT: filtersByAccount, ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass, ASSET_CLASS: filtersByAssetClass = [],
TAG: filtersByTag DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag = []
} = groupBy(filters, ({ type }) => { } = groupBy(filters, ({ type }) => {
return type; return type;
}); });
const filterByDataSource = filters?.find(({ type }) => { if (filtersByAccount.length > 0) {
return type === 'DATA_SOURCE';
})?.id;
const filterBySymbol = filters?.find(({ type }) => {
return type === 'SYMBOL';
})?.id;
const searchQuery = filters?.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
if (filtersByAccount?.length > 0) {
where.accountId = { where.accountId = {
in: filtersByAccount.map(({ id }) => { in: filtersByAccount.map(({ id }) => {
return id; return id;
@ -587,7 +578,7 @@ export class ActivitiesService {
where.isDraft = false; where.isDraft = false;
} }
if (filtersByAssetClass?.length > 0) { if (filtersByAssetClass.length > 0) {
where.SymbolProfile = { where.SymbolProfile = {
OR: [ OR: [
{ {
@ -623,8 +614,8 @@ export class ActivitiesService {
where.SymbolProfile, where.SymbolProfile,
{ {
AND: [ AND: [
{ dataSource: filterByDataSource as DataSource }, { dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol } { symbol: filterBySymbol.id }
] ]
} }
] ]
@ -632,19 +623,19 @@ export class ActivitiesService {
} else { } else {
where.SymbolProfile = { where.SymbolProfile = {
AND: [ AND: [
{ dataSource: filterByDataSource as DataSource }, { dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol } { symbol: filterBySymbol.id }
] ]
}; };
} }
} }
if (searchQuery) { if (filterBySearchQuery) {
const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [ const searchQueryWhereInput: Prisma.SymbolProfileWhereInput[] = [
{ id: { mode: 'insensitive', startsWith: searchQuery } }, { id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: searchQuery } }, { isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: searchQuery } }, { name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: searchQuery } } { symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
]; ];
if (where.SymbolProfile) { if (where.SymbolProfile) {
@ -663,7 +654,7 @@ export class ActivitiesService {
} }
} }
if (filtersByTag?.length > 0) { if (filtersByTag.length > 0) {
where.tags = { where.tags = {
some: { some: {
OR: filtersByTag.map(({ id }) => { OR: filtersByTag.map(({ id }) => {

27
apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts

@ -133,13 +133,10 @@ export class AssetProfilesService {
}; };
} }
const searchQuery = filters.find(({ type }) => {
return type === 'SEARCH_QUERY';
})?.id;
const { const {
ASSET_SUB_CLASS: filtersByAssetSubClass, ASSET_SUB_CLASS: [filterByAssetSubClass] = [],
DATA_SOURCE: filtersByDataSource DATA_SOURCE: [filterByDataSource] = [],
SEARCH_QUERY: [filterBySearchQuery] = []
} = groupBy(filters, ({ type }) => { } = groupBy(filters, ({ type }) => {
return type; return type;
}); });
@ -149,20 +146,20 @@ export class AssetProfilesService {
by: ['dataSource', 'symbol'] by: ['dataSource', 'symbol']
}); });
if (filtersByAssetSubClass) { if (filterByAssetSubClass) {
where.assetSubClass = AssetSubClass[filtersByAssetSubClass[0].id]; where.assetSubClass = AssetSubClass[filterByAssetSubClass.id];
} }
if (filtersByDataSource) { if (filterByDataSource) {
where.dataSource = DataSource[filtersByDataSource[0].id]; where.dataSource = DataSource[filterByDataSource.id];
} }
if (searchQuery) { if (filterBySearchQuery) {
where.OR = [ where.OR = [
{ id: { mode: 'insensitive', startsWith: searchQuery } }, { id: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ isin: { mode: 'insensitive', startsWith: searchQuery } }, { isin: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ name: { mode: 'insensitive', startsWith: searchQuery } }, { name: { mode: 'insensitive', startsWith: filterBySearchQuery.id } },
{ symbol: { mode: 'insensitive', startsWith: searchQuery } } { symbol: { mode: 'insensitive', startsWith: filterBySearchQuery.id } }
]; ];
} }

4
apps/api/src/app/export/export.service.ts

@ -35,7 +35,7 @@ export class ExportService {
userId: string; userId: string;
userSettings: UserSettings; userSettings: UserSettings;
}): Promise<ExportResponse> { }): Promise<ExportResponse> {
const { ACCOUNT: filtersByAccount } = groupBy(filters, ({ type }) => { const { ACCOUNT: filtersByAccount = [] } = groupBy(filters, ({ type }) => {
return type; return type;
}); });
const platformsMap: { [platformId: string]: Platform } = {}; const platformsMap: { [platformId: string]: Platform } = {};
@ -59,7 +59,7 @@ export class ExportService {
const where: Prisma.AccountWhereInput = { userId }; const where: Prisma.AccountWhereInput = { userId };
if (filtersByAccount?.length > 0) { if (filtersByAccount.length > 0) {
where.id = { where.id = {
in: filtersByAccount.map(({ id }) => { in: filtersByAccount.map(({ id }) => {
return id; return id;

51
apps/api/src/app/portfolio/portfolio.service.ts

@ -95,6 +95,7 @@ import {
parseISO, parseISO,
set set
} from 'date-fns'; } from 'date-fns';
import { groupBy } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator'; import { PortfolioCalculator } from './calculator/portfolio-calculator';
import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory'; import { PortfolioCalculatorFactory } from './calculator/portfolio-calculator.factory';
@ -138,20 +139,16 @@ export class PortfolioService {
}): Promise<AccountWithValue[]> { }): Promise<AccountWithValue[]> {
const where: Prisma.AccountWhereInput = { userId }; const where: Prisma.AccountWhereInput = { userId };
const filterByAccount = filters?.find(({ type }) => { const {
return type === 'ACCOUNT'; ACCOUNT: [filterByAccount] = [],
})?.id; DATA_SOURCE: [filterByDataSource] = [],
SYMBOL: [filterBySymbol] = []
const filterByDataSource = filters?.find(({ type }) => { } = groupBy(filters, ({ type }) => {
return type === 'DATA_SOURCE'; return type;
})?.id; });
const filterBySymbol = filters?.find(({ type }) => {
return type === 'SYMBOL';
})?.id;
if (filterByAccount) { if (filterByAccount) {
where.id = filterByAccount; where.id = filterByAccount.id;
} }
if (filterByDataSource && filterBySymbol) { if (filterByDataSource && filterBySymbol) {
@ -159,8 +156,8 @@ export class PortfolioService {
some: { some: {
SymbolProfile: { SymbolProfile: {
AND: [ AND: [
{ dataSource: filterByDataSource as DataSource }, { dataSource: filterByDataSource.id as DataSource },
{ symbol: filterBySymbol } { symbol: filterBySymbol.id }
] ]
} }
} }
@ -274,17 +271,20 @@ export class PortfolioService {
let activitiesCount = 0; let activitiesCount = 0;
const searchQuery = filters.find(({ type }) => { const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy(
return type === 'SEARCH_QUERY'; filters,
})?.id; ({ type }) => {
return type;
}
);
if (searchQuery) { if (filterBySearchQuery) {
const fuse = new Fuse(accounts, { const fuse = new Fuse(accounts, {
keys: ['name', 'platform.name'], keys: ['name', 'platform.name'],
threshold: 0.3 threshold: 0.3
}); });
accounts = fuse.search(searchQuery).map(({ item }) => { accounts = fuse.search(filterBySearchQuery.id).map(({ item }) => {
return item; return item;
}); });
} }
@ -376,17 +376,20 @@ export class PortfolioService {
let holdings = Object.values(holdingsMap); let holdings = Object.values(holdingsMap);
const searchQuery = filters.find(({ type }) => { const { SEARCH_QUERY: [filterBySearchQuery] = [] } = groupBy(
return type === 'SEARCH_QUERY'; filters,
})?.id; ({ type }) => {
return type;
}
);
if (searchQuery) { if (filterBySearchQuery) {
const fuse = new Fuse(holdings, { const fuse = new Fuse(holdings, {
keys: ['isin', 'name', 'symbol'], keys: ['isin', 'name', 'symbol'],
threshold: 0.3 threshold: 0.3
}); });
holdings = fuse.search(searchQuery).map(({ item }) => { holdings = fuse.search(filterBySearchQuery.id).map(({ item }) => {
return item; return item;
}); });
} }

38
libs/ui/src/lib/services/data.service.ts

@ -96,17 +96,17 @@ export class DataService {
if (filters && filters.length > 0) { if (filters && filters.length > 0) {
const { const {
ACCOUNT: filtersByAccount, ACCOUNT: filtersByAccount = [],
ASSET_CLASS: filtersByAssetClass, ASSET_CLASS: filtersByAssetClass = [],
ASSET_SUB_CLASS: filtersByAssetSubClass, ASSET_SUB_CLASS: filtersByAssetSubClass = [],
DATA_SOURCE: [filterByDataSource] = [], DATA_SOURCE: [filterByDataSource] = [],
HOLDING_TYPE: filtersByHoldingType, HOLDING_TYPE: [filterByHoldingType] = [],
PRESET_ID: filtersByPresetId, PRESET_ID: [filterByPresetId] = [],
SEARCH_QUERY: filtersBySearchQuery, SEARCH_QUERY: [filterBySearchQuery] = [],
SYMBOL: [filterBySymbol] = [], SYMBOL: [filterBySymbol] = [],
TAG: filtersByTag TAG: filtersByTag = []
} = groupBy(filters, (filter) => { } = groupBy(filters, ({ type }) => {
return filter.type; return type;
}); });
if (filterByDataSource) { if (filterByDataSource) {
@ -117,7 +117,7 @@ export class DataService {
params = params.append('symbol', filterBySymbol.id); params = params.append('symbol', filterBySymbol.id);
} }
if (filtersByAccount) { if (filtersByAccount.length > 0) {
params = params.append( params = params.append(
'accounts', 'accounts',
filtersByAccount filtersByAccount
@ -128,7 +128,7 @@ export class DataService {
); );
} }
if (filtersByAssetClass) { if (filtersByAssetClass.length > 0) {
params = params.append( params = params.append(
'assetClasses', 'assetClasses',
filtersByAssetClass filtersByAssetClass
@ -139,7 +139,7 @@ export class DataService {
); );
} }
if (filtersByAssetSubClass) { if (filtersByAssetSubClass.length > 0) {
params = params.append( params = params.append(
'assetSubClasses', 'assetSubClasses',
filtersByAssetSubClass filtersByAssetSubClass
@ -150,19 +150,19 @@ export class DataService {
); );
} }
if (filtersByHoldingType) { if (filterByHoldingType) {
params = params.append('holdingType', filtersByHoldingType[0].id); params = params.append('holdingType', filterByHoldingType.id);
} }
if (filtersByPresetId) { if (filterByPresetId) {
params = params.append('presetId', filtersByPresetId[0].id); params = params.append('presetId', filterByPresetId.id);
} }
if (filtersBySearchQuery) { if (filterBySearchQuery) {
params = params.append('query', filtersBySearchQuery[0].id); params = params.append('query', filterBySearchQuery.id);
} }
if (filtersByTag) { if (filtersByTag.length > 0) {
params = params.append( params = params.append(
'tags', 'tags',
filtersByTag filtersByTag

Loading…
Cancel
Save