[angular-16-irish-monitor]: Add monitor-indicator-stakeholder-base-component

This commit is contained in:
Konstantinos Triantafyllou 2023-11-14 10:17:18 +02:00
parent e80f73f9a9
commit edda550b63
3 changed files with 427 additions and 8 deletions

View File

@ -30,10 +30,10 @@ import {StakeholderBaseComponent} from "../../monitor-admin/utils/stakeholder-ba
This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations! This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
</p> </p>
<p> <p>
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route">funders</a>, Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="_route">funders</a>,
<a [routerLink]="['../organization']" [relativeTo]="route">research institutions</a> and <a [routerLink]="['../organization']" [relativeTo]="_route">research institutions</a> and
<a [routerLink]="['../ri']" [relativeTo]="route">research initiatives</a>) <a [routerLink]="['../ri']" [relativeTo]="_route">research initiatives</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics. for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="_route">methodology and terminology</a> page on how we produce the metrics.
</p> </p>
</div> </div>
</div> </div>
@ -54,10 +54,10 @@ import {StakeholderBaseComponent} from "../../monitor-admin/utils/stakeholder-ba
This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations! This is the current set of indicator themes we cover. Well keep enriching it as new requests and data are coming into the <a href="https://graph.openaire.eu" class="text-graph" target="_blank">OpenAIRE Graph</a>. We are at your disposal, should you have any recommendations!
</p> </p>
<p> <p>
Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="route" class="uk-text-lowercase">{{entities.funders}}</a>, Check out the indicator pages (for <a [routerLink]="['../funder']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.funders}}</a>,
<a [routerLink]="['../organization']" [relativeTo]="route" class="uk-text-lowercase">{{entities.organizations}}</a> and <a [routerLink]="['../organization']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.organizations}}</a> and
<a [routerLink]="['../ri']" [relativeTo]="route" class="uk-text-lowercase">{{entities.ris}}</a>) <a [routerLink]="['../ri']" [relativeTo]="_route" class="uk-text-lowercase">{{entities.ris}}</a>)
for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="route">methodology and terminology</a> page on how we produce the metrics. for the specific indicators for each type of dashboard, and the <a [routerLink]="['../../methodology']" [relativeTo]="_route">methodology and terminology</a> page on how we produce the metrics.
</p> </p>
</div> </div>
</div> </div>

View File

@ -0,0 +1,415 @@
import {ChangeDetectorRef, Directive, HostListener, OnInit, ViewRef} from "@angular/core";
import {IndicatorStakeholderBaseComponent} from "../monitor-admin/utils/stakeholder-base.component";
import {DomSanitizer} from "@angular/platform-browser";
import {
Category,
Indicator, IndicatorPath,
IndicatorSize,
Section,
Stakeholder,
SubCategory,
Topic,
Visibility
} from "./entities/stakeholder";
import {LayoutService} from "../dashboard/sharedComponents/sidebar/layout.service";
import {ClickEvent} from "../utils/click/click-outside-or-esc.directive";
import {Session, User} from "../login/utils/helper.class";
import {Filter, Value} from "../searchPages/searchUtils/searchHelperClasses.class";
import {RangeFilter} from "../utils/rangeFilter/rangeFilterHelperClasses.class";
import {RangeFilterComponent} from "../utils/rangeFilter/rangeFilter.component";
import {Dates, StringUtils} from "../utils/string-utils.class";
import {Params} from "@angular/router";
import {StatisticsService} from "../monitor-admin/utils/services/statistics.service";
@Directive()
export abstract class MonitorIndicatorStakeholderBaseComponent extends IndicatorStakeholderBaseComponent implements OnInit {
/** Status */
public loading: boolean = true;
public isMobile: boolean = false;
public isFullscreen: boolean = false;
/** Variables */
public user: User;
public view: Visibility;
public stakeholder: Stakeholder;
public activeTopic: Topic = null;
public activeCategory: Category = null;
public activeSubCategory: SubCategory = null;
public filters: Filter[] = [];
public queryParams: any = {};
public periodFilter: RangeFilter = {
title: "Time range",
filterId: "year",
originalFilterIdFrom: null,
originalFilterIdTo: null,
selectedFromValue: null,
selectedToValue: null,
selectedFromAndToValues: ""
};
rangeFilter: RangeFilterComponent;
public numberResults: Map<string, number> = new Map<string, number>();
public chartsActiveType: Map<string, IndicatorPath> = new Map<string, IndicatorPath>();
public currentYear = new Date().getFullYear();
/** Services */
protected sanitizer: DomSanitizer;
protected cdr: ChangeDetectorRef;
protected layoutService: LayoutService;
protected statisticsService: StatisticsService;
@HostListener('fullscreenchange', ['$event'])
@HostListener('webkitfullscreenchange', ['$event'])
@HostListener('mozfullscreenchange', ['$event'])
@HostListener('MSFullscreenChange', ['$event'])
screenChange() {
this.isFullscreen = !this.isFullscreen;
}
ngOnInit() {
this.layoutService.isMobile.subscribe(isMobile => {
this.isMobile = isMobile;
this.cdr.detectChanges();
});
}
protected setView(params: Params) {
this.loading = false;
if (params['topic']) {
this.activeTopic = this.stakeholder.topics.find(topic => topic.alias === decodeURIComponent(params['topic']) && this.hasPermission(topic.visibility));
if (this.activeTopic) {
if (params['category']) {
this.activeCategory = this.activeTopic.categories.find(category =>
(category.alias === params['category']) && this.hasPermission(category.visibility));
if (!this.activeCategory) {
this.navigateToError();
return;
}
} else {
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
if (this.activeCategory) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
this.hasPermission(subCategory.visibility));
if (this.activeSubCategory) {
this.setIndicators();
}
}
return;
}
if (this.activeCategory) {
if (params['subCategory']) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
(subCategory.alias === params['subCategory'] && this.hasPermission(subCategory.visibility)));
if (!this.activeSubCategory) {
this.navigateToError();
return;
}
} else {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory =>
this.hasPermission(subCategory.visibility));
}
if (this.activeSubCategory) {
this.setIndicators();
} else {
this.navigateToError();
}
return;
} else {
this.activeSubCategory = null;
}
} else {
this.navigateToError();
return;
}
} else {
this.activeTopic = this.stakeholder.topics.find(topic => this.hasPermission(topic.visibility));
if (this.activeTopic) {
this.activeCategory = this.activeTopic.categories.find(category => this.hasPermission(category.visibility));
if (this.activeCategory) {
this.activeSubCategory = this.activeCategory.subCategories.find(subCategory => this.hasPermission(subCategory.visibility));
if (this.activeSubCategory) {
this.setIndicators();
}
}
}
}
}
protected handleQueryParams(queryParams, params) {
this.queryParams = Object.assign({}, queryParams);
this.initializeFilters();
this.setView(params);
if(!this.user && (this.filters.filter(filter => this.queryParams[filter.filterId]).length > 0 || this.queryParams['year'])) {
if(queryParams['view']) {
this._router.navigate([], {queryParams: {view: queryParams['view']}});
} else {
this._router.navigate([], {queryParams: {}});
}
}
this.view = queryParams['view'];
}
protected initializeFilters() {
this.periodFilter.selectedFromValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[0] : "";
this.periodFilter.selectedToValue = (this.queryParams['year'] && this.queryParams['year'].indexOf("range") == 0) ? this.queryParams['year'].split("range")[1].split(":")[1] : "";
this.validateYearRange(false);
for (let filter of this.filters) {
if (this.queryParams[filter.filterId]) {
for (let value of filter.values) {
if (value.id == StringUtils.URIDecode(StringUtils.unquote(this.queryParams[filter.filterId]))) {
value.selected = true;
filter.countSelectedValues = 1;
break;
}
}
} else {
this.clearFilter(filter);
}
}
}
protected validateYearRange(navigateTo: boolean = false) {
let validYears = true;
if (this.periodFilter.selectedToValue && (this.periodFilter.selectedToValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedToValue, Dates.currentYear - 20, Dates.currentYear))) {
this.periodFilter.selectedToValue = Dates.currentYear + "";
validYears = false;
}
if (this.periodFilter.selectedFromValue && (this.periodFilter.selectedFromValue.length == 0 || !Dates.isValidYear(this.periodFilter.selectedFromValue, Dates.currentYear - 20, Dates.currentYear))) {
this.periodFilter.selectedFromValue = Dates.currentYear - 20 + "";
validYears = false;
}
if (this.periodFilter.selectedFromValue && this.periodFilter.selectedFromValue.length && this.periodFilter.selectedToValue && this.periodFilter.selectedToValue.length > 0 && parseInt(this.periodFilter.selectedFromValue, 10) > parseInt(this.periodFilter.selectedToValue, 10)) {
this.periodFilter.selectedFromValue = this.periodFilter.selectedToValue;
validYears = false;
}
if (!validYears || navigateTo) {
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
this.queryParams["year"] = 'range' + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : '') + ":" + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "");
} else {
delete this.queryParams["year"];
}
this._router.navigate([], {queryParams: this.queryParams});
this.setIndicators();
}
}
protected getFullUrl(indicatorPath: IndicatorPath) {
return this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
}
protected setIndicators() {
this.periodFilter.selectedFromAndToValues = (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue ? ((this.periodFilter.selectedFromValue && !this.periodFilter.selectedToValue ? "From " : "") + (!this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? "Until " : "") + (this.periodFilter.selectedFromValue ? this.periodFilter.selectedFromValue : "") +
(this.periodFilter.selectedFromValue && this.periodFilter.selectedToValue ? " - " : "") + (this.periodFilter.selectedToValue ? this.periodFilter.selectedToValue : "")) : "");
//clear numbers when filters change
this.numberResults.clear();
let urls: Map<string, [number, number][]> = new Map<string, [number, number][]>();
this.activeSubCategory.numbers.forEach((section, i) => {
section.indicators.forEach((number, j) => {
if (this.hasPermission(number.visibility)) {
let url = this.getFullUrl(number.indicatorPaths[0]);
const pair = JSON.stringify([number.indicatorPaths[0].source, url]);
const indexes = urls.get(pair) ? urls.get(pair) : [];
indexes.push([i, j]);
urls.set(pair, indexes);
}
});
});
urls.forEach((indexes, pair) => {
pair = JSON.parse(pair);
let activeSubcategory = this.activeSubCategory._id;
this.subscriptions.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(pair[0]), pair[1]).subscribe(response => {
if(activeSubcategory === this.activeSubCategory._id) {
indexes.forEach(([i, j]) => {
if( this.activeSubCategory?.numbers[i]?.indicators[j]) {
let result = JSON.parse(JSON.stringify(response));
this.activeSubCategory.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => {
if (result) {
result = result[jsonPath];
}
});
if (typeof result === 'string' || typeof result === 'number') {
result = Number(result);
if (result === Number.NaN) {
result = 0;
}
} else {
result = 0;
}
this.numberResults.set(i + '-' + j, result);
}
});
}
}));
});
this.activeSubCategory.charts.forEach((section, i) => {
section.indicators.forEach((indicator, j) => {
if (indicator.indicatorPaths.length > 0) {
indicator.indicatorPaths[0].safeResourceUrl = this.getUrlByStakeHolder(indicator.indicatorPaths[0]);
this.chartsActiveType.set(i + '-' + j, indicator.indicatorPaths[0]);
}
});
});
if (this.cdr && !(this.cdr as ViewRef).destroyed) {
this.cdr.detectChanges();
}
}
protected navigateToError() {
this._router.navigate([this.properties.errorLink], {queryParams: {'page': this._router.url}});
}
public getUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.sanitizer.bypassSecurityTrustResourceUrl(
this.indicatorUtils.getChartUrl(indicatorPath.source, this.getFullUrl(indicatorPath)));
}
public setActiveChart(i: number, j: number, type: string) {
let activeChart = this.activeSubCategory.charts[i].indicators[j].indicatorPaths.filter(indicatorPath => indicatorPath.type === type)[0];
activeChart.safeResourceUrl = this.getUrlByStakeHolder(activeChart);
this.chartsActiveType.set(i + '-' + j, activeChart);
}
public filter() {
this.validateYearRange(true);
}
public filterChanged($event, navigate: boolean = true) {
let selected = "";
for (let value of $event.value.values) {
if (value.selected) {
selected = value.id;
break;
}
}
if (selected) {
this.queryParams[$event.value.filterId] = StringUtils.quote(StringUtils.URIEncode(selected));
} else {
delete this.queryParams[$event.value.filterId];
}
if (navigate) {
this._router.navigate([], {queryParams: this.queryParams});
this.setIndicators();
}
}
public countSelectedFilters(): number {
let count = 0;
if (this.periodFilter.selectedFromAndToValues.length > 0) {
count += 1;
}
for (let filter of this.filters) {
count += filter.countSelectedValues;
}
return count;
}
public clearAll() {
for (let filter of this.filters) {
this.clearFilter(filter);
}
this.periodFilter.selectedFromValue = "";
this.periodFilter.selectedToValue = "";
this.validateYearRange(true)
}
public clearFilter(filter: Filter) {
filter.countSelectedValues = 0;
filter.radioValue = "";
for (let value of filter.values) {
if (value.selected) {
value.selected = false;
}
}
if (this.queryParams[filter.filterId]) {
delete this.queryParams[filter.filterId];
}
}
clearPeriodFilter() {
if (this.periodFilter.selectedFromValue || this.periodFilter.selectedToValue) {
this.periodFilter.selectedFromValue = "";
this.periodFilter.selectedToValue = "";
if(this.rangeFilter) {
this.rangeFilter.clearFilter();
}
this.filter();
}
}
clearFilterValue(filter: Filter, value: Value) {
value.selected = false;
filter.radioValue = '';
filter.countSelectedValues = filter.countSelectedValues - 1;
this.filterChanged({
value:filter
});
}
public hasPermission(visibility: Visibility): boolean {
if(visibility === 'PUBLIC') {
return true;
} else if(visibility === 'RESTRICTED') {
return (!this.view || this.view === 'RESTRICTED') && this.isMember(this.stakeholder);
} else {
return !this.view && this.isManager(this.stakeholder);
}
}
public isMember(stakeholder: Stakeholder) {
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user)
|| Session.isManager(stakeholder.type, stakeholder.alias, this.user) || Session.isMember(stakeholder.type, stakeholder.alias, this.user));
}
public isManager(stakeholder: Stakeholder) {
return this.user && (Session.isPortalAdministrator(this.user) || Session.isCurator(stakeholder.type, this.user) || Session.isManager(stakeholder.type, stakeholder.alias, this.user));
}
public countSubCategoriesToShow(category: Category): number {
return category.subCategories.filter(subCategory => this.hasPermission(subCategory.visibility)).length;
}
public countSectionsWithIndicatorsToShow(sections: Section[]):number {
return sections.map(section => this.countIndicatorsToShow(section.indicators)).reduce((sum, current) => sum + current, 0);
}
public countIndicatorsToShow(indicators: Indicator[]): number {
return indicators.filter(indicator => this.hasPermission(indicator.visibility)).length;
}
public getNumberClassBySize(size: IndicatorSize) {
if (size === 'small') {
return 'uk-width-medium@m uk-width-1-1';
} else if (size === 'medium') {
return 'uk-width-1-4@l uk-width-1-2@m uk-width-1-1';
} else {
return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
}
}
public getChartClassBySize(size: IndicatorSize) {
if (size === 'small') {
return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
} else if (size === 'medium') {
return 'uk-width-1-2@l uk-width-1-1';
} else {
return 'uk-width-1-1';
}
}
public printReport() {
window.print();
}
toggleDescriptionOverlay(event, indicator: Indicator) {
event.stopPropagation();
indicator.descriptionOverlay = !indicator.descriptionOverlay;
}
closeDescriptionOverlay(event: ClickEvent, indicator: Indicator) {
if(event.clicked && indicator.descriptionOverlay) {
indicator.descriptionOverlay = false;
}
}
}

View File

@ -90,6 +90,10 @@ export abstract class BaseComponent implements OnDestroy {
this._meta.updateTag({content: this.title}, "property='og:title'"); this._meta.updateTag({content: this.title}, "property='og:title'");
} }
} }
this.trackView();
}
public trackView() {
if (this._piwikService) { if (this._piwikService) {
this.subscriptions.push(this._piwikService.trackView(properties, this.title).subscribe()); this.subscriptions.push(this._piwikService.trackView(properties, this.title).subscribe());
} }