This is an automated email from the ASF dual-hosted git repository. chenyulin0719 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/yunikorn-web.git
The following commit(s) were added to refs/heads/master by this push: new 440f03b [YUNIKORN-2491] Persist queue and partition selection and prompt user to pick queue on Applications page (#180) 440f03b is described below commit 440f03b8a74560e9a05e34126eb4c5043104c3af Author: Denis Coric <deni...@gmail.com> AuthorDate: Wed May 8 15:34:18 2024 +0800 [YUNIKORN-2491] Persist queue and partition selection and prompt user to pick queue on Applications page (#180) - When a user lands on the applications page, queue selection is automatically focused and waits for user to select the queue - Selected queue,partition pair is stored in localStorage Closes: #180 Signed-off-by: Yu-Lin Chen <chenyulin0...@apache.org> --- .../components/apps-view/apps-view.component.html | 2 +- .../components/apps-view/apps-view.component.ts | 70 +++++++++++--- .../components/nodes-view/nodes-view.component.ts | 101 ++++++++++++--------- .../queues-view/queues-view.component.ts | 6 +- src/app/utils/common.util.ts | 13 +++ 5 files changed, 132 insertions(+), 60 deletions(-) diff --git a/src/app/components/apps-view/apps-view.component.html b/src/app/components/apps-view/apps-view.component.html index 78f408a..8cf5896 100644 --- a/src/app/components/apps-view/apps-view.component.html +++ b/src/app/components/apps-view/apps-view.component.html @@ -31,7 +31,7 @@ <div class="dropdown-wrapper"> <label class="dropdown-label">Queue: </label> <mat-form-field> - <mat-select [(value)]="leafQueueSelected" (selectionChange)="onQueueSelectionChanged($event)"> + <mat-select [(value)]="leafQueueSelected" (selectionChange)="onQueueSelectionChanged($event)" #queueSelect> <mat-option *ngFor="let queue of leafQueueList" [value]="queue.value"> {{ queue.name }} </mat-option> diff --git a/src/app/components/apps-view/apps-view.component.ts b/src/app/components/apps-view/apps-view.component.ts index 5bbcc96..edbf0bb 100644 --- a/src/app/components/apps-view/apps-view.component.ts +++ b/src/app/components/apps-view/apps-view.component.ts @@ -21,7 +21,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { MatPaginator } from '@angular/material/paginator'; import { MatTableDataSource } from '@angular/material/table'; import { MatSort } from '@angular/material/sort'; -import { MatSelectChange } from '@angular/material/select'; +import { MatSelectChange, MatSelect } from '@angular/material/select'; import { finalize, debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { NgxSpinnerService } from 'ngx-spinner'; import { fromEvent } from 'rxjs'; @@ -46,6 +46,7 @@ export class AppsViewComponent implements OnInit { @ViewChild('appSort', { static: true }) appSort!: MatSort; @ViewChild('allocSort', { static: true }) allocSort!: MatSort; @ViewChild('searchInput', { static: true }) searchInput!: ElementRef; + @ViewChild('queueSelect', { static: false }) queueSelect!: MatSelect; appDataSource = new MatTableDataSource<AppInfo>([]); appColumnDef: ColumnDef[] = []; @@ -62,7 +63,7 @@ export class AppsViewComponent implements OnInit { partitionSelected = ''; leafQueueList: DropdownItem[] = []; leafQueueSelected = ''; - + detailToggle: boolean = false; constructor( @@ -135,7 +136,7 @@ export class AppsViewComponent implements OnInit { this.partitionList.push(new PartitionInfo(part.name, part.name)); }); - this.partitionSelected = list[0].name; + this.partitionSelected = CommonUtil.getStoredPartition(list[0].name); this.fetchQueuesForPartition(this.partitionSelected); } else { this.partitionList = [new PartitionInfo('-- Select --', '')]; @@ -143,6 +144,7 @@ export class AppsViewComponent implements OnInit { this.leafQueueList = [new DropdownItem('-- Select --', '')]; this.leafQueueSelected = ''; this.appDataSource.data = []; + this.clearQueueSelection(); } }); } @@ -161,14 +163,37 @@ export class AppsViewComponent implements OnInit { if (data && data.rootQueue) { const leafQueueList = this.generateLeafQueueList(data.rootQueue); this.leafQueueList = [new DropdownItem('-- Select --', ''), ...leafQueueList]; - this.leafQueueSelected = ''; this.fetchApplicationsUsingQueryParams(); + this.setDefaultQueue(leafQueueList); } else { this.leafQueueList = [new DropdownItem('-- Select --', '')]; } }); } + setDefaultQueue(queueList: DropdownItem[]): void { + const storedPartitionAndQueue = localStorage.getItem('selectedPartitionAndQueue'); + + if (!storedPartitionAndQueue || storedPartitionAndQueue.indexOf(':') < 0) { + setTimeout(() => this.openQueueSelection(), 0); + return; + } + + const [storedPartition, storedQueue] = storedPartitionAndQueue.split(':'); + if (this.partitionSelected !== storedPartition) return; + + const storedQueueDropdownItem = queueList.find((queue) => queue.value === storedQueue); + if (storedQueueDropdownItem) { + this.leafQueueSelected = storedQueueDropdownItem.value; + this.fetchAppListForPartitionAndQueue(this.partitionSelected, this.leafQueueSelected); + return; + } else { + this.leafQueueSelected = ''; + this.appDataSource.data = []; + setTimeout(() => this.openQueueSelection(), 0); // Allows render to finish and then opens the queue select dropdown + } + } + generateLeafQueueList(rootQueue: QueueInfo, list: DropdownItem[] = []): DropdownItem[] { if (rootQueue && rootQueue.isLeaf) { list.push(new DropdownItem(rootQueue.queueName, rootQueue.queueName)); @@ -205,6 +230,7 @@ export class AppsViewComponent implements OnInit { this.partitionSelected = partitionName; this.leafQueueSelected = queueName; this.fetchAppListForPartitionAndQueue(partitionName, queueName); + CommonUtil.setStoredQueueAndPartition(partitionName, queueName); } this.router.navigate([], { @@ -284,6 +310,7 @@ export class AppsViewComponent implements OnInit { this.partitionSelected = selected.value; this.appDataSource.data = []; this.removeRowSelection(); + this.clearQueueSelection(); this.fetchQueuesForPartition(this.partitionSelected); } else { this.searchText = ''; @@ -291,6 +318,7 @@ export class AppsViewComponent implements OnInit { this.leafQueueSelected = ''; this.appDataSource.data = []; this.removeRowSelection(); + this.clearQueueSelection(); } } @@ -301,35 +329,51 @@ export class AppsViewComponent implements OnInit { this.appDataSource.data = []; this.removeRowSelection(); this.fetchAppListForPartitionAndQueue(this.partitionSelected, this.leafQueueSelected); + CommonUtil.setStoredQueueAndPartition(this.partitionSelected, this.leafQueueSelected); } else { this.searchText = ''; this.leafQueueSelected = ''; this.appDataSource.data = []; this.removeRowSelection(); + this.clearQueueSelection(); } } - formatResources(colValue:string):string[]{ - const arr:string[]=colValue.split("<br/>") + formatResources(colValue: string): string[] { + const arr: string[] = colValue.split('<br/>'); // Check if there are "cpu" or "Memory" elements in the array - const hasCpu = arr.some((item) => item.toLowerCase().includes("cpu")); - const hasMemory = arr.some((item) => item.toLowerCase().includes("memory")); + const hasCpu = arr.some((item) => item.toLowerCase().includes('cpu')); + const hasMemory = arr.some((item) => item.toLowerCase().includes('memory')); if (!hasCpu) { - arr.unshift("CPU: n/a"); + arr.unshift('CPU: n/a'); } if (!hasMemory) { - arr.unshift("Memory: n/a"); + arr.unshift('Memory: n/a'); } // Concatenate the two arrays, with "cpu" and "Memory" elements first - const cpuAndMemoryElements = arr.filter((item) => item.toLowerCase().includes("CPU") || item.toLowerCase().includes("Memory")); - const otherElements = arr.filter((item) => !item.toLowerCase().includes("CPU") && !item.toLowerCase().includes("Memory")); + const cpuAndMemoryElements = arr.filter( + (item) => item.toLowerCase().includes('CPU') || item.toLowerCase().includes('Memory') + ); + const otherElements = arr.filter( + (item) => !item.toLowerCase().includes('CPU') && !item.toLowerCase().includes('Memory') + ); const result = cpuAndMemoryElements.concat(otherElements); return result; } - toggle(){ + clearQueueSelection() { + CommonUtil.setStoredQueueAndPartition(''); + this.leafQueueSelected = ''; + this.openQueueSelection(); + } + + openQueueSelection() { + this.queueSelect.open(); + } + + toggle() { this.detailToggle = !this.detailToggle; } } diff --git a/src/app/components/nodes-view/nodes-view.component.ts b/src/app/components/nodes-view/nodes-view.component.ts index 9bcf58a..e4c4f51 100644 --- a/src/app/components/nodes-view/nodes-view.component.ts +++ b/src/app/components/nodes-view/nodes-view.component.ts @@ -39,9 +39,8 @@ import { PartitionInfo } from '@app/models/partition-info.model'; export class NodesViewComponent implements OnInit { @ViewChild('nodesViewMatPaginator', { static: true }) nodePaginator!: MatPaginator; @ViewChild('allocationMatPaginator', { static: true }) allocPaginator!: MatPaginator; - @ViewChild('nodeSort', {static: true }) nodeSort!: MatSort; - @ViewChild('allocSort', {static: true }) allocSort!: MatSort; - + @ViewChild('nodeSort', { static: true }) nodeSort!: MatSort; + @ViewChild('allocSort', { static: true }) allocSort!: MatSort; nodeDataSource = new MatTableDataSource<NodeInfo>([]); nodeColumnDef: ColumnDef[] = []; @@ -58,7 +57,10 @@ export class NodesViewComponent implements OnInit { detailToggle: boolean = false; filterValue: string = ''; - constructor(private scheduler: SchedulerService, private spinner: NgxSpinnerService) {} + constructor( + private scheduler: SchedulerService, + private spinner: NgxSpinnerService + ) {} ngOnInit() { this.nodeDataSource.paginator = this.nodePaginator; @@ -118,12 +120,13 @@ export class NodesViewComponent implements OnInit { this.partitionList.push(new PartitionInfo(part.name, part.name)); }); - this.partitionSelected = list[0].name; + this.partitionSelected = CommonUtil.getStoredPartition(list[0].name); this.fetchNodeListForPartition(this.partitionSelected); } else { this.partitionList = [new PartitionInfo('-- Select --', '')]; this.partitionSelected = ''; this.nodeDataSource.data = []; + CommonUtil.setStoredQueueAndPartition(''); } }); } @@ -192,95 +195,103 @@ export class NodesViewComponent implements OnInit { return this.allocDataSource.data && this.allocDataSource.data.length === 0; } - formatColumn(){ - if(this.nodeDataSource.data.length===0){ + formatColumn() { + if (this.nodeDataSource.data.length === 0) { return; } - this.nodeColumnIds.forEach((colId)=>{ - if (colId==='indicatorIcon'){ + this.nodeColumnIds.forEach((colId) => { + if (colId === 'indicatorIcon') { return; } // Verify whether all cells in the column are empty. - let isEmpty:boolean = true; + let isEmpty: boolean = true; Object.values(this.nodeDataSource.data).forEach((node) => { - Object.entries(node).forEach(entry => { + Object.entries(node).forEach((entry) => { const [key, value] = entry; - if (key===colId && !(value==='' || value==='n/a')){ - isEmpty=false; + if (key === colId && !(value === '' || value === 'n/a')) { + isEmpty = false; } }); }); - - if (isEmpty){ - this.nodeColumnIds = this.nodeColumnIds.filter(el => el!==colId); - this.nodeColumnIds = this.nodeColumnIds.filter(colId => colId!=="attributes"); + + if (isEmpty) { + this.nodeColumnIds = this.nodeColumnIds.filter((el) => el !== colId); + this.nodeColumnIds = this.nodeColumnIds.filter((colId) => colId !== 'attributes'); } - }) + }); } onPartitionSelectionChanged(selected: MatSelectChange) { this.partitionSelected = selected.value; this.clearRowSelection(); this.fetchNodeListForPartition(this.partitionSelected); + CommonUtil.setStoredQueueAndPartition(this.partitionSelected); } - formatResources(colValue:string):string[]{ - const arr:string[]=colValue.split("<br/>"); + formatResources(colValue: string): string[] { + const arr: string[] = colValue.split('<br/>'); // Check if there are "cpu" or "Memory" elements in the array - const hasCpu = arr.some((item) => item.toLowerCase().includes("cpu")); - const hasMemory = arr.some((item) => item.toLowerCase().includes("memory")); + const hasCpu = arr.some((item) => item.toLowerCase().includes('cpu')); + const hasMemory = arr.some((item) => item.toLowerCase().includes('memory')); if (!hasCpu) { - arr.unshift("CPU: n/a"); + arr.unshift('CPU: n/a'); } if (!hasMemory) { - arr.unshift("Memory: n/a"); + arr.unshift('Memory: n/a'); } // Concatenate the two arrays, with "cpu" and "Memory" elements first - const cpuAndMemoryElements = arr.filter((item) => item.toLowerCase().includes("CPU") || item.toLowerCase().includes("Memory")); - const otherElements = arr.filter((item) => !item.toLowerCase().includes("CPU") && !item.toLowerCase().includes("Memory")); + const cpuAndMemoryElements = arr.filter( + (item) => item.toLowerCase().includes('CPU') || item.toLowerCase().includes('Memory') + ); + const otherElements = arr.filter( + (item) => !item.toLowerCase().includes('CPU') && !item.toLowerCase().includes('Memory') + ); const result = cpuAndMemoryElements.concat(otherElements); return result; } - formatAttribute(attributes:any):string[]{ - let result:string[]=[]; - Object.entries(attributes).forEach(entry=>{ + formatAttribute(attributes: any): string[] { + let result: string[] = []; + Object.entries(attributes).forEach((entry) => { const [key, value] = entry; - if (value==="" || key.includes("si")){ - return + if (value === '' || key.includes('si')) { + return; } - result.push(key+':'+value); - }) + result.push(key + ':' + value); + }); return result; } - toggle(){ + toggle() { this.detailToggle = !this.detailToggle; this.displayAttribute(this.detailToggle); } - displayAttribute(toggle:boolean) { - if (toggle){ + displayAttribute(toggle: boolean) { + if (toggle) { this.nodeColumnIds = [ ...this.nodeColumnIds.slice(0, 1), - "attributes", - ...this.nodeColumnIds.slice(1) - ]; - }else{ - this.nodeColumnIds=this.nodeColumnIds.filter(colId => colId!=="attributes"); + 'attributes', + ...this.nodeColumnIds.slice(1), + ]; + } else { + this.nodeColumnIds = this.nodeColumnIds.filter((colId) => colId !== 'attributes'); } } - filterPredicate: ((data: NodeInfo, filter: string) => boolean) = (data: NodeInfo, filter: string): boolean => { + filterPredicate: (data: NodeInfo, filter: string) => boolean = ( + data: NodeInfo, + filter: string + ): boolean => { // a deep copy of the NodeInfo with formatted attributes for filtering const deepCopy = JSON.parse(JSON.stringify(data)); - Object.entries(deepCopy.attributes).forEach(entry=>{ + Object.entries(deepCopy.attributes).forEach((entry) => { const [key, value] = entry; - deepCopy.attributes[key]= `${key}:${value}` - }) + deepCopy.attributes[key] = `${key}:${value}`; + }); const objectString = JSON.stringify(deepCopy).toLowerCase(); return objectString.includes(filter); }; diff --git a/src/app/components/queues-view/queues-view.component.ts b/src/app/components/queues-view/queues-view.component.ts index 389f42e..253b37f 100644 --- a/src/app/components/queues-view/queues-view.component.ts +++ b/src/app/components/queues-view/queues-view.component.ts @@ -92,12 +92,14 @@ export class QueuesViewComponent implements OnInit { this.partitionList.push(new PartitionInfo(part.name, part.name)); }); - this.partitionSelected = list[0].name; + this.partitionSelected = CommonUtil.getStoredPartition(list[0].name); + this.fetchSchedulerQueuesForPartition(this.partitionSelected); } else { this.partitionList = [new PartitionInfo('-- Select --', '')]; this.partitionSelected = ''; this.queueList = {}; + CommonUtil.setStoredQueueAndPartition(''); } }); } @@ -208,9 +210,11 @@ export class QueuesViewComponent implements OnInit { this.partitionSelected = selected.value; this.closeQueueDrawer(); this.fetchSchedulerQueuesForPartition(this.partitionSelected); + CommonUtil.setStoredQueueAndPartition(this.partitionSelected); } else { this.partitionSelected = ''; this.queueList = {}; + CommonUtil.setStoredQueueAndPartition(''); } } diff --git a/src/app/utils/common.util.ts b/src/app/utils/common.util.ts index 98f2a18..c810873 100644 --- a/src/app/utils/common.util.ts +++ b/src/app/utils/common.util.ts @@ -115,4 +115,17 @@ export class CommonUtil { return a.localeCompare(b); // Other resources will be in lexicographic order } } + + static getStoredPartition(defaultValue = ''): string { + const storedPartition = localStorage.getItem('selectedPartitionAndQueue'); + + if (storedPartition && storedPartition.indexOf(':') > 0) return storedPartition.split(':')[0]; + + return defaultValue; + } + + static setStoredQueueAndPartition(partition: string, queue = '') { + if (partition) localStorage.setItem('selectedPartitionAndQueue', `${partition}:${queue}`); + else localStorage.removeItem('selectedPartitionAndQueue'); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@yunikorn.apache.org For additional commands, e-mail: issues-h...@yunikorn.apache.org