Merge remote-tracking branch 'origin/develop'

This commit is contained in:
Konstantinos Triantafyllou 2023-07-12 13:00:35 +03:00
commit 8d6205bb32
24 changed files with 1165 additions and 623 deletions

View File

@ -5,13 +5,13 @@
"ng": "ng",
"start": "ng serve --port 4600 --disable-host-check --host 0.0.0.0",
"build": "ng build",
"build-dev": "ng build --configuration=development --source-map --base-href /dashboard/",
"build-dev": "ng build --configuration=development --source-map",
"build-beta": "ng build --configuration=beta --base-href /dashboard/ --source-map",
"build-prod": "ng build --configuration production --base-href /dashboard/ --source-map",
"webpack-bundle-analyzer": "ng build --stats-json && webpack-bundle-analyzer dist/monitor-dashboard/browser/stats.json --host 0.0.0.0",
"test": "ng test",
"e2e": "ng e2e",
"dev:ssr": "ng run monitor-dashboard:serve-ssr",
"dev:ssr": "ng run monitor-dashboard:serve-ssr --port 4600",
"serve:ssr": "node dist/monitor-dashboard/server/main.js",
"build:ssr-dev": "npm run build-dev && ng run monitor-dashboard:server:development",
"build:ssr-beta": "npm run build-beta && ng run monitor-dashboard:server:beta",
@ -34,6 +34,7 @@
"@angular/platform-server": "^14.2.3",
"@angular/router": "^14.2.3",
"@nguniversal/express-engine": "^14.2.0",
"axios": "^1.4.0",
"clipboard": "^1.5.16",
"core-js": "^2.5.4",
"express": "^4.15.2",
@ -52,12 +53,12 @@
"@angular/compiler-cli": "^14.2.3",
"@angular/language-service": "^14.2.3",
"@nguniversal/builders": "^14.2.0",
"@types/ckeditor": "^4.9.10",
"@types/compression": "^1.7.0",
"@types/express": "^4.17.0",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"@types/ckeditor": "^4.9.10",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~5.0.0",

View File

@ -1,14 +1,22 @@
import 'zone.js/node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import {ngExpressEngine} from '@nguniversal/express-engine';
import * as express from 'express';
import * as compression from 'compression';
import { join } from 'path';
import {join} from 'path';
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
import {AppServerModule} from './src/main.server';
import {APP_BASE_HREF} from '@angular/common';
import {existsSync} from 'fs';
import {REQUEST, RESPONSE} from "./src/app/openaireLibrary/utils/tokens";
import {properties} from "./src/environments/environment";
import axios, {AxiosHeaders} from "axios";
import {Stakeholder} from "./src/app/openaireLibrary/monitor/entities/stakeholder";
import {CacheIndicators} from "./src/cache-indicators";
import {Session, User} from "./src/app/openaireLibrary/login/utils/helper.class";
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
@ -16,23 +24,90 @@ export function app() {
server.use(compression());
const distFolder = join(process.cwd(), 'dist/monitor-dashboard/browser');
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
let cacheIndicators: CacheIndicators = new CacheIndicators();
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
inlineCriticalCss: false
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.use('/cache', function (req, res, next) {
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS, POST, DELETE');
res.header('Access-Control-Max-Age', "1800");
next();
});
server.post('/cache/:alias', jsonParser, async (req, res) => {
await checkPermissions(req, res, (stakeholder, user) => {
if (cacheIndicators.completed(stakeholder._id)) {
res.send({
id: stakeholder._id,
report: cacheIndicators.createReport(stakeholder._id, cacheIndicators.stakeholderToCacheItems(stakeholder), stakeholder.name, user.email)
});
} else {
res.status(409).send('There is another active caching process for this stakeholder');
}
});
});
server.get('/cache/:alias', async (req, res) => {
await checkPermissions(req, res, stakeholder => {
if (cacheIndicators.exists(stakeholder._id)) {
res.send({
id: stakeholder._id,
report: cacheIndicators.getReport(stakeholder._id)
});
} else {
res.status(404).send('There is not an active caching process for this stakeholder');
}
});
});
async function checkPermissions(req, res, access: (stakeholder, user) => void) {
let headers: AxiosHeaders = new AxiosHeaders();
headers.set('Cookie', req.headers.cookie);
let userinfoRes = (await axios.get<any>(properties.userInfoUrl, {
withCredentials: true,
headers: headers
}).catch(error => {
return error.response;
}));
if (userinfoRes.status === 200) {
let user = new User(userinfoRes.data);
let stakeholderRes = (await axios.get<Stakeholder>(properties.monitorServiceAPIURL + '/stakeholder/' + encodeURIComponent(req.params.alias), {
withCredentials: true,
headers: headers
}).catch(error => {
return error.response;
}));
if (stakeholderRes.status === 200) {
let stakeholder = stakeholderRes.data;
if (Session.isPortalAdministrator(user) || Session.isCurator(stakeholder.type, user)) {
access(stakeholder, user);
} else {
res.status(403).send('You are forbidden to access this resource');
}
} else {
res.status(stakeholderRes.status).send(stakeholderRes.statusText);
}
} else {
res.status(userinfoRes.status).send(userinfoRes.data.message);
}
}
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, {
@ -51,13 +126,13 @@ export function app() {
}
);
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {

View File

@ -17,6 +17,7 @@
</main>
<bottom id="bottom" *ngIf="isFrontPage" [centered]="true" [properties]="properties" [showMenuItems]="true"></bottom>
<notification-sidebar *ngIf="!isMobile && user && notificationGroupsInitialized" [configuration]="notificationConfiguration" [user]="user"></notification-sidebar>
<cache-indicators *ngIf="stakeholder && !isFrontPage && isCurator()" [alias]="stakeholder.alias"></cache-indicators>
</div>
<div *ngIf="view" class="preview uk-text-small uk-flex uk-flex-middle">
<span>You are currently in a <span class="uk-text-bold">"Preview"</span> mode. <span class="uk-visible@m"><a (click)="removeView()">The current view</a> of this dashboard may differ.</span></span>

View File

@ -1,4 +1,4 @@
import {ChangeDetectorRef, Component, HostListener, OnDestroy, OnInit} from '@angular/core';
import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute, Data, NavigationEnd, Params, Router} from '@angular/router';
import {EnvProperties} from './openaireLibrary/utils/properties/env-properties';
import {Role, Session, User} from './openaireLibrary/login/utils/helper.class';
@ -18,7 +18,6 @@ import {LinksResolver} from "./search/links-resolver";
import {Header} from "./openaireLibrary/sharedComponents/navigationBar.component";
import {properties} from "../environments/environment";
import {ConfigurationService} from "./openaireLibrary/utils/configuration/configuration.service";
import {Option} from "./openaireLibrary/sharedComponents/input/input.component";
import {StakeholderUtils} from "./utils/indicator-utils";
import {SmoothScroll} from "./openaireLibrary/utils/smooth-scroll";
import {ConnectHelper} from "./openaireLibrary/connect/connectHelper";
@ -343,7 +342,8 @@ export class AppComponent implements OnInit, OnDestroy {
false, [], null, {resultbestaccessright: '"' + encodeURIComponent("Open Access") + '"'},
null, null, null, null)
);
this.resourcesService.setResources(this.menuItems, "/" + this.stakeholder.alias);
this.resourcesService.setResources(this.menuItems, '', this.monitorLink, '_blank');
this.menuItems.push(new MenuItem("support", "Support", this.monitorLink + '/support/', "", false, [], null, {}));
if (this.stakeholder.type === "funder") {
this.menuItems.push(
new MenuItem("develop", "Develop",
@ -402,12 +402,8 @@ export class AppComponent implements OnInit, OnDestroy {
this.monitorLink + '/browse', "", false, [], null, {}, null, null, null, null, "_self")
);
this.resourcesService.setResources(this.menuItems, '', this.monitorLink);
let about = new MenuItem("about", "About", "", "", false, [], null, {});
about.items = [
new MenuItem("how-it-works", "How it works", this.monitorLink + '/about/how-it-works', "", false, [], null, {}, null, null, null, null, "_self"),
new MenuItem("faqs", "FAQs", this.monitorLink + '/about/faqs',"", false, [], null, {}, null, null, null, null, "_self")
]
this.menuItems.push(about);
this.menuItems.push(new MenuItem("support", "Support", this.monitorLink + '/support/', "", false, [], null, {}, null, null, null, null, "_self"));
this.menuItems.push(new MenuItem("about", "About", this.monitorLink + '/about/', "", false, [], null, {}, null, null, null, null, "_self"));
if (this.hasAdminMenu) {
this.adminMenuItems = [];
this.backItem = null;

View File

@ -29,9 +29,10 @@ import {LoginGuard} from "./openaireLibrary/login/loginGuard.guard";
import {IconsModule} from "./openaireLibrary/utils/icons/icons.module";
import {IconsService} from "./openaireLibrary/utils/icons/icons.service";
import {incognito} from "./openaireLibrary/utils/icons/icons";
import {CacheIndicatorsModule} from "./cache-indicators/cache-indicators.module";
@NgModule({
imports: [
SharedModule,
BrowserAnimationsModule,
@ -44,7 +45,7 @@ import {incognito} from "./openaireLibrary/utils/icons/icons";
CookieLawModule,
BrowserModule.withServerTransition({appId: 'serverApp'}),
AppRoutingModule,
SideBarModule, Schema2jsonldModule, RoleVerificationModule, LoadingModule, NotificationsSidebarModule, IconsModule
SideBarModule, Schema2jsonldModule, RoleVerificationModule, LoadingModule, NotificationsSidebarModule, IconsModule, CacheIndicatorsModule
],
declarations: [AppComponent, OpenaireErrorPageComponent],
exports: [AppComponent],

View File

@ -0,0 +1,9 @@
@import (reference) "~src/assets/openaire-theme/less/_import-variables.less";
.cache-progress {
position: fixed;
bottom: 0;
right: 0;
transform: translate(-50%, -50%);
z-index: @global-z-index;
}

View File

@ -0,0 +1,78 @@
import {Component, Inject, Input, OnChanges, OnDestroy, OnInit, PLATFORM_ID, SimpleChanges} from "@angular/core";
import {Report} from "../../cache-indicators";
import {CacheIndicatorsService} from "../utils/services/cache-indicators.service";
import {interval, Subject, Subscription} from "rxjs";
import {map, switchMap, takeUntil} from "rxjs/operators";
@Component({
selector: 'cache-indicators',
template: `
<div *ngIf="report" class="cache-progress">
<div class="uk-position-relative" [attr.uk-tooltip]="'Caching indicators process for ' + alias">
<div class="uk-progress-circle" [attr.percentage]="report.percentage?report.percentage:0" [style]="'--percentage: ' + (report.percentage?report.percentage:0)"></div>
<button *ngIf="report.percentage === 100" (click)="clear()" class="uk-icon-button uk-icon-button-xsmall uk-button-default uk-position-top-right"><icon name="close" [flex]="true" ratio="0.8"></icon></button>
</div>
</div>
`,
styleUrls: ['cache-indicators.component.less']
})
export class CacheIndicatorsComponent implements OnInit, OnChanges, OnDestroy {
report: Report;
subscriptions: Subscription[] = [];
interval: number = 10000;
readonly destroy$ = new Subject();
@Input() alias: string;
constructor(private cacheIndicatorsService: CacheIndicatorsService,
@Inject(PLATFORM_ID) private platformId) {
}
ngOnInit() {
this.getReport();
}
ngOnChanges(changes: SimpleChanges) {
if(changes.alias) {
this.getReport();
}
}
getReport() {
this.clear();
this.subscriptions.push(this.cacheIndicatorsService.getReport(this.alias).subscribe(report => {
this.getReportInterval(report);
}));
}
getReportInterval(report: Report) {
if(this.isBrowser && (this.report || !report?.completed)) {
this.report = report;
this.subscriptions.push(interval(this.interval).pipe(
map(() => this.cacheIndicatorsService.getReport(this.alias)),
switchMap(report => report),
takeUntil(this.destroy$)).subscribe(report => {
console.log(this.alias);
this.report = report;
if(this.report.completed) {
this.destroy$.next();
}
}));
}
}
clear() {
this.subscriptions.forEach(subscription => {
subscription.unsubscribe();
})
this.report = null;
}
get isBrowser() {
return this.platformId === 'browser';
}
ngOnDestroy() {
this.clear();
}
}

View File

@ -0,0 +1,11 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {CacheIndicatorsComponent} from "./cache-indicators.component";
import {IconsModule} from "../openaireLibrary/utils/icons/icons.module";
@NgModule({
imports: [CommonModule, IconsModule],
declarations: [CacheIndicatorsComponent],
exports: [CacheIndicatorsComponent]
})
export class CacheIndicatorsModule {}

View File

@ -15,93 +15,106 @@ import {NotifyFormComponent} from "../../openaireLibrary/notifications/notify-fo
import {NotificationUtils} from "../../openaireLibrary/notifications/notification-utils";
import {Notification} from "../../openaireLibrary/notifications/notifications";
import {NotificationHandler} from "../../openaireLibrary/utils/notification-handler";
import {StatsProfilesService} from "../../utils/services/stats-profiles.service";
@Component({
selector: 'edit-stakeholder',
template: `
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid uk-grid-large" uk-grid>
<div class="uk-width-1-2@m">
<div input id="name" [formInput]="stakeholderFb.get('name')"
placeholder="Name"></div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="stakeholderFb.get('alias')"
placeholder="URL Alias"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_id')"
placeholder="Index ID"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_name')"
placeholder="Index Name"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_shortName')"
placeholder="Index Short Name"></div>
</div>
<div class="uk-width-1-1">
<div input [type]="'textarea'" placeholder="Description"
[rows]="4" [formInput]="stakeholderFb.get('description')"></div>
</div>
<div class="uk-width-1-1">
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<div *ngIf="!stakeholderFb.get('isUpload').value" class="uk-grid uk-grid-column-large" uk-grid>
<div class="uk-margin-top uk-width-auto@l uk-width-1-1">
<div class="uk-grid uk-grid-column-large uk-flex-middle" uk-grid>
<div class="uk-width-auto@l uk-width-1-1 uk-flex uk-flex-center">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle uk-flex-wrap"
(click)="file.click()">
<icon name="cloud_upload" [flex]="true"></icon>
<span class="uk-margin-small-left">Upload a file</span>
</button>
</div>
<div class="uk-text-center uk-text-bold uk-width-expand">
OR
</div>
<form *ngIf="stakeholderFb" [formGroup]="stakeholderFb">
<div class="uk-grid uk-grid-large" uk-grid>
<div class="uk-width-1-2@m">
<div input id="name" [formInput]="stakeholderFb.get('name')"
placeholder="Name"></div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="stakeholderFb.get('alias')"
placeholder="URL Alias"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_id')"
placeholder="Index ID"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_name')"
placeholder="Index Name"></div>
</div>
<div class="uk-width-1-3@m">
<div input [formInput]="stakeholderFb.get('index_shortName')"
placeholder="Index Short Name"></div>
</div>
<div class="uk-width-1-2@m">
<div *ngIf="statsProfiles" input [formInput]="stakeholderFb.get('statsProfile')" [type]="'select'"
[options]="statsProfiles"
placeholder="Stats Profile"></div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="stakeholderFb.get('locale')" [type]="'select'"
[options]="stakeholderUtils.locales"
placeholder="Locale"></div>
</div>
<div class="uk-width-1-1">
<div input [type]="'textarea'" placeholder="Description"
[rows]="4" [formInput]="stakeholderFb.get('description')"></div>
</div>
<div class="uk-width-1-1">
<input #file id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
<div *ngIf="!stakeholderFb.get('isUpload').value" class="uk-grid uk-grid-column-large" uk-grid>
<div class="uk-margin-top uk-width-auto@l uk-width-1-1">
<div class="uk-grid uk-grid-column-large uk-flex-middle" uk-grid>
<div class="uk-width-auto@l uk-width-1-1 uk-flex uk-flex-center">
<button class="uk-button uk-button-primary uk-flex uk-flex-middle uk-flex-wrap"
(click)="file.click()">
<icon name="cloud_upload" [flex]="true"></icon>
<span class="uk-margin-small-left">Upload a file</span>
</button>
</div>
<div class="uk-text-center uk-text-bold uk-width-expand">
OR
</div>
</div>
<div input class="uk-width-expand" type="logoURL" [placeholder]="'Link to the logo'" [formInput]="stakeholderFb.get('logoUrl')"></div>
</div>
<div *ngIf="stakeholderFb.get('isUpload').value" class="uk-width-1-1 uk-flex uk-flex-middle">
<div class="uk-card uk-card-default uk-text-center uk-border-circle">
<img class="uk-position-center uk-blend-multiply" [src]="photo">
</div>
<div class="uk-margin-left">
<button (click)="remove()" class="uk-button-danger uk-icon-button uk-icon-button-small">
<icon [flex]="true" ratio="0.8" name="delete"></icon>
</button>
</div>
<div class="uk-margin-small-left">
<button class="uk-button-secondary uk-icon-button uk-icon-button-small" (click)="file.click()">
<icon [flex]="true" ratio="0.8" name="edit"></icon>
</button>
</div>
</div>
<!-- Full width error message -->
<div *ngIf="uploadError" class="uk-text-danger uk-margin-small-top uk-width-1-1">{{uploadError}}</div>
</div>
<div [class]="canChooseType ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('visibility')"
[placeholder]="'Select a status'"
[options]="stakeholderUtils.statuses" type="select"></div>
</div>
<div [class]="canChooseType ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('type')"
[placeholder]="'Select a type'"
[options]="types" type="select"></div>
</div>
<ng-container *ngIf="canChooseType">
<div class="uk-width-1-3@m">
<div [placeholder]="'Select a template'"
input [formInput]="stakeholderFb.get('defaultId')"
[options]="defaultStakeholdersOptions" type="select"></div>
</div>
</ng-container>
<div input class="uk-width-expand" type="logoURL" [placeholder]="'Link to the logo'"
[formInput]="stakeholderFb.get('logoUrl')"></div>
</div>
<div *ngIf="stakeholderFb.get('isUpload').value" class="uk-width-1-1 uk-flex uk-flex-middle">
<div class="uk-card uk-card-default uk-text-center uk-border-circle">
<img class="uk-position-center uk-blend-multiply" [src]="photo">
</div>
<div class="uk-margin-left">
<button (click)="remove()" class="uk-button-danger uk-icon-button uk-icon-button-small">
<icon [flex]="true" ratio="0.8" name="delete"></icon>
</button>
</div>
<div class="uk-margin-small-left">
<button class="uk-button-secondary uk-icon-button uk-icon-button-small" (click)="file.click()">
<icon [flex]="true" ratio="0.8" name="edit"></icon>
</button>
</div>
</div>
<!-- Full width error message -->
<div *ngIf="uploadError" class="uk-text-danger uk-margin-small-top uk-width-1-1">{{uploadError}}</div>
</div>
</form>
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form class="uk-width-1-1 uk-margin-large-top uk-margin-medium-bottom"></div>
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('visibility')"
[placeholder]="'Select a status'"
[options]="stakeholderUtils.statuses" type="select"></div>
</div>
<div [class]="canChooseTemplate ? 'uk-width-1-3@m' : 'uk-width-1-2@m'">
<div input [formInput]="stakeholderFb.get('type')"
[placeholder]="'Select a type'"
[options]="types" type="select"></div>
</div>
<ng-container *ngIf="canChooseTemplate">
<div class="uk-width-1-3@m">
<div [placeholder]="'Select a template'"
input [formInput]="stakeholderFb.get('defaultId')"
[options]="defaultStakeholdersOptions" type="select"></div>
</div>
</ng-container>
</div>
</form>
<div #notify [class.uk-hidden]="!stakeholderFb" notify-form
class="uk-width-1-1 uk-margin-large-top uk-margin-medium-bottom"></div>
`,
styleUrls: ['edit-stakeholder.component.less']
})
@ -117,8 +130,9 @@ export class EditStakeholderComponent implements OnDestroy {
public stakeholder: Stakeholder;
public isDefault: boolean;
public isNew: boolean;
public loading: boolean = false;
public loading: boolean = false;
public types: Option[];
public statsProfiles: string[];
public properties: EnvProperties = properties;
private subscriptions: any[] = [];
/**
@ -130,11 +144,12 @@ export class EditStakeholderComponent implements OnDestroy {
public deleteCurrentPhoto: boolean = false;
private maxsize: number = 200 * 1024;
user: User;
@ViewChild('notify', { static: true }) notify: NotifyFormComponent;
@ViewChild('notify', {static: true}) notify: NotifyFormComponent;
private notification: Notification;
constructor(private fb: UntypedFormBuilder,
private stakeholderService: StakeholderService,
private statsProfileService: StatsProfilesService,
private utilsService: UtilitiesService, private userManagementService: UserManagementService,) {
}
@ -151,92 +166,110 @@ export class EditStakeholderComponent implements OnDestroy {
this.isDefault = isDefault;
this.isNew = isNew;
this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
this.user = user;
this.types = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
this.stakeholderFb = this.fb.group({
_id: this.fb.control(this.stakeholder._id),
defaultId: this.fb.control(this.stakeholder.defaultId),
name: this.fb.control(this.stakeholder.name, Validators.required),
description: this.fb.control(this.stakeholder.description),
index_name: this.fb.control(this.stakeholder.index_name, Validators.required),
index_id: this.fb.control(this.stakeholder.index_id, Validators.required),
index_shortName: this.fb.control(this.stakeholder.index_shortName, Validators.required),
creationDate: this.fb.control(this.stakeholder.creationDate),
alias: this.fb.control(this.stakeholder.alias,
[
Validators.required,
this.stakeholderUtils.aliasValidatorString(
this.alias.filter(alias => alias !== this.stakeholder.alias)
)]
),
isDefault: this.fb.control((this.isDefault)),
visibility: this.fb.control(this.stakeholder.visibility, Validators.required),
type: this.fb.control(this.stakeholder.type, Validators.required),
topics: this.fb.control(this.stakeholder.topics),
isUpload: this.fb.control(this.stakeholder.isUpload),
logoUrl: this.fb.control(this.stakeholder.logoUrl),
});
if (this.stakeholder.isUpload) {
this.user = user;
if (this.isCurator) {
this.subscriptions.push(this.statsProfileService.getStatsProfiles().subscribe(statsProfiles => {
this.statsProfiles = statsProfiles;
}, error => {
this.statsProfiles = [];
}));
} else {
this.statsProfiles = [];
}
this.types = this.stakeholderUtils.getTypesByUserRoles(this.user, this.stakeholder.alias);
this.stakeholderFb = this.fb.group({
_id: this.fb.control(this.stakeholder._id),
defaultId: this.fb.control(this.stakeholder.defaultId),
name: this.fb.control(this.stakeholder.name, Validators.required),
description: this.fb.control(this.stakeholder.description),
index_name: this.fb.control(this.stakeholder.index_name, Validators.required),
index_id: this.fb.control(this.stakeholder.index_id, Validators.required),
index_shortName: this.fb.control(this.stakeholder.index_shortName, Validators.required),
statsProfile: this.fb.control(this.stakeholder.statsProfile, Validators.required),
locale: this.fb.control(this.stakeholder.locale, Validators.required),
creationDate: this.fb.control(this.stakeholder.creationDate),
alias: this.fb.control(this.stakeholder.alias,
[
Validators.required,
this.stakeholderUtils.aliasValidatorString(
this.alias.filter(alias => alias !== this.stakeholder.alias)
)]
),
isDefault: this.fb.control((this.isDefault)),
visibility: this.fb.control(this.stakeholder.visibility, Validators.required),
type: this.fb.control(this.stakeholder.type, Validators.required),
topics: this.fb.control(this.stakeholder.topics),
isUpload: this.fb.control(this.stakeholder.isUpload),
logoUrl: this.fb.control(this.stakeholder.logoUrl),
});
if (this.stakeholder.isUpload) {
this.stakeholderFb.get('logoUrl').clearValidators();
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
} else {
this.stakeholderFb.get('logoUrl').setValidators([StringUtils.urlValidator()]);
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
}
this.subscriptions.push(this.stakeholderFb.get('isUpload').valueChanges.subscribe(value => {
if (value == true) {
this.stakeholderFb.get('logoUrl').clearValidators();
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
this.stakeholderFb.updateValueAndValidity();
} else {
this.stakeholderFb.get('logoUrl').setValidators([StringUtils.urlValidator()]);
this.stakeholderFb.get('logoUrl').updateValueAndValidity();
this.stakeholderFb.updateValueAndValidity();
}
this.subscriptions.push(this.stakeholderFb.get('isUpload').valueChanges.subscribe(value => {
if (value == true) {
this.stakeholderFb.get('logoUrl').clearValidators();
this.stakeholderFb.updateValueAndValidity();
} else {
this.stakeholderFb.get('logoUrl').setValidators([StringUtils.urlValidator()]);
this.stakeholderFb.updateValueAndValidity();
}
}));
this.secure = (!this.stakeholderFb.get('logoUrl').value || this.stakeholderFb.get('logoUrl').value.includes('https://'));
this.subscriptions.push(this.stakeholderFb.get('logoUrl').valueChanges.subscribe(value => {
this.secure = (!value || value.includes('https://'));
}));
this.initPhoto();
if (!isDefault) {
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
this.onTypeChange(value, defaultStakeholders);
}));
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, Validators.required));
}
if (!isNew) {
this.notification = NotificationUtils.editStakeholder(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.notify.reset(this.notification.message);
if (this.isAdmin) {
if (this.disableAlias) {
setTimeout(() => {
this.stakeholderFb.get('alias').disable();
}, 0);
}
} else {
}));
this.secure = (!this.stakeholderFb.get('logoUrl').value || this.stakeholderFb.get('logoUrl').value.includes('https://'));
this.subscriptions.push(this.stakeholderFb.get('logoUrl').valueChanges.subscribe(value => {
this.secure = (!value || value.includes('https://'));
}));
this.initPhoto();
this.subscriptions.push(this.stakeholderFb.get('type').valueChanges.subscribe(value => {
this.onTypeChange(value, defaultStakeholders);
}));
this.stakeholderFb.setControl('defaultId', this.fb.control(stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
if (!this.isNew) {
this.notification = NotificationUtils.editStakeholder(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
this.notify.reset(this.notification.message);
if (this.isAdmin) {
if (this.disableAlias) {
setTimeout(() => {
this.stakeholderFb.get('alias').disable();
this.stakeholderFb.get('index_id').disable();
this.stakeholderFb.get('index_name').disable();
this.stakeholderFb.get('index_shortName').disable();
}, 0);
}
} else {
if (!this.isCurator) {
setTimeout(() => {
this.stakeholderFb.get('statsProfile').disable();
}, 0);
}
setTimeout(() => {
this.stakeholderFb.get('type').disable();
}, 0);
} else {
this.notification = NotificationUtils.createStakeholder(this.user.firstname + ' ' + this.user.lastname);
this.notify.reset(this.notification.message);
setTimeout(() => {
this.stakeholderFb.get('type').enable();
this.stakeholderFb.get('alias').disable();
this.stakeholderFb.get('index_id').disable();
this.stakeholderFb.get('index_name').disable();
this.stakeholderFb.get('index_shortName').disable();
}, 0);
}
}));
setTimeout(() => {
this.stakeholderFb.get('type').disable();
}, 0);
} else {
this.notification = NotificationUtils.createStakeholder(this.user.firstname + ' ' + this.user.lastname);
this.notify.reset(this.notification.message);
setTimeout(() => {
this.stakeholderFb.get('type').enable();
}, 0);
}
}));
}
public get isAdmin() {
return Session.isPortalAdministrator(this.user);
}
public get isCurator() {
return this.stakeholder && (this.isAdmin || Session.isCurator(this.stakeholder.type, this.user));
}
public get disabled(): boolean {
return (this.stakeholderFb && this.stakeholderFb.invalid) ||
(this.stakeholderFb && this.stakeholderFb.pristine && !this.isNew && !this.file) ||
@ -247,8 +280,8 @@ export class EditStakeholderComponent implements OnDestroy {
return this.stakeholderFb && this.stakeholderFb.dirty;
}
public get canChooseType(): boolean {
return !this.stakeholderFb.get('isDefault').value && this.isNew && this.stakeholderFb.get('type').valid && !!this.defaultStakeholdersOptions;
public get canChooseTemplate(): boolean {
return this.isNew && this.stakeholderFb.get('type').valid && !!this.defaultStakeholdersOptions;
}
reset() {
@ -262,7 +295,7 @@ export class EditStakeholderComponent implements OnDestroy {
}
onTypeChange(value, defaultStakeholders: Stakeholder[]) {
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, Validators.required));
this.stakeholderFb.setControl('defaultId', this.fb.control(this.stakeholder.defaultId, (this.isDefault && !this.isNew)?[]:Validators.required));
this.defaultStakeholdersOptions = [{
label: 'New blank profile',
value: '-1'
@ -276,7 +309,7 @@ export class EditStakeholderComponent implements OnDestroy {
}
public save(callback: Function, errorCallback: Function = null) {
this.loading = true;
this.loading = true;
if (this.file) {
this.subscriptions.push(this.utilsService.uploadPhoto(this.properties.utilsService + "/upload/" + encodeURIComponent(this.stakeholderFb.getRawValue().type) + "/" + encodeURIComponent(this.stakeholderFb.getRawValue().alias), this.file).subscribe(res => {
this.deletePhoto();
@ -297,12 +330,13 @@ export class EditStakeholderComponent implements OnDestroy {
public saveStakeholder(callback: Function, errorCallback: Function = null) {
if (this.isNew) {
if (!this.stakeholderFb.getRawValue().isDefault) {
let stakeholder = this.defaultStakeholders.find(value => value._id === this.stakeholderFb.getRawValue().defaultId);
this.stakeholderFb.setValue(this.stakeholderUtils.createFunderFromDefaultProfile(this.stakeholderFb.getRawValue(),
(stakeholder ? stakeholder.topics : [])));
}
let defaultStakeholder = this.defaultStakeholders.find(value => value._id === this.stakeholderFb.getRawValue().defaultId);
this.stakeholderFb.setValue(this.stakeholderUtils.createFunderFromDefaultProfile(this.stakeholderFb.getRawValue(),
(defaultStakeholder ? defaultStakeholder.topics : []), this.stakeholderFb.getRawValue().isDefault));
this.removePhoto();
if(this.stakeholderFb.getRawValue().isDefault) {
this.stakeholderFb.get('defaultId').setValue(null);
}
this.subscriptions.push(this.stakeholderService.buildStakeholder(this.properties.monitorServiceAPIURL,
this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
this.notification.entity = stakeholder._id;
@ -312,13 +346,13 @@ export class EditStakeholderComponent implements OnDestroy {
this.notify.sendNotification(this.notification);
NotificationHandler.rise(stakeholder.name + ' has been <b>successfully created</b>');
callback(stakeholder);
this.loading = false;
this.loading = false;
}, error => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
if (errorCallback) {
errorCallback(error)
}
this.loading = false;
this.loading = false;
}));
} else {
this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.stakeholderFb.getRawValue()).subscribe(stakeholder => {
@ -328,14 +362,14 @@ export class EditStakeholderComponent implements OnDestroy {
this.notification.groups = [Role.curator(stakeholder.type), Role.manager(stakeholder.type, stakeholder.alias)];
this.notify.sendNotification(this.notification);
NotificationHandler.rise(stakeholder.name + ' has been <b>successfully saved</b>');
callback(stakeholder);
this.loading = false;
callback(stakeholder);
this.loading = false;
}, error => {
NotificationHandler.rise('An error has occurred. Please try again later', 'danger');
if (errorCallback) {
errorCallback(error)
}
this.loading = false;
this.loading = false;
}));
}
}

View File

@ -69,6 +69,9 @@
<li>
<a (click)="editStakeholder(stakeholder, !stakeholder.defaultId); hide(element)">Edit</a>
</li>
<li *ngIf="isCurator">
<a (click)="createReport(stakeholder);hide(element)">Cache Indicators</a>
</li>
<li class="uk-nav-divider"></li>
<ng-template ngFor [ngForOf]="stakeholderUtils.visibility" let-v>
<li [class.uk-active]="stakeholder.visibility === v.value">

View File

@ -13,6 +13,8 @@ import {Session} from "../openaireLibrary/login/utils/helper.class";
import {EditStakeholderComponent} from "../general/edit-stakeholder/edit-stakeholder.component";
import {properties} from "../../environments/environment";
import {ActivatedRoute} from "@angular/router";
import {CacheIndicatorsService} from "../utils/services/cache-indicators.service";
import {NotificationHandler} from "../openaireLibrary/utils/notification-handler";
type Tab = 'all' | 'templates'| 'profiles';
@ -62,6 +64,7 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
@ViewChild('editStakeholderComponent', { static: true }) editStakeholderComponent: EditStakeholderComponent;
constructor(private stakeholderService: StakeholderService,
private cacheIndicatorsService: CacheIndicatorsService,
private userManagementService: UserManagementService,
private route: ActivatedRoute,
private title: Title,
@ -202,6 +205,15 @@ export class ManageStakeholdersComponent implements OnInit, OnDestroy {
this.editStakeholderModal.open();
}
public createReport(stakeholder: Stakeholder) {
this.cacheIndicatorsService.createReport(stakeholder.alias).subscribe(report => {
NotificationHandler.rise('A caching process for ' + stakeholder.name + ' has been started.' )
}, error => {
console.log(error);
NotificationHandler.rise(error.message(), 'danger');
});
}
public deleteStakeholderOpen(stakeholder: Stakeholder) {
this.stakeholder = stakeholder;
this.deleteStakeholderModal.alertTitle = 'Delete ' + this.stakeholder.index_name;

View File

@ -108,7 +108,7 @@
class="uk-text-small uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{indicator.name}}</div>
<div class="number uk-text-small uk-text-bold">
<span *ngIf="numberResults.get(i + '-' + j)"
[innerHTML]="numberResults.get(i + '-' + j) | numberRound: 2:1"></span>
[innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j)">--</span>
</div>
<div *ngIf="indicator.description || indicator.additionalDescription"
@ -159,7 +159,7 @@
class="uk-text-xsmall uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{indicator.name}}</div>
<div class="number uk-text-small uk-text-bold">
<span *ngIf="numberResults.get(i + '-' + j)"
[innerHTML]="numberResults.get(i + '-' + j) | numberRound: 2:1"></span>
[innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j)">--</span>
</div>
</div>

View File

@ -485,7 +485,7 @@ export class MonitorComponent implements OnInit, OnDestroy {
urls.forEach((indexes, pair) => {
pair = JSON.parse(pair);
let activeSubcategory = this.activeSubCategory._id;
this.subscriptions.push(this.statisticsService.getNumbers(this.statisticsService.getSourceType(pair[0]), pair[1]).subscribe(response => {
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]) {
@ -524,7 +524,7 @@ export class MonitorComponent implements OnInit, OnDestroy {
public getUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.sanitizer.bypassSecurityTrustResourceUrl(
this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, indicatorPath, this.getfl0(), this.periodFilter.selectedFromValue, this.periodFilter.selectedToValue, this.getCoFunded())));
this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, indicatorPath, this.getfl0(), this.periodFilter.selectedFromValue, this.periodFilter.selectedToValue, this.getCoFunded())));
}
public setActiveChart(i: number, j: number, type: string) {

View File

@ -15,14 +15,14 @@ import {RouterModule} from "@angular/router";
{ path: 'advanced/dataproviders', loadChildren: () => import('./searchPages/advanced/searchDataProviders.module').then(m => m.MonitorAdvancedSearchDataProvidersModule)},
{ path: 'advanced/organizations', loadChildren: () => import('./searchPages/advanced/searchOrganizations.module').then(m => m.MonitorAdvancedSearchOrganizationsModule)},
// Landing Pages
{ path: 'result', loadChildren: () => import('./landingPages/result/libResult.module').then(m => m.LibResultModule)},
{ path: 'publication', loadChildren: () => import('./landingPages/publication/libPublication.module').then(m => m.LibPublicationModule)},
{ path: 'dataset', loadChildren: () => import('./landingPages/dataset/libDataset.module').then(m => m.LibDatasetModule)},
{ path: 'software', loadChildren: () => import('./landingPages/software/libSoftware.module').then(m => m.LibSoftwareModule)},
{ path: 'other', loadChildren: () => import('./landingPages/orp/libOrp.module').then(m => m.LibOrpModule)},
{ path: 'project', loadChildren: () => import('./landingPages/project/libProject.module').then(m => m.LibProjectModule)},
{ path: 'dataprovider', loadChildren: () => import('./landingPages/dataProvider/libDataProvider.module').then(m => m.LibDataProviderModule)},
{ path: 'organization', loadChildren: () => import('./landingPages/organization/libOrganization.module').then(m => m.LibOrganizationModule)},
{ path: 'result', loadChildren: () => import('./landingPages/result/libResult.module').then(m => m.LibResultModule), data: {hasMenuSearchBar: true}},
{ path: 'publication', loadChildren: () => import('./landingPages/publication/libPublication.module').then(m => m.LibPublicationModule), data: {hasMenuSearchBar: true}},
{ path: 'dataset', loadChildren: () => import('./landingPages/dataset/libDataset.module').then(m => m.LibDatasetModule), data: {hasMenuSearchBar: true}},
{ path: 'software', loadChildren: () => import('./landingPages/software/libSoftware.module').then(m => m.LibSoftwareModule), data: {hasMenuSearchBar: true}},
{ path: 'other', loadChildren: () => import('./landingPages/orp/libOrp.module').then(m => m.LibOrpModule), data: {hasMenuSearchBar: true}},
{ path: 'project', loadChildren: () => import('./landingPages/project/libProject.module').then(m => m.LibProjectModule), data: {hasMenuSearchBar: true}},
{ path: 'dataprovider', loadChildren: () => import('./landingPages/dataProvider/libDataProvider.module').then(m => m.LibDataProviderModule), data: {hasMenuSearchBar: true}},
{ path: 'organization', loadChildren: () => import('./landingPages/organization/libOrganization.module').then(m => m.LibOrganizationModule), data: {hasMenuSearchBar: true}},
])]
})
export class SearchModule {}

View File

@ -68,7 +68,7 @@
</div>
<div class="uk-text-small uk-text-truncate uk-margin-xsmall-bottom uk-margin-right">{{indicator.name}}</div>
<div class="number uk-text-small uk-text-bold">
<span *ngIf="numberResults.get(i + '-' + j)" [innerHTML]="numberResults.get(i + '-' + j) | numberRound: 2:1"></span>
<span *ngIf="numberResults.get(i + '-' + j)" [innerHTML]="(indicator.indicatorPaths[0].format == 'NUMBER'?(numberResults.get(i + '-' + j) | numberRound: 2:1:stakeholder.locale):(numberResults.get(i + '-' + j) | numberPercentage: stakeholder.locale))"></span>
<span *ngIf="!numberResults.get(i + '-' + j)">--</span>
</div>
</div>
@ -257,11 +257,16 @@
now</a>
</div>
</div>
<div class="uk-width-1-1">
<div class="uk-width-1-2@m">
<div input [formInput]="indicatorPath.get('source')" placeholder="Source"
[options]="isAdministrator?indicatorUtils.allSourceTypes:indicatorUtils.sourceTypes" type="select">
</div>
</div>
<div class="uk-width-1-2@m">
<div input [formInput]="indicatorPath.get('format')" placeholder="Format"
[options]="indicatorUtils.formats" type="select">
</div>
</div>
</div>
</div>
<div formArrayName="jsonPath" class="uk-width-1-1">
@ -297,8 +302,8 @@
<div class="uk-width-1-1 uk-flex uk-flex-center">
<div class="uk-flex uk-position-relative">
<span class="uk-padding number number-preview uk-flex uk-flex-column uk-flex-center uk-text-center">
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value !== 0">
{{numberIndicatorPaths.at(i).get('result').value | number}}
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value !== 0"
[innerHTML]="(numberIndicatorPaths.at(i).get('format').value == 'NUMBER'?(numberIndicatorPaths.at(i).get('result').value | numberRound: 2:1:stakeholder.locale):(numberIndicatorPaths.at(i).get('result').value | numberPercentage: stakeholder.locale))">
</span>
<span *ngIf="numberIndicatorPaths.at(i).get('result').valid && numberIndicatorPaths.at(i).get('result').value === 0">
--

View File

@ -11,6 +11,7 @@ import {
ViewChild
} from "@angular/core";
import {
Format,
Indicator,
IndicatorPath,
IndicatorSize,
@ -20,7 +21,14 @@ import {
Visibility
} from "../openaireLibrary/monitor/entities/stakeholder";
import {IndicatorUtils, StakeholderUtils} from "../utils/indicator-utils";
import {AbstractControl, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from "@angular/forms";
import {
AbstractControl,
UntypedFormArray,
UntypedFormBuilder,
UntypedFormControl,
UntypedFormGroup,
Validators
} from "@angular/forms";
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
import {StatisticsService} from "../utils/services/statistics.service";
import {HelperFunctions} from "../openaireLibrary/utils/HelperFunctions.class";
@ -96,9 +104,9 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
@ViewChild('editNumberNotify', {static: true}) editNumberNotify: NotifyFormComponent;
@ViewChild('editChartNotify', {static: true}) editChartNotify: NotifyFormComponent;
@ViewChild('deleteNotify', {static: true}) deleteNotify: NotifyFormComponent;
public isFullscreen: boolean = false;
@HostListener('fullscreenchange', ['$event'])
@HostListener('webkitfullscreenchange', ['$event'])
@HostListener('mozfullscreenchange', ['$event'])
@ -106,14 +114,14 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
screenChange(event) {
this.isFullscreen = !this.isFullscreen;
}
/**
* Subscriptions
**/
private subscriptions: any[] = [];
private urlSubscriptions: any[] = [];
private numberSubscription: any[] = [];
constructor(private layoutService: LayoutService,
private stakeholderService: StakeholderService,
private statisticsService: StatisticsService,
@ -124,7 +132,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
private sanitizer: DomSanitizer) {
this.filesToUpload = [];
}
ngOnInit(): void {
if (this.stakeholder) {
this.setCharts();
@ -136,7 +144,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.initReorder();
})
}
ngOnDestroy(): void {
this.subscriptions.forEach(value => {
if (value instanceof Subscriber) {
@ -156,11 +164,11 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
});
}
ngAfterViewInit(): void {
this.initReorder();
}
ngOnChanges(changes: SimpleChanges): void {
if (this.canEdit) {
if (changes.topicIndex || changes.categoryIndex || changes.subcategoryIndex) {
@ -170,7 +178,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
}
}
initReorder() {
this.subscriptions.forEach(value => {
if (value instanceof Function) {
@ -223,11 +231,11 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
});
}
}
hide(element: any) {
UIkit.dropdown(element).hide();
}
setCharts() {
this.chartSections = this.fb.array([]);
this.charts.forEach(section => {
@ -251,7 +259,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
})
});
}
setNumbers() {
this.numberSections = this.fb.array([]);
this.numberResults.clear();
@ -267,7 +275,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
indicators: this.fb.control(section.indicators)
}));
section.indicators.forEach((number, j) => {
let url = this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, number.indicatorPaths[0]);
let url = this.indicatorUtils.getFullUrl(this.stakeholder, number.indicatorPaths[0]);
const pair = JSON.stringify([number.indicatorPaths[0].source, url]);
const indexes = urls.get(pair) ? urls.get(pair) : [];
indexes.push([i, j]);
@ -285,14 +293,14 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
if (response) {
this.calculateResults(response, indexes);
} else {
this.numberSubscription.push(this.statisticsService.getNumbers(this.statisticsService.getSourceType(parsed[0]), parsed[1]).subscribe(response => {
this.numberSubscription.push(this.statisticsService.getNumbers(this.indicatorUtils.getSourceType(parsed[0]), parsed[1]).subscribe(response => {
this.calculateResults(response, indexes);
this.numberResponses.set(pair, response);
}));
}
});
}
private calculateResults(response: any, indexes: [number, number][]) {
indexes.forEach(([i, j]) => {
let result = JSON.parse(JSON.stringify(response));
@ -312,7 +320,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.numberResults.set(i + '-' + j, result);
});
}
get charts(): Section[] {
if (this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
@ -322,7 +330,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
return [];
}
}
get numbers(): Section[] {
if (this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
@ -332,30 +340,30 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
return [];
}
}
set numbers(sections: Section[]) {
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = sections;
}
get open(): boolean {
return this.layoutService.open;
}
get canEdit() {
return this.stakeholder &&
this.stakeholder.topics[this.topicIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex];
}
public get numberIndicatorPaths(): UntypedFormArray {
return this.numberIndicatorFb.get('indicatorPaths') as UntypedFormArray;
}
public get chartIndicatorPaths(): UntypedFormArray {
return this.chartIndicatorFb.get('indicatorPaths') as UntypedFormArray;
}
public getNumberClassBySize(size: IndicatorSize) {
if (size === 'small') {
return 'uk-width-medium@m uk-width-1-1';
@ -365,7 +373,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
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';
@ -375,7 +383,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
return 'uk-width-1-1';
}
}
public addJsonPath(index: number) {
if (index == 0 && this.getJsonPath(index).getRawValue()[index].indexOf(",") != -1) {
//if in the first path there are more than one paths comma separated, split them and autogenerate the forms
@ -390,13 +398,13 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.getJsonPath(index).push(this.fb.control('', Validators.required));
}
}
public removeJsonPath(i: number, j: number) {
if (this.getJsonPath(i).enabled) {
this.getJsonPath(i).removeAt(j);
}
}
public validateJsonPath(index: number, dirty: boolean = false) {
let indicatorPath: UntypedFormGroup = <UntypedFormGroup>this.numberIndicatorPaths.at(index);
if (this.indicator.defaultId === null) {
@ -442,33 +450,33 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}, 500);
}));
}
public getJsonPath(index: number): UntypedFormArray {
return this.numberIndicatorPaths.at(index).get('jsonPath') as UntypedFormArray;
}
public getCurrentJsonPath(index: number): string[] {
return this.section.indicators[this.index].indicatorPaths[index].jsonPath;
}
public getParameters(index: number): UntypedFormArray {
return this.chartIndicatorPaths.at(index).get('parameters') as UntypedFormArray;
}
public getParameter(index: number, key: string): UntypedFormControl {
return this.getParameters(index).controls.filter(control => control.value.key === key)[0] as UntypedFormControl;
}
private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.sanitizer.bypassSecurityTrustResourceUrl(
this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
}
private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
return this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath));
return this.indicatorUtils.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath));
}
public addNumberIndicatorPath(url: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), source: string = 'stats-tool', jsonPath: UntypedFormArray = new UntypedFormArray([])) {
public addNumberIndicatorPath(url: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), source: string = 'stats-tool', jsonPath: UntypedFormArray = new UntypedFormArray([]), format: Format = "NUMBER") {
if (jsonPath.length === 0) {
jsonPath.push(this.fb.control('', Validators.required));
}
@ -476,8 +484,8 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]),
jsonPath: jsonPath,
result: this.fb.control(0, Validators.required),
// parameters: parameters,
source: this.fb.control(source, Validators.required)
source: this.fb.control(source, Validators.required),
format: this.fb.control(format, Validators.required)
}
));
let index = this.numberIndicatorPaths.length - 1;
@ -489,7 +497,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
this.numberIndicatorPaths.at(index).get('result').setValue(null);
if (this.numberIndicatorPaths.at(index).get('url').valid) {
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.statisticsService.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.statisticsService.numberSources.get(this.statisticsService.getNumberSource(value)));
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(value), value, this.stakeholder, this.numberIndicatorPaths.at(index).get('jsonPath').value, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(value)));
if (!this.isStakeholderParametersValid(indicatorPath)) {
// default profile
if (this.stakeholder.defaultId == null) {
@ -523,7 +531,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
})
);
this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => {
if (this.indicator.indicatorPaths[index]) {
this.indicator.indicatorPaths[index].jsonPath = value;
@ -543,7 +551,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.numberIndicatorPaths.at(index).get('source').disable();
}
}
public addChartIndicatorPath(value: string = '', parameters: UntypedFormArray = new UntypedFormArray([]), disableUrl: boolean = false, type: string = null) {
this.chartIndicatorPaths.push(this.fb.group({
url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]),
@ -558,7 +566,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value);
this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
if (this.chartIndicatorPaths.at(index).get('url').valid) {
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
if (!this.isStakeholderParametersValid(indicatorPath)) {
// default profile
if (this.stakeholder.defaultId == null) {
@ -586,13 +594,13 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}));
}
}
private isStakeholderParametersValid(indicatorPath: IndicatorPath) {
return !((indicatorPath.chartObject && Object.keys(indicatorPath.parameters).indexOf("index_id") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_name") == -1 && Object.keys(indicatorPath.parameters).indexOf("index_shortName") == -1)
|| (!indicatorPath.chartObject && indicatorPath.url.indexOf("index_id") == -1 && indicatorPath.url.indexOf("index_name") == -1 && (indicatorPath.url).indexOf("index_shortName") == -1));
}
private getJsonPathAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
let jsonPath = this.fb.array([]);
if (indicatorPath.jsonPath) {
@ -602,7 +610,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
return jsonPath;
}
private getParametersAsFormArray(indicatorPath: IndicatorPath): UntypedFormArray {
let parameters = this.fb.array([]);
if (indicatorPath.parameters) {
@ -624,7 +632,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
return parameters;
}
public editNumberIndicatorOpen(section: Section, id = null) {
this.urlParameterizedMessage = null;
this.section = section;
@ -645,7 +653,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
defaultId: this.fb.control(this.indicator.defaultId)
});
this.indicator.indicatorPaths.forEach(indicatorPath => {
this.addNumberIndicatorPath(this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), indicatorPath.parameters, indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath));
this.addNumberIndicatorPath(this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), indicatorPath.parameters, indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath), indicatorPath.format);
});
} else {
this.indicator = new Indicator('', '', '', 'number', 'small', 'small', "PUBLIC", []);
@ -685,7 +693,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editNumberModal.stayOpen = true;
this.editNumberModal.open();
}
public editChartIndicatorOpen(section: Section, id = null) {
this.urlParameterizedMessage = null;
this.urlSubscriptions.forEach(value => {
@ -752,7 +760,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editChartModal.stayOpen = true;
this.editChartModal.open();
}
saveIndicator() {
this.editing = true;
if (this.indicator.type === 'chart') {
@ -834,7 +842,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
}));
}
saveIndicators(sections) {
this.editing = true;
let path = [
@ -892,10 +900,10 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editing = false;
this.importLoading = false;
}));
}
reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) {
this.editing = true;
let path = [
@ -916,7 +924,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editing = false;
}));
}
hasDifference(index: number): boolean {
let hasDifference = false;
this.chartIndicatorPaths.at(index).value.parameters.forEach((parameter) => {
@ -928,22 +936,22 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !==
this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString();
}
public get isAdministrator(): boolean {
return Session.isPortalAdministrator(this.user);
}
public get isCurator(): boolean {
return this.isAdministrator || Session.isCurator(this.stakeholder.type, this.user);
}
refreshIndicator() {
this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart', true);
this.indicator.indicatorPaths.forEach(indicatorPath => {
indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
});
}
deleteIndicatorOpen(section: Section, indicatorId: string, type: string, childrenAction: string = null) {
this.indicatorChildrenActionOnDelete = null;
if (childrenAction == "delete") {
@ -951,7 +959,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
} else if (childrenAction == "disconnect") {
this.indicatorChildrenActionOnDelete = childrenAction;
}
this.section = section;
if (type === 'chart') {
this.index = this.charts.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
@ -967,7 +975,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.deleteModal.stayOpen = true;
this.deleteModal.open();
}
deleteIndicator() {
this.editing = true;
let path = [
@ -1018,7 +1026,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.deleteModal.cancel();
}));
}
changeIndicatorStatus(sectionId: string, indicator: Indicator, visibility: Visibility) {
this.editing = true;
let path = [
@ -1046,7 +1054,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editing = false;
}));
}
saveSection(focused: boolean, sectionControl: AbstractControl, index: number, type: IndicatorType = "chart") {
if (!focused && sectionControl.dirty) {
this.editing = true;
@ -1081,7 +1089,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}));
}
}
createSection(index = -1, type: IndicatorType = 'chart') {
this.editing = true;
this.section = new Section(type, null, null, this.stakeholder.alias);
@ -1123,7 +1131,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editing = false;
}));
}
// deleteNumberSectionOpen(section: Section, index: number) {
// this.section = section;
// this.index = index;
@ -1141,7 +1149,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
// this.deleteChartSectionModal.okButtonText = 'Yes';
// this.deleteChartSectionModal.open();
// }
deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) {
if (!this.editing && !section.defaultId) {
this.sectionTypeToDelete = type;
@ -1151,7 +1159,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
} else if (childrenAction == "disconnect") {
this.sectionChildrenActionOnDelete = childrenAction;
}
this.section = section;
this.index = index;
this.deleteSectionModal.alertTitle = 'Delete Section';
@ -1161,7 +1169,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.deleteSectionModal.open();
}
}
deleteSection() {
this.editing = true;
let path = [
@ -1197,21 +1205,22 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.deleteSectionModal.cancel();
}));
}
private checkForSchemaEnhancements(url: string) {
this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url);
}
migrateFromOldImportJsonFile(charts){
migrateFromOldImportJsonFile(charts) {
// first section contains numbers
// second contains charts
let hasNumbers = false;
for (let chart of charts) {
for (let chart of charts) {
if (chart['type'] == 'number') {
hasNumbers = true;
break;
}
}
let chartsSection = (hasNumbers?1:0); // no numbers: first sections contains charts
let chartsSection = (hasNumbers ? 1 : 0); // no numbers: first sections contains charts
for (let chart of charts) {
if (chart['sectionIndex'] == null) {
chart['sectionIndex'] = chart['type'] == 'chart' ? chartsSection : 0;
@ -1219,7 +1228,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
}
return charts;
}
importIndicatorsAndSave(charts: any[]) {
let sectionsToSave: Section[] = [];
let countIndicators = 0;
@ -1246,7 +1255,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
} else if (!chart.url) {
invalid_file_message = "No indicator url is specified.";
}
if (invalid_file_message) {
UIkit.notification(invalid_file_message, {
status: 'danger',
@ -1257,9 +1266,9 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.importLoading = false;
break;
}
if (chart.type == "chart") {
indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(chart.url), chart.url, chart.type, this.stakeholder);
indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.indicatorUtils.getChartSource(chart.url), chart.url, chart.type, this.stakeholder);
for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].charts) {
for (let chart of section.indicators) {
if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
@ -1267,11 +1276,11 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
exists = true;
}
}
}
} else if (chart.type == "number") {
indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.statisticsService.getNumberSource(chart.url), chart.url, this.stakeholder,
chart.jsonPath, this.statisticsService.numberSources.get(this.statisticsService.getNumberSource(chart.url)));
indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.indicatorUtils.getNumberSource(chart.url), chart.url, this.stakeholder,
chart.jsonPath, this.indicatorUtils.numberSources.get(this.indicatorUtils.getNumberSource(chart.url)));
for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.index].numbers) {
for (let chart of section.indicators) {
if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
@ -1279,7 +1288,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
exists = true;
}
}
}
}
if (!this.isStakeholderParametersValid(indicatorPath)) {
@ -1290,9 +1299,9 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
sectionsToSave[chart['sectionIndex']].indicators.push(i);
countIndicators++;
}
}
if (duplicates > 0) {
UIkit.notification(duplicates + " urls already exist and will not be imported!", {
status: 'warning',
@ -1313,7 +1322,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.editing = false;
this.importLoading = false;
} else if (sectionsToSave.length > 0 && countIndicators > 0) {
this.saveIndicators(sectionsToSave)
this.saveIndicators(sectionsToSave.filter(section => !!section));
}
if (sectionsToSave.length == 0 || countIndicators == 0) {
UIkit.notification(" No urls imported!", {
@ -1325,7 +1334,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.importLoading = false;
}
}
public exportIndicators(subcategoryIndex) {
this.editing = true;
let indicators = [];
@ -1338,7 +1347,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
"type": indicator.type, "name": indicator.name, "jsonPath": indicatorPath.jsonPath,
"description": indicator.description, "additionalDescription": indicator.additionalDescription,
"visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
"url": this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)),
"url": this.indicatorUtils.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)),
"sectionTitle": section.title,
"sectionType": section.type,
"sectionIndex": index
@ -1348,7 +1357,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
});
index++;
});
this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[subcategoryIndex].charts.forEach(section => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(indicatorPath => {
@ -1365,13 +1374,13 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
});
});
index++;
});
let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null;
let category = topic ? topic.categories[this.categoryIndex] : null;
let subCategory = category ? category.subCategories[this.subcategoryIndex] : null;
var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(indicators)], {type: 'application/json'}));
var a = window.document.createElement('a');
window.document.body.appendChild(a);
@ -1381,10 +1390,10 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
a.click();
window.URL.revokeObjectURL(jsonFileUrl);
a.remove(); // remove the element
this.editing = false;
}
fileChangeEvent(fileInput: any, index) {
this.index = index;
this.editing = true;
@ -1392,7 +1401,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.filesToUpload = <Array<File>>fileInput.target.files;
this.upload();
}
upload() {
if (this.filesToUpload.length == 0) {
console.error("There is no selected file to upload.");
@ -1417,11 +1426,11 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
return;
}
}
this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
let json_result = JSON.parse(result);
// validate file
if (!json_result || json_result.length == 0) {
UIkit.notification("Importing file is empty", {
@ -1445,7 +1454,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
this.importLoading = false;
});
}
makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
return new Promise((resolve, reject) => {
const formData: any = new FormData();
@ -1466,7 +1475,7 @@ export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterV
xhr.send(formData);
});
}
copyToClipboard(value) {
const tempBox = document.createElement('textarea');
tempBox.style.position = 'fixed';

View File

@ -27,7 +27,7 @@ export class AdminDashboardGuard implements CanActivate, CanActivateChild {
check(path: string, alias: string): Observable<boolean> | boolean {
let errorCode = LoginErrorCodes.NOT_LOGIN;
return zip(
this.userManagementService.getUserInfo() ,this.stakeholderService.getStakeholder(alias)
this.userManagementService.getUserInfo(), this.stakeholderService.getStakeholder(alias)
).pipe(take(1),map(res => {
if(res[0]) {
errorCode = LoginErrorCodes.NOT_ADMIN;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {properties} from "../../../environments/environment";
import {BehaviorSubject, Observable} from "rxjs";
import {CustomOptions} from "../../openaireLibrary/services/servicesUtils/customOptions.class";
import {map, tap} from "rxjs/operators";
import {Report} from "../../../cache-indicators";
@Injectable({
providedIn: 'root'
})
export class CacheIndicatorsService {
constructor(private http: HttpClient) {
}
createReport(alias: string) {
return this.http.post<any>(properties.domain + properties.baseLink + '/cache/' + alias, {}, CustomOptions.registryOptions())
.pipe(map(res => res.report));
}
getReport(alias: string) {
return this.http.get<any>(properties.domain + properties.baseLink + '/cache/' + alias, CustomOptions.registryOptions())
.pipe(map(res => res.report));
}
}

View File

@ -3,69 +3,23 @@ import {HttpClient} from "@angular/common/http";
import {Observable} from "rxjs";
import {SourceType} from "../../openaireLibrary/monitor/entities/stakeholder";
import {properties} from "../../../environments/environment";
import {IndicatorUtils} from "../indicator-utils";
@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class StatisticsService {
numberSources: Map<SourceType, string[]> = new Map<SourceType, string[]>();
chartSources: Map<SourceType, string[]> = new Map<SourceType, string[]>();
constructor(private http:HttpClient) {
this.numberSources.set('statistics', [properties.statisticsAPIURL]);
this.numberSources.set('search', [properties.searchAPIURLLAst]);
this.numberSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/","https://beta.services.openaire.eu/stats-tool/","https://services.openaire.eu/stats-tool/","https://services.openaire.eu/monitor-stats-tool/"]);
this.chartSources.set('stats-tool', [properties.monitorStatsFrameUrl, "http://marilyn.athenarc.gr:8080/stats-api/", "http://88.197.53.71:8080/stats-api/", "https://stats.madgik.di.uoa.gr/stats-api/","https://beta.services.openaire.eu/stats-tool/","https://services.openaire.eu/stats-tool/","https://services.openaire.eu/monitor-stats-tool/"]);
this.chartSources.set('old', [properties.statisticsFrameAPIURL]);
this.chartSources.set('image', [""]);
indicatorsUtils = new IndicatorUtils();
constructor(private http: HttpClient) {}
getNumbers(source: SourceType, url: string): Observable<any> {
if (source !== null) {
return this.http.get<any>(this.indicatorsUtils.getNumberUrl(source, url));
} else {
return this.http.get<any>(url);
}
}
getSourceType(source:string):SourceType{
let sourceType: SourceType = 'search';
this.numberSources.forEach((values, key) => {
if(key == source) {
sourceType = key;
}
});
return sourceType;
}
getNumbers(source: SourceType, url: string): Observable<any> {
if(source !== null) {
return this.http.get<any>(this.numberSources.get(source)[0] + url);
} else {
return this.http.get<any>(url);
}
}
getChartUrl(source: SourceType, url: string): string {
return this.chartSources.get(source)[0] + url;
}
getNumberUrl(source: string, url: string): string {
return this.numberSources.get(this.getSourceType(source))[0] + url;
}
getNumberSource(url: string): SourceType {
let source: SourceType = 'search';
this.numberSources.forEach((values, key) => {
values.forEach((value) => {
if(value !== '' && url.indexOf(value) !== -1) {
source = key;
}
});
});
return source;
}
getChartSource(url: string): SourceType {
let source: SourceType = 'image';
this.chartSources.forEach((values, key) => {
values.forEach((value) => {
if(value !== '' && url.indexOf(value) !== -1) {
source = key;
}
});
});
return source;
}
}

View File

@ -0,0 +1,20 @@
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {properties} from "../../../environments/environment";
import {Observable} from "rxjs";
import {CustomOptions} from "../../openaireLibrary/services/servicesUtils/customOptions.class";
import {map} from "rxjs/operators";
@Injectable({
providedIn: 'root'
})
export class StatsProfilesService {
constructor(private http: HttpClient) {
}
getStatsProfiles(): Observable<string[]> {
return this.http.get<any[]>(properties.monitorStatsFrameUrl + 'schema/profiles')
.pipe(map(profiles => profiles.map(profile => profile.name)));
}
}

262
src/cache-indicators.ts Normal file
View File

@ -0,0 +1,262 @@
import {IndicatorType, Stakeholder} from "./app/openaireLibrary/monitor/entities/stakeholder";
import axios from "axios";
import {IndicatorUtils} from "./app/utils/indicator-utils";
import {Composer} from "./app/openaireLibrary/utils/email/composer";
import {properties} from "./environments/environment";
import {error} from "protractor";
export interface CacheItem {
reportId: string,
type: IndicatorType,
url: string
}
export class Report {
creator: string;
name: string;
success: number;
errors: {
url: string,
status: number
}[];
total: number;
completed: boolean;
percentage: number
constructor(total: number, name: string, creator: string) {
this.creator = creator;
this.name = name;
this.success = 0;
this.errors = [];
this.total = total;
this.completed = false;
}
setPercentage() {
this.percentage = Math.floor((this.success + this.errors.length) / this.total * 100);
}
}
export class CacheIndicators {
private static BATCH_SIZE = 10;
private reports: Map<string, Report> = new Map<string, Report>();
private queue: CacheItem[] = [];
private process: Promise<void>;
private isFinished: boolean = true;
stakeholderToCacheItems(stakeholder: Stakeholder) {
let cacheItems: CacheItem[] = [];
let indicatorUtils = new IndicatorUtils();
stakeholder.topics.forEach(topic => {
topic.categories.forEach(category => {
category.subCategories.forEach(subCategory => {
subCategory.numbers.forEach(section => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(indicatorPath => {
let url = indicatorUtils.getNumberUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath));
cacheItems.push({
reportId: stakeholder._id,
type: 'number',
url: url
});
});
});
});
subCategory.charts.forEach(section => {
section.indicators.forEach(indicator => {
indicator.indicatorPaths.forEach(indicatorPath => {
let url = indicatorUtils.getChartUrl(indicatorPath.source, indicatorUtils.getFullUrl(stakeholder, indicatorPath));
cacheItems.push({
reportId: stakeholder._id,
type: 'chart',
url: url
});
});
});
});
});
});
});
return cacheItems;
}
public exists(id: string) {
return this.reports.has(id);
}
public completed(id: string) {
return !this.exists(id) || this.reports.get(id).completed;
}
public createReport(id: string, cacheItems: CacheItem[], name: string, creator: string) {
let report = new Report(cacheItems.length, name, creator);
this.reports.set(id, report);
this.addItemsToQueue(cacheItems);
return report;
}
public getReport(id: string) {
return this.reports.get(id);
}
private async processQueue() {
this.isFinished = false;
while (this.queue.length > 0) {
let batch = this.queue.splice(0, CacheIndicators.BATCH_SIZE);
await this.processBatch(batch);
}
}
private async processBatch(batch: CacheItem[]) {
let promises: Promise<any>[] = [];
let ids = new Set<string>();
batch.forEach(item => {
let promise;
ids.add(item.reportId);
if (item.type === 'chart') {
let [url, json] = item.url.split('?json=');
json = decodeURIComponent(json);
json = statsToolParser(JSON.parse(json));
promise = axios.post(url, json);
} else {
promise = axios.get(item.url);
}
promises.push(promise.then(response => {
let report = this.reports.get(item.reportId);
if (report) {
report.success++;
report.setPercentage();
}
return response;
}).catch(error => {
let report = this.reports.get(item.reportId);
if (report) {
report.errors.push({url: item.url, status: error.response.status});
report.setPercentage();
}
return error.response;
}));
});
await Promise.all(promises);
ids.forEach(id => {
let report = this.reports.get(id);
if (report?.percentage === 100) {
report.completed = true;
this.sendEmail(report);
}
});
}
private addItemsToQueue(cacheItems: CacheItem[]) {
cacheItems.forEach(item => {
this.queue.push(item);
});
if (this.isFinished) {
this.processQueue().then(() => {
this.isFinished = true;
});
}
}
sendEmail(report: Report) {
let email = Composer.composeEmailToReportCachingProcess(report);
axios.post(properties.adminToolsAPIURL + "sendMail/", email).catch(error => {
console.error(error);
});
}
}
export function statsToolParser(dataJSONobj: any): any {
let RequestInfoObj = Object.assign({});
switch (dataJSONobj.library) {
case "GoogleCharts":
//Pass the Chart library to ChartDataFormatter
RequestInfoObj.library = dataJSONobj.library;
RequestInfoObj.orderBy = dataJSONobj.orderBy;
//Create ChartInfo Object Array
RequestInfoObj.chartsInfo = [];
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
//along with the requested Chart type
RequestInfoObj.chartsInfo = dataJSONobj.chartDescription.queriesInfo;
break;
case "eCharts":
//Pass the Chart library to ChartDataFormatter
RequestInfoObj.library = dataJSONobj.library;
RequestInfoObj.orderBy = dataJSONobj.orderBy;
//Create ChartInfo Object Array
RequestInfoObj.chartsInfo = [];
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
//along with the requested Chart type
for (let index = 0; index < dataJSONobj.chartDescription.queries.length; index++) {
let element = dataJSONobj.chartDescription.queries[index];
var ChartInfoObj = Object.assign({});
if (element.type === undefined)
ChartInfoObj.type = dataJSONobj.chartDescription.series[index].type;
else
ChartInfoObj.type = element.type;
if (element.name === undefined)
ChartInfoObj.name = null;
else
ChartInfoObj.name = element.name;
ChartInfoObj.query = element.query;
RequestInfoObj.chartsInfo.push(ChartInfoObj);
}
break;
case "HighCharts":
RequestInfoObj.library = dataJSONobj.library;
RequestInfoObj.orderBy = dataJSONobj.orderBy;
//Pass the Chart type to ChartDataFormatter
var defaultType = dataJSONobj.chartDescription.chart.type;
//Create ChartInfo Object Array
RequestInfoObj.chartsInfo = [];
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
//along with the requested Chart type
dataJSONobj.chartDescription.queries.forEach(element => {
var ChartInfoObj = Object.assign({});
if (element.type === undefined)
ChartInfoObj.type = defaultType;
else
ChartInfoObj.type = element.type;
if (element.name === undefined)
ChartInfoObj.name = null;
else
ChartInfoObj.name = element.name;
ChartInfoObj.query = element.query;
RequestInfoObj.chartsInfo.push(ChartInfoObj);
});
break;
case "HighMaps":
RequestInfoObj.library = dataJSONobj.library;
//Create ChartInfo Object Array
RequestInfoObj.chartsInfo = [];
//Create ChartInfo and pass the Chart data queries to ChartDataFormatter
dataJSONobj.mapDescription.queries.forEach(element => {
var ChartInfoObj = Object.assign({});
if (element.name === undefined)
ChartInfoObj.name = null;
else
ChartInfoObj.name = element.name;
ChartInfoObj.query = element.query;
RequestInfoObj.chartsInfo.push(ChartInfoObj);
});
break;
default:
console.log("Unsupported Library: " + dataJSONobj.library);
}
return RequestInfoObj;
}

View File

@ -62,7 +62,6 @@ export let properties: EnvProperties = {
cookieDomain: ".di.uoa.gr",
feedbackmail: "openaire.test@gmail.com",
cacheUrl: "http://scoobydoo.di.uoa.gr:3000/get?url=",
// monitorServiceAPIURL: "https://services.openaire.eu/uoa-monitor-service",
monitorServiceAPIURL: "http://duffy.di.uoa.gr:19380/uoa-monitor-service",
adminToolsAPIURL: "http://duffy.di.uoa.gr:19380/uoa-monitor-service/",
notificationsAPIURL: "http://duffy.di.uoa.gr:19380/uoa-monitor-service/notification/",
@ -75,7 +74,7 @@ export let properties: EnvProperties = {
csvLimit: 2000,
pagingLimit: 20,
resultsPerPage: 10,
baseLink: "/dashboard",
baseLink: "/",
domain: "http://mpagasas.di.uoa.gr:4600",
searchLinkToResult: "/search/result?id=",
searchLinkToPublication: "/search/publication?articleId=",

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/dashboard"/>
<base href="/"/>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta name="description" content="OpenAIRE Monitor, funder, funding, research, "/>
<meta property="og:description" content="OpenAIRE Monitor, funder, funding"/>