Browse Source

Added support for testing scraping

pull/2777/head
Hugo Persson 2 years ago
parent
commit
9156a3eae3
  1. 1
      .gitignore
  2. 17
      apps/api/src/app/admin/admin.controller.ts
  3. 5
      apps/api/src/app/admin/admin.module.ts
  4. 3
      apps/api/src/services/data-provider/data-provider.module.ts
  5. 51
      apps/api/src/services/data-provider/manual/manual.service.ts
  6. 18
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
  7. 3
      apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
  8. 4
      apps/client/src/app/services/admin.service.ts

1
.gitignore

@ -40,3 +40,4 @@ yarn-error.log
# System Files # System Files
.DS_Store .DS_Store
Thumbs.db Thumbs.db
.nx/cache

17
apps/api/src/app/admin/admin.controller.ts

@ -45,6 +45,7 @@ import { AdminService } from './admin.service';
import { UpdateAssetProfileDto } from './update-asset-profile.dto'; import { UpdateAssetProfileDto } from './update-asset-profile.dto';
import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto'; import { UpdateBulkMarketDataDto } from './update-bulk-market-data.dto';
import { UpdateMarketDataDto } from './update-market-data.dto'; import { UpdateMarketDataDto } from './update-market-data.dto';
import { ManualService } from '@ghostfolio/api/services/data-provider/manual/manual.service';
@Controller('admin') @Controller('admin')
export class AdminController { export class AdminController {
@ -52,6 +53,7 @@ export class AdminController {
private readonly adminService: AdminService, private readonly adminService: AdminService,
private readonly apiService: ApiService, private readonly apiService: ApiService,
private readonly dataGatheringService: DataGatheringService, private readonly dataGatheringService: DataGatheringService,
private readonly manualService: ManualService,
private readonly marketDataService: MarketDataService, private readonly marketDataService: MarketDataService,
@Inject(REQUEST) private readonly request: RequestWithUser @Inject(REQUEST) private readonly request: RequestWithUser
) {} ) {}
@ -246,8 +248,8 @@ export class AdminController {
} }
if (dataSource === 'MANUAL' && isDryRun && isToday(date)) { if (dataSource === 'MANUAL' && isDryRun && isToday(date)) {
// TODO //const marketData = await this.manualService.scrape()
Logger.log(`Check ${symbol} via scraperConfiguration`); Logger.log(`Check ${symbol} via scraperConfiguration`, dataSource);
} }
return this.dataGatheringService.gatherSymbolForDate({ return this.dataGatheringService.gatherSymbolForDate({
@ -258,6 +260,17 @@ export class AdminController {
}); });
} }
@Post('test-scraper')
@UseGuards(AuthGuard('jwt'))
public async testScraper(
@Body() data: { config: string }
): Promise<{ price: number }> {
const {url, selector} = JSON.parse(data.config);
let price = await this.manualService.scrape(url ,selector);
return {price: price};
}
@Get('market-data') @Get('market-data')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
public async getMarketData( public async getMarketData(

5
apps/api/src/app/admin/admin.module.ts

@ -26,10 +26,11 @@ import { QueueModule } from './queue/queue.module';
PropertyModule, PropertyModule,
QueueModule, QueueModule,
SubscriptionModule, SubscriptionModule,
SymbolProfileModule SymbolProfileModule,
], ],
controllers: [AdminController], controllers: [AdminController],
providers: [AdminService], providers: [AdminService],
exports: [AdminService] exports: [AdminService],
}) })
export class AdminModule {} export class AdminModule {}

3
apps/api/src/services/data-provider/data-provider.module.ts

@ -29,6 +29,7 @@ import { DataProviderService } from './data-provider.service';
PropertyModule, PropertyModule,
RedisCacheModule, RedisCacheModule,
SymbolProfileModule SymbolProfileModule
], ],
providers: [ providers: [
AlphaVantageService, AlphaVantageService,
@ -74,6 +75,6 @@ import { DataProviderService } from './data-provider.service';
}, },
YahooFinanceDataEnhancerService YahooFinanceDataEnhancerService
], ],
exports: [DataProviderService, YahooFinanceService] exports: [DataProviderService, YahooFinanceService, ManualService]
}) })
export class DataProviderModule {} export class DataProviderModule {}

51
apps/api/src/services/data-provider/manual/manual.service.ts

@ -1,17 +1,10 @@
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface';
import { import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces';
IDataProviderHistoricalResponse,
IDataProviderResponse
} from '@ghostfolio/api/services/interfaces/interfaces';
import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service';
import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service';
import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config'; import { DEFAULT_REQUEST_TIMEOUT } from '@ghostfolio/common/config';
import { import { DATE_FORMAT, extractNumberFromString, getYesterday } from '@ghostfolio/common/helper';
DATE_FORMAT,
extractNumberFromString,
getYesterday
} from '@ghostfolio/common/helper';
import { Granularity } from '@ghostfolio/common/types'; import { Granularity } from '@ghostfolio/common/types';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, SymbolProfile } from '@prisma/client'; import { DataSource, SymbolProfile } from '@prisma/client';
@ -72,9 +65,10 @@ export class ManualService implements DataProviderInterface {
defaultMarketPrice, defaultMarketPrice,
headers = {}, headers = {},
selector, selector,
url url
} = symbolProfile.scraperConfiguration ?? {}; } = symbolProfile.scraperConfiguration ?? {};
Logger.log(symbolProfile);
if (defaultMarketPrice) { if (defaultMarketPrice) {
const historical: { const historical: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
@ -95,23 +89,10 @@ export class ManualService implements DataProviderInterface {
} else if (selector === undefined || url === undefined) { } else if (selector === undefined || url === undefined) {
return {}; return {};
} }
Logger.log("Here");
const abortController = new AbortController();
setTimeout(() => {
abortController.abort();
}, DEFAULT_REQUEST_TIMEOUT);
const { body } = await got(url, {
headers,
// @ts-ignore
signal: abortController.signal
});
const $ = cheerio.load(body);
const value = extractNumberFromString($(selector).text());
const value = await this.scrape(url, selector, headers);
return { return {
[symbol]: { [symbol]: {
[format(getYesterday(), DATE_FORMAT)]: { [format(getYesterday(), DATE_FORMAT)]: {
@ -129,6 +110,26 @@ export class ManualService implements DataProviderInterface {
} }
} }
public async scrape(url: string, selector: string, headers = {}): Promise<number>{
const abortController = new AbortController();
const { body } = await got(url, {
headers,
// @ts-ignore
signal: abortController.signal
});
setTimeout(() => {
abortController.abort();
}, DEFAULT_REQUEST_TIMEOUT);
const $ = cheerio.load(body);
return extractNumberFromString($(selector).first().text());
}
public getName(): DataSource { public getName(): DataSource {
return DataSource.MANUAL; return DataSource.MANUAL;
} }

18
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts

@ -240,21 +240,17 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
}); });
} }
public onTestScraper(symbol: string) { public onTestScraper() {
const today = new Date();
this.adminService this.adminService
.gatherSymbol({ .testScrapeConfig(this.assetProfileForm.controls['scraperConfiguration'].value)
dataSource: 'MANUAL',
date: today,
isDryRun: true,
symbol
})
.pipe(takeUntil(this.unsubscribeSubject)) .pipe(takeUntil(this.unsubscribeSubject))
.subscribe((response) => { .subscribe((response) => {
if (response !== null) { if (response && response.price) {
alert($localize`Please try again.`); console.log(response);
alert($localize`Current Market Price is:` + ' ' + response.price);
} else { } else {
alert($localize`Current Market Price is:` + ' ' + response); alert($localize`Please try again.`);
} }
}); });
} }

3
apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html

@ -241,6 +241,7 @@
formControlName="scraperConfiguration" formControlName="scraperConfiguration"
matInput matInput
type="text" type="text"
val
(keyup.enter)="$event.stopPropagation()" (keyup.enter)="$event.stopPropagation()"
></textarea> ></textarea>
<button <button
@ -248,7 +249,7 @@
mat-flat-button mat-flat-button
type="button" type="button"
[disabled]="assetProfileForm.controls['scraperConfiguration'].value === '{}'" [disabled]="assetProfileForm.controls['scraperConfiguration'].value === '{}'"
(click)="onTestScraper(assetProfile?.symbol)" (click)="onTestScraper()"
> >
<ng-container i18n>Test</ng-container> <ng-container i18n>Test</ng-container>
</button> </button>

4
apps/client/src/app/services/admin.service.ts

@ -187,6 +187,10 @@ export class AdminService {
return this.http.post<MarketData | void>(url, {}); return this.http.post<MarketData | void>(url, {});
} }
public testScrapeConfig(config: string){
return this.http.post<any>(`/api/v1/admin/test-scraper`, {config});
}
public fetchSymbolForDate({ public fetchSymbolForDate({
dataSource, dataSource,
date, date,

Loading…
Cancel
Save