Browse Source

Feature/extend export by custom asset profiles (#5165)

* Extend export by custom asset profiles

* Update changelog
pull/5198/head
Attila Cseh 3 days ago
committed by GitHub
parent
commit
806df6c0c5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 2
      apps/api/src/app/export/export.module.ts
  3. 89
      apps/api/src/app/export/export.service.ts
  4. 1
      libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts
  5. 14
      libs/common/src/lib/interfaces/export.interface.ts

1
CHANGELOG.md

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Extended the export functionality by custom asset profiles
- Improved the platform icon in the create or update platform dialog of the admin control
- Localized the durations of the coupon system
- Refactored the resources pages to standalone

2
apps/api/src/app/export/export.module.ts

@ -2,6 +2,7 @@ import { AccountModule } from '@ghostfolio/api/app/account/account.module';
import { OrderModule } from '@ghostfolio/api/app/order/order.module';
import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module';
import { ApiModule } from '@ghostfolio/api/services/api/api.module';
import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module';
import { TagModule } from '@ghostfolio/api/services/tag/tag.module';
import { Module } from '@nestjs/common';
@ -14,6 +15,7 @@ import { ExportService } from './export.service';
imports: [
AccountModule,
ApiModule,
MarketDataModule,
OrderModule,
TagModule,
TransformDataSourceInRequestModule

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

@ -1,17 +1,19 @@
import { AccountService } from '@ghostfolio/api/app/account/account.service';
import { OrderService } from '@ghostfolio/api/app/order/order.service';
import { environment } from '@ghostfolio/api/environments/environment';
import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service';
import { TagService } from '@ghostfolio/api/services/tag/tag.service';
import { Filter, Export } from '@ghostfolio/common/interfaces';
import { Injectable } from '@nestjs/common';
import { Platform, Prisma } from '@prisma/client';
import { groupBy } from 'lodash';
import { groupBy, uniqBy } from 'lodash';
@Injectable()
export class ExportService {
public constructor(
private readonly accountService: AccountService,
private readonly marketDataService: MarketDataService,
private readonly orderService: OrderService,
private readonly tagService: TagService
) {}
@ -108,6 +110,36 @@ export class ExportService {
}
);
const customAssetProfiles = uniqBy(
activities
.map(({ SymbolProfile }) => {
return SymbolProfile;
})
.filter(({ userId: assetProfileUserId }) => {
return assetProfileUserId === userId;
}),
({ id }) => {
return id;
}
);
const marketDataByAssetProfile = Object.fromEntries(
await Promise.all(
customAssetProfiles.map(async ({ dataSource, id, symbol }) => {
const marketData = (
await this.marketDataService.marketDataItems({
where: { dataSource, symbol }
})
).map(({ date, marketPrice }) => ({
date: date.toISOString(),
marketPrice
}));
return [id, marketData] as const;
})
)
);
const tags = (await this.tagService.getTagsForUser(userId))
.filter(
({ id, isUsed }) =>
@ -128,6 +160,55 @@ export class ExportService {
return {
meta: { date: new Date().toISOString(), version: environment.version },
accounts,
assetProfiles: customAssetProfiles.map(
({
assetClass,
assetSubClass,
comment,
countries,
currency,
cusip,
dataSource,
figi,
figiComposite,
figiShareClass,
holdings,
id,
isActive,
isin,
name,
scraperConfiguration,
sectors,
symbol,
symbolMapping,
url
}) => {
return {
assetClass,
assetSubClass,
comment,
countries: countries as unknown as Prisma.JsonArray,
currency,
cusip,
dataSource,
figi,
figiComposite,
figiShareClass,
holdings: holdings as unknown as Prisma.JsonArray,
id,
isActive,
isin,
marketData: marketDataByAssetProfile[id],
name,
scraperConfiguration:
scraperConfiguration as unknown as Prisma.JsonArray,
sectors: sectors as unknown as Prisma.JsonArray,
symbol,
symbolMapping,
url
};
}
),
platforms: Object.values(platformsMap),
tags,
activities: activities.map(
@ -155,11 +236,7 @@ export class ExportService {
currency: currency ?? SymbolProfile.currency,
dataSource: SymbolProfile.dataSource,
date: date.toISOString(),
symbol:
['FEE', 'INTEREST', 'LIABILITY'].includes(type) ||
(SymbolProfile.dataSource === 'MANUAL' && type === 'BUY')
? SymbolProfile.name
: SymbolProfile.symbol,
symbol: SymbolProfile.symbol,
tags: currentTags.map(({ id: tagId }) => {
return tagId;
})

1
libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts

@ -14,6 +14,7 @@ export interface EnhancedSymbolProfile {
countries: Country[];
createdAt: Date;
currency?: string;
cusip?: string;
dataProviderInfo?: DataProviderInfo;
dataSource: DataSource;
dateOfFirstActivity?: Date;

14
libs/common/src/lib/interfaces/export.interface.ts

@ -1,4 +1,11 @@
import { Account, Order, Platform, Tag } from '@prisma/client';
import {
Account,
DataSource,
Order,
Platform,
SymbolProfile,
Tag
} from '@prisma/client';
import { AccountBalance } from './account-balance.interface';
@ -15,7 +22,10 @@ export interface Export {
| 'symbolProfileId'
| 'updatedAt'
| 'userId'
> & { date: string; symbol: string })[];
> & { dataSource: DataSource; date: string; symbol: string })[];
assetProfiles: (Omit<SymbolProfile, 'createdAt' | 'updatedAt' | 'userId'> & {
marketData: { date: string; marketPrice: number }[];
})[];
meta: {
date: string;
version: string;

Loading…
Cancel
Save