Project

General

Profile

1
import {
2
  AfterViewInit,
3
  ChangeDetectorRef,
4
  Component,
5
  Input,
6
  OnChanges,
7
  OnDestroy,
8
  OnInit,
9
  SimpleChanges,
10
  ViewChild
11
} from "@angular/core";
12
import {
13
  Indicator,
14
  IndicatorPath, IndicatorSize,
15
  IndicatorType,
16
  Section,
17
  Stakeholder,
18
  Visibility
19
} from "../openaireLibrary/monitor/entities/stakeholder";
20
import {IndicatorUtils, StakeholderUtils} from "../utils/indicator-utils";
21
import {FormArray, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms";
22
import {AlertModal} from "../openaireLibrary/utils/modal/alert";
23
import {StatisticsService} from "../utils/services/statistics.service";
24
import {HelperFunctions} from "../openaireLibrary/utils/HelperFunctions.class";
25
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
26
import {Reorder, StakeholderService} from "../openaireLibrary/monitor/services/stakeholder.service";
27
import {EnvProperties} from "../openaireLibrary/utils/properties/env-properties";
28
import {Subscriber} from "rxjs";
29
import {LayoutService} from "../openaireLibrary/dashboard/sharedComponents/sidebar/layout.service";
30
import {Router} from "@angular/router";
31
import {Role, Session, User} from "../openaireLibrary/login/utils/helper.class";
32
import {UserManagementService} from "../openaireLibrary/services/user-management.service";
33
import {StringUtils} from "../openaireLibrary/utils/string-utils.class";
34
import {Notification} from "../openaireLibrary/notifications/notifications";
35
import {NotificationUtils} from "../openaireLibrary/notifications/notification-utils";
36
import {NotifyFormComponent} from "../openaireLibrary/notifications/notify-form/notify-form.component";
37

    
38
declare var UIkit;
39

    
40
@Component({
41
  selector: 'indicators',
42
  templateUrl: './indicators.component.html',
43
  styleUrls: ['indicators.component.css']
44
})
45
export class IndicatorsComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
46
  filesToUpload: Array<File>;
47
  errorMessage = "";
48
  enableUpload: boolean = true;
49

    
50
  @Input()
51
  public properties: EnvProperties = null;
52
  @Input()
53
  public topicIndex: number = 0;
54
  @Input()
55
  public categoryIndex: number = 0;
56
  @Input()
57
  public subcategoryIndex: number = 0;
58
  public stakeholder: Stakeholder = null;
59
  public user: User = null;
60
  public preview: string;
61
  public indicatorUtils: IndicatorUtils = new IndicatorUtils();
62
  public stakeholderUtils: StakeholderUtils = new StakeholderUtils();
63
  public numberIndicatorFb: FormGroup;
64
  public chartIndicatorFb: FormGroup;
65
  public chartSections: FormArray;
66
  public numberSections: FormArray;
67
  /**
68
   * Editable indicator
69
   */
70
  public section: Section;
71
  public indicator: Indicator;
72
  public index: number = -1;
73
  /**
74
   * Displayed chart and numbers base on Top filters
75
   */
76
  public displayCharts: Section[] = [];
77
  public displayNumbers: Section[] = [];
78
  /**
79
   * Top filters
80
   */
81
  @Input()
82
  public filters: FormGroup;
83
  public editing: boolean = false;
84
  /** Safe Urls*/
85
  public safeUrls: Map<string, SafeResourceUrl> = new Map<string, SafeResourceUrl>([]);
86
  public numberResults: Map<string, number> = new Map<string, number>();
87
  private subscriptions: any[] = [];
88
  private urlSubscriptions: any[] = [];
89
  @ViewChild('editChartModal', { static: true }) editChartModal: AlertModal;
90
  @ViewChild('editNumberModal', { static: true }) editNumberModal: AlertModal;
91
  @ViewChild('deleteModal', { static: true }) deleteModal: AlertModal;
92
  //@ViewChild('deleteAllModal') deleteAllModal: AlertModal;
93
  //@ViewChild('deleteAndDisconnectModal') deleteAndDisconnectModal: AlertModal;
94
  //@ViewChild('deleteChartSectionModal') deleteChartSectionModal: AlertModal;
95
  //@ViewChild('deleteNumberSectionModal') deleteNumberSectionModal: AlertModal;
96
  @ViewChild('deleteSectionModal', { static: true }) deleteSectionModal: AlertModal;
97
  public sectionTypeToDelete: string;
98
  public sectionChildrenActionOnDelete: string;
99
  public indicatorChildrenActionOnDelete: string;
100
  private firstLoad: boolean = true;
101
  
102
  urlParameterizedMessage = null;
103
  showCheckForSchemaEnhancements:boolean = false;
104
  private notification: Notification;
105
  @ViewChild('editNumberNotify', { static: true }) editNumberNotify: NotifyFormComponent;
106
  @ViewChild('editChartNotify', { static: true }) editChartNotify: NotifyFormComponent;
107
  @ViewChild('deleteNotify', { static: true }) deleteNotify: NotifyFormComponent;
108
  constructor(private layoutService: LayoutService,
109
              private stakeholderService: StakeholderService,
110
              private statisticsService: StatisticsService,
111
              private userManagementService: UserManagementService,
112
              private fb: FormBuilder,
113
              private router: Router,
114
              private cdr: ChangeDetectorRef,
115
              private sanitizer: DomSanitizer) {
116
    this.filesToUpload = [];
117
  }
118
  
119
  ngOnInit(): void {
120
    this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
121
      this.user = user;
122
    }));
123
    this.subscriptions.push(this.stakeholderService.getStakeholderAsObservable().subscribe(stakeholder => {
124
      this.stakeholder = stakeholder;
125
      if (this.stakeholder && this.firstLoad) {
126
        this.buildFilters();
127
        this.buildSections();
128
        this.filterCharts();
129
        this.filterNumbers();
130
        this.setPreview();
131
        this.firstLoad = false;
132
      }
133
    }));
134
  }
135
  
136
  ngOnDestroy(): void {
137
    this.subscriptions.forEach(value => {
138
      if (value instanceof Subscriber) {
139
        value.unsubscribe();
140
      } else if (value instanceof Function) {
141
        value();
142
      }
143
    });
144
    this.urlSubscriptions.forEach(value => {
145
      if (value instanceof Subscriber) {
146
        value.unsubscribe();
147
      }
148
    });
149
  }
150
  
151
  ngAfterViewInit(): void {
152
    this.initReorder();
153
  }
154
  
155
  ngOnChanges(changes: SimpleChanges): void {
156
    if (this.canEdit) {
157
      if (changes.topicIndex || changes.categoryIndex || changes.subcategoryIndex) {
158
        this.buildSections();
159
        this.buildFilters();
160
        this.initReorder();
161
      }
162
      this.filterCharts();
163
      this.filterNumbers();
164
    }
165
    this.setPreview();
166
  }
167
  
168
  setNumberIndicators() {
169
    this.numberResults.clear();
170
    let urls: Map<string, [number, number][]> = new Map<string, [number, number][]>();
171
    this.numbers.forEach((section, i) => {
172
      section.indicators.forEach((number, j) => {
173
        let url = this.indicatorUtils.getFullUrlWithFilters(this.stakeholder, number.indicatorPaths[0]);
174
        const pair = JSON.stringify([number.indicatorPaths[0].source, url]);
175
        const indexes = urls.get(pair) ? urls.get(pair) : [];
176
        indexes.push([i, j]);
177
        urls.set(pair, indexes);
178
      });
179
    });
180
    urls.forEach((indexes, pair) => {
181
      pair = JSON.parse(pair);
182
      this.subscriptions.push(this.statisticsService.getNumbers(this.statisticsService.getSourceType(pair[0]), pair[1]).subscribe(response => {
183
        indexes.forEach(([i, j]) => {
184
          let result = JSON.parse(JSON.stringify(response));
185
          this.numbers[i].indicators[j].indicatorPaths[0].jsonPath.forEach(jsonPath => {
186
            if (result) {
187
              result = result[jsonPath];
188
            }
189
          });
190
          if (typeof result === 'string' || typeof result === 'number') {
191
            result = Number(result);
192
            if (result === Number.NaN) {
193
              result = 0;
194
            }
195
          } else {
196
            result = 0;
197
          }
198
          this.numberResults.set(i + '-' + j, result);
199
        });
200
      }));
201
    });
202
  }
203
  
204
  setPreview() {
205
    if (this.stakeholder) {
206
      this.preview = '/' + this.stakeholder.alias;
207
      if (this.stakeholder.topics[this.topicIndex]) {
208
        this.preview = '/' + this.stakeholder.alias + '/' + this.stakeholder.topics[this.topicIndex].alias;
209
        if (this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]) {
210
          this.preview += '/' + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].alias;
211
          if (this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
212
            this.preview += '/' + this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].alias;
213
          }
214
        }
215
      }
216
    }
217
  }
218
  
219
  initReorder() {
220
    this.subscriptions.forEach(value => {
221
      if (value instanceof Function) {
222
        value();
223
      }
224
    });
225
    if (document !== undefined) {
226
      let callback = (list, type: IndicatorType, action: 'moved' | 'added' | 'removed'): void => {
227
        let items: HTMLCollection = list.current.children;
228
        let reordered = [];
229
        for (let i = 0; i < items.length; i++) {
230
          if (items.item(i).id) {
231
            reordered.push(items.item(i).id);
232
          }
233
        }
234
        let reorder: Reorder = {
235
          action: action,
236
          target: list.detail[1].id,
237
          ids: reordered
238
        }
239
        this.reorderIndicators(list.current.id.toString().split('-')[1], type, reorder);
240
      };
241
      this.numbers.forEach((section) => {
242
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#number-' + section._id, (list): void => {
243
          callback(list, "number", 'moved');
244
        }));
245
        this.subscriptions.push(UIkit.util.on(document, 'added', '#number-' + section._id, (list): void => {
246
          callback(list, "number", 'added');
247
        }));
248
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#number-' + section._id, (list): void => {
249
          callback(list, "number", 'removed');
250
        }));
251
      });
252
      this.charts.forEach((section) => {
253
        this.subscriptions.push(UIkit.util.on(document, 'moved', '#chart-' + section._id, (list): void => {
254
          callback(list, "chart", 'moved');
255
        }));
256
        this.subscriptions.push(UIkit.util.on(document, 'added', '#chart-' + section._id, (list): void => {
257
          callback(list, "chart", 'added');
258
        }));
259
        this.subscriptions.push(UIkit.util.on(document, 'removed', '#chart-' + section._id, (list): void => {
260
          callback(list, "chart", 'removed');
261
        }));
262
      });
263
    }
264
  }
265
  
266
  hide(element: any) {
267
    UIkit.dropdown(element).hide();
268
  }
269
  
270
  private buildFilters() {
271
    this.subscriptions.push(this.filters.get('chartType').valueChanges.subscribe(value => {
272
      this.onChartTypeChange(value);
273
    }));
274
    
275
    this.subscriptions.push(this.filters.get('status').valueChanges.subscribe(value => {
276
      this.onStatusChange(value);
277
    }));
278
    this.subscriptions.push(this.filters.get('keyword').valueChanges.subscribe(value => {
279
      this.onKeywordChange(value);
280
    }));
281
  }
282
  
283
  private buildSections() {
284
    this.numberSections = this.fb.array([]);
285
    this.numbers.forEach(section => {
286
      this.numberSections.push(this.fb.group({
287
        _id: this.fb.control(section._id),
288
        title: this.fb.control(section.title),
289
        creationDate: this.fb.control(section.creationDate),
290
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
291
        defaultId: this.fb.control(section.defaultId),
292
        type: this.fb.control(section.type),
293
        indicators: this.fb.control(section.indicators)
294
      }));
295
    });
296
    this.chartSections = this.fb.array([]);
297
    this.charts.forEach(section => {
298
      this.chartSections.push(this.fb.group({
299
        _id: this.fb.control(section._id),
300
        title: this.fb.control(section.title),
301
        creationDate: this.fb.control(section.creationDate),
302
        stakeholderAlias: this.fb.control(section.stakeholderAlias),
303
        defaultId: this.fb.control(section.defaultId),
304
        type: this.fb.control(section.type),
305
        indicators: this.fb.control(section.indicators)
306
      }));
307
    });
308
  }
309
  
310
  filterCharts() {
311
    this.displayCharts = this.filterChartType(
312
      this.filterStatus(this.filterByKeyword(HelperFunctions.copy(this.charts), this.filters.value.keyword),
313
        this.filters.value.status),
314
      this.filters.value.chartType
315
    );
316
    this.displayCharts.forEach(section => {
317
      section.indicators.forEach(indicator => {
318
        indicator.indicatorPaths.forEach(indicatorPath => {
319
          let url = this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath);
320
          if (!this.safeUrls.get('url')) {
321
            indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
322
            this.safeUrls.set(url, indicatorPath.safeResourceUrl);
323
          }
324
        });
325
      })
326
    });
327
    this.buildSections();
328
  }
329
  
330
  filterNumbers() {
331
    this.displayNumbers = this.filterStatus(
332
      this.filterByKeyword(HelperFunctions.copy(this.numbers), this.filters.value.keyword),
333
      this.filters.value.status);
334
    this.buildSections();
335
    this.setNumberIndicators();
336
  }
337
  
338
  onChartTypeChange(value) {
339
    this.displayCharts = this.filterChartType(HelperFunctions.copy(this.charts), value);
340
  }
341
  
342
  
343
  onStatusChange(value) {
344
    this.displayCharts = this.filterStatus(HelperFunctions.copy(this.charts), value);
345
    this.displayNumbers = this.filterStatus(HelperFunctions.copy(this.numbers), value);
346
  }
347
  
348
  onKeywordChange(value) {
349
    this.displayCharts = this.filterByKeyword(HelperFunctions.copy(this.charts), value);
350
    this.displayNumbers = this.filterByKeyword(HelperFunctions.copy(this.numbers), value);
351
  }
352
  
353
  private filterChartType(sections: Section[], value): Section[] {
354
    if (value !== 'all') {
355
      sections.forEach(section =>
356
        section.indicators = section.indicators.filter(indicator =>
357
          indicator.indicatorPaths.filter(indicatorPath => indicatorPath.type === value).length > 0));
358
    }
359
    return sections;
360
  }
361
  
362
  
363
  private filterStatus(sections: Section[], value): Section[] {
364
    if (value !== 'all') {
365
      sections.forEach(section =>
366
        section.indicators = section.indicators.filter(indicator => indicator.visibility === value));
367
    }
368
    return sections;
369
  }
370
  
371
  private filterByKeyword(sections: Section[], value): Section[] {
372
    if (value !== null && value !== '') {
373
      sections.forEach(section =>
374
        section.indicators = section.indicators.filter(indicator => (indicator.name && indicator.name.toLowerCase().includes(value.toLowerCase()))
375
          || (indicator.description && indicator.description.toLowerCase().includes(value.toLowerCase()))
376
          || (indicator.additionalDescription && indicator.additionalDescription.toLowerCase().includes(value.toLowerCase()))
377
          || indicator.indicatorPaths.filter(indicatorPath => (indicatorPath.parameters && indicatorPath.parameters.title &&
378
            indicatorPath.parameters.title.includes(value.toLowerCase()))).length > 0));
379
    }
380
    return sections;
381
  }
382
  
383
  get charts(): Section[] {
384
    if (this.stakeholder.topics[this.topicIndex] &&
385
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
386
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
387
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
388
    } else {
389
      return [];
390
    }
391
  }
392
  
393
  set charts(sections: Section[]) {
394
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts = sections;
395
  }
396
  
397
  get numbers(): Section[] {
398
    if (this.stakeholder.topics[this.topicIndex] &&
399
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
400
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]) {
401
      return this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
402
    } else {
403
      return [];
404
    }
405
  }
406
  
407
  set numbers(sections: Section[]) {
408
    this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = sections;
409
  }
410
  
411
  get open(): boolean {
412
    return this.layoutService.open;
413
  }
414
  
415
  get canReorder(): boolean {
416
    return this.filters.value.chartType === 'all' &&
417
      this.filters.value.status === 'all' && this.filters.value.keyword === '' && !this.editing;
418
  }
419
  
420
  get canEdit() {
421
    return this.stakeholder &&
422
      this.stakeholder.topics[this.topicIndex] &&
423
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex] &&
424
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex];
425
  }
426
  
427
  public get numberIndicatorPaths(): FormArray {
428
    return this.numberIndicatorFb.get('indicatorPaths') as FormArray;
429
  }
430
  
431
  public get chartIndicatorPaths(): FormArray {
432
    return this.chartIndicatorFb.get('indicatorPaths') as FormArray;
433
  }
434
  
435
  public getNumberClassBySize(size: IndicatorSize) {
436
    if (size === 'small') {
437
      return 'uk-width-1-4@xl uk-width-1-3@l uk-width-1-2@m uk-width-1-1';
438
    } else if (size === 'medium') {
439
      return 'uk-width-1-3@l uk-width-1-2@m uk-width-1-1';
440
    } else {
441
      return 'uk-width-1-2@l uk-width-1-1@m uk-width-1-1';
442
    }
443
  }
444
  
445
  public getChartClassBySize(size: IndicatorSize) {
446
    if (size === 'small') {
447
      return 'uk-width-1-3@xl uk-width-1-2@m uk-width-1-1';
448
    } else if (size === 'medium') {
449
      return 'uk-width-1-2@l uk-width-1-1';
450
    } else {
451
      return 'uk-width-1-1';
452
    }
453
  }
454
  
455
  public addJsonPath(index: number) {
456
    if (index == 0 && this.getJsonPath(index).getRawValue()[index].indexOf(",") != -1) {
457
      //if in the first path there are more than one paths comma separated, split them and autogenerate the forms
458
      let paths = this.getJsonPath(index).getRawValue()[index].split(",");
459
      for (let i = 0; i < paths.length; i++) {
460
        if (i != 0) {
461
          this.getJsonPath(index).push(this.fb.control('', Validators.required));
462
        }
463
      }
464
      this.getJsonPath(index).setValue(paths)
465
    } else {
466
      this.getJsonPath(index).push(this.fb.control('', Validators.required));
467
    }
468
  }
469
  
470
  public removeJsonPath(i: number, j: number) {
471
    if (this.getJsonPath(i).enabled) {
472
      this.getJsonPath(i).removeAt(j);
473
    }
474
  }
475
  
476
  public validateJsonPath(index: number, dirty: boolean = false) {
477
    let indicatorPath: FormGroup = <FormGroup>this.numberIndicatorPaths.at(index);
478
    if (this.indicator.defaultId === null) {
479
      this.getJsonPath(index).disable();
480
    }
481
    indicatorPath.get('result').setErrors({validating: true});
482
    this.subscriptions.push(this.statisticsService.getNumbers(null, indicatorPath.get('url').value).subscribe(response => {
483
      let result = JSON.parse(JSON.stringify(response));
484
      this.getJsonPath(index).controls.forEach(jsonPath => {
485
        if (result) {
486
          result = result[jsonPath.value];
487
        }
488
      });
489
      setTimeout(() => {
490
        if (this.indicator.defaultId === null) {
491
          this.getJsonPath(index).enable();
492
          if (dirty) {
493
            this.getJsonPath(index).markAsDirty();
494
          }
495
        }
496
        indicatorPath.get('result').setErrors(null);
497
        if (typeof result === 'string' || typeof result === 'number') {
498
          result = Number(result);
499
          if (result !== Number.NaN) {
500
            indicatorPath.get('result').setValue(result);
501
          } else {
502
            indicatorPath.get('result').setValue(0);
503
          }
504
        } else {
505
          indicatorPath.get('result').setValue(0);
506
        }
507
      }, 500);
508
    }, error => {
509
      setTimeout(() => {
510
        if (this.indicator.defaultId === null) {
511
          this.getJsonPath(index).enable();
512
          if (dirty) {
513
            this.getJsonPath(index).markAsDirty();
514
          }
515
        }
516
        indicatorPath.get('result').setErrors(null);
517
        indicatorPath.get('result').setValue(0);
518
      }, 500);
519
    }));
520
  }
521
  
522
  public getJsonPath(index: number): FormArray {
523
    return this.numberIndicatorPaths.at(index).get('jsonPath') as FormArray;
524
  }
525
  
526
  public getCurrentJsonPath(index: number): string[] {
527
    return this.section.indicators[this.index].indicatorPaths[index].jsonPath;
528
  }
529
  
530
  public getParameters(index: number): FormArray {
531
    return this.chartIndicatorPaths.at(index).get('parameters') as FormArray;
532
  }
533
  
534
  public getParameter(index: number, key: string): FormControl {
535
    return this.getParameters(index).controls.filter(control => control.value.key === key)[0] as FormControl;
536
  }
537
  
538
  private getSecureUrlByStakeHolder(indicatorPath: IndicatorPath) {
539
    return this.sanitizer.bypassSecurityTrustResourceUrl(
540
      this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
541
  }
542
  
543
  private getUrlByStakeHolder(indicatorPath: IndicatorPath) {
544
    return this.statisticsService.getChartUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath));
545
  }
546
  
547
  public addNumberIndicatorPath(url: string = '', parameters: FormArray = new FormArray([]), source: string = 'stats-tool', jsonPath: FormArray = new FormArray([])) {
548
    if (jsonPath.length === 0) {
549
      jsonPath.push(this.fb.control('', Validators.required));
550
    }
551
    this.numberIndicatorPaths.push(this.fb.group({
552
        url: this.fb.control(url, [Validators.required, StringUtils.urlValidator()]),
553
        jsonPath: jsonPath,
554
        result: this.fb.control(0, Validators.required),
555
        // parameters: parameters,
556
        source: this.fb.control(source, Validators.required)
557
      }
558
    ));
559
    let index = this.numberIndicatorPaths.length - 1;
560
    if (this.numberIndicatorPaths.at(index).get('url').valid) {
561
      this.validateJsonPath(index);
562
      this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
563
    }
564
    if (this.indicator.defaultId === null) {
565
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
566
          this.numberIndicatorPaths.at(index).get('result').setValue(null);
567
          if (this.numberIndicatorPaths.at(index).get('url').valid) {
568
            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)));
569
            if (!this.isStakeholderParametersValid(indicatorPath)) {
570
              // default profile
571
              if (this.stakeholder.defaultId == null) {
572
                this.urlParameterizedMessage = "This indicator couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
573
              } else {
574
                this.urlParameterizedMessage = "This indicator couldn't be generated properly. Please make sure chart data is for the current stakeholder."
575
              }
576
            } else {
577
              this.urlParameterizedMessage = null;
578
            }
579
            this.checkForSchemaEnhancements(this.numberIndicatorPaths.at(index).get('url').value);
580
            if (this.indicator.indicatorPaths[index]) {
581
              this.indicator.indicatorPaths[index] = indicatorPath;
582
            } else {
583
              this.indicator.indicatorPaths.push(indicatorPath);
584
            }
585
            if (indicatorPath.source) {
586
              this.numberIndicatorPaths.at(index).get('source').setValue(indicatorPath.source);
587
            }
588
            if (indicatorPath.jsonPath.length > 1 && this.getJsonPath(index).length == 1) {
589
              let paths = indicatorPath.jsonPath;
590
              for (let i = 0; i < paths.length; i++) {
591
                if (i == this.getJsonPath(index).length) {
592
                  this.getJsonPath(index).push(this.fb.control('', Validators.required));
593
                }
594
              }
595
              this.getJsonPath(index).setValue(paths)
596
            }
597
          } else {
598
            this.urlParameterizedMessage = null;
599
          }
600
        })
601
      );
602
      
603
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('jsonPath').valueChanges.subscribe(value => {
604
          if (this.indicator.indicatorPaths[index]) {
605
            this.indicator.indicatorPaths[index].jsonPath = value;
606
          }
607
          this.numberIndicatorPaths.at(index).get('result').setValue(null);
608
        })
609
      );
610
      this.subscriptions.push(this.numberIndicatorPaths.at(index).get('source').valueChanges.subscribe(value => {
611
          if (this.indicator.indicatorPaths[index]) {
612
            this.indicator.indicatorPaths[index].source = value;
613
          }
614
        })
615
      );
616
    } else {
617
      this.numberIndicatorPaths.at(index).get('url').disable();
618
      this.numberIndicatorPaths.at(index).get('jsonPath').disable();
619
      this.numberIndicatorPaths.at(index).get('source').disable();
620
    }
621
  }
622
  
623
  public addChartIndicatorPath(value: string = '', parameters: FormArray = new FormArray([]), disableUrl: boolean = false, type: string = null) {
624
    this.chartIndicatorPaths.push(this.fb.group({
625
        url: this.fb.control(value, [Validators.required, StringUtils.urlValidator()]),
626
        parameters: parameters,
627
        type: this.fb.control(type)
628
      }
629
    ));
630
    let index = this.chartIndicatorPaths.length - 1;
631
    if (disableUrl) {
632
      this.chartIndicatorPaths.at(index).get('url').disable();
633
    } else {
634
      this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value);
635
      this.urlSubscriptions.push(this.chartIndicatorPaths.at(index).get('url').valueChanges.subscribe(value => {
636
        if (this.chartIndicatorPaths.at(index).get('url').valid) {
637
          let indicatorPath: IndicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(value), value, this.chartIndicatorPaths.at(index).get('type').value, this.stakeholder);
638
          if (!this.isStakeholderParametersValid(indicatorPath)) {
639
            // default profile
640
            if (this.stakeholder.defaultId == null) {
641
              this.urlParameterizedMessage = "This chart couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly."
642
            } else {
643
              this.urlParameterizedMessage = "This chart couldn't be generated properly. Please make sure chart data is for the current stakeholder."
644
            }
645
          } else {
646
            this.urlParameterizedMessage = null;
647
          }
648
          this.checkForSchemaEnhancements(this.chartIndicatorPaths.at(index).get('url').value);
649
          (this.chartIndicatorPaths.at(index) as FormGroup).get('type').setValue(indicatorPath.type);
650
          let parameters = this.getParametersAsFormArray(indicatorPath);
651
          (this.chartIndicatorPaths.at(index) as FormGroup).setControl('parameters', parameters);
652
          if (!this.indicator.indicatorPaths[index]) {
653
            this.indicator.indicatorPaths[index] = indicatorPath;
654
            this.indicator.indicatorPaths[index].safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
655
          } else {
656
            indicatorPath.safeResourceUrl = this.indicator.indicatorPaths[index].safeResourceUrl;
657
            this.indicator.indicatorPaths[index] = indicatorPath;
658
          }
659
        } else {
660
          this.urlParameterizedMessage = null;
661
        }
662
      }));
663
    }
664
  }
665
  private  isStakeholderParametersValid(indicatorPath:IndicatorPath) {
666
    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)
667
    || (!indicatorPath.chartObject && indicatorPath.url.indexOf("index_id") == -1 && indicatorPath.url.indexOf("index_name") == -1 && (indicatorPath.url).indexOf("index_shortName") == -1));
668

    
669
  }
670
  private getJsonPathAsFormArray(indicatorPath: IndicatorPath): FormArray {
671
    let jsonPath = this.fb.array([]);
672
    if (indicatorPath.jsonPath) {
673
      indicatorPath.jsonPath.forEach(path => {
674
        jsonPath.push(this.fb.control(path, Validators.required));
675
      });
676
    }
677
    return jsonPath;
678
  }
679
  
680
  private getParametersAsFormArray(indicatorPath: IndicatorPath): FormArray {
681
    let parameters = this.fb.array([]);
682
    if (indicatorPath.parameters) {
683
      Object.keys(indicatorPath.parameters).forEach(key => {
684
        if (this.indicatorUtils.ignoredParameters.indexOf(key) === -1) {
685
          if (this.indicatorUtils.parametersValidators.has(key)) {
686
            parameters.push(this.fb.group({
687
              key: this.fb.control(key),
688
              value: this.fb.control(indicatorPath.parameters[key], this.indicatorUtils.parametersValidators.get(key))
689
            }));
690
          } else {
691
            parameters.push(this.fb.group({
692
              key: this.fb.control(key),
693
              value: this.fb.control(indicatorPath.parameters[key])
694
            }));
695
          }
696
        }
697
      });
698
    }
699
    return parameters;
700
  }
701
  
702
  public editNumberIndicatorOpen(section: Section, id = null) {
703
    this.urlParameterizedMessage = null;
704
    this.section = section;
705
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
706
    if (this.index !== -1) {
707
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
708
      this.numberIndicatorFb = this.fb.group({
709
        _id: this.fb.control(this.indicator._id),
710
        name: this.fb.control(this.indicator.name, Validators.required),
711
        description: this.fb.control(this.indicator.description),
712
        creationDate: this.fb.control(this.indicator.creationDate),
713
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
714
        visibility: this.fb.control(this.indicator.visibility),
715
        indicatorPaths: this.fb.array([], Validators.required),
716
        type: this.fb.control(this.indicator.type),
717
        width: this.fb.control(this.indicator.width),
718
        height: this.fb.control(this.indicator.height),
719
        defaultId: this.fb.control(this.indicator.defaultId)
720
      });
721
      this.indicator.indicatorPaths.forEach(indicatorPath => {
722
        this.addNumberIndicatorPath(this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)), indicatorPath.parameters, indicatorPath.source, this.getJsonPathAsFormArray(indicatorPath));
723
      });
724
    } else {
725
      this.indicator = new Indicator('', '', '', 'number', 'small', 'small', "PUBLIC", []);
726
      this.numberIndicatorFb = this.fb.group({
727
        _id: this.fb.control(this.indicator._id),
728
        name: this.fb.control(this.indicator.name, Validators.required),
729
        description: this.fb.control(this.indicator.description),
730
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
731
        visibility: this.fb.control(this.indicator.visibility),
732
        indicatorPaths: this.fb.array([], Validators.required),
733
        type: this.fb.control(this.indicator.type),
734
        width: this.fb.control(this.indicator.width),
735
        height: this.fb.control(this.indicator.height),
736
        defaultId: this.fb.control(this.indicator.defaultId)
737
      });
738
      this.addNumberIndicatorPath();
739
    }
740
    if (this.indicator.defaultId) {
741
      setTimeout(() => {
742
        this.numberIndicatorFb.get('description').disable();
743
      }, 0);
744
    }
745
    this.editNumberModal.cancelButtonText = 'Cancel';
746
    this.editNumberModal.okButtonLeft = false;
747
    this.editNumberModal.alertMessage = false;
748
    if (this.index === -1) {
749
      this.editNumberModal.alertTitle = 'Create a new number indicator';
750
      this.editNumberModal.okButtonText = 'Save';
751
      this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
752
      this.editNumberNotify.reset(this.notification.message);
753
    } else {
754
      this.editNumberModal.alertTitle = 'Edit number indicator\'s information';
755
      this.editNumberModal.okButtonText = 'Save Changes';
756
      this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
757
      this.editNumberNotify.reset(this.notification.message);
758
    }
759
    this.editNumberModal.open();
760
  }
761

    
762
  public editChartIndicatorOpen(section: Section, id = null) {
763
    this.urlParameterizedMessage = null;
764
    this.urlSubscriptions.forEach(value => {
765
      if (value instanceof Subscriber) {
766
        value.unsubscribe();
767
      }
768
    });
769
    this.section = section;
770
    this.index = (id) ? section.indicators.findIndex(value => value._id === id) : -1;
771
    if (this.index !== -1) {
772
      this.indicator = HelperFunctions.copy(this.section.indicators[this.index]);
773
      this.chartIndicatorFb = this.fb.group({
774
        _id: this.fb.control(this.indicator._id),
775
        name: this.fb.control(this.indicator.name),
776
        creationDate: this.fb.control(this.indicator.creationDate),
777
        description: this.fb.control(this.indicator.description),
778
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
779
        visibility: this.fb.control(this.indicator.visibility),
780
        indicatorPaths: this.fb.array([]),
781
        width: this.fb.control(this.indicator.width),
782
        height: this.fb.control(this.indicator.height),
783
        defaultId: this.fb.control(this.indicator.defaultId)
784
      });
785
      this.indicator.indicatorPaths.forEach(indicatorPath => {
786
        this.addChartIndicatorPath(this.getUrlByStakeHolder(indicatorPath),
787
          this.getParametersAsFormArray(indicatorPath), this.indicator.defaultId !== null, indicatorPath.type);
788
        indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
789
      });
790
    } else {
791
      this.indicator = new Indicator('', '', '', 'chart', 'medium', 'medium', "PUBLIC", []);
792
      this.chartIndicatorFb = this.fb.group({
793
        _id: this.fb.control(this.indicator._id),
794
        name: this.fb.control(this.indicator.name),
795
        description: this.fb.control(this.indicator.description),
796
        additionalDescription: this.fb.control(this.indicator.additionalDescription),
797
        visibility: this.fb.control(this.indicator.visibility),
798
        indicatorPaths: this.fb.array([]),
799
        width: this.fb.control(this.indicator.width, Validators.required),
800
        height: this.fb.control(this.indicator.height, Validators.required),
801
        defaultId: this.fb.control(this.indicator.defaultId)
802
      });
803
      this.addChartIndicatorPath();
804
    }
805
    if (this.indicator.defaultId) {
806
      setTimeout(() => {
807
        this.chartIndicatorFb.get('description').disable();
808
      }, 0);
809
    }
810
    this.editChartModal.cancelButtonText = 'Cancel';
811
    this.editChartModal.okButtonLeft = false;
812
    this.editChartModal.alertMessage = false;
813
    if (this.index === -1) {
814
      this.editChartModal.alertTitle = 'Create a new chart indicator';
815
      this.editChartModal.okButtonText = 'Save';
816
      this.notification = NotificationUtils.createIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
817
      this.editChartNotify.reset(this.notification.message);
818
    } else {
819
      this.editChartModal.alertTitle = 'Edit chart indicator\'s information';
820
      this.editChartModal.okButtonText = 'Save Changes';
821
      this.notification = NotificationUtils.editIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);;
822
      this.editChartNotify.reset(this.notification.message);
823
    }
824
    this.editChartModal.open();
825
  }
826
  
827
  saveIndicator() {
828
    this.editing = true;
829
    if (this.indicator.type === 'chart') {
830
      this.chartIndicatorFb.get('description').enable();
831
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type, true);
832
      this.section = this.charts.find(section => section._id === this.section._id);
833
    } else {
834
      this.numberIndicatorFb.get('description').enable();
835
      this.indicator = this.indicatorUtils.generateIndicatorByForm(this.numberIndicatorFb.value, this.indicator.indicatorPaths, this.indicator.type, false);
836
      this.section = this.numbers.find(section => section._id === this.section._id);
837
    }
838
    let path = [
839
      this.stakeholder._id,
840
      this.stakeholder.topics[this.topicIndex]._id,
841
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
842
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
843
      this.section._id
844
    ];
845
    this.subscriptions.push(this.stakeholderService.saveElement(this.properties.monitorServiceAPIURL, this.indicator, path).subscribe(indicator => {
846
      if (this.index !== -1) {
847
        this.section.indicators[this.index] = indicator;
848
      } else {
849
        this.section.indicators.push(indicator);
850
      }
851
      this.notification.entity = indicator._id;
852
      this.notification.stakeholder = this.stakeholder.alias;
853
      this.notification.stakeholderType =  this.stakeholder.type;
854
      this.notification.groups = [Role.curator( this.stakeholder.type)];
855
      if(this.stakeholder.defaultId) {
856
        this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
857
        if (this.indicator.type === "chart") {
858
          this.filterCharts();
859
          this.chartIndicatorFb = null;
860
          this.editChartNotify.sendNotification(this.notification);
861
        } else {
862
          this.filterNumbers();
863
          this.numberIndicatorFb = null;
864
          this.editNumberNotify.sendNotification(this.notification);
865
        }
866
      } else {
867
        this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null,  this.stakeholder._id).subscribe(stakeholders => {
868
          stakeholders.forEach(value => {
869
            this.notification.groups.push(Role.manager(value.type, value.alias))
870
          });
871
          if (this.indicator.type === "chart") {
872
            this.filterCharts();
873
            this.chartIndicatorFb = null;
874
            this.editChartNotify.sendNotification(this.notification);
875
          } else {
876
            this.filterNumbers();
877
            this.numberIndicatorFb = null;
878
            this.editNumberNotify.sendNotification(this.notification);
879
          }
880
        });
881
      }
882
      UIkit.notification('Indicator has been <b>successfully saved</b>', {
883
        status: 'success',
884
        timeout: 6000,
885
        pos: 'bottom-right'
886
      });
887
      this.editing = false;
888
    }, error => {
889
      this.chartIndicatorFb = null;
890
      UIkit.notification(error.error.message, {
891
        status: 'danger',
892
        timeout: 6000,
893
        pos: 'bottom-right'
894
      });
895
      this.editing = false;
896
    }));
897
  }
898
  saveIndicators(indicators:Indicator[]) {
899
    this.editing = true;
900
    let path = [
901
      this.stakeholder._id,
902
      this.stakeholder.topics[this.topicIndex]._id,
903
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
904
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
905
    ];
906
    this.subscriptions.push(this.stakeholderService.saveBulkElements(this.properties.monitorServiceAPIURL, indicators, path).subscribe(stakeholder => {
907
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts;
908
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers = stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers;
909
      this.buildSections();
910
      this.filterCharts();
911
      this.filterNumbers();
912
      this.editing = false;
913
      UIkit.notification("Success! Indicated were imported", {
914
        status: 'success',
915
        timeout: 6000,
916
        pos: 'bottom-right'
917
      });
918
    }, error => {
919
      this.chartIndicatorFb = null;
920
      UIkit.notification(error.error.message, {
921
        status: 'danger',
922
        timeout: 6000,
923
        pos: 'bottom-right'
924
      });
925
      this.editing = false;
926
    }));
927

    
928

    
929
  }
930
  reorderIndicators(sectionId: string, type: IndicatorType, reorder: Reorder) {
931
    this.editing = true;
932
    let path = [
933
      this.stakeholder._id,
934
      this.stakeholder.topics[this.topicIndex]._id,
935
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
936
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
937
      sectionId
938
    ];
939
    this.subscriptions.push(this.stakeholderService.reorderIndicators(this.properties.monitorServiceAPIURL, path, reorder, type).subscribe(indicators => {
940
      if (type === 'chart') {
941
        this.charts.find(section => section._id === sectionId).indicators = indicators;
942
        this.filterCharts();
943
      } else {
944
        this.numbers.find(section => section._id === sectionId).indicators = indicators;
945
        this.filterNumbers();
946
      }
947
      this.editing = false;
948
    }));
949
  }
950
  
951
  hasDifference(index: number): boolean {
952
    let hasDifference = false;
953
    this.chartIndicatorPaths.at(index).value.parameters.forEach((parameter) => {
954
      if (parameter.value !== this.indicator.indicatorPaths[index].parameters[parameter.key]) {
955
        hasDifference = true;
956
        return;
957
      }
958
    });
959
    return hasDifference || this.indicator.indicatorPaths[index].safeResourceUrl.toString() !==
960
      this.getSecureUrlByStakeHolder(this.indicator.indicatorPaths[index]).toString();
961
  }
962
  
963
  public get isAdministrator(): boolean {
964
    return Session.isPortalAdministrator(this.user);
965
  }
966
  
967
  public get isCurator(): boolean {
968
    return this.isAdministrator || Session.isMonitorCurator(this.user);
969
  }
970
  
971
  refreshIndicator() {
972
    this.indicator = this.indicatorUtils.generateIndicatorByForm(this.chartIndicatorFb.value, this.indicator.indicatorPaths, 'chart', true);
973
    this.indicator.indicatorPaths.forEach(indicatorPath => {
974
      indicatorPath.safeResourceUrl = this.getSecureUrlByStakeHolder(indicatorPath);
975
    });
976
  }
977
  
978
  deleteIndicatorOpen(section: Section, indicatorId: string, type: string, childrenAction: string = null) {
979
    this.indicatorChildrenActionOnDelete = null;
980
    if (childrenAction == "delete") {
981
      this.indicatorChildrenActionOnDelete = childrenAction;
982
    } else if (childrenAction == "disconnect") {
983
      this.indicatorChildrenActionOnDelete = childrenAction;
984
    }
985
    
986
    this.section = section;
987
    if (type === 'chart') {
988
      this.index = this.charts.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
989
    } else {
990
      this.index = this.numbers.find(value => value._id == section._id).indicators.findIndex(value => value._id == indicatorId);
991
    }
992
    this.indicator = section.indicators.find(value => value._id == indicatorId);
993
    this.deleteModal.alertTitle = 'Delete indicator';
994
    this.deleteModal.cancelButtonText = 'No';
995
    this.deleteModal.okButtonText = 'Yes';
996
    this.notification = NotificationUtils.deleteIndicator(this.user.firstname + ' ' + this.user.lastname, this.stakeholder.name);
997
    this.deleteNotify.reset(this.notification.message);
998
    this.deleteModal.open();
999
  }
1000
  
1001
  deleteIndicator() {
1002
    this.editing = true;
1003
    let path = [
1004
      this.stakeholder._id,
1005
      this.stakeholder.topics[this.topicIndex]._id,
1006
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1007
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
1008
      this.section._id,
1009
      this.indicator._id
1010
    ];
1011
    this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.indicatorChildrenActionOnDelete).subscribe(() => {
1012
      if (this.indicator.type === 'chart') {
1013
        this.charts.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
1014
        this.filterCharts();
1015
      } else {
1016
        this.numbers.find(section => section._id === this.section._id).indicators.splice(this.index, 1);
1017
        this.filterNumbers();
1018
      }
1019
      UIkit.notification('Indicator has been <b>successfully deleted</b>', {
1020
        status: 'success',
1021
        timeout: 6000,
1022
        pos: 'bottom-right'
1023
      });
1024
      this.notification.entity = this.indicator._id;
1025
      this.notification.stakeholder = this.stakeholder.alias;
1026
      this.notification.stakeholderType =  this.stakeholder.type;
1027
      this.notification.groups = [Role.curator( this.stakeholder.type)];
1028
      if(this.stakeholder.defaultId) {
1029
        this.notification.groups.push(Role.manager(this.stakeholder.type, this.stakeholder.alias));
1030
        this.deleteNotify.sendNotification(this.notification);
1031
      } else {
1032
        this.stakeholderService.getStakeholders(this.properties.monitorServiceAPIURL, null,  this.stakeholder._id).subscribe(stakeholders => {
1033
          stakeholders.forEach(value => {
1034
            this.notification.groups.push(Role.manager(value.type, value.alias))
1035
          });
1036
          this.deleteNotify.sendNotification(this.notification);
1037
        });
1038
      }
1039
      this.editing = false;
1040
    }, error => {
1041
      UIkit.notification(error.error.message, {
1042
        status: 'danger',
1043
        timeout: 6000,
1044
        pos: 'bottom-right'
1045
      });
1046
      this.editing = false;
1047
    }));
1048
  }
1049
  
1050
  changeIndicatorStatus(sectionId: string, indicator: Indicator, visibility: Visibility) {
1051
    this.editing = true;
1052
    let path = [
1053
      this.stakeholder._id,
1054
      this.stakeholder.topics[this.topicIndex]._id,
1055
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1056
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
1057
      sectionId,
1058
      indicator._id
1059
    ];
1060
    this.subscriptions.push(this.stakeholderService.changeVisibility(this.properties.monitorServiceAPIURL, path, visibility).subscribe(visibility => {
1061
      indicator.visibility = visibility;
1062
      UIkit.notification('Indicator has been <b>successfully changed</b> to ' + indicator.visibility.toLowerCase(), {
1063
        status: 'success',
1064
        timeout: 6000,
1065
        pos: 'bottom-right'
1066
      });
1067
      this.editing = false;
1068
    }, error => {
1069
      UIkit.notification('An error has been occurred. Try again later', {
1070
        status: 'danger',
1071
        timeout: 6000,
1072
        pos: 'bottom-right'
1073
      });
1074
      this.editing = false;
1075
    }));
1076
  }
1077
  
1078
  saveSection(section: Section, index: number, type: IndicatorType = "chart") {
1079
    this.editing = true;
1080
    let path = [
1081
      this.stakeholder._id,
1082
      this.stakeholder.topics[this.topicIndex]._id,
1083
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1084
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
1085
    ];
1086
    this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, section, path, index).subscribe(section => {
1087
      if (type === 'chart') {
1088
        this.charts[index] = section;
1089
        this.filterCharts();
1090
      } else {
1091
        this.numbers[index] = section;
1092
        this.filterNumbers();
1093
      }
1094
      this.initReorder();
1095
      UIkit.notification('Section has been <b>successfully saved</b>', {
1096
        status: 'success',
1097
        timeout: 6000,
1098
        pos: 'bottom-right'
1099
      });
1100
      this.editing = false;
1101
    }, error => {
1102
      UIkit.notification(error.error.message, {
1103
        status: 'danger',
1104
        timeout: 6000,
1105
        pos: 'bottom-right'
1106
      });
1107
      this.editing = false;
1108
    }));
1109
  }
1110
  
1111
  createSection(index = -1, type: IndicatorType = 'chart') {
1112
    this.editing = true;
1113
    this.section = new Section(type, null, null, this.stakeholder.alias);
1114
    let path = [
1115
      this.stakeholder._id,
1116
      this.stakeholder.topics[this.topicIndex]._id,
1117
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1118
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id
1119
    ];
1120
    this.subscriptions.push(this.stakeholderService.saveSection(this.properties.monitorServiceAPIURL, this.section, path, index).subscribe(section => {
1121
      if (type === 'chart') {
1122
        if (index !== -1) {
1123
          this.charts.splice(index, 0, section);
1124
        } else {
1125
          this.charts.push(section);
1126
        }
1127
        this.filterCharts();
1128
      } else {
1129
        if (index !== -1) {
1130
          this.numbers.splice(index, 0, section);
1131
        } else {
1132
          this.numbers.push(section);
1133
        }
1134
        this.filterNumbers();
1135
      }
1136
      this.initReorder();
1137
      UIkit.notification('Section has been <b>successfully created</b>', {
1138
        status: 'success',
1139
        timeout: 6000,
1140
        pos: 'bottom-right'
1141
      });
1142
      this.editing = false;
1143
    }, error => {
1144
      UIkit.notification(error.error.message, {
1145
        status: 'danger',
1146
        timeout: 6000,
1147
        pos: 'bottom-right'
1148
      });
1149
      this.editing = false;
1150
    }));
1151
  }
1152
  
1153
  // deleteNumberSectionOpen(section: Section, index: number) {
1154
  //   this.section = section;
1155
  //   this.index = index;
1156
  //   this.deleteNumberSectionModal.alertTitle = 'Delete Section';
1157
  //   this.deleteNumberSectionModal.cancelButtonText = 'No';
1158
  //   this.deleteNumberSectionModal.okButtonText = 'Yes';
1159
  //   this.deleteNumberSectionModal.open();
1160
  // }
1161
  //
1162
  // deleteChartSectionOpen(section: Section, index: number) {
1163
  //   this.section = section;
1164
  //   this.index = index;
1165
  //   this.deleteChartSectionModal.alertTitle = 'Delete Section';
1166
  //   this.deleteChartSectionModal.cancelButtonText = 'No';
1167
  //   this.deleteChartSectionModal.okButtonText = 'Yes';
1168
  //   this.deleteChartSectionModal.open();
1169
  // }
1170
  
1171
  deleteSectionOpen(section: Section, index: number, type: IndicatorType, childrenAction: string = null) {
1172
    if (!this.editing && !section.defaultId) {
1173
      this.sectionTypeToDelete = type;
1174
      this.sectionChildrenActionOnDelete = null;
1175
      if (childrenAction == "delete") {
1176
        this.sectionChildrenActionOnDelete = childrenAction;
1177
      } else if (childrenAction == "disconnect") {
1178
        this.sectionChildrenActionOnDelete = childrenAction;
1179
      }
1180
      
1181
      this.section = section;
1182
      this.index = index;
1183
      this.deleteSectionModal.alertTitle = 'Delete Section';
1184
      this.deleteSectionModal.cancelButtonText = 'No';
1185
      this.deleteSectionModal.okButtonText = 'Yes';
1186
      this.deleteSectionModal.open();
1187
    }
1188
  }
1189
  
1190
  deleteSection() {
1191
    this.editing = true;
1192
    let path = [
1193
      this.stakeholder._id,
1194
      this.stakeholder.topics[this.topicIndex]._id,
1195
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex]._id,
1196
      this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex]._id,
1197
      this.section._id
1198
    ];
1199
    this.subscriptions.push(this.stakeholderService.deleteElement(this.properties.monitorServiceAPIURL, path, this.sectionChildrenActionOnDelete).subscribe(() => {
1200
      if (this.sectionTypeToDelete === "chart") {
1201
        this.charts.splice(this.index, 1);
1202
        this.filterCharts();
1203
      } else {
1204
        this.numbers.splice(this.index, 1);
1205
        this.filterNumbers();
1206
      }
1207
      this.initReorder();
1208
      UIkit.notification('Section has been <b>successfully deleted</b>', {
1209
        status: 'success',
1210
        timeout: 6000,
1211
        pos: 'bottom-right'
1212
      });
1213
      this.editing = false;
1214
    }, error => {
1215
      UIkit.notification(error.error.message, {
1216
        status: 'danger',
1217
        timeout: 6000,
1218
        pos: 'bottom-right'
1219
      });
1220
      this.editing = false;
1221
    }));
1222
  }
1223
  private checkForSchemaEnhancements(url:string){
1224
    //new schema
1225
    this.showCheckForSchemaEnhancements = this.isAdministrator && url && !this.properties.useOldStatisticsSchema && this.indicatorUtils.checkForSchemaEnhancements(url);
1226
  }
1227
 importIndicatorsAndSave( charts:any[]){
1228

    
1229
    let indicators:Indicator[] = [];
1230
   // name description additionalDescription, height, width, visibility
1231
   let noValidParams = 0;
1232
   let duplicates = 0;
1233
    for(let chart of charts) {
1234
       let exists = false;
1235
      let indicatorPath;
1236
      if (chart.type == "chart") {
1237
        indicatorPath = this.indicatorUtils.generateIndicatorByChartUrl(this.statisticsService.getChartSource(chart.url), chart.url, chart.type, this.stakeholder);
1238
        for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].charts) {
1239
          for (let chart of section.indicators) {
1240
            if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
1241
              duplicates++;
1242
              exists = true;
1243
            }
1244
          }
1245

    
1246
        }
1247
      }else if (chart.type == "number") {
1248
        indicatorPath = this.indicatorUtils.generateIndicatorByNumberUrl(this.statisticsService.getNumberSource(chart.url), chart.url, this.stakeholder,
1249
          chart.jsonPath, this.statisticsService.numberSources.get(this.statisticsService.getNumberSource(chart.url)));
1250
        for (let section of this.stakeholder.topics[this.topicIndex].categories[this.categoryIndex].subCategories[this.subcategoryIndex].numbers) {
1251
          for (let chart of section.indicators) {
1252
            if (JSON.stringify(chart.indicatorPaths[0].chartObject) == JSON.stringify(indicatorPath.chartObject)) {
1253
              duplicates++;
1254
              exists = true;
1255
            }
1256
          }
1257

    
1258
        }
1259
      }
1260
      if (!this.isStakeholderParametersValid(indicatorPath)) {
1261
        noValidParams++;
1262
      }
1263
      if (!exists) {
1264
        let i: Indicator = new Indicator(chart.name, chart.description, chart.additionalDescription, chart.type, chart.width, chart.height, chart.visibility, [indicatorPath]);
1265
        indicators.push(i);
1266
      }
1267

    
1268
    }
1269
    if(duplicates > 0 ){
1270
      UIkit.notification(duplicates + " urls already exist and will not be imported!", {
1271
        status: 'warning',
1272
        timeout: 6000,
1273
        pos: 'bottom-right'
1274
      });
1275
    }
1276
    if(noValidParams > 0 ){
1277
      let noValidMesseage = "Some indicators couldn't be generated properly. Please make sure chart data is for the current stakeholder.";
1278
      if (this.stakeholder.defaultId == null) {
1279
        noValidMesseage = "Some indicators couldn't be generated properly. Stakeholders based on this profile may not inherit the data correctly.";
1280
      }
1281
      UIkit.notification(noValidMesseage, {
1282
        status: 'danger',
1283
        timeout: 6000,
1284
        pos: 'bottom-right'
1285
      });
1286
    }else if(indicators.length > 0){
1287
      console.log(indicators);
1288
      this.saveIndicators(indicators)
1289
    }
1290
    if(indicators.length == 0){
1291
      UIkit.notification(  " No urls imported!", {
1292
        status: 'warning',
1293
        timeout: 6000,
1294
        pos: 'bottom-right'
1295
      });
1296
    }
1297
 }
1298

    
1299
  public export_indicators() {
1300
    console.debug("Export indicators");
1301

    
1302
    let indicators = [];
1303
    let index: number = 0;
1304

    
1305
    this.displayNumbers.forEach(section => {
1306
      section.indicators.forEach(indicator => {
1307
        indicator.indicatorPaths.forEach(indicatorPath => {
1308
          console.debug("export number: ", this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath)));
1309
          indicators[index] = {
1310
            "type": indicator.type, "name": indicator.name, "jsonPath":indicatorPath.jsonPath,
1311
            "description": indicator.description, "additionalDescription": indicator.additionalDescription,
1312
            "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
1313
            "url": this.statisticsService.getNumberUrl(indicatorPath.source, this.indicatorUtils.getFullUrl(this.stakeholder, indicatorPath))};
1314
          index++;
1315
        });
1316
      });
1317
    });
1318

    
1319
    this.displayCharts.forEach(section => {
1320
      section.indicators.forEach(indicator => {
1321
        indicator.indicatorPaths.forEach(indicatorPath => {
1322
          console.debug("export chart: "+this.getUrlByStakeHolder(indicatorPath));
1323
          indicators[index] = {
1324
            "type": indicator.type, "name": indicator.name,
1325
            "description": indicator.description, "additionalDescription": indicator.additionalDescription,
1326
            "visibility": indicator.visibility, "width": indicator.width, "height": indicator.height,
1327
            "url": this.getUrlByStakeHolder(indicatorPath)};
1328
          index++;
1329
        });
1330
      });
1331
    });
1332

    
1333
    let topic = this.stakeholder ? this.stakeholder.topics[this.topicIndex] : null;
1334
    let category = topic ? topic.categories[this.categoryIndex] : null;
1335
    let subCategory = category ? category.subCategories[this.subcategoryIndex] : null;
1336

    
1337
    var jsonFileUrl = window.URL.createObjectURL(new Blob([JSON.stringify(indicators)], {type: 'application/json'}));
1338
    var a = window.document.createElement('a');
1339
    window.document.body.appendChild(a);
1340
    a.setAttribute('style', 'display: none');
1341
    a.href = jsonFileUrl;
1342
    a.download = this.stakeholder.alias + "_"+topic.alias + "_" + category.alias + "_" + subCategory.alias + ".json";
1343
    a.click();
1344
    window.URL.revokeObjectURL(jsonFileUrl);
1345
    a.remove(); // remove the element
1346
  }
1347

    
1348
  fileChangeEvent(fileInput: any) {
1349
    this.filesToUpload = <Array<File>>fileInput.target.files;
1350
    this.upload();
1351
  }
1352

    
1353
  upload() {
1354
    this.enableUpload = false;
1355
    // this.errorMessage = "";
1356
    console.debug(this.filesToUpload);
1357
    if (this.filesToUpload.length == 0) {
1358
      console.error("There is no selected file to upload.");
1359
      // this.errorMessage = "There is no selected file to upload.";
1360
      return;
1361
    } else {
1362
      if (this.filesToUpload[0].name.indexOf(".json") == -1 || (this.filesToUpload[0].type != "application/json")) {
1363
        // this.errorMessage = "No valid file type. The required type is JSON";
1364
        console.error("No valid file type. The required type is JSON");
1365
        return;
1366
      }
1367
    }
1368
    // this.loading.open();
1369

    
1370
    this.makeFileRequest(this.properties.utilsService + '/upload?type=json', [], this.filesToUpload).then(async (result: string) => {
1371

    
1372
      let json_result = JSON.parse(result);
1373
      this.importIndicatorsAndSave(json_result);
1374
      this.endOfFetching();
1375
    }, (error) => {
1376
      this.enableUpload = true;
1377
      console.error("An error occurred");
1378
    });
1379
  }
1380

    
1381
  makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
1382
    return new Promise((resolve, reject) => {
1383
      const formData: any = new FormData();
1384
      const xhr = new XMLHttpRequest();
1385
      for (let i = 0; i < files.length; i++) {
1386
        formData.append("uploads[]", files[i], files[i].name);
1387
      }
1388
      xhr.onreadystatechange = function () {
1389
        if (xhr.readyState == 4) {
1390
          if (xhr.status == 200) {
1391
            resolve(xhr.response);
1392
          } else {
1393
            reject(xhr.response);
1394
          }
1395
        }
1396
      };
1397
      xhr.open("POST", url, true);
1398
      xhr.send(formData);
1399
    });
1400
  }
1401

    
1402
  endOfFetching() {
1403
    //   this.showReport = true;
1404
    this.enableUpload = true;
1405
  }
1406

    
1407
}
(3-3/7)