import { AfterViewInit, Component, Input, OnInit, ViewChild, } from "@angular/core"; import { AxesTypes, DataType, FilterDefaults, PlotConfig, PlotRequest, PlotService, PlotType, RenderOptions, RenderOptionsMap, Suggestion, TagField, } from "../plot.service"; import { UntypedFormControl, FormsModule } from "@angular/forms"; import { MatSnackBar } from "@angular/material/snack-bar"; import { LimitByComponent } from "../limit-by/limit-by.component"; import { YAxisDefinitionComponent } from "../y-axis-definition/y-axis-definition.component"; import { QueryAutocompleteComponent } from "../query-autocomplete/query-autocomplete.component"; import { DateRange, LoadingEvent, PlotViewComponent, } from "../plot-view/plot-view.component"; import { GalleryViewComponent } from "../gallery-view/gallery-view.component"; import { WidgetDimensions } from "../dashboard.service"; import { DatePickerComponent, DateValue, } from "../components/datepicker/date-picker.component"; import { MatFormField, MatLabel, MatError } from "@angular/material/form-field"; import { MatSelect } from "@angular/material/select"; import { NgFor, NgIf } from "@angular/common"; import { MatOption } from "@angular/material/core"; import { MatCheckbox } from "@angular/material/checkbox"; import { MatIconAnchor, MatButton } from "@angular/material/button"; import { RouterLink } from "@angular/router"; import { MatTooltip } from "@angular/material/tooltip"; @Component({ selector: "pdb-visualization-page", templateUrl: "./visualization-page.component.html", styleUrls: ["./visualization-page.component.scss"], standalone: true, imports: [ QueryAutocompleteComponent, DatePickerComponent, MatFormField, MatLabel, MatSelect, FormsModule, NgFor, MatOption, LimitByComponent, MatCheckbox, YAxisDefinitionComponent, NgIf, MatError, MatIconAnchor, RouterLink, MatButton, MatTooltip, PlotViewComponent, GalleryViewComponent, ], }) export class VisualizationPageComponent implements OnInit, AfterViewInit { readonly DATE_PATTERN = "YYYY-MM-DD HH:mm:ss"; // for moment-JS @Input() defaultConfig?: PlotConfig; @Input() galleryEnabled = true; dateRange = new UntypedFormControl( "2019-10-05 00:00:00 - 2019-10-11 23:59:59", ); selectedPlotType = new Array(); plotTypes: PlotType[] = []; tagFields: Array = new Array(); groupBy = new Array(); @ViewChild("limitbycomponent") private limitbycomponent!: LimitByComponent; @ViewChild("y1AxisDefinitionComponent", { read: YAxisDefinitionComponent }) private y1AxisDefinitionComponent!: YAxisDefinitionComponent; @ViewChild("y2AxisDefinitionComponent", { read: YAxisDefinitionComponent }) private y2AxisDefinitionComponent!: YAxisDefinitionComponent; @ViewChild("query") query!: QueryAutocompleteComponent; @ViewChild("plotView") plotView!: PlotViewComponent; @ViewChild("galleryView") galleryView!: GalleryViewComponent; @ViewChild("datePicker") datePicker!: DatePickerComponent; enableGallery = false; splitBy: TagField | undefined = undefined; y2AxisAvailable = false; intervalUnit = "NO_INTERVAL"; intervalValue = 1; renderBarChartTickLabels = false; plotJobActive = false; constructor(private plotService: PlotService, private snackBar: MatSnackBar) { const params = new URLSearchParams(window.location.search); if (!this.defaultConfig && params.get("config")) { const config = JSON.parse(params.get("config")!); this.defaultConfig = config; } } showError(message: string) { this.snackBar.open(message, "", { duration: 5000, verticalPosition: "top", }); } ngOnInit() { ( window).initDatePicker(); this.plotTypes = this.plotService.getPlotTypes(); this.selectedPlotType.push(this.plotTypes[0]); this.plotService.getFilterDefaults().subscribe( (filterDefaults: FilterDefaults) => { filterDefaults.fields.forEach((name: string) => { this.tagFields.push(new TagField(name)); }, (error: any) => { this.showError(error.error.message); }); const groupByDefaults = this.defaultConfig ? this.defaultConfig.groupBy : filterDefaults.groupBy; this.groupBy = this.tagFields.filter((val) => groupByDefaults.includes(val.name) ); this.splitBy = this.tagFields.find((val) => filterDefaults.splitBy == val.name ); if (this.defaultConfig) { this.plot(); } }, ); } ngAfterViewInit(): void { if (this.defaultConfig) { const c = this.defaultConfig; this.query.suggestionFetcherEnabled = false; this.query.queryField.setValue( new Suggestion(c.query, c.query, c.query.length), ); this.query.suggestionFetcherEnabled = true; this.selectedPlotType = this.plotTypes.filter((pt) => c.aggregates.includes(pt.id) ); this.changePlotType(this.selectedPlotType); this.updateDateRange(c.dateRange, false); this.limitbycomponent.limitBy = c.limitBy; this.limitbycomponent.limit = c.limit; this.intervalUnit = c.intervalUnit; this.intervalValue = c.intervalValue; this.y1AxisDefinitionComponent.yAxisScale = c.y1.axisScale; this.y1AxisDefinitionComponent.minYValue = c.y1.rangeMin; this.y1AxisDefinitionComponent.maxYValue = c.y1.rangeMax; this.y1AxisDefinitionComponent.yAxisUnit = c.y1.rangeUnit; if (c.y2) { this.y2AxisDefinitionComponent.yAxisScale = c.y2.axisScale; this.y2AxisDefinitionComponent.minYValue = c.y2.rangeMin; this.y2AxisDefinitionComponent.maxYValue = c.y2.rangeMax; this.y2AxisDefinitionComponent.yAxisUnit = c.y2.rangeUnit; } } } toggleGallery(event: Event) { this.galleryView.show = this.enableGallery; } loading(event: LoadingEvent) { this.plotJobActive = event.loading; } updateDateRange(newDateRange: DateValue, updatePlot = true) { this.datePicker.setDateValue(newDateRange); if (updatePlot) { this.plot(); } } changePlotType(selectedPlotTypes: Array) { const compatiblePlotTypes = this.plotTypes.filter((pt) => pt.compatible(selectedPlotTypes) ); this.plotTypes.forEach((pt) => pt.active = false); compatiblePlotTypes.forEach((pt) => pt.active = true); const axesTypes = this.getAxes(); this.y2AxisAvailable = axesTypes.y.length == 2; } selectedPlotTypesContains(plotTypeIds: Array) { return this.selectedPlotType.filter((pt) => plotTypeIds.includes(pt.id)) .length > 0; } dateRangeAsString(): DateValue { return this.datePicker.getDateValue(); } gallery() { if (this.splitBy != null) { this.plotView.imageUrl = ""; this.plotView.stats = null; this.galleryView.show = true; const request = this.createPlotRequest(); this.galleryView.renderGallery(request, this.splitBy.name); } else { console.error("variable splitBy was null when rendering gallery"); } } getAxes(): AxesTypes { const x = new Array(); const y = new Array(); for (var i = 0; i < this.selectedPlotType.length; i++) { var plotType = this.selectedPlotType[i]; if (!x.includes(plotType.xAxis)) { x.push(plotType.xAxis); } if (!y.includes(plotType.yAxis)) { y.push(plotType.yAxis); } } return new AxesTypes(x, y); } abort() { this.plotService.abort(( window).submitterId).subscribe({ complete: () => { }, }); } plot() { const config = this.createPlotConfig(); this.plotView.plot(config, this.plotDimensionSupplier); } plotDimensionSupplier(): WidgetDimensions { const results = document.getElementById("results"); return new WidgetDimensions( results != null ? results.offsetWidth - 1 : 1024, results != null ? results.offsetHeight - 1 : 1024, ); } createPlotConfig(): PlotConfig { const aggregates = new Array(); this.selectedPlotType.forEach((a) => aggregates.push(a.id)); const y1 = this.y1AxisDefinitionComponent.getAxisDefinition(); const y2 = this.y2AxisDefinitionComponent ? this.y2AxisDefinitionComponent.getAxisDefinition() : undefined; const config = new PlotConfig( this.query.query, this.groupBy.map((o) => o.name), this.limitbycomponent.limitBy, this.limitbycomponent.limit, y1, y2, this.datePicker.getDateValue(), // dateRange aggregates, // aggregates this.intervalUnit, this.intervalValue, this.renderBarChartTickLabels, ); return config; } createPlotRequest(): PlotRequest { const results = document.getElementById("results"); const config = this.createPlotConfig(); const renderOptions: RenderOptionsMap = { "main": new RenderOptions( results!.offsetHeight - 1, results!.offsetWidth - 1, true, true, ), "thumbnail": new RenderOptions(200, 300, false, false), }; const request = new PlotRequest( ( window).submitterId, config, renderOptions, ); return request; } serializedConfig(): string { try { const config = this.createPlotConfig(); return JSON.stringify(config); } catch (e) { return ""; } } }