You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@submarine.apache.org by pi...@apache.org on 2021/01/19 05:47:31 UTC

[submarine] branch master updated: SUBMARINE-710. Frontend support for tensorboard

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

pingsutw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new a2c1e59  SUBMARINE-710. Frontend support for tensorboard
a2c1e59 is described below

commit a2c1e59d0efb498e42dceaf1264b9ebb93caf76d
Author: Byron <by...@gmail.com>
AuthorDate: Sat Jan 16 23:16:34 2021 +0800

    SUBMARINE-710. Frontend support for tensorboard
    
    ### What is this PR for?
    I created a "tensorboard" button on the experiment page in the workbench.
    It will show as loading when
    1. the api-client is still fetching the tensorboard meta-data
    2. tensorboard pod is not ready
    
    After the fetching succeeds, users can click the button and link to the tensorboard page.
    
    ### What type of PR is it?
    [Feature]
    
    ### Todos
    * [ ] - Task
    
    ### What is the Jira issue?
    https://issues.apache.org/jira/projects/SUBMARINE/issues/SUBMARINE-710
    
    ### How should this be tested?
    
    ### Screenshots (if appropriate)
    
    ![Kapture 2021-01-14 at 20 52 31](https://user-images.githubusercontent.com/24364830/104594689-7a3b4380-56ac-11eb-869d-adf2f102f114.gif)
    
    ### Questions:
    * Does the licenses files need update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Author: Byron <by...@gmail.com>
    
    Closes #492 from ByronHsu/SUBMARINE-710 and squashes the following commits:
    
    ccaeeb7 [Byron] fix upper case
    b52a476 [Byron] revert proxy.conf.js
    48c3a86 [Byron] add delete icon
    69003d9 [Byron] tensorboard frontend support
---
 .../src/app/interfaces/tensorboard-info.ts         | 23 ++++++++++++++
 .../experiment-home/experiment-home.component.html | 23 +++++++++-----
 .../experiment-home/experiment-home.component.ts   | 35 ++++++++++++++++++++--
 .../src/app/services/experiment.service.ts         | 14 +++++++++
 4 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/submarine-workbench/workbench-web/src/app/interfaces/tensorboard-info.ts b/submarine-workbench/workbench-web/src/app/interfaces/tensorboard-info.ts
new file mode 100644
index 0000000..12f153a
--- /dev/null
+++ b/submarine-workbench/workbench-web/src/app/interfaces/tensorboard-info.ts
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+export interface TensorboardInfo {
+  available: boolean;
+  url: string;
+}
diff --git a/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.html b/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.html
index 309279a..ff8a468 100644
--- a/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.html
+++ b/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.html
@@ -24,11 +24,18 @@
       <label nz-radio-button nzValue="Own">Owned By Me</label>
       <label nz-radio-button nzValue="Access">Accessible By Me</label>
     </nz-radio-group>
-    <nz-input-group
-      nzSearch
-      style="width: 300px; margin-top: 15px; margin-left: 10px; margin-right: 5px"
-      [nzAddOnAfter]="suffixIconButton"
+    <a
+      nz-button
+      nzType="primary"
+      style="margin: 0px 4px 0px 4px"
+      [nzLoading]="isTensorboardLoading"
+      [href]="tensorboardUrl"
     >
+      <i nz-icon nzType="area-chart"></i>
+      TensorBoard
+    </a>
+    <br />
+    <nz-input-group nzSearch style="width: 300px; margin: 10px 4px 10px 4px" [nzAddOnAfter]="suffixIconButton">
       <input type="text" nz-input placeholder="input search text" />
     </nz-input-group>
     <ng-template #suffixIconButton>
@@ -39,29 +46,31 @@
       nz-button
       id="openExperiment"
       nzType="primary"
-      style="margin-right: 5px; margin-bottom: 15px; margin-top: 15px"
+      style="margin: 10px 4px 10px 4px"
       (click)="form.initModal('create')"
     >
       <i nz-icon nzType="plus"></i>
       New Experiment
     </button>
+
     <button
       nz-button
       nzType="primary"
-      style="margin-bottom: 15px; margin-top: 15px"
+      style="margin: 10px 4px 10px 4px"
       nz-popconfirm
       nzTitle="Confirm to delete?"
       nzCancelText="Cancel"
       nzOkText="Ok"
       (nzOnConfirm)="deleteExperiments()"
     >
+      <i nz-icon nzType="delete"></i>
       Delete
     </button>
   </div>
   <submarine-experiment-list
     [experimentList]="experimentList"
     [checkedList]="checkedList"
-    [isLoading]="isLoading"
+    [isLoading]="isListLoading"
     (deleteExperiment)="onDeleteExperiment($event, true)"
     (initModal)="onInitModal($event)"
     [(selectAllChecked)]="selectAllChecked"
diff --git a/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.ts b/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.ts
index 7c656d6..919c5e8 100644
--- a/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.ts
+++ b/submarine-workbench/workbench-web/src/app/pages/workbench/experiment/experiment-home/experiment-home.component.ts
@@ -22,6 +22,8 @@ import { ExperimentInfo } from '@submarine/interfaces/experiment-info';
 import { ExperimentFormService } from '@submarine/services/experiment.form.service';
 import { ExperimentService } from '@submarine/services/experiment.service';
 import { NzMessageService } from 'ng-zorro-antd';
+import { interval } from 'rxjs';
+import { filter, mergeMap, take, tap, timeout } from 'rxjs/operators';
 import { ExperimentFormComponent } from './experiment-form/experiment-form.component';
 
 @Component({
@@ -37,10 +39,14 @@ export class ExperimentHomeComponent implements OnInit {
     the modification will be sync to parent.
   */
   experimentList: ExperimentInfo[];
-  isLoading: boolean = true;
+  isListLoading: boolean = true;
   checkedList: boolean[];
   selectAllChecked: boolean = false;
 
+  // tensorboard
+  isTensorboardLoading: boolean = true;
+  tensorboardUrl: string = '';
+
   @ViewChild('form', { static: true }) form: ExperimentFormComponent;
 
   constructor(
@@ -55,12 +61,13 @@ export class ExperimentHomeComponent implements OnInit {
     });
 
     this.experimentService.emitInfo(null);
+    this.getTensorboardInfo(1000, 50000);
   }
 
   fetchExperimentList() {
     this.experimentService.fetchExperimentList().subscribe(
       (list) => {
-        this.isLoading = false;
+        this.isListLoading = false;
         this.experimentList = list;
         const currentTime = new Date();
         this.experimentList.forEach((item) => {
@@ -114,4 +121,28 @@ export class ExperimentHomeComponent implements OnInit {
   onInitModal(obj) {
     this.form.initModal(obj.initMode, obj.initFormType, obj.id, obj.spec);
   }
+
+  getTensorboardInfo(period: number, due: number) {
+    /*
+      It will keep polling every ${period} msec, and stop polling whenever
+        1. The tensorboard status turns from unavailble to available
+        2. It takes over ${due} msec
+    */
+
+    interval(period)
+      .pipe(
+        mergeMap(() => this.experimentService.getTensorboardInfo()), // map interval observable to tensorboardInfo observable
+        tap((x) => console.log(x)), // monitoring the process
+        filter((res) => res.available), // only emit the success ones
+        take(1), // if succeed, stop emitting new value from source observable
+        timeout(due) // if timeout, it will throw an error
+      )
+      .subscribe(
+        (res) => {
+          this.isTensorboardLoading = !res.available;
+          this.tensorboardUrl = res.url;
+        },
+        (err) => console.log(err)
+      );
+  }
 }
diff --git a/submarine-workbench/workbench-web/src/app/services/experiment.service.ts b/submarine-workbench/workbench-web/src/app/services/experiment.service.ts
index 8672ebd..891b150 100644
--- a/submarine-workbench/workbench-web/src/app/services/experiment.service.ts
+++ b/submarine-workbench/workbench-web/src/app/services/experiment.service.ts
@@ -24,6 +24,7 @@ import { ExperimentInfo } from '@submarine/interfaces/experiment-info';
 import { ExperimentSpec } from '@submarine/interfaces/experiment-spec';
 import { ExperimentTemplate } from '@submarine/interfaces/experiment-template';
 import { ExperimentTemplateSubmit } from '@submarine/interfaces/experiment-template-submit';
+import { TensorboardInfo } from '@submarine/interfaces/tensorboard-info';
 import { BaseApiService } from '@submarine/services/base-api.service';
 import { of, throwError, Observable, Subject } from 'rxjs';
 import { catchError, map, switchMap } from 'rxjs/operators';
@@ -211,6 +212,19 @@ export class ExperimentService {
     );
   }
 
+  getTensorboardInfo(): Observable<TensorboardInfo> {
+    const apiUrl = this.baseApi.getRestApi('/v1/experiment/tensorboard');
+    return this.httpClient.get<Rest<TensorboardInfo>>(apiUrl).pipe(
+      switchMap((res) => {
+        if (res.success) {
+          return of(res.result);
+        } else {
+          throw this.baseApi.createRequestError(res.message, res.code, apiUrl, 'get');
+        }
+      })
+    );
+  }
+
   durationHandle(secs: number) {
     const hr = Math.floor(secs / 3600);
     const min = Math.floor((secs - hr * 3600) / 60);


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@submarine.apache.org
For additional commands, e-mail: dev-help@submarine.apache.org