Browse Source

Centralize asset profile logic

pull/6991/head
Thomas Kaul 3 days ago
parent
commit
0057641c74
  1. 56
      apps/api/src/app/admin/admin.service.ts
  2. 29
      apps/api/src/services/queues/data-gathering/data-gathering.service.ts
  3. 58
      apps/api/src/services/symbol-profile/symbol-profile.service.ts
  4. 46
      libs/common/src/lib/helper.ts

56
apps/api/src/app/admin/admin.service.ts

@ -14,6 +14,7 @@ import {
PROPERTY_IS_USER_SIGNUP_ENABLED
} from '@ghostfolio/common/config';
import {
applySymbolProfileOverrides,
getAssetProfileIdentifier,
getCurrencyFromSymbol,
isCurrency
@ -29,7 +30,6 @@ import {
EnhancedSymbolProfile,
Filter
} from '@ghostfolio/common/interfaces';
import { Sector } from '@ghostfolio/common/interfaces/sector.interface';
import { MarketDataPreset } from '@ghostfolio/common/types';
import {
@ -349,25 +349,26 @@ export class AdminService {
}
let marketData: AdminMarketDataItem[] = await Promise.all(
assetProfiles.map(
async ({
assetProfiles.map(async (assetProfile) => {
const {
_count,
activities,
assetClass,
assetSubClass,
comment,
countries,
currency,
dataSource,
id,
isActive,
isUsedByUsersWithSubscription,
name,
sectors,
symbol,
SymbolProfileOverrides
}) => {
let countriesCount = countries ? Object.keys(countries).length : 0;
symbol
} = assetProfile;
const { assetClass, assetSubClass, countries, name, sectors } =
applySymbolProfileOverrides(
assetProfile,
assetProfile.SymbolProfileOverrides
);
const countriesCount = countries ? Object.keys(countries).length : 0;
const lastMarketPrice = lastMarketPriceMap.get(
getAssetProfileIdentifier({ dataSource, symbol })
@ -381,33 +382,7 @@ export class AdminService {
);
})?._count ?? 0;
let sectorsCount = sectors ? Object.keys(sectors).length : 0;
if (SymbolProfileOverrides) {
assetClass = SymbolProfileOverrides.assetClass ?? assetClass;
assetSubClass =
SymbolProfileOverrides.assetSubClass ?? assetSubClass;
if (
(SymbolProfileOverrides.countries as unknown as Prisma.JsonArray)
?.length > 0
) {
countriesCount = (
SymbolProfileOverrides.countries as unknown as Prisma.JsonArray
).length;
}
name = SymbolProfileOverrides.name ?? name;
if (
(SymbolProfileOverrides.sectors as unknown as Sector[])?.length >
0
) {
sectorsCount = (
SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray
).length;
}
}
const sectorsCount = sectors ? Object.keys(sectors).length : 0;
return {
assetClass,
@ -428,8 +403,7 @@ export class AdminService {
isUsedByUsersWithSubscription: await isUsedByUsersWithSubscription,
watchedByCount: _count.watchedBy
};
}
)
})
);
if (presetId) {

29
apps/api/src/services/queues/data-gathering/data-gathering.service.ts

@ -176,9 +176,28 @@ export class DataGatheringService {
);
for (const [symbol, assetProfile] of Object.entries(assetProfiles)) {
const symbolMapping = symbolProfiles.find((symbolProfile) => {
return symbolProfile.symbol === symbol;
})?.symbolMapping;
const symbolProfile = symbolProfiles.find(
({ symbol: symbolProfileSymbol }) => {
return symbolProfileSymbol === symbol;
}
);
const symbolMapping = symbolProfile?.symbolMapping;
// Persist the data provider’s classification in the base asset profile;
// the asset profile override stays a separate layer applied at read time
const {
assetClass: assetClassFromDataProvider,
assetSubClass: assetSubClassFromDataProvider
} = assetProfile;
// Apply the asset profile override so the data enhancers treat the asset
// profile correctly (e.g. enrich an ETF that the data provider classifies
// as a stock)
if (symbolProfile) {
assetProfile.assetClass = symbolProfile.assetClass;
assetProfile.assetSubClass = symbolProfile.assetSubClass;
}
for (const dataEnhancer of this.dataEnhancers) {
try {
@ -197,6 +216,10 @@ export class DataGatheringService {
}
}
// Restore the data provider’s classification before persisting
assetProfile.assetClass = assetClassFromDataProvider;
assetProfile.assetSubClass = assetSubClassFromDataProvider;
const {
assetClass,
assetSubClass,

58
apps/api/src/services/symbol-profile/symbol-profile.service.ts

@ -1,5 +1,6 @@
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { UNKNOWN_KEY } from '@ghostfolio/common/config';
import { applySymbolProfileOverrides } from '@ghostfolio/common/helper';
import {
AssetProfileIdentifier,
EnhancedSymbolProfile,
@ -192,21 +193,28 @@ export class SymbolProfileService {
})[]
): EnhancedSymbolProfile[] {
return symbolProfiles.map((symbolProfile) => {
const symbolProfileWithOverrides = applySymbolProfileOverrides(
symbolProfile,
symbolProfile.SymbolProfileOverrides
);
const item = {
...symbolProfile,
...symbolProfileWithOverrides,
activitiesCount: 0,
countries: this.getCountries(
symbolProfile?.countries as unknown as Prisma.JsonArray
symbolProfileWithOverrides?.countries as unknown as Prisma.JsonArray
),
dateOfFirstActivity: undefined as Date,
holdings: this.getHoldings(
symbolProfile?.holdings as unknown as Prisma.JsonArray
symbolProfileWithOverrides?.holdings as unknown as Prisma.JsonArray
),
scraperConfiguration: this.getScraperConfiguration(
symbolProfileWithOverrides
),
scraperConfiguration: this.getScraperConfiguration(symbolProfile),
sectors: this.getSectors(
symbolProfile?.sectors as unknown as Prisma.JsonArray
symbolProfileWithOverrides?.sectors as unknown as Prisma.JsonArray
),
symbolMapping: this.getSymbolMapping(symbolProfile),
symbolMapping: this.getSymbolMapping(symbolProfileWithOverrides),
watchedByCount: 0
};
@ -217,45 +225,7 @@ export class SymbolProfileService {
item.dateOfFirstActivity = symbolProfile.activities?.[0]?.date;
delete item.activities;
if (item.SymbolProfileOverrides) {
item.assetClass =
item.SymbolProfileOverrides.assetClass ?? item.assetClass;
item.assetSubClass =
item.SymbolProfileOverrides.assetSubClass ?? item.assetSubClass;
if (
(item.SymbolProfileOverrides.countries as unknown as Prisma.JsonArray)
?.length > 0
) {
item.countries = this.getCountries(
item.SymbolProfileOverrides.countries as unknown as Prisma.JsonArray
);
}
if (
(item.SymbolProfileOverrides.holdings as unknown as Holding[])
?.length > 0
) {
item.holdings = this.getHoldings(
item.SymbolProfileOverrides.holdings as unknown as Prisma.JsonArray
);
}
item.name = item.SymbolProfileOverrides.name ?? item.name;
if (
(item.SymbolProfileOverrides.sectors as unknown as Sector[])?.length >
0
) {
item.sectors = this.getSectors(
item.SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray
);
}
item.url = item.SymbolProfileOverrides.url ?? item.url;
delete item.SymbolProfileOverrides;
}
return item;
});

46
libs/common/src/lib/helper.ts

@ -1,5 +1,12 @@
import { NumberParser } from '@internationalized/number';
import { Type as ActivityType, DataSource, MarketData } from '@prisma/client';
import {
Type as ActivityType,
DataSource,
MarketData,
Prisma,
SymbolProfile,
SymbolProfileOverrides
} from '@prisma/client';
import { Big } from 'big.js';
import { isISO4217CurrencyCode } from 'class-validator';
import {
@ -47,6 +54,43 @@ export const DATE_FORMAT = 'yyyy-MM-dd';
export const DATE_FORMAT_MONTHLY = 'MMMM yyyy';
export const DATE_FORMAT_YEARLY = 'yyyy';
export function applySymbolProfileOverrides<T extends Partial<SymbolProfile>>(
symbolProfile: T,
symbolProfileOverrides: SymbolProfileOverrides | null
): T {
if (!symbolProfileOverrides) {
return symbolProfile;
}
const symbolProfileWithOverrides = { ...symbolProfile } as T;
symbolProfileWithOverrides.assetClass =
symbolProfileOverrides.assetClass ?? symbolProfile.assetClass;
symbolProfileWithOverrides.assetSubClass =
symbolProfileOverrides.assetSubClass ?? symbolProfile.assetSubClass;
if ((symbolProfileOverrides.countries as Prisma.JsonArray)?.length > 0) {
symbolProfileWithOverrides.countries = symbolProfileOverrides.countries;
}
if ((symbolProfileOverrides.holdings as Prisma.JsonArray)?.length > 0) {
symbolProfileWithOverrides.holdings = symbolProfileOverrides.holdings;
}
symbolProfileWithOverrides.name =
symbolProfileOverrides.name ?? symbolProfile.name;
if ((symbolProfileOverrides.sectors as Prisma.JsonArray)?.length > 0) {
symbolProfileWithOverrides.sectors = symbolProfileOverrides.sectors;
}
symbolProfileWithOverrides.url =
symbolProfileOverrides.url ?? symbolProfile.url;
return symbolProfileWithOverrides;
}
export function calculateBenchmarkTrend({
days,
historicalData

Loading…
Cancel
Save