Feature/extend scraper configuration support (#2110)
* Extend scraper configuration support * Update changelog
This commit is contained in:
parent
b7f635bdfc
commit
e62da06c5c
@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added pagination to the historical market data table of the admin control panel
|
- Added pagination to the historical market data table of the admin control panel
|
||||||
|
- Added the attribute `headers` to the scraper configuration
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Extended the asset profile details dialog in the admin control panel by the scraper configuration
|
||||||
|
|
||||||
## 1.284.0 - 2023-06-27
|
## 1.284.0 - 2023-06-27
|
||||||
|
|
||||||
|
@ -249,12 +249,14 @@ export class AdminService {
|
|||||||
public async patchAssetProfileData({
|
public async patchAssetProfileData({
|
||||||
comment,
|
comment,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
scraperConfiguration,
|
||||||
symbol,
|
symbol,
|
||||||
symbolMapping
|
symbolMapping
|
||||||
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
||||||
await this.symbolProfileService.updateSymbolProfile({
|
await this.symbolProfileService.updateSymbolProfile({
|
||||||
comment,
|
comment,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
scraperConfiguration,
|
||||||
symbol,
|
symbol,
|
||||||
symbolMapping
|
symbolMapping
|
||||||
});
|
});
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Prisma } from '@prisma/client';
|
||||||
import { IsObject, IsOptional, IsString } from 'class-validator';
|
import { IsObject, IsOptional, IsString } from 'class-validator';
|
||||||
|
|
||||||
export class UpdateAssetProfileDto {
|
export class UpdateAssetProfileDto {
|
||||||
@ -5,6 +6,10 @@ export class UpdateAssetProfileDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
comment?: string;
|
comment?: string;
|
||||||
|
|
||||||
|
@IsObject()
|
||||||
|
@IsOptional()
|
||||||
|
scraperConfiguration?: Prisma.InputJsonObject;
|
||||||
|
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
symbolMapping?: {
|
symbolMapping?: {
|
||||||
|
@ -67,8 +67,12 @@ export class ManualService implements DataProviderInterface {
|
|||||||
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles(
|
const [symbolProfile] = await this.symbolProfileService.getSymbolProfiles(
|
||||||
[{ symbol, dataSource: this.getName() }]
|
[{ symbol, dataSource: this.getName() }]
|
||||||
);
|
);
|
||||||
const { defaultMarketPrice, selector, url } =
|
const {
|
||||||
symbolProfile.scraperConfiguration ?? {};
|
defaultMarketPrice,
|
||||||
|
headers = {},
|
||||||
|
selector,
|
||||||
|
url
|
||||||
|
} = symbolProfile.scraperConfiguration ?? {};
|
||||||
|
|
||||||
if (defaultMarketPrice) {
|
if (defaultMarketPrice) {
|
||||||
const historical: {
|
const historical: {
|
||||||
@ -91,7 +95,7 @@ export class ManualService implements DataProviderInterface {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const get = bent(url, 'GET', 'string', 200, {});
|
const get = bent(url, 'GET', 'string', 200, headers);
|
||||||
|
|
||||||
const html = await get();
|
const html = await get();
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
@ -96,11 +96,12 @@ export class SymbolProfileService {
|
|||||||
public updateSymbolProfile({
|
public updateSymbolProfile({
|
||||||
comment,
|
comment,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
scraperConfiguration,
|
||||||
symbol,
|
symbol,
|
||||||
symbolMapping
|
symbolMapping
|
||||||
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
}: Prisma.SymbolProfileUpdateInput & UniqueAsset) {
|
||||||
return this.prismaService.symbolProfile.update({
|
return this.prismaService.symbolProfile.update({
|
||||||
data: { comment, symbolMapping },
|
data: { comment, scraperConfiguration, symbolMapping },
|
||||||
where: { dataSource_symbol: { dataSource, symbol } }
|
where: { dataSource_symbol: { dataSource, symbol } }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -195,6 +196,8 @@ export class SymbolProfileService {
|
|||||||
if (scraperConfiguration) {
|
if (scraperConfiguration) {
|
||||||
return {
|
return {
|
||||||
defaultMarketPrice: scraperConfiguration.defaultMarketPrice as number,
|
defaultMarketPrice: scraperConfiguration.defaultMarketPrice as number,
|
||||||
|
headers:
|
||||||
|
scraperConfiguration.headers as ScraperConfiguration['headers'],
|
||||||
selector: scraperConfiguration.selector as string,
|
selector: scraperConfiguration.selector as string,
|
||||||
url: scraperConfiguration.url as string
|
url: scraperConfiguration.url as string
|
||||||
};
|
};
|
||||||
|
@ -13,6 +13,7 @@ import { AdminService } from '@ghostfolio/client/services/admin.service';
|
|||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
import {
|
import {
|
||||||
AdminMarketDataDetails,
|
AdminMarketDataDetails,
|
||||||
|
ScraperConfiguration,
|
||||||
UniqueAsset
|
UniqueAsset
|
||||||
} from '@ghostfolio/common/interfaces';
|
} from '@ghostfolio/common/interfaces';
|
||||||
import { translate } from '@ghostfolio/ui/i18n';
|
import { translate } from '@ghostfolio/ui/i18n';
|
||||||
@ -34,6 +35,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
public assetProfile: AdminMarketDataDetails['assetProfile'];
|
public assetProfile: AdminMarketDataDetails['assetProfile'];
|
||||||
public assetProfileForm = this.formBuilder.group({
|
public assetProfileForm = this.formBuilder.group({
|
||||||
comment: '',
|
comment: '',
|
||||||
|
scraperConfiguration: '',
|
||||||
symbolMapping: ''
|
symbolMapping: ''
|
||||||
});
|
});
|
||||||
public assetSubClass: string;
|
public assetSubClass: string;
|
||||||
@ -103,6 +105,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
|
|
||||||
this.assetProfileForm.setValue({
|
this.assetProfileForm.setValue({
|
||||||
comment: this.assetProfile?.comment ?? '',
|
comment: this.assetProfile?.comment ?? '',
|
||||||
|
scraperConfiguration: JSON.stringify(
|
||||||
|
this.assetProfile?.scraperConfiguration ?? {}
|
||||||
|
),
|
||||||
symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping ?? {})
|
symbolMapping: JSON.stringify(this.assetProfile?.symbolMapping ?? {})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -148,8 +153,15 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public onSubmit() {
|
public onSubmit() {
|
||||||
|
let scraperConfiguration = {};
|
||||||
let symbolMapping = {};
|
let symbolMapping = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
scraperConfiguration = JSON.parse(
|
||||||
|
this.assetProfileForm.controls['scraperConfiguration'].value
|
||||||
|
);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
symbolMapping = JSON.parse(
|
symbolMapping = JSON.parse(
|
||||||
this.assetProfileForm.controls['symbolMapping'].value
|
this.assetProfileForm.controls['symbolMapping'].value
|
||||||
@ -157,6 +169,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
|
|||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
const assetProfileData: UpdateAssetProfileDto = {
|
const assetProfileData: UpdateAssetProfileDto = {
|
||||||
|
scraperConfiguration,
|
||||||
symbolMapping,
|
symbolMapping,
|
||||||
comment: this.assetProfileForm.controls['comment'].value ?? null
|
comment: this.assetProfileForm.controls['comment'].value ?? null
|
||||||
};
|
};
|
||||||
|
@ -162,6 +162,17 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="assetProfile?.dataSource === 'MANUAL'">
|
||||||
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
|
<mat-label i18n>Scraper Configuration</mat-label>
|
||||||
|
<textarea
|
||||||
|
cdkTextareaAutosize
|
||||||
|
formControlName="scraperConfiguration"
|
||||||
|
matInput
|
||||||
|
type="text"
|
||||||
|
></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field appearance="outline" class="w-100">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Note</mat-label>
|
<mat-label i18n>Note</mat-label>
|
||||||
|
@ -191,12 +191,13 @@ export class AdminService {
|
|||||||
public patchAssetProfile({
|
public patchAssetProfile({
|
||||||
comment,
|
comment,
|
||||||
dataSource,
|
dataSource,
|
||||||
|
scraperConfiguration,
|
||||||
symbol,
|
symbol,
|
||||||
symbolMapping
|
symbolMapping
|
||||||
}: UniqueAsset & UpdateAssetProfileDto) {
|
}: UniqueAsset & UpdateAssetProfileDto) {
|
||||||
return this.http.patch<EnhancedSymbolProfile>(
|
return this.http.patch<EnhancedSymbolProfile>(
|
||||||
`/api/v1/admin/profile-data/${dataSource}/${symbol}`,
|
`/api/v1/admin/profile-data/${dataSource}/${symbol}`,
|
||||||
{ comment, symbolMapping }
|
{ comment, scraperConfiguration, symbolMapping }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
export interface ScraperConfiguration {
|
export interface ScraperConfiguration {
|
||||||
defaultMarketPrice?: number;
|
defaultMarketPrice?: number;
|
||||||
|
headers?: { [key: string]: string };
|
||||||
selector: string;
|
selector: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user