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(':', '.');
+ }
+
+}