Feature/ghostfolio in numbers (#175)
* Add Ghostfolio in numbers section * Update changelog
This commit is contained in:
parent
a2440fc067
commit
66c955ad6c
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Added a _Ghostfolio in Numbers_ section to the about page
|
||||
|
||||
## 1.18.0 - 16.06.2021
|
||||
|
||||
### Changed
|
||||
|
@ -5,6 +5,8 @@ import { permissions } from '@ghostfolio/common/permissions';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Currency } from '@prisma/client';
|
||||
import * as bent from 'bent';
|
||||
import { subDays } from 'date-fns';
|
||||
|
||||
@Injectable()
|
||||
export class InfoService {
|
||||
@ -28,6 +30,10 @@ export class InfoService {
|
||||
globalPermissions.push(permissions.enableSocialLogin);
|
||||
}
|
||||
|
||||
if (this.configurationService.get('ENABLE_FEATURE_STATISTICS')) {
|
||||
globalPermissions.push(permissions.enableStatistics);
|
||||
}
|
||||
|
||||
if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
|
||||
globalPermissions.push(permissions.enableSubscription);
|
||||
}
|
||||
@ -37,7 +43,8 @@ export class InfoService {
|
||||
platforms,
|
||||
currencies: Object.values(Currency),
|
||||
demoAuthToken: this.getDemoAuthToken(),
|
||||
lastDataGathering: await this.getLastDataGathering()
|
||||
lastDataGathering: await this.getLastDataGathering(),
|
||||
statistics: await this.getStatistics()
|
||||
};
|
||||
}
|
||||
|
||||
@ -54,4 +61,67 @@ export class InfoService {
|
||||
|
||||
return lastDataGathering?.value ? new Date(lastDataGathering.value) : null;
|
||||
}
|
||||
|
||||
private async getStatistics() {
|
||||
if (!this.configurationService.get('ENABLE_FEATURE_STATISTICS')) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const activeUsers1d = await this.countActiveUsers(1);
|
||||
const activeUsers30d = await this.countActiveUsers(30);
|
||||
const gitHubStargazers = await this.countGitHubStargazers();
|
||||
|
||||
return {
|
||||
activeUsers1d,
|
||||
activeUsers30d,
|
||||
gitHubStargazers
|
||||
};
|
||||
}
|
||||
|
||||
private async countActiveUsers(aDays: number) {
|
||||
return await this.prisma.user.count({
|
||||
orderBy: {
|
||||
Analytics: {
|
||||
updatedAt: 'desc'
|
||||
}
|
||||
},
|
||||
where: {
|
||||
AND: [
|
||||
{
|
||||
NOT: {
|
||||
Analytics: null
|
||||
}
|
||||
},
|
||||
{
|
||||
Analytics: {
|
||||
updatedAt: {
|
||||
gt: subDays(new Date(), aDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async countGitHubStargazers(): Promise<number> {
|
||||
try {
|
||||
const get = bent(
|
||||
`https://api.github.com/repos/ghostfolio/ghostfolio`,
|
||||
'GET',
|
||||
'json',
|
||||
200,
|
||||
{
|
||||
'User-Agent': 'request'
|
||||
}
|
||||
);
|
||||
|
||||
const { stargazers_count } = await get();
|
||||
return stargazers_count;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export class ConfigurationService {
|
||||
ENABLE_FEATURE_CUSTOM_SYMBOLS: bool({ default: false }),
|
||||
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: bool({ default: false }),
|
||||
ENABLE_FEATURE_SOCIAL_LOGIN: bool({ default: false }),
|
||||
ENABLE_FEATURE_STATISTICS: bool({ default: false }),
|
||||
ENABLE_FEATURE_SUBSCRIPTION: bool({ default: false }),
|
||||
GOOGLE_CLIENT_ID: str({ default: 'dummyClientId' }),
|
||||
GOOGLE_SECRET: str({ default: 'dummySecret' }),
|
||||
|
@ -8,6 +8,7 @@ export interface Environment extends CleanedEnvAccessors {
|
||||
ENABLE_FEATURE_CUSTOM_SYMBOLS: boolean;
|
||||
ENABLE_FEATURE_FEAR_AND_GREED_INDEX: boolean;
|
||||
ENABLE_FEATURE_SOCIAL_LOGIN: boolean;
|
||||
ENABLE_FEATURE_STATISTICS: boolean;
|
||||
ENABLE_FEATURE_SUBSCRIPTION: boolean;
|
||||
GOOGLE_CLIENT_ID: string;
|
||||
GOOGLE_SECRET: string;
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||
import { UserService } from '@ghostfolio/client/services/user/user.service';
|
||||
import { baseCurrency } from '@ghostfolio/common/config';
|
||||
import { User } from '@ghostfolio/common/interfaces';
|
||||
import { Statistics } from '@ghostfolio/common/interfaces/statistics.interface';
|
||||
import { hasPermission, permissions } from '@ghostfolio/common/permissions';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@ -14,8 +17,10 @@ import { environment } from '../../../environments/environment';
|
||||
})
|
||||
export class AboutPageComponent implements OnInit {
|
||||
public baseCurrency = baseCurrency;
|
||||
public hasPermissionForStatistics: boolean;
|
||||
public isLoggedIn: boolean;
|
||||
public lastPublish = environment.lastPublish;
|
||||
public statistics: Statistics;
|
||||
public user: User;
|
||||
public version = environment.version;
|
||||
|
||||
@ -26,6 +31,7 @@ export class AboutPageComponent implements OnInit {
|
||||
*/
|
||||
public constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private dataService: DataService,
|
||||
private userService: UserService
|
||||
) {}
|
||||
|
||||
@ -33,6 +39,19 @@ export class AboutPageComponent implements OnInit {
|
||||
* Initializes the controller
|
||||
*/
|
||||
public ngOnInit() {
|
||||
this.dataService
|
||||
.fetchInfo()
|
||||
.subscribe(({ globalPermissions, statistics }) => {
|
||||
this.hasPermissionForStatistics = hasPermission(
|
||||
globalPermissions,
|
||||
permissions.enableStatistics
|
||||
);
|
||||
|
||||
this.statistics = statistics;
|
||||
|
||||
this.changeDetectorRef.markForCheck();
|
||||
});
|
||||
|
||||
this.userService.stateChanged
|
||||
.pipe(takeUntil(this.unsubscribeSubject))
|
||||
.subscribe((state) => {
|
||||
|
@ -60,6 +60,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasPermissionForStatistics" class="mb-5 row">
|
||||
<div class="col">
|
||||
<h3 class="mb-3 text-center" i18n>Ghostfolio in Numbers</h3>
|
||||
<mat-card class="mb-3">
|
||||
<mat-card-content>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<h3 class="mb-0" [hidden]="!statistics?.activeUsers1d">
|
||||
{{ statistics?.activeUsers1d ?? '-' }}
|
||||
</h3>
|
||||
<div class="h6 mb-0">
|
||||
Active Users <small class="text-muted">(Last 24 hours)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<h3 class="mb-0" [hidden]="!statistics?.activeUsers30d">
|
||||
{{ statistics?.activeUsers30d ?? '-' }}
|
||||
</h3>
|
||||
<div class="h6 m-b0">
|
||||
Active Users <small class="text-muted">(Last 30 days)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<h3 class="mb-0" [hidden]="!statistics?.gitHubStargazers">
|
||||
{{ statistics?.gitHubStargazers ?? '-' }}
|
||||
</h3>
|
||||
<div class="h6 mb-0">Stars on GitHub</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 row">
|
||||
<div class="col">
|
||||
<h3 class="mb-3 text-center" i18n>Changelog</h3>
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Currency } from '@prisma/client';
|
||||
|
||||
import { Statistics } from './statistics.interface';
|
||||
|
||||
export interface InfoItem {
|
||||
currencies: Currency[];
|
||||
demoAuthToken: string;
|
||||
@ -10,4 +12,5 @@ export interface InfoItem {
|
||||
type: string;
|
||||
};
|
||||
platforms: { id: string; name: string }[];
|
||||
statistics: Statistics;
|
||||
}
|
||||
|
5
libs/common/src/lib/interfaces/statistics.interface.ts
Normal file
5
libs/common/src/lib/interfaces/statistics.interface.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Statistics {
|
||||
activeUsers1d: number;
|
||||
activeUsers30d: number;
|
||||
gitHubStargazers: number;
|
||||
}
|
@ -15,6 +15,7 @@ export const permissions = {
|
||||
deleteOrder: 'deleteOrder',
|
||||
deleteUser: 'deleteUser',
|
||||
enableSocialLogin: 'enableSocialLogin',
|
||||
enableStatistics: 'enableStatistics',
|
||||
enableSubscription: 'enableSubscription',
|
||||
readForeignPortfolio: 'readForeignPortfolio',
|
||||
updateAccount: 'updateAccount',
|
||||
|
Loading…
x
Reference in New Issue
Block a user