You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/06/29 06:44:54 UTC

[incubator-streampipes] branch dev updated: [STREAMPIPES-546] Support data download for configured query

This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 147cba6bd [STREAMPIPES-546] Support data download for configured query
147cba6bd is described below

commit 147cba6bd0bcef7f4afa4d2b5b20368792a7cf5d
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Wed Jun 29 08:44:44 2022 +0200

    [STREAMPIPES-546] Support data download for configured query
---
 .../src/lib/apis/datalake-rest.service.ts          |  56 +++---
 .../data-download-dialog.component.ts              | 178 ------------------
 .../data-explorer-dashboard-widget.component.ts    |   5 +-
 ui/src/app/data-explorer/data-explorer.module.ts   |   2 +-
 .../data-download-dialog.component.html            |  30 +--
 .../data-download-dialog.component.scss            |   6 +-
 .../data-download-dialog.component.ts              | 208 +++++++++++++++++++++
 7 files changed, 264 insertions(+), 221 deletions(-)

diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
index aa85aeddc..b9b608e2f 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
@@ -17,7 +17,7 @@
  */
 
 import { Injectable } from '@angular/core';
-import { HttpClient, HttpRequest } from '@angular/common/http';
+import { HttpClient, HttpParams, HttpRequest } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { DataLakeMeasure, PageResult, SpQueryResult } from '../model/gen/streampipes-model';
 import { map } from 'rxjs/operators';
@@ -49,9 +49,9 @@ export class DatalakeRestService {
           queryParams: DatalakeQueryParameters,
           ignoreLoadingBar?: boolean): Observable<SpQueryResult> {
     const url = this.dataLakeUrl + '/measurements/' + index;
-    const headers = ignoreLoadingBar ? { ignoreLoadingBar: '' } : {};
+    const headers = ignoreLoadingBar ? {ignoreLoadingBar: ''} : {};
     // @ts-ignore
-    return this.http.get<SpQueryResult>(url, { params: queryParams }, headers);
+    return this.http.get<SpQueryResult>(url, {params: queryParams}, headers);
   }
 
 
@@ -62,7 +62,7 @@ export class DatalakeRestService {
       itemsPerPage, undefined, undefined, order, undefined, undefined);
 
     // @ts-ignore
-    return this.http.get<PageResult>(url, { params: queryParams });
+    return this.http.get<PageResult>(url, {params: queryParams});
   }
 
   getTagValues(index: string,
@@ -71,38 +71,38 @@ export class DatalakeRestService {
       .pipe(map(r => r as Map<string, string[]>));
   }
 
-  downloadRawData(index, format) {
-    const url = this.dataLakeUrl + '/measurements/' + index + '/download?format=' + format;
+  downloadRawData(index: string,
+                  format: string,
+                  startTime?: number,
+                  endTime?: number) {
+    const queryParams = (startTime && endTime) ? {format, startDate: startTime, endDate: endTime} : {format};
+    return this.buildDownloadRequest(index, queryParams);
+  }
+
+  downloadQueriedData(
+    index: string,
+    format: string,
+    queryParams: DatalakeQueryParameters) {
+
+    (queryParams as any).format = format;
+    return this.buildDownloadRequest(index, queryParams);
+
+  }
 
+  buildDownloadRequest(index: string,
+                       queryParams: any) {
+    const url = this.dataLakeUrl + '/measurements/' + index + '/download';
     const request = new HttpRequest('GET', url, {
       reportProgress: true,
-      responseType: 'text'
+      responseType: 'text',
+      params: this.toHttpParams(queryParams)
     });
 
     return this.http.request(request);
   }
 
-  downloadQueriedData(
-    index,
-    format,
-    startDate?,
-    endDate?,
-    columns?,
-    aggregationFunction?,
-    aggregationTimeUnit?,
-    aggregationTimeValue?,
-    groupingsTags?,
-    order?,
-    limit?,
-    offset?) {
-    const url = this.dataLakeUrl + '/measurements/' + index + '/download';
-    const timeInterval = aggregationTimeValue + aggregationTimeUnit;
-
-    const queryParams: DatalakeQueryParameters = this.getQueryParameters(columns, startDate, endDate, undefined,
-      limit, offset, groupingsTags, order, aggregationFunction, timeInterval);
-    (queryParams as any).format = format;
-
-    return this.http.get(url, {params: queryParams as any, responseType: 'text', reportProgress: true});
+  toHttpParams(queryParamObject: any): HttpParams {
+    return new HttpParams({fromObject: queryParamObject});
   }
 
   removeData(index: string) {
diff --git a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.ts b/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.ts
deleted file mode 100644
index d290a6ed1..000000000
--- a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.ts
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-import { HttpEventType } from '@angular/common/http';
-import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core';
-import { MatStepper } from '@angular/material/stepper';
-import { DatalakeRestService, DateRange } from '@streampipes/platform-services';
-import { DialogRef } from '@streampipes/shared-ui';
-
-
-@Component({
-    selector: 'sp-data-download-dialog',
-    templateUrl: 'data-download-dialog.component.html',
-    styleUrls: ['./data-download-dialog.component.scss']
-})
-// tslint:disable-next-line:component-class-suffix
-export class DataDownloadDialogComponent implements OnInit {
-
-    @Input() index: string;
-    @Input() date: DateRange;
-
-    downloadFormat = 'csv';
-    selectedData = 'visible';
-    downloadFinish = false;
-    downloadedMBs: number = undefined;
-
-    @ViewChild('stepper', { static: true }) stepper: MatStepper;
-
-    downloadHttpRequestSubscribtion;
-
-    dateRange: Date [] = []; // [0] start, [1] end
-
-
-    constructor(public dialogRef: DialogRef<DataDownloadDialogComponent>,
-                public datalakeRestService: DatalakeRestService) {
-    }
-
-    ngOnInit() {
-        this.dateRange[0] = this.date.startDate;
-        this.dateRange[1] = this.date.endDate;
-    }
-
-    downloadData() {
-        this.nextStep();
-        switch (this.selectedData) {
-            case 'all':
-                this.performRequest(this.datalakeRestService.downloadRawData(this.index, this.downloadFormat), '', '');
-                break;
-            case 'customInterval':
-                this.performRequest(this.datalakeRestService.downloadQueriedData(this.index, this.downloadFormat,
-                    this.date.startDate.getTime(), this.date.endDate.getTime()), this.getDateString(this.date.startDate),
-                  this.getDateString(this.date.endDate));
-
-        }
-    }
-
-    performRequest(request, startDate, endDate) {
-        this.downloadHttpRequestSubscribtion = request.subscribe(event => {
-            // progress
-            if (event.type === HttpEventType.DownloadProgress) {
-                this.downloadedMBs = event.loaded / 1024 / 1014;
-            }
-
-            // finished
-            if (event.type === HttpEventType.Response) {
-                this.createFile(event.body, this.downloadFormat, this.index, startDate, endDate);
-                this.downloadFinish = true;
-            }
-        });
-    }
-
-    convertData(data, format, xAxesKey, yAxesKeys) {
-        const indexXKey = data.headers.findIndex(headerName => headerName === xAxesKey);
-        const indicesYKeys = [];
-        yAxesKeys.forEach(key => {
-            indicesYKeys.push(data.headers.findIndex(headerName => headerName === key));
-        });
-
-        if (format === 'json') {
-            const resultJson = [];
-
-
-            data.rows.forEach(row => {
-                const tmp = {'time': new Date(row[indexXKey]).getTime()};
-                indicesYKeys.forEach(index => {
-                    if (row[index] !== undefined) {
-                        tmp[data.headers[index]] = row[index];
-                    }
-                });
-                resultJson.push(tmp);
-            });
-
-            return JSON.stringify(resultJson);
-        } else {
-            // CSV
-            let resultCsv = '';
-
-            // header
-            resultCsv += xAxesKey;
-            yAxesKeys.forEach(key => {
-                resultCsv += ';';
-                resultCsv += key;
-            });
-
-
-            // content
-            data.rows.forEach(row => {
-                resultCsv += '\n';
-                resultCsv += new Date(row[indexXKey]).getTime();
-                indicesYKeys.forEach(index => {
-                    resultCsv += ';';
-                    if (row[index] !== undefined) {
-                        resultCsv += row[index];
-                    }
-                });
-            });
-
-            return resultCsv;
-        }
-    }
-
-    createFile(data, format, fileName, startDate, endDate) {
-        const a = document.createElement('a');
-        document.body.appendChild(a);
-        a.style.display = 'display: none';
-
-        let name = 'sp_' + startDate + '_' + fileName + '.' + this.downloadFormat;
-        name = name.replace('__', '_');
-
-        const url = window.URL.createObjectURL(new Blob([String(data)], { type: 'data:text/' + format + ';charset=utf-8' }));
-        a.href = url;
-        a.download = name;
-        a.click();
-        window.URL.revokeObjectURL(url);
-    }
-
-    cancelDownload() {
-        try {
-            this.downloadHttpRequestSubscribtion.unsubscribe();
-        } finally {
-            this.exitDialog();
-        }
-    }
-
-
-    exitDialog(): void {
-        this.dialogRef.close();
-    }
-
-    nextStep() {
-        this.stepper.next();
-    }
-
-    previousStep() {
-        this.stepper.previous();
-    }
-
-    getDateString(date: Date): string {
-       return date.toLocaleDateString() + 'T' + date.toLocaleTimeString().replace(':', '.')
-                                                                          .replace(':', '.');
-    }
-
-}
diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
index acfd81975..b3f5e4f96 100644
--- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
+++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
@@ -19,14 +19,14 @@
 import { Component, ComponentFactoryResolver, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
 import { GridsterItemComponent } from 'angular-gridster2';
 import {
-  DashboardItem,
+  DashboardItem, DataExplorerDataConfig,
   DataExplorerWidgetModel,
   DataLakeMeasure,
   DataViewDataExplorerService,
   DateRange,
   TimeSettings
 } from '@streampipes/platform-services';
-import { DataDownloadDialogComponent } from '../data-download-dialog/data-download-dialog.component';
+import { DataDownloadDialogComponent } from '../../dialogs/data-download-dialog/data-download-dialog.component';
 import { interval } from 'rxjs';
 import { takeWhile } from 'rxjs/operators';
 import { DataExplorerWidgetRegistry } from '../../registry/data-explorer-widget-registry';
@@ -157,6 +157,7 @@ export class DataExplorerDashboardWidgetComponent implements OnInit {
       data: {
         'index': this.dataLakeMeasure.measureName,
         'date': DateRange.fromTimeSettings(this.timeSettings),
+        'dataConfig': this.configuredWidget.dataConfig as DataExplorerDataConfig
       }
     });
   }
diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts
index c3bc93d39..f4e1a4432 100644
--- a/ui/src/app/data-explorer/data-explorer.module.ts
+++ b/ui/src/app/data-explorer/data-explorer.module.ts
@@ -50,7 +50,7 @@ import {
 } from '@streampipes/platform-services';
 import { CoreUiModule } from '../core-ui/core-ui.module';
 import { CustomMaterialModule } from '../CustomMaterial/custom-material.module';
-import { DataDownloadDialogComponent } from './components/data-download-dialog/data-download-dialog.component';
+import { DataDownloadDialogComponent } from './dialogs/data-download-dialog/data-download-dialog.component';
 import { DataExplorerDashboardGridComponent } from './components/widget-view/grid-view/data-explorer-dashboard-grid.component';
 import { DataExplorerDashboardOverviewComponent } from './components/overview/data-explorer-dashboard-overview.component';
 import { DataExplorerDashboardPanelComponent } from './components/panel/data-explorer-dashboard-panel.component';
diff --git a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.html b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.html
similarity index 71%
rename from ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.html
rename to ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.html
index c30e2eedd..7d61db70b 100644
--- a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.html
+++ b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.html
@@ -22,11 +22,19 @@
             <mat-horizontal-stepper #stepper>
                 <mat-step>
                     <ng-template matStepLabel>Select Data</ng-template>
-                    <div>
+                    <div class="mt-10">
                         <mat-radio-group class="example-radio-group" [(ngModel)]="selectedData">
-                            <!--                    <mat-radio-button value="visible" class="example-radio-button">-->
-                            <!--                        Visible data in charts-->
-                            <!--                    </mat-radio-button>-->
+                            <mat-radio-button value="visible" class="example-radio-button">
+                                Currently configured query
+                            </mat-radio-button>
+                            <div fxLayout="column" fxLayoutAlign="start start" class="ml-35 mb-10" *ngIf="selectedData === 'visible' && dataConfig.sourceConfigs.length > 1">
+                                <h5>Select source (only one source can be exported in a single file)</h5>
+                                <mat-radio-group class="example-radio-group" [(ngModel)]="selectedQueryIndex">
+                                    <mat-radio-button [value]="i" *ngFor="let sourceConfig of dataConfig.sourceConfigs; let i = index">
+                                        {{sourceConfig.measureName}}
+                                    </mat-radio-button>
+                                </mat-radio-group>
+                            </div>
                             <mat-radio-button value="all" class="example-radio-button">
                                 All data in database
                             </mat-radio-button>
@@ -69,13 +77,14 @@
 
                 <mat-step>
                     <ng-template matStepLabel>Download</ng-template>
-                    <div div fxLayout="column" fxLayoutAlign="space-around center" *ngIf="!downloadFinish">
-                        <mat-spinner></mat-spinner>
-                        <label *ngIf="downloadedMBs !== undefined">{{downloadedMBs      | number : '1.0-2' }} Mb</label>
+                    <div fxFlex="100" fxLayout="column" fxLayoutAlign="center center" *ngIf="!downloadFinish" class="mt-35">
+                        <mat-spinner [diameter]="30" color="accent"></mat-spinner>
+                        <label *ngIf="downloadedMBs !== undefined">{{downloadedMBs | number : '1.0-2' }} Mb</label>
                         <button mat-button warn color="warn" (click)="cancelDownload()">Cancel</button>
                     </div>
-                    <div div fxLayout="column" fxLayoutAlign="space-around center" *ngIf="downloadFinish">
+                    <div fxLayout="column" fxLayoutAlign="space-around center" *ngIf="downloadFinish">
                         <mat-icon class="icon-check">check</mat-icon>
+                        <h5>Download successful</h5>
                     </div>
                 </mat-step>
             </mat-horizontal-stepper>
@@ -85,14 +94,13 @@
 
     <mat-divider></mat-divider>
     <div class="sp-dialog-actions actions-align-right">
-        <button mat-button mat-raised-button *ngIf="stepper.selectedIndex == 1" (click)="previousStep()">Previous</button>
+        <button mat-button mat-raised-button class="mat-basic" style="margin-right: 10px;" (click)="exitDialog()">Close</button>
+        <button mat-button mat-raised-button class="mat-basic" style="margin-right: 10px;" *ngIf="stepper.selectedIndex == 1" (click)="previousStep()">Previous</button>
         <button mat-button mat-raised-button *ngIf="stepper.selectedIndex < 1" color="accent" (click)="nextStep()">
             Next
         </button>
         <button mat-button mat-raised-button *ngIf="stepper.selectedIndex == 1" color="accent" (click)="downloadData()">
             Download
         </button>
-        <button mat-button mat-raised-button *ngIf="downloadFinish" (click)="exitDialog()">Close
-        </button>
     </div>
 </div>
diff --git a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.scss b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.scss
similarity index 96%
rename from ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.scss
rename to ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.scss
index 2529ad95f..fd0f2f09b 100644
--- a/ui/src/app/data-explorer/components/data-download-dialog/data-download-dialog.component.scss
+++ b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.scss
@@ -16,7 +16,7 @@
  *
  */
 
-@import '../../../../scss/sp/sp-dialog.scss';
+@import 'src/scss/sp/sp-dialog';
 
 example-radio-group {
     display: flex;
@@ -61,6 +61,10 @@ mat-dialog-content {
     margin-left: 35px;
 }
 
+.mt-35 {
+    margin-top: 35px;
+}
+
 .form-field-date {
     top: -10px;
     width: 270px;
diff --git a/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.ts b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.ts
new file mode 100644
index 000000000..93db48e93
--- /dev/null
+++ b/ui/src/app/data-explorer/dialogs/data-download-dialog/data-download-dialog.component.ts
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { HttpEventType } from '@angular/common/http';
+import { Component, Inject, Input, OnInit, ViewChild } from '@angular/core';
+import { MatStepper } from '@angular/material/stepper';
+import {
+  DataExplorerDataConfig, DatalakeQueryParameters,
+  DatalakeRestService,
+  DataViewQueryGeneratorService,
+  DateRange, SourceConfig
+} from '@streampipes/platform-services';
+import { DialogRef } from '@streampipes/shared-ui';
+
+
+@Component({
+  selector: 'sp-data-download-dialog',
+  templateUrl: 'data-download-dialog.component.html',
+  styleUrls: ['./data-download-dialog.component.scss']
+})
+// tslint:disable-next-line:component-class-suffix
+export class DataDownloadDialogComponent implements OnInit {
+
+  @Input() index: string;
+  @Input() date: DateRange;
+  @Input() dataConfig: DataExplorerDataConfig;
+
+  downloadFormat = 'csv';
+  selectedData = 'visible';
+  downloadFinish = false;
+  downloadedMBs: number = undefined;
+  selectedQueryIndex = 0;
+
+  @ViewChild('stepper', {static: true}) stepper: MatStepper;
+
+  downloadHttpRequestSubscribtion;
+
+  dateRange: Date [] = []; // [0] start, [1] end
+
+
+  constructor(public dialogRef: DialogRef<DataDownloadDialogComponent>,
+              public datalakeRestService: DatalakeRestService,
+              private dataViewQueryService: DataViewQueryGeneratorService) {
+  }
+
+  ngOnInit() {
+    this.dateRange[0] = this.date.startDate;
+    this.dateRange[1] = this.date.endDate;
+  }
+
+  downloadData() {
+    this.nextStep();
+    const startTime = this.date.startDate.getTime();
+    const endTime = this.date.endDate.getTime();
+    const startDateString = this.getDateString(this.date.startDate);
+    const endDateString = this.getDateString(this.date.endDate);
+    switch (this.selectedData) {
+      case 'all':
+        this.performRequest(this.datalakeRestService.downloadRawData(this.index, this.downloadFormat), '', '');
+        break;
+      case 'customInterval':
+        this.performRequest(this.datalakeRestService.downloadRawData(this.index, this.downloadFormat,
+            startTime, endTime), startDateString,
+          endDateString);
+        break;
+      case 'visible':
+        this.performRequest(
+          this.datalakeRestService
+            .downloadQueriedData(
+              this.index,
+              this.downloadFormat,
+              this.generateQueryRequest(startTime, endTime)
+            ),
+          startDateString,
+          endDateString
+        );
+
+    }
+  }
+
+  generateQueryRequest(startTime: number,
+                       endTime: number): DatalakeQueryParameters {
+    return this.dataViewQueryService
+      .generateQuery(startTime, endTime, this.dataConfig.sourceConfigs[this.selectedQueryIndex]);
+  }
+
+  performRequest(request, startDate, endDate) {
+    this.downloadHttpRequestSubscribtion = request.subscribe(event => {
+      // progress
+      if (event.type === HttpEventType.DownloadProgress) {
+        this.downloadedMBs = event.loaded / 1024 / 1014;
+      }
+
+      // finished
+      if (event.type === HttpEventType.Response) {
+        this.createFile(event.body, this.downloadFormat, this.index, startDate, endDate);
+        this.downloadFinish = true;
+      }
+    });
+  }
+
+  convertData(data, format, xAxesKey, yAxesKeys) {
+    const indexXKey = data.headers.findIndex(headerName => headerName === xAxesKey);
+    const indicesYKeys = [];
+    yAxesKeys.forEach(key => {
+      indicesYKeys.push(data.headers.findIndex(headerName => headerName === key));
+    });
+
+    if (format === 'json') {
+      const resultJson = [];
+
+
+      data.rows.forEach(row => {
+        const tmp = {'time': new Date(row[indexXKey]).getTime()};
+        indicesYKeys.forEach(index => {
+          if (row[index] !== undefined) {
+            tmp[data.headers[index]] = row[index];
+          }
+        });
+        resultJson.push(tmp);
+      });
+
+      return JSON.stringify(resultJson);
+    } else {
+      // CSV
+      let resultCsv = '';
+
+      // header
+      resultCsv += xAxesKey;
+      yAxesKeys.forEach(key => {
+        resultCsv += ';';
+        resultCsv += key;
+      });
+
+
+      // content
+      data.rows.forEach(row => {
+        resultCsv += '\n';
+        resultCsv += new Date(row[indexXKey]).getTime();
+        indicesYKeys.forEach(index => {
+          resultCsv += ';';
+          if (row[index] !== undefined) {
+            resultCsv += row[index];
+          }
+        });
+      });
+
+      return resultCsv;
+    }
+  }
+
+  createFile(data, format, fileName, startDate, endDate) {
+    const a = document.createElement('a');
+    document.body.appendChild(a);
+    a.style.display = 'display: none';
+
+    let name = 'sp_' + startDate + '_' + fileName + '.' + this.downloadFormat;
+    name = name.replace('__', '_');
+
+    const url = window.URL.createObjectURL(new Blob([String(data)], {type: 'data:text/' + format + ';charset=utf-8'}));
+    a.href = url;
+    a.download = name;
+    a.click();
+    window.URL.revokeObjectURL(url);
+  }
+
+  cancelDownload() {
+    try {
+      this.downloadHttpRequestSubscribtion.unsubscribe();
+    } finally {
+      this.exitDialog();
+    }
+  }
+
+
+  exitDialog(): void {
+    this.dialogRef.close();
+  }
+
+  nextStep() {
+    this.stepper.next();
+  }
+
+  previousStep() {
+    this.stepper.previous();
+  }
+
+  getDateString(date: Date): string {
+    return date.toLocaleDateString() + 'T' + date.toLocaleTimeString().replace(':', '.')
+      .replace(':', '.');
+  }
+
+}