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
commit f9e9f1b3430b5d99c3e22dd291bd817629cb93d8 Author: Dominik Riemer <[email protected]> AuthorDate: Mon Aug 23 15:33:16 2021 +0200 [STREAMPIPES-415] Add pie chart widget to data explorer --- .../datalake/DatalakeQueryParameterBuilder.ts | 14 +++ .../datalake/DatalakeQueryParameters.ts | 1 + .../data-explorer-designer-panel.component.html | 6 ++ ...ta-explorer-widget-data-settings.component.html | 3 + .../data-explorer-dashboard-widget.component.html | 13 +++ .../config/pie-chart-widget-config.component.html | 74 +++++++++++++ .../config/pie-chart-widget-config.component.scss} | 17 --- .../config/pie-chart-widget-config.component.ts | 61 +++++++++++ .../widgets/pie/model/pie-chart-widget.model.ts} | 29 ++--- .../widgets/pie/pie-chart-widget.component.html | 32 ++++++ .../widgets/pie/pie-chart-widget.component.scss} | 17 --- .../widgets/pie/pie-chart-widget.component.ts | 118 +++++++++++++++++++++ ui/src/app/data-explorer/data-explorer.module.ts | 4 + ui/src/scss/sp/layout.scss | 7 ++ 14 files changed, 349 insertions(+), 47 deletions(-) diff --git a/ui/src/app/core-services/datalake/DatalakeQueryParameterBuilder.ts b/ui/src/app/core-services/datalake/DatalakeQueryParameterBuilder.ts index cb50c23..50c057f 100644 --- a/ui/src/app/core-services/datalake/DatalakeQueryParameterBuilder.ts +++ b/ui/src/app/core-services/datalake/DatalakeQueryParameterBuilder.ts @@ -17,6 +17,7 @@ */ import { DatalakeQueryParameters } from './DatalakeQueryParameters'; +import { FilterCondition } from '../../data-explorer/components/widgets/pie/model/pie-chart-widget.model'; export class DatalakeQueryParameterBuilder { @@ -35,6 +36,12 @@ export class DatalakeQueryParameterBuilder { this.queryParams.endDate = endTime; } + public withCountOnly(): DatalakeQueryParameterBuilder { + this.queryParams.countOnly = true; + + return this; + } + public withAutoAggregation(aggregationFunction: string) { this.queryParams.autoAggregate = true; this.queryParams.aggregationFunction = aggregationFunction; @@ -103,6 +110,13 @@ export class DatalakeQueryParameterBuilder { return this; } + public withFilters(filterConditions: FilterCondition[]): DatalakeQueryParameterBuilder { + const filters = filterConditions.map(f => '[' + f.field + ',' + f.operator + ',' + f.condition + ']'); + this.queryParams.filter = filters.toString(); + + return this; + } + public build(): DatalakeQueryParameters { return this.queryParams; } diff --git a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts b/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts index f4c4fce..2386e65 100644 --- a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts +++ b/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts @@ -29,6 +29,7 @@ export class DatalakeQueryParameters { public timeInterval: string; public countOnly: boolean; public autoAggregate: boolean; + public filter: string; } diff --git a/ui/src/app/data-explorer/components/designer-panel/data-explorer-designer-panel.component.html b/ui/src/app/data-explorer/components/designer-panel/data-explorer-designer-panel.component.html index 490e684..1b32b2e 100644 --- a/ui/src/app/data-explorer/components/designer-panel/data-explorer-designer-panel.component.html +++ b/ui/src/app/data-explorer/components/designer-panel/data-explorer-designer-panel.component.html @@ -77,6 +77,12 @@ [dataLakeMeasure]="dataLakeMeasure"> </sp-data-explorer-density-chart-widget-config> </div> + <div *ngIf="currentlyConfiguredWidget.widgetType === 'pie-chart'" class="h-100 p-0"> + <sp-data-explorer-pie-chart-widget-config + [currentlyConfiguredWidget]="currentlyConfiguredWidget" + [dataLakeMeasure]="dataLakeMeasure"> + </sp-data-explorer-pie-chart-widget-config> + </div> </div> <div *ngIf="selectedIndex == 2"> <sp-data-explorer-widget-appearance-settings diff --git a/ui/src/app/data-explorer/components/designer-panel/data-settings/data-explorer-widget-data-settings.component.html b/ui/src/app/data-explorer/components/designer-panel/data-settings/data-explorer-widget-data-settings.component.html index 8c7543b..817c5dc 100644 --- a/ui/src/app/data-explorer/components/designer-panel/data-settings/data-explorer-widget-data-settings.component.html +++ b/ui/src/app/data-explorer/components/designer-panel/data-settings/data-explorer-widget-data-settings.component.html @@ -57,6 +57,9 @@ <mat-option [value]="'line-chart'"> <span class="pipeline-name">Line Chart</span> </mat-option> + <mat-option [value]="'pie-chart'"> + <span class="pipeline-name">Pie Chart</span> + </mat-option> <mat-option [value]="'table'"> <span class="pipeline-name">Table</span> </mat-option> diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html index 174f729..3621b24 100644 --- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html +++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html @@ -121,6 +121,19 @@ class="h-100"> </sp-data-explorer-density-chart-widget> </div> + <div *ngIf="configuredWidget.widgetType === 'pie-chart'" class="h-100 p-0"> + <sp-data-explorer-pie-chart-widget + (removeWidgetCallback)="removeWidget()" + [timeSettings]="timeSettings" + [gridsterItem]="dashboardItem" + [gridsterItemComponent]="gridsterItemComponent" + [editMode]="editMode" + [dataViewDashboardItem]="dashboardItem" + [dataExplorerWidget]="configuredWidget" + [dataLakeMeasure]="dataLakeMeasure" + class="h-100"> + </sp-data-explorer-pie-chart-widget> + </div> </div> </div> </div> diff --git a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html new file mode 100644 index 0000000..ef99b85 --- /dev/null +++ b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.html @@ -0,0 +1,74 @@ +<!-- + ~ 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. + ~ + --> + +<div fxFlex="100" fxLayout="column"> + <h5>Indicator Field</h5> + <mat-form-field color="accent" fxFlex="100"> + <mat-label>Select Field</mat-label> + <mat-select [(value)]="currentlyConfiguredWidget.dataConfig.selectedProperty" + (selectionChange)="setSelectedProperties($event)"> + <mat-option [value]="property.runtimeName" + *ngFor="let property of currentlyConfiguredWidget.dataConfig.availableProperties"> + <span class="pipeline-name">{{ property.runtimeName }}</span> + </mat-option> + </mat-select> + </mat-form-field> + + <h5>Filter</h5> + <div> + <button mat-button mat-raised-button color="accent" class="small-button" + (click)="addFilter()" + style="margin-right:10px;margin-bottom: 15px;">Add Filter + </button> + </div> + <div *ngFor="let filter of currentlyConfiguredWidget.dataConfig.selectedFilters" fxFlex="100" fxLayout="column"> + <div fxFlex="100" fxLayout="column"> + <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center"> + <mat-form-field color="accent" class="w-50-px mr-15"> + <mat-label>Operator</mat-label> + <mat-select [(value)]="filter.operator"> + <mat-option [value]="'='"> + <span class="pipeline-name">=</span> + </mat-option> + <mat-option [value]="'<'"> + <span class="pipeline-name"><</span> + </mat-option> + <mat-option [value]="'<='"> + <span class="pipeline-name"><=</span> + </mat-option> + <mat-option [value]="'>='"> + <span class="pipeline-name">>=</span> + </mat-option> + <mat-option [value]="'>'"> + <span class="pipeline-name">></span> + </mat-option> + </mat-select> + </mat-form-field> + <mat-form-field color="accent" class="mr-15"> + <mat-label>Condition</mat-label> + <input matInput [(ngModel)]="filter.condition" (ngModelChange)="triggerDataRefresh()"> + </mat-form-field> + <button mat-icon-button color="accent" + (click)="remove(filter.index)"> + <i class="material-icons">remove</i> + </button> + </div> + </div> + </div> +</div> + diff --git a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.scss similarity index 68% copy from ui/src/app/core-services/datalake/DatalakeQueryParameters.ts copy to ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.scss index f4c4fce..13cbc4a 100644 --- a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts +++ b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.scss @@ -15,20 +15,3 @@ * limitations under the License. * */ - -export class DatalakeQueryParameters { - public columns: string; - public startDate: number; - public endDate: number; - public page: number; - public limit: number; - public offset: number; - public groupBy: string; - public order: string; - public aggregationFunction: string; - public timeInterval: string; - public countOnly: boolean; - public autoAggregate: boolean; - -} - diff --git a/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts new file mode 100644 index 0000000..511620e --- /dev/null +++ b/ui/src/app/data-explorer/components/widgets/pie/config/pie-chart-widget-config.component.ts @@ -0,0 +1,61 @@ +/* + * 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 { Component, OnInit } from '@angular/core'; +import { BaseWidgetConfig } from '../../base/base-widget-config'; +import { EventPropertyUnion } from '../../../../../core-model/gen/streampipes-model'; +import { FilterCondition, PieChartWidgetModel } from '../model/pie-chart-widget.model'; +import { MatSelectChange } from '@angular/material/select'; + +@Component({ + selector: 'sp-data-explorer-pie-chart-widget-config', + templateUrl: './pie-chart-widget-config.component.html', + styleUrls: ['./pie-chart-widget-config.component.scss'] +}) +export class PieWidgetConfigComponent extends BaseWidgetConfig<PieChartWidgetModel> implements OnInit { + + ngOnInit(): void { + this.updateWidgetConfigOptions(); + } + + setSelectedProperties(selectedColumn: MatSelectChange) { + this.currentlyConfiguredWidget.dataConfig.selectedFilters.forEach(f => f.field = selectedColumn.value); + this.triggerDataRefresh(); + } + + protected updateWidgetConfigOptions() { + if (this.dataLakeMeasure.measureName && !this.currentlyConfiguredWidget.dataConfig.availableProperties) { + this.currentlyConfiguredWidget.dataConfig.availableProperties = this.dataLakeMeasure.eventSchema.eventProperties; + this.currentlyConfiguredWidget.dataConfig.selectedFilters = []; + } + } + + addFilter() { + const newFilter = { + number: this.currentlyConfiguredWidget.dataConfig.selectedFilters.length, + field: this.currentlyConfiguredWidget.dataConfig.selectedProperty, + operator: '=' + } as unknown as FilterCondition; + this.currentlyConfiguredWidget.dataConfig.selectedFilters.push(newFilter); + } + + remove(index: number) { + this.currentlyConfiguredWidget.dataConfig.selectedFilters.splice(index, 1); + } + +} diff --git a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts b/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts similarity index 62% copy from ui/src/app/core-services/datalake/DatalakeQueryParameters.ts copy to ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts index f4c4fce..9e42bd3 100644 --- a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts +++ b/ui/src/app/data-explorer/components/widgets/pie/model/pie-chart-widget.model.ts @@ -16,19 +16,22 @@ * */ -export class DatalakeQueryParameters { - public columns: string; - public startDate: number; - public endDate: number; - public page: number; - public limit: number; - public offset: number; - public groupBy: string; - public order: string; - public aggregationFunction: string; - public timeInterval: string; - public countOnly: boolean; - public autoAggregate: boolean; +import { DataExplorerWidgetModel, EventPropertyUnion } from '../../../../../core-model/gen/streampipes-model'; +export interface FilterCondition { + index: number; + field: string; + operator: string; + condition: string; } + +export interface PieChartDataConfig { + selectedProperty: string; + availableProperties: EventPropertyUnion[]; + selectedFilters: FilterCondition[]; +} + +export interface PieChartWidgetModel extends DataExplorerWidgetModel { + dataConfig: PieChartDataConfig; +} diff --git a/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.html b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.html new file mode 100644 index 0000000..202fb78 --- /dev/null +++ b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.html @@ -0,0 +1,32 @@ +<!-- + ~ 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. + ~ + --> + +<div fxLayout="column" fxFlex="100" + [ngStyle]="{background: dataExplorerWidget.baseAppearanceConfig.backgroundColor, color: dataExplorerWidget.baseAppearanceConfig.textColor}"> + + <sp-load-data-spinner *ngIf="showIsLoadingData" class="h-100"></sp-load-data-spinner> + <sp-no-data-in-date-range *ngIf="showNoDataInDateRange" [viewDateRange]="timeSettings"></sp-no-data-in-date-range> + + <plotly-plot *ngIf="data !== undefined && !showNoDataInDateRange && !showIsLoadingData" + [divId]="dataExplorerWidget._id" + class="plotly-container" + [data]="data" + [layout]="graph.layout" + [config]="graph.config"> + </plotly-plot> +</div> diff --git a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.scss similarity index 68% copy from ui/src/app/core-services/datalake/DatalakeQueryParameters.ts copy to ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.scss index f4c4fce..13cbc4a 100644 --- a/ui/src/app/core-services/datalake/DatalakeQueryParameters.ts +++ b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.scss @@ -15,20 +15,3 @@ * limitations under the License. * */ - -export class DatalakeQueryParameters { - public columns: string; - public startDate: number; - public endDate: number; - public page: number; - public limit: number; - public offset: number; - public groupBy: string; - public order: string; - public aggregationFunction: string; - public timeInterval: string; - public countOnly: boolean; - public autoAggregate: boolean; - -} - diff --git a/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.ts b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.ts new file mode 100644 index 0000000..16b067d --- /dev/null +++ b/ui/src/app/data-explorer/components/widgets/pie/pie-chart-widget.component.ts @@ -0,0 +1,118 @@ +/* + * 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 { Component, OnInit } from '@angular/core'; +import { BaseDataExplorerWidget } from '../base/base-data-explorer-widget'; +import { DatalakeRestService } from '../../../../platform-services/apis/datalake-rest.service'; +import { WidgetConfigurationService } from '../../../services/widget-configuration.service'; +import { ResizeService } from '../../../services/resize.service'; +import { DataResult } from '../../../../core-model/datalake/DataResult'; +import { DatalakeQueryParameters } from '../../../../core-services/datalake/DatalakeQueryParameters'; +import { DatalakeQueryParameterBuilder } from '../../../../core-services/datalake/DatalakeQueryParameterBuilder'; +import { Observable, zip } from 'rxjs'; +import { FilterCondition, PieChartWidgetModel } from './model/pie-chart-widget.model'; + +@Component({ + selector: 'sp-data-explorer-pie-chart-widget', + templateUrl: './pie-chart-widget.component.html', + styleUrls: ['./pie-chart-widget.component.scss'] +}) +export class PieChartWidgetComponent extends BaseDataExplorerWidget<PieChartWidgetModel> implements OnInit { + + data = [ + { + values: [], + labels: [], + type: 'pie' + } + ]; + + graph = { + layout: { + font: { + color: '#FFF' + }, + autosize: true, + plot_bgcolor: '#fff', + paper_bgcolor: '#fff', + }, + + config: { + modeBarButtonsToRemove: ['lasso2d', 'select2d', 'toggleSpikelines', 'toImage'], + displaylogo: false, + displayModeBar: false, + responsive: true + } + }; + + constructor(dataLakeRestService: DatalakeRestService, + widgetConfigurationService: WidgetConfigurationService, + resizeService: ResizeService) { + super(dataLakeRestService, widgetConfigurationService, resizeService); + } + + refreshData() { + this.data[0].labels = this.dataExplorerWidget.dataConfig.selectedFilters.map(f => f.condition); + const queries = this.getQueries(); + zip(...queries) + .subscribe(result => { + this.prepareData(result); + }); + } + + getQueries(): Observable<DataResult>[] { + return this.dataExplorerWidget.dataConfig.selectedFilters + .map(sf => this.dataLakeRestService.getData(this.dataLakeMeasure.measureName, this.buildQuery(sf))); + } + + refreshView() { + this.updateAppearance(); + } + + prepareData(results: DataResult[]) { + if (results.length > 0) { + results.forEach((result, i) => { + if (result.total > 0) { + this.data[0].values[i] = result.rows[0][1]; + } + }); + } + + } + + updateAppearance() { + this.graph.layout.paper_bgcolor = this.dataExplorerWidget.baseAppearanceConfig.backgroundColor; + this.graph.layout.plot_bgcolor = this.dataExplorerWidget.baseAppearanceConfig.backgroundColor; + this.graph.layout.font.color = this.dataExplorerWidget.baseAppearanceConfig.textColor; + } + + buildQuery(filter: FilterCondition): DatalakeQueryParameters { + return DatalakeQueryParameterBuilder.create(this.timeSettings.startTime, this.timeSettings.endTime) + .withColumnFilter([this.dataExplorerWidget.dataConfig.selectedProperty]) + .withFilters([filter]) + .withCountOnly() + .build(); + } + + onResize(width: number, height: number) { + this.graph.layout.autosize = false; + (this.graph.layout as any).width = width; + (this.graph.layout as any).height = height; + } + +} diff --git a/ui/src/app/data-explorer/data-explorer.module.ts b/ui/src/app/data-explorer/data-explorer.module.ts index 01e9abb..ffe31a9 100644 --- a/ui/src/app/data-explorer/data-explorer.module.ts +++ b/ui/src/app/data-explorer/data-explorer.module.ts @@ -77,6 +77,8 @@ import { HistogramChartWidgetComponent } from './components/widgets/histogram/hi import { HistogramWidgetConfigComponent } from './components/widgets/histogram/config/histogram-chart-widget-config.component'; import { DensityChartWidgetComponent } from './components/widgets/density/density-chart-widget.component'; import { DensityWidgetConfigComponent } from './components/widgets/density/config/density-chart-widget-config.component'; +import { PieChartWidgetComponent } from './components/widgets/pie/pie-chart-widget.component'; +import { PieWidgetConfigComponent } from './components/widgets/pie/config/pie-chart-widget-config.component'; export const MY_NATIVE_FORMATS = { fullPickerInput: { @@ -146,6 +148,8 @@ export const MY_NATIVE_FORMATS = { LineChartWidgetConfigComponent, LoadDataSpinnerComponent, NoDataInDateRangeComponent, + PieChartWidgetComponent, + PieWidgetConfigComponent, SelectPropertiesComponent, TableWidgetComponent, TableWidgetConfigComponent, diff --git a/ui/src/scss/sp/layout.scss b/ui/src/scss/sp/layout.scss index dc856d6..b610584 100644 --- a/ui/src/scss/sp/layout.scss +++ b/ui/src/scss/sp/layout.scss @@ -72,6 +72,13 @@ margin-right: 10px; } +.mr-15 { + margin-right: 15px; +} + +.w-50-px { + width: 50px; +} .w-100 { width: 100%;
