316 lines
14 KiB
TypeScript
316 lines
14 KiB
TypeScript
import {Component, Input, Output, EventEmitter, ChangeDetectorRef} from '@angular/core';
|
|
import {Metrics} from '../../../utils/entities/metrics';
|
|
import {MetricsService} from '../../../services/metrics.service';
|
|
import {ErrorCodes} from '../../../utils/properties/errorCodes';
|
|
|
|
import {Subscription, zip} from 'rxjs';
|
|
import {EnvProperties} from '../../../utils/properties/env-properties';
|
|
import {ClickEvent} from "../../../utils/click/click-outside-or-esc.directive";
|
|
import {NumberUtils} from "../../../utils/number-utils.class";
|
|
import {OpenaireEntities} from "../../../utils/properties/searchFields";
|
|
|
|
@Component({
|
|
selector: 'metrics',
|
|
template: `
|
|
<div *ngIf="metrics && (pageViews >0 || metrics.totalViews > 0|| metrics.totalDownloads >0)" class="metrics">
|
|
<a class="m-badge uk-link-reset uk-display-block" (click)="clickedMetrics()">
|
|
<img src="assets/common-assets/logo-small-usage-counts.png" loading="lazy" alt="usage counts">
|
|
<div class="uk-margin-xsmall-top uk-text-primary uk-text-small uk-text-bold uk-text-center">
|
|
<ng-container *ngIf="isNumberLarge(total, true); else notFormattedTotal">{{formatNumber(total)}}</ng-container>
|
|
<ng-template #notFormattedTotal>{{total | number}}</ng-template>
|
|
</div>
|
|
</a>
|
|
<div class="uk-dropdown uk-background-default" uk-dropdown="pos: right-top; mode: click; offset: 30; animation: uk-animation-fade">
|
|
<div class="uk-padding-small uk-width-large@s uk-width-medium">
|
|
<div *ngIf="entityType == 'projects'" class="uk-alert uk-alert-primary uk-margin-small-bottom">
|
|
<span class="uk-text-center uk-text-small">{{openaireEntities.PROJECT}} metrics
|
|
are derived from aggregating individual {{openaireEntities.RESULTS}} metrics.
|
|
</span>
|
|
</div>
|
|
<div class="uk-child-width-1-3@m uk-grid-small uk-grid-divider uk-margin-top" uk-grid>
|
|
<div class="uk-text-center">
|
|
<div class="number uk-text-bold uk-text-background">
|
|
<ng-container *ngIf="metrics.totalDownloads == 0">-</ng-container>
|
|
<span *ngIf="metrics.totalDownloads > 0" [attr.uk-tooltip]="metrics.totalDownloads >= 1000 ? 'cls: uk-active' : 'cls: uk-invisible'"
|
|
title="{{metrics.totalDownloads | number}}">{{formatNumber(metrics.totalDownloads)}}</span>
|
|
<!-- <ng-container *ngIf="isNumberLarge(metrics.totalDownloads); else notFormattedDownloads">{{formatNumber(metrics.totalDownloads)}}</ng-container>-->
|
|
<!-- <ng-template #notFormattedDownloads>{{metrics.totalDownloads | number}}</ng-template>-->
|
|
</div>
|
|
<div>Downloads</div>
|
|
</div>
|
|
<div class="uk-text-center">
|
|
<div class="number uk-text-bold uk-text-background">
|
|
<ng-container *ngIf="pageViews == 0">-</ng-container>
|
|
<span *ngIf="pageViews > 0" [attr.uk-tooltip]="pageViews >= 1000 ? 'cls: uk-active' : 'cls: uk-invisible'"
|
|
title="{{pageViews | number}}">{{formatNumber(pageViews)}}</span>
|
|
<!-- <ng-container *ngIf="isNumberLarge(pageViews); else notFormattedPageviews">{{formatNumber(pageViews)}}</ng-container>-->
|
|
<!-- <ng-template #notFormattedPageviews>{{pageViews | number}}</ng-template>-->
|
|
</div>
|
|
<div>OpenAIRE views</div>
|
|
</div>
|
|
<div class="uk-text-center">
|
|
<div class="number uk-text-bold uk-text-background">
|
|
<ng-container *ngIf="metrics.totalViews == 0">-</ng-container>
|
|
<span *ngIf="metrics.totalViews > 0" [attr.uk-tooltip]="metrics.totalViews >= 1000 ? 'cls: uk-active' : 'cls: uk-invisible'"
|
|
title="{{metrics.totalViews | number}}">{{formatNumber(metrics.totalViews)}}</span>
|
|
<!-- <ng-container *ngIf="isNumberLarge(metrics.totalViews); else notFormattedTotalviews">{{formatNumber(metrics.totalViews)}}</ng-container>-->
|
|
<!-- <ng-template #notFormattedTotalviews>{{metrics.totalViews | number}}</ng-template> -->
|
|
</div>
|
|
<div>Total views</div>
|
|
</div>
|
|
</div>
|
|
<div class="uk-padding">
|
|
<div *ngIf="metricsClicked && (metrics.infos?.size > 0 || (metrics.totalViews > 0 && viewsFrameUrl) || (metrics.totalDownloads > 0 && downloadsFrameUrl))"
|
|
class="uk-position-relative uk-visible-toggle uk-slider uk-margin-small-top" tabIndex="1" uk-slider>
|
|
<div class="uk-slider-container">
|
|
<ul class="uk-slider-items uk-child-width-1-1">
|
|
<li *ngIf="metrics && metrics.infos.size > 0" class="uk-overflow-auto" style="height: 200px">
|
|
<table
|
|
class="uk-table uk-table-small uk-table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th class="uk-text-center uk-text-bold">From</th>
|
|
<th class="uk-text-center uk-text-bold">Views</th>
|
|
<th class="uk-text-center uk-text-bold">Downloads</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr *ngFor="let key of getKeys(metrics.infos)">
|
|
<td class="uk-width-1-3 uk-text-center uk-text-truncate" uk-tooltip [title]="metrics.infos.get(key).name">
|
|
<a href="{{metrics.infos.get(key).url}}">
|
|
{{metrics.infos.get(key).name}}
|
|
</a>
|
|
</td>
|
|
<td class="uk-text-center">
|
|
<ng-container *ngIf="isNumberLarge(metrics.infos.get(key).numOfViews, true); else notFormattedNumViews">{{formatNumber(metrics.infos.get(key).numOfViews)}}</ng-container>
|
|
<ng-template #notFormattedNumViews>{{metrics.infos.get(key).numOfViews | number}}</ng-template>
|
|
<!-- <span *ngIf="metrics.infos.get(key).numOfViews > 0 && metrics.infos.get(key).openaireViews > 0">
|
|
( {{metrics.infos.get(key).openaireViews | number}} from OpenAIRE )
|
|
</span>-->
|
|
</td>
|
|
<td class="uk-text-center">
|
|
<ng-container *ngIf="isNumberLarge(metrics.infos.get(key).numOfDownloads, true); else notFormattedNumDownloads">{{formatNumber(metrics.infos.get(key).numOfDownloads)}}</ng-container>
|
|
<ng-template #notFormattedNumDownloads>{{metrics.infos.get(key).numOfDownloads | number}}</ng-template>
|
|
<!--<span *ngIf="metrics.infos.get(key).numOfDownloads > 0 && metrics.infos.get(key).openaireDownloads > 0">
|
|
( {{metrics.infos.get(key).openaireDownloads | number}} from OpenAIRE )
|
|
</span>-->
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</li>
|
|
<!-- remove check for datasources when with the new charts -->
|
|
<li *ngIf="metricsClicked && metrics.totalViews > 0 && viewsFrameUrl">
|
|
<i-frame [height]="200" [url]=viewsFrameUrl [addClass]="false"
|
|
customContainerClass="uk-background-default" customIframeClass="uk-blend-multiply"></i-frame>
|
|
</li>
|
|
<li *ngIf="metricsClicked && metrics.totalDownloads > 0 && downloadsFrameUrl">
|
|
<i-frame [height]="200" [url]=downloadsFrameUrl [addClass]="false"
|
|
customContainerClass="uk-background-default" customIframeClass="uk-blend-multiply"></i-frame>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<a class="uk-position-center-left-out" uk-slider-item="previous"><span uk-icon="icon: chevron-left; ratio: 2"></span></a>
|
|
<a class="uk-position-center-right-out" uk-slider-item="next"><span uk-icon="icon: chevron-right; ratio: 2"></span></a>
|
|
</div>
|
|
</div>
|
|
<div class="uk-flex uk-flex-center uk-flex-middle">
|
|
<span class="uk-margin-small-right uk-text-small uk-text-meta">Powered by</span>
|
|
<a href="https://usagecounts.openaire.eu" target="_blank">
|
|
<img width="120" src="assets/common-assets/logo-large-usage-counts.png" loading="lazy" alt="usage counts">
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
styleUrls: ['metrics.component.less']
|
|
})
|
|
|
|
export class MetricsComponent {
|
|
@Output() metricsResults = new EventEmitter();
|
|
@Input() id: string;
|
|
@Input() entityType: string;
|
|
@Input() entity: string;
|
|
//@Input() name: string = "";
|
|
@Input() pageViews: number = 0;
|
|
@Input() properties: EnvProperties;
|
|
@Input() shortView: boolean = false;
|
|
@Input() open = false;
|
|
@Input() viewsFrameUrl: string;
|
|
@Input() downloadsFrameUrl: string;
|
|
|
|
public metrics: Metrics;
|
|
public errorCodes: ErrorCodes;
|
|
private sub: Subscription;
|
|
private timeouts: any[] = [];
|
|
|
|
public metricsClicked: boolean = false;
|
|
|
|
public status: number;
|
|
public state: number = -1;
|
|
public openaireEntities = OpenaireEntities;
|
|
|
|
constructor(private metricsService: MetricsService, private cdr: ChangeDetectorRef) {
|
|
}
|
|
|
|
ngOnInit() {
|
|
this.errorCodes = new ErrorCodes();
|
|
if (typeof document !== 'undefined') {
|
|
this.status = this.errorCodes.LOADING;
|
|
this.getMetrics();
|
|
}
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
if(this.sub) {
|
|
this.sub.unsubscribe();
|
|
}
|
|
}
|
|
|
|
public get total(): number {
|
|
return +this.pageViews + +this.metrics.totalViews + +this.metrics.totalDownloads;
|
|
}
|
|
|
|
private getMetrics() {
|
|
//queries from old API - replaced with queries to the stats tool
|
|
/* this.sub = this.metricsService.getMetrics(this.id, this.entityType, this.properties).subscribe(
|
|
data => {
|
|
this.metrics = data;
|
|
this.cdr.detectChanges();
|
|
this.status = this.errorCodes.DONE;
|
|
this.metricsResults.emit({
|
|
totalViews: this.metrics.totalViews,
|
|
totalDownloads: this.metrics.totalDownloads,
|
|
pageViews: this.metrics.pageViews
|
|
});
|
|
},
|
|
err => {
|
|
if (err.status == '404') {
|
|
this.status = this.errorCodes.NOT_FOUND;
|
|
} else if (err.status == '500') {
|
|
this.status = this.errorCodes.ERROR;
|
|
} else {
|
|
this.status = this.errorCodes.NOT_AVAILABLE;
|
|
}
|
|
this.metricsResults.emit({
|
|
totalViews: 0,
|
|
totalDownloads: 0
|
|
});
|
|
}
|
|
);*/
|
|
let obs;
|
|
if (this.entityType == "results") {
|
|
obs = zip(this.metricsService.getMetricsNumber(this.id, "usagestats.results.views", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.results.downloads", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.results.views.openaire", this.properties),
|
|
this.metricsService.getMetricsNumbersByRepository(this.id, "usagestats.results.viewsdownloads.repository", this.properties)
|
|
|
|
);
|
|
|
|
} else if (this.entityType == "projects") {
|
|
obs = zip(this.metricsService.getMetricsNumber(this.id, "usagestats.projects.views", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.projects.downloads", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.projects.views.openaire", this.properties));
|
|
|
|
} else if (this.entityType == "datasources") {
|
|
obs = zip(this.metricsService.getMetricsNumber(this.id, "usagestats.views.repository.local", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.downloads.repository.local", this.properties),
|
|
this.metricsService.getMetricsNumber(this.id, "usagestats.views.openaire", this.properties),
|
|
// this.metricsService.getMetricsNumber(this.id, "usagestats.downloads.repository.openaire", this.properties)
|
|
);
|
|
|
|
}
|
|
|
|
this.sub = obs.subscribe(
|
|
data => {
|
|
this.metrics = new Metrics();
|
|
this.metrics.infos = new Map<string, { "name": string, "url": string, "numOfDownloads": string, "numOfViews": string, "openaireDownloads": string, "openaireViews": string }>();
|
|
this.metrics.totalViews = data[0] ? data[0] : 0;
|
|
this.metrics.totalDownloads = data[1] ? data[1] : 0;
|
|
this.metrics.pageViews = data[2] ? data[2] : 0;
|
|
this.metrics.infos = data[3] ? data[3] : 0;
|
|
this.cdr.detectChanges();
|
|
this.status = this.errorCodes.DONE;
|
|
this.metricsResults.emit({
|
|
totalViews: this.metrics.totalViews,
|
|
totalDownloads: this.metrics.totalDownloads,
|
|
pageViews: this.metrics.pageViews
|
|
});
|
|
},
|
|
err => {
|
|
if (err.status == '404') {
|
|
this.status = this.errorCodes.NOT_FOUND;
|
|
} else if (err.status == '500') {
|
|
this.status = this.errorCodes.ERROR;
|
|
} else {
|
|
this.status = this.errorCodes.NOT_AVAILABLE;
|
|
}
|
|
this.metricsResults.emit({
|
|
totalViews: 0,
|
|
totalDownloads: 0,
|
|
pageViews: 0
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
public close(event: ClickEvent) {
|
|
if(event.clicked && this.state !== -1) {
|
|
this.timeouts.forEach(timeout => {
|
|
clearTimeout(timeout);
|
|
});
|
|
this.state = -1;
|
|
this.timeouts = [];
|
|
}
|
|
}
|
|
|
|
public clickedMetrics() {
|
|
setTimeout( () => {
|
|
this.metricsClicked = true;
|
|
});
|
|
}
|
|
|
|
public toggle(event) {
|
|
this.metricsClicked = true;
|
|
|
|
event.stopPropagation();
|
|
if(this.state !== -1) {
|
|
this.timeouts.forEach(timeout => {
|
|
clearTimeout(timeout);
|
|
});
|
|
this.state = -1;
|
|
this.timeouts = [];
|
|
} else {
|
|
this.state++;
|
|
this.timeouts.push(setTimeout(() => {
|
|
this.state++;
|
|
this.timeouts.push(setTimeout(() => {
|
|
this.state++;
|
|
this.timeouts.push(setTimeout(() => {
|
|
this.state++;
|
|
this.timeouts.push(setTimeout(() => {
|
|
this.state++;
|
|
}, 400));
|
|
}, 800));
|
|
}, 300));
|
|
}, 100));
|
|
}
|
|
this.cdr.detectChanges();
|
|
}
|
|
|
|
public getKeys(map) {
|
|
return Array.from(map.keys());
|
|
}
|
|
|
|
public isNumberLarge(num: number | string, bigNum: boolean = false) {
|
|
let limit: number = bigNum ? 100000000 : 100000;
|
|
return (+num) >= limit;
|
|
}
|
|
|
|
public formatNumber(num: number | string) {
|
|
let formatted = NumberUtils.roundNumber(+num);
|
|
return formatted.number + formatted.size;
|
|
}
|
|
}
|