Feature/introduce isUsedByUsersWithSubscription flag (#3573)

pull/3574/head
Thomas Kaul 2 months ago committed by GitHub
parent d5c56fb16c
commit 43afb16808
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -27,12 +27,13 @@ import {
} from '@ghostfolio/common/interfaces'; } from '@ghostfolio/common/interfaces';
import { MarketDataPreset } from '@ghostfolio/common/types'; import { MarketDataPreset } from '@ghostfolio/common/types';
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { import {
AssetClass, AssetClass,
AssetSubClass, AssetSubClass,
DataSource, DataSource,
Prisma, Prisma,
PrismaClient,
Property, Property,
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
@ -212,8 +213,11 @@ export class AdminService {
} }
} }
const extendedPrismaClient = this.getExtendedPrismaClient();
try {
let [assetProfiles, count] = await Promise.all([ let [assetProfiles, count] = await Promise.all([
this.prismaService.symbolProfile.findMany({ extendedPrismaClient.symbolProfile.findMany({
orderBy, orderBy,
skip, skip,
take, take,
@ -229,6 +233,7 @@ export class AdminService {
currency: true, currency: true,
dataSource: true, dataSource: true,
id: true, id: true,
isUsedByUsersWithSubscription: true,
name: true, name: true,
Order: { Order: {
orderBy: [{ date: 'asc' }], orderBy: [{ date: 'asc' }],
@ -243,8 +248,9 @@ export class AdminService {
this.prismaService.symbolProfile.count({ where }) this.prismaService.symbolProfile.count({ where })
]); ]);
let marketData: AdminMarketDataItem[] = assetProfiles.map( let marketData: AdminMarketDataItem[] = await Promise.all(
({ assetProfiles.map(
async ({
_count, _count,
assetClass, assetClass,
assetSubClass, assetSubClass,
@ -253,12 +259,15 @@ export class AdminService {
currency, currency,
dataSource, dataSource,
id, id,
isUsedByUsersWithSubscription,
name, name,
Order, Order,
sectors, sectors,
symbol symbol
}) => { }) => {
const countriesCount = countries ? Object.keys(countries).length : 0; const countriesCount = countries
? Object.keys(countries).length
: 0;
const marketDataItemCount = const marketDataItemCount =
marketDataItems.find((marketDataItem) => { marketDataItems.find((marketDataItem) => {
return ( return (
@ -281,9 +290,11 @@ export class AdminService {
marketDataItemCount, marketDataItemCount,
sectorsCount, sectorsCount,
activitiesCount: _count.Order, activitiesCount: _count.Order,
date: Order?.[0]?.date date: Order?.[0]?.date,
isUsedByUsersWithSubscription: await isUsedByUsersWithSubscription
}; };
} }
)
); );
if (presetId) { if (presetId) {
@ -304,6 +315,11 @@ export class AdminService {
count, count,
marketData marketData
}; };
} finally {
await extendedPrismaClient.$disconnect();
Logger.debug('Disconnect extended prisma client', 'AdminService');
}
} }
public async getMarketDataBySymbol({ public async getMarketDataBySymbol({
@ -431,6 +447,52 @@ export class AdminService {
return response; return response;
} }
private getExtendedPrismaClient() {
Logger.debug('Connect extended prisma client', 'AdminService');
const symbolProfileExtension = Prisma.defineExtension((client) => {
return client.$extends({
result: {
symbolProfile: {
isUsedByUsersWithSubscription: {
compute: async ({ id }) => {
const { _count } =
await this.prismaService.symbolProfile.findUnique({
select: {
_count: {
select: {
Order: {
where: {
User: {
Subscription: {
some: {
expiresAt: {
gt: new Date()
}
}
}
}
}
}
}
}
},
where: {
id
}
});
return _count.Order > 0;
}
}
}
}
});
});
return new PrismaClient().$extends(symbolProfileExtension);
}
private async getMarketDataForCurrencies(): Promise<AdminMarketData> { private async getMarketDataForCurrencies(): Promise<AdminMarketData> {
const marketDataItems = await this.prismaService.marketData.groupBy({ const marketDataItems = await this.prismaService.marketData.groupBy({
_count: true, _count: true,

@ -6,8 +6,14 @@ import {
ghostfolioScraperApiSymbolPrefix ghostfolioScraperApiSymbolPrefix
} from '@ghostfolio/common/config'; } from '@ghostfolio/common/config';
import { getDateFormatString } from '@ghostfolio/common/helper'; import { getDateFormatString } from '@ghostfolio/common/helper';
import { Filter, UniqueAsset, User } from '@ghostfolio/common/interfaces'; import {
Filter,
InfoItem,
UniqueAsset,
User
} from '@ghostfolio/common/interfaces';
import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface'; import { AdminMarketDataItem } from '@ghostfolio/common/interfaces/admin-market-data.interface';
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
import { translate } from '@ghostfolio/ui/i18n'; import { translate } from '@ghostfolio/ui/i18n';
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
@ -97,22 +103,11 @@ export class AdminMarketDataComponent
new MatTableDataSource(); new MatTableDataSource();
public defaultDateFormat: string; public defaultDateFormat: string;
public deviceType: string; public deviceType: string;
public displayedColumns = [ public displayedColumns: string[] = [];
'select',
'nameWithSymbol',
'dataSource',
'assetClass',
'assetSubClass',
'date',
'activitiesCount',
'marketDataItemCount',
'sectorsCount',
'countriesCount',
'comment',
'actions'
];
public filters$ = new Subject<Filter[]>(); public filters$ = new Subject<Filter[]>();
public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix; public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix;
public hasPermissionForSubscription: boolean;
public info: InfoItem;
public isLoading = false; public isLoading = false;
public isUUID = isUUID; public isUUID = isUUID;
public placeholder = ''; public placeholder = '';
@ -134,6 +129,33 @@ export class AdminMarketDataComponent
private router: Router, private router: Router,
private userService: UserService private userService: UserService
) { ) {
this.info = this.dataService.fetchInfo();
this.hasPermissionForSubscription = hasPermission(
this.info?.globalPermissions,
permissions.enableSubscription
);
this.displayedColumns = [
'select',
'nameWithSymbol',
'dataSource',
'assetClass',
'assetSubClass',
'date',
'activitiesCount',
'marketDataItemCount',
'sectorsCount',
'countriesCount'
];
if (this.hasPermissionForSubscription) {
this.displayedColumns.push('isUsedByUsersWithSubscription');
}
this.displayedColumns.push('comment');
this.displayedColumns.push('actions');
this.route.queryParams this.route.queryParams
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((params) => { .subscribe((params) => {

@ -144,6 +144,15 @@
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="isUsedByUsersWithSubscription">
<th *matHeaderCellDef class="px-1" mat-header-cell></th>
<td *matCellDef="let element" class="px-1" mat-cell>
@if (element.isUsedByUsersWithSubscription) {
<gf-premium-indicator [enableLink]="false" />
}
</td>
</ng-container>
<ng-container matColumnDef="comment"> <ng-container matColumnDef="comment">
<th *matHeaderCellDef class="px-1" mat-header-cell></th> <th *matHeaderCellDef class="px-1" mat-header-cell></th>
<td *matCellDef="let element" class="px-1" mat-cell> <td *matCellDef="let element" class="px-1" mat-cell>

@ -1,5 +1,6 @@
import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module';
import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter'; import { GfActivitiesFilterComponent } from '@ghostfolio/ui/activities-filter';
import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
@ -24,6 +25,7 @@ import { GfCreateAssetProfileDialogModule } from './create-asset-profile-dialog/
GfActivitiesFilterComponent, GfActivitiesFilterComponent,
GfAssetProfileDialogModule, GfAssetProfileDialogModule,
GfCreateAssetProfileDialogModule, GfCreateAssetProfileDialogModule,
GfPremiumIndicatorComponent,
GfSymbolModule, GfSymbolModule,
MatButtonModule, MatButtonModule,
MatCheckboxModule, MatCheckboxModule,

@ -15,6 +15,7 @@ export interface AdminMarketDataItem {
date: Date; date: Date;
id: string; id: string;
isBenchmark?: boolean; isBenchmark?: boolean;
isUsedByUsersWithSubscription?: boolean;
marketDataItemCount: number; marketDataItemCount: number;
name: string; name: string;
sectorsCount: number; sectorsCount: number;

Loading…
Cancel
Save