Feature/ghostfolio in numbers (#175)

* Add Ghostfolio in numbers section

* Update changelog
pull/176/head
Thomas 3 years ago committed by GitHub
parent a2440fc067
commit 66c955ad6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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;
}

@ -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…
Cancel
Save