Feature/add queries to market data table in admin control (#2153)
* Add queries * ETF_WITHOUT_COUNTRIES * ETF_WITHOUT_SECTORS * Update changelog
This commit is contained in:
parent
81ef95e13e
commit
68a9a7f6f9
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
|
||||
- Added hints to the activity types in the create or edit activity dialog
|
||||
- Added queries to the historical market data table of the admin control panel
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -15,7 +15,10 @@ import {
|
||||
Filter
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import type { RequestWithUser } from '@ghostfolio/common/types';
|
||||
import type {
|
||||
MarketDataQuery,
|
||||
RequestWithUser
|
||||
} from '@ghostfolio/common/types';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
@ -249,6 +252,7 @@ export class AdminController {
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
public async getMarketData(
|
||||
@Query('assetSubClasses') filterByAssetSubClasses?: string,
|
||||
@Query('queryId') queryId?: MarketDataQuery,
|
||||
@Query('skip') skip?: number,
|
||||
@Query('sortColumn') sortColumn?: string,
|
||||
@Query('sortDirection') sortDirection?: Prisma.SortOrder,
|
||||
@ -279,6 +283,7 @@ export class AdminController {
|
||||
|
||||
return this.adminService.getMarketData({
|
||||
filters,
|
||||
queryId,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
skip: isNaN(skip) ? undefined : skip,
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
Filter,
|
||||
UniqueAsset
|
||||
} from '@ghostfolio/common/interfaces';
|
||||
import { MarketDataQuery } from '@ghostfolio/common/types';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { AssetSubClass, Prisma, Property, SymbolProfile } from '@prisma/client';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
@ -103,12 +104,14 @@ export class AdminService {
|
||||
|
||||
public async getMarketData({
|
||||
filters,
|
||||
queryId,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
skip,
|
||||
take = DEFAULT_PAGE_SIZE
|
||||
take = Number.MAX_SAFE_INTEGER
|
||||
}: {
|
||||
filters?: Filter[];
|
||||
queryId?: MarketDataQuery;
|
||||
skip?: number;
|
||||
sortColumn?: string;
|
||||
sortDirection?: Prisma.SortOrder;
|
||||
@ -118,6 +121,13 @@ export class AdminService {
|
||||
[{ symbol: 'asc' }];
|
||||
const where: Prisma.SymbolProfileWhereInput = {};
|
||||
|
||||
if (
|
||||
queryId === 'ETF_WITHOUT_COUNTRIES' ||
|
||||
queryId === 'ETF_WITHOUT_SECTORS'
|
||||
) {
|
||||
filters = [{ id: 'ETF', type: 'ASSET_SUB_CLASS' }];
|
||||
}
|
||||
|
||||
const { ASSET_SUB_CLASS: filtersByAssetSubClass } = groupBy(
|
||||
filters,
|
||||
(filter) => {
|
||||
@ -146,7 +156,7 @@ export class AdminService {
|
||||
}
|
||||
}
|
||||
|
||||
const [assetProfiles, count] = await Promise.all([
|
||||
let [assetProfiles, count] = await Promise.all([
|
||||
this.prismaService.symbolProfile.findMany({
|
||||
orderBy,
|
||||
skip,
|
||||
@ -174,9 +184,7 @@ export class AdminService {
|
||||
this.prismaService.symbolProfile.count({ where })
|
||||
]);
|
||||
|
||||
return {
|
||||
count,
|
||||
marketData: assetProfiles.map(
|
||||
let marketData = assetProfiles.map(
|
||||
({
|
||||
_count,
|
||||
assetClass,
|
||||
@ -211,7 +219,25 @@ export class AdminService {
|
||||
date: Order?.[0]?.date
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if (queryId) {
|
||||
if (queryId === 'ETF_WITHOUT_COUNTRIES') {
|
||||
marketData = marketData.filter(({ countriesCount }) => {
|
||||
return countriesCount === 0;
|
||||
});
|
||||
} else if (queryId === 'ETF_WITHOUT_SECTORS') {
|
||||
marketData = marketData.filter(({ sectorsCount }) => {
|
||||
return sectorsCount === 0;
|
||||
});
|
||||
}
|
||||
|
||||
count = marketData.length;
|
||||
}
|
||||
|
||||
return {
|
||||
count,
|
||||
marketData
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -51,13 +51,26 @@ export class AdminMarketDataComponent
|
||||
AssetSubClass.PRECIOUS_METAL,
|
||||
AssetSubClass.PRIVATE_EQUITY,
|
||||
AssetSubClass.STOCK
|
||||
].map((assetSubClass) => {
|
||||
]
|
||||
.map((assetSubClass) => {
|
||||
return {
|
||||
id: assetSubClass,
|
||||
id: assetSubClass.toString(),
|
||||
label: translate(assetSubClass),
|
||||
type: 'ASSET_SUB_CLASS'
|
||||
type: <Filter['type']>'ASSET_SUB_CLASS'
|
||||
};
|
||||
});
|
||||
})
|
||||
.concat([
|
||||
{
|
||||
id: 'ETF_WITHOUT_COUNTRIES',
|
||||
label: $localize`ETFs without Countries`,
|
||||
type: <Filter['type']>'QUERY_ID'
|
||||
},
|
||||
{
|
||||
id: 'ETF_WITHOUT_SECTORS',
|
||||
label: $localize`ETFs without Sectors`,
|
||||
type: <Filter['type']>'QUERY_ID'
|
||||
}
|
||||
]);
|
||||
public currentDataSource: DataSource;
|
||||
public currentSymbol: string;
|
||||
public dataSource: MatTableDataSource<AdminMarketDataItem> =
|
||||
@ -237,6 +250,12 @@ export class AdminMarketDataComponent
|
||||
) {
|
||||
this.isLoading = true;
|
||||
|
||||
this.pageSize =
|
||||
this.activeFilters.length === 1 &&
|
||||
this.activeFilters[0].type === 'QUERY_ID'
|
||||
? undefined
|
||||
: DEFAULT_PAGE_SIZE;
|
||||
|
||||
if (pageIndex === 0 && this.paginator) {
|
||||
this.paginator.pageIndex = 0;
|
||||
}
|
||||
|
@ -95,7 +95,9 @@ export class AdminService {
|
||||
params = params.append('sortDirection', sortDirection);
|
||||
}
|
||||
|
||||
if (take) {
|
||||
params = params.append('take', take);
|
||||
}
|
||||
|
||||
return this.http.get<AdminMarketData>('/api/v1/admin/market-data', {
|
||||
params
|
||||
|
@ -57,6 +57,7 @@ export class DataService {
|
||||
ACCOUNT: filtersByAccount,
|
||||
ASSET_CLASS: filtersByAssetClass,
|
||||
ASSET_SUB_CLASS: filtersByAssetSubClass,
|
||||
QUERY_ID: filtersByQueryId,
|
||||
TAG: filtersByTag
|
||||
} = groupBy(filters, (filter) => {
|
||||
return filter.type;
|
||||
@ -95,6 +96,10 @@ export class DataService {
|
||||
);
|
||||
}
|
||||
|
||||
if (filtersByQueryId) {
|
||||
params = params.append('queryId', filtersByQueryId[0].id);
|
||||
}
|
||||
|
||||
if (filtersByTag) {
|
||||
params = params.append(
|
||||
'tags',
|
||||
|
@ -1,5 +1,11 @@
|
||||
export interface Filter {
|
||||
id: string;
|
||||
label?: string;
|
||||
type: 'ACCOUNT' | 'ASSET_CLASS' | 'ASSET_SUB_CLASS' | 'SYMBOL' | 'TAG';
|
||||
type:
|
||||
| 'ACCOUNT'
|
||||
| 'ASSET_CLASS'
|
||||
| 'ASSET_SUB_CLASS'
|
||||
| 'QUERY_ID'
|
||||
| 'SYMBOL'
|
||||
| 'TAG';
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import type { ColorScheme } from './color-scheme.type';
|
||||
import type { DateRange } from './date-range.type';
|
||||
import type { Granularity } from './granularity.type';
|
||||
import type { GroupBy } from './group-by.type';
|
||||
import type { MarketDataQuery } from './market-data-query.type';
|
||||
import type { MarketState } from './market-state.type';
|
||||
import type { Market } from './market.type';
|
||||
import type { OrderWithAccount } from './order-with-account.type';
|
||||
@ -23,6 +24,7 @@ export type {
|
||||
Granularity,
|
||||
GroupBy,
|
||||
Market,
|
||||
MarketDataQuery,
|
||||
MarketState,
|
||||
OrderWithAccount,
|
||||
RequestWithUser,
|
||||
|
1
libs/common/src/lib/types/market-data-query.type.ts
Normal file
1
libs/common/src/lib/types/market-data-query.type.ts
Normal file
@ -0,0 +1 @@
|
||||
export type MarketDataQuery = 'ETF_WITHOUT_COUNTRIES' | 'ETF_WITHOUT_SECTORS';
|
@ -16,6 +16,7 @@ const locales = {
|
||||
MONTH: $localize`Month`,
|
||||
MONTHS: $localize`Months`,
|
||||
OTHER: $localize`Other`,
|
||||
QUERY_ID: $localize`Query`,
|
||||
RETIREMENT_PROVISION: $localize`Retirement Provision`,
|
||||
SATELLITE: $localize`Satellite`,
|
||||
SECURITIES: $localize`Securities`,
|
||||
|
Loading…
x
Reference in New Issue
Block a user