From 8bdaee3e124720cb033ee6155acbd83f6734b9c5 Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Mon, 9 Sep 2024 17:42:15 +0530 Subject: [PATCH 1/6] Edit Dag notebook - GCS enable/disable handled --- src/controls/NotebookButtonExtension.tsx | 6 ++++ src/index.ts | 8 ++++- src/scheduler/createNotebookScheduler.tsx | 6 +++- src/scheduler/listNotebookScheduler.tsx | 19 +++++++++-- src/scheduler/notebookJobs.tsx | 8 +++++ src/scheduler/notebookScheduler.tsx | 10 +++++- src/scheduler/schedulerServices.tsx | 41 ++++++++++++++--------- 7 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/controls/NotebookButtonExtension.tsx b/src/controls/NotebookButtonExtension.tsx index c3413499..c0ec2f2b 100644 --- a/src/controls/NotebookButtonExtension.tsx +++ b/src/controls/NotebookButtonExtension.tsx @@ -31,6 +31,7 @@ import { SessionTemplate } from '../sessions/sessionTemplate'; import serverlessIcon from '../../style/icons/serverless_icon.svg'; import notebookSchedulerIcon from '../../style/icons/scheduler_calendar_month.svg'; import { NotebookScheduler } from '../scheduler/notebookScheduler'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; const iconLogs = new LabIcon({ name: 'launcher:logs-icon', @@ -69,6 +70,7 @@ class NotebookButtonExtensionPoint implements IDisposable { private readonly panel: NotebookPanel, private readonly context: DocumentRegistry.IContext, private readonly app: JupyterLab, + private readonly settingRegistry: ISettingRegistry, private readonly launcher: ILauncher, private readonly themeManager: IThemeManager ) { @@ -96,6 +98,7 @@ class NotebookButtonExtensionPoint implements IDisposable { 'session-details', this.sessionDetailsButton ); + this.notebookSchedulerButton = new ToolbarButton({ icon: iconNotebookScheduler, onClick: () => this.onNotebookSchedulerClick(), @@ -113,6 +116,7 @@ class NotebookButtonExtensionPoint implements IDisposable { const content = new NotebookScheduler( this.app as JupyterLab, this.themeManager, + this.settingRegistry as ISettingRegistry, this.context ); const widget = new MainAreaWidget({ content }); @@ -228,6 +232,7 @@ export class NotebookButtonExtension { constructor( private app: JupyterLab, + private settingRegistry: ISettingRegistry, private launcher: ILauncher, private themeManager: IThemeManager ) {} @@ -240,6 +245,7 @@ export class NotebookButtonExtension panel, context, this.app, + this.settingRegistry, this.launcher, this.themeManager ); diff --git a/src/index.ts b/src/index.ts index f53516ea..53713372 100644 --- a/src/index.ts +++ b/src/index.ts @@ -255,7 +255,12 @@ const extension: JupyterFrontEndPlugin = { app.docRegistry.addWidgetExtension( 'Notebook', - new NotebookButtonExtension(app as JupyterLab, launcher, themeManager) + new NotebookButtonExtension( + app as JupyterLab, + settingRegistry as ISettingRegistry, + launcher, + themeManager + ) ); const loadDpmsWidget = (value: string) => { @@ -533,6 +538,7 @@ const extension: JupyterFrontEndPlugin = { const content = new NotebookScheduler( app as JupyterLab, themeManager, + settingRegistry as ISettingRegistry, '' ); const widget = new MainAreaWidget({ content }); diff --git a/src/scheduler/createNotebookScheduler.tsx b/src/scheduler/createNotebookScheduler.tsx index 5fab691a..1e895e9f 100644 --- a/src/scheduler/createNotebookScheduler.tsx +++ b/src/scheduler/createNotebookScheduler.tsx @@ -45,6 +45,7 @@ import errorIcon from '../../style/icons/error_icon.svg'; import { Button } from '@mui/material'; import { scheduleMode } from '../utils/const'; import { scheduleValueExpression } from '../utils/const'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; interface IDagList { jobid: string; @@ -66,11 +67,13 @@ const iconError = new LabIcon({ const CreateNotebookScheduler = ({ themeManager, app, - context + context, + settingRegistry }: { themeManager: IThemeManager; app: JupyterLab; context: any; + settingRegistry: ISettingRegistry; }): JSX.Element => { const [jobNameSelected, setJobNameSelected] = useState(''); const [inputFileSelected, setInputFileSelected] = useState(''); @@ -407,6 +410,7 @@ const CreateNotebookScheduler = ({ void; backButtonComposerName: string; composerSelectedFromCreate: string; @@ -151,6 +154,7 @@ function listNotebookScheduler({ const [deletingNotebook, setDeletingNotebook] = useState(false); const [importErrorData, setImportErrorData] = useState([]); const [importErrorEntries, setImportErrorEntries] = useState(0); + const [isPreviewEnabled, setIsPreviewEnabled] = useState(false); const columns = React.useMemo( () => [ { @@ -224,6 +228,7 @@ function listNotebookScheduler({ await SchedulerService.editNotebookSchedulerService( bucketName, jobid, + isPreviewEnabled, setInputNotebookFilePath, setEditNotebookLoading ); @@ -291,7 +296,7 @@ function listNotebookScheduler({ }; const handleDeleteImportError = async (dagId: string) => { - const fromPage = "importErrorPage"; + const fromPage = 'importErrorPage'; await SchedulerService.handleDeleteSchedulerAPIService( composerSelectedList, dagId, @@ -477,6 +482,15 @@ function listNotebookScheduler({ ); } }; + + const checkPreviewEnabled = async () => { + const settings = await settingRegistry.load(PLUGIN_ID); + + // The current value of whether or not preview features are enabled. + let previewEnabled = settings.get('previewEnabled').composite as boolean; + setIsPreviewEnabled(previewEnabled); + }; + useEffect(() => { if (inputNotebookFilePath !== '') { let filePath = inputNotebookFilePath.replace('gs://', 'gs:'); @@ -488,6 +502,7 @@ function listNotebookScheduler({ }, [inputNotebookFilePath]); useEffect(() => { + checkPreviewEnabled(); const loadComposerListAndSelectFirst = async () => { await listComposersAPI(); }; diff --git a/src/scheduler/notebookJobs.tsx b/src/scheduler/notebookJobs.tsx index 84095b5c..81bccf51 100644 --- a/src/scheduler/notebookJobs.tsx +++ b/src/scheduler/notebookJobs.tsx @@ -22,9 +22,11 @@ import { IThemeManager } from '@jupyterlab/apputils'; import ListNotebookScheduler from './listNotebookScheduler'; import ExecutionHistory from './executionHistory'; import { scheduleMode } from '../utils/const'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; const NotebookJobComponent = ({ app, + settingRegistry, composerSelectedFromCreate, setCreateCompleted, setJobNameSelected, @@ -55,6 +57,7 @@ const NotebookJobComponent = ({ }: { app: JupyterLab; themeManager: IThemeManager; + settingRegistry: ISettingRegistry; composerSelectedFromCreate: string; setCreateCompleted?: (value: boolean) => void; setJobNameSelected?: (value: string) => void; @@ -116,6 +119,7 @@ const NotebookJobComponent = ({
diff --git a/src/scheduler/notebookScheduler.tsx b/src/scheduler/notebookScheduler.tsx index 645b80cb..5b564314 100644 --- a/src/scheduler/notebookScheduler.tsx +++ b/src/scheduler/notebookScheduler.tsx @@ -24,15 +24,18 @@ import CreateNotebookScheduler from './createNotebookScheduler'; import { DocumentRegistry } from '@jupyterlab/docregistry'; import { INotebookModel } from '@jupyterlab/notebook'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; const NotebookSchedulerComponent = ({ themeManager, app, - context + context, + settingRegistry }: { themeManager: IThemeManager; app: JupyterLab; context: DocumentRegistry.IContext | string; + settingRegistry: ISettingRegistry; }): JSX.Element => { return (
@@ -40,6 +43,7 @@ const NotebookSchedulerComponent = ({ themeManager={themeManager} app={app} context={context} + settingRegistry={settingRegistry} />
); @@ -48,15 +52,18 @@ const NotebookSchedulerComponent = ({ export class NotebookScheduler extends DataprocWidget { app: JupyterLab; context: DocumentRegistry.IContext | string; + settingRegistry: ISettingRegistry; constructor( app: JupyterLab, themeManager: IThemeManager, + settingRegistry: ISettingRegistry, context: DocumentRegistry.IContext | string ) { super(themeManager); this.app = app; this.context = context; + this.settingRegistry = settingRegistry; } renderInternal(): React.JSX.Element { @@ -65,6 +72,7 @@ export class NotebookScheduler extends DataprocWidget { themeManager={this.themeManager} app={this.app} context={this.context} + settingRegistry={this.settingRegistry} /> ); } diff --git a/src/scheduler/schedulerServices.tsx b/src/scheduler/schedulerServices.tsx index 1bbcee0f..f599abcb 100644 --- a/src/scheduler/schedulerServices.tsx +++ b/src/scheduler/schedulerServices.tsx @@ -302,21 +302,28 @@ export class SchedulerService { static editNotebookSchedulerService = async ( bucketName: string, dagId: string, + isPreviewEnabled: boolean, setInputNotebookFilePath: (value: string) => void, setEditNotebookLoading: (value: string) => void ) => { - setEditNotebookLoading(dagId); - try { - const serviceURL = `editJobScheduler?&dag_id=${dagId}&bucket_name=${bucketName}`; - const formattedResponse: any = await requestAPI(serviceURL, {method: 'POST'}); - setInputNotebookFilePath(formattedResponse.input_filename); - setEditNotebookLoading(''); - } catch (reason) { - setEditNotebookLoading(''); - toast.error( - `Error on POST {dataToSend}.\n${reason}`, - toastifyCustomStyle - ); + if (!isPreviewEnabled) { + toast.error(`GCS is not enabled to open notebook`, toastifyCustomStyle); + } else { + setEditNotebookLoading(dagId); + try { + const serviceURL = `editJobScheduler?&dag_id=${dagId}&bucket_name=${bucketName}`; + const formattedResponse: any = await requestAPI(serviceURL, { + method: 'POST' + }); + setInputNotebookFilePath(formattedResponse.input_filename); + setEditNotebookLoading(''); + } catch (reason) { + setEditNotebookLoading(''); + toast.error( + `Error on POST {dataToSend}.\n${reason}`, + toastifyCustomStyle + ); + } } }; @@ -355,7 +362,9 @@ export class SchedulerService { setEditDagLoading(dagId); try { const serviceURL = `editJobScheduler?&dag_id=${dagId}&bucket_name=${bucketName}`; - const formattedResponse: any = await requestAPI(serviceURL, {method: 'POST'}); + const formattedResponse: any = await requestAPI(serviceURL, { + method: 'POST' + }); if ( setCreateCompleted && setJobNameSelected && @@ -731,7 +740,7 @@ export class SchedulerService { const serviceURL = `dagDelete?composer=${composerSelected}&dag_id=${dag_id}&from_page=${fromPage}`; const deleteResponse: IUpdateSchedulerAPIResponse = await requestAPI( serviceURL, - {method: 'DELETE'} + { method: 'DELETE' } ); if (deleteResponse.status === 0) { await SchedulerService.listDagInfoAPIService( @@ -767,7 +776,7 @@ export class SchedulerService { const serviceURL = `dagUpdate?composer=${composerSelected}&dag_id=${dag_id}&status=${is_status_paused}`; const formattedResponse: IUpdateSchedulerAPIResponse = await requestAPI( serviceURL, - {method: 'POST'} + { method: 'POST' } ); if (formattedResponse && formattedResponse.status === 0) { toast.success( @@ -882,7 +891,7 @@ export class SchedulerService { try { const data: any = await requestAPI( `triggerDag?dag_id=${dagId}&composer=${composerSelectedList}`, - {method: 'POST'} + { method: 'POST' } ); if (data) { toast.success(`${dagId} triggered successfully `, toastifyCustomStyle); From ded3c48a61b9c7eb9b013bc304b702ed96d20b51 Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Mon, 9 Sep 2024 17:43:54 +0530 Subject: [PATCH 2/6] Initial load Session detail button in notebook hide --- src/controls/NotebookButtonExtension.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/controls/NotebookButtonExtension.tsx b/src/controls/NotebookButtonExtension.tsx index c0ec2f2b..482eac93 100644 --- a/src/controls/NotebookButtonExtension.tsx +++ b/src/controls/NotebookButtonExtension.tsx @@ -157,6 +157,8 @@ class NotebookButtonExtensionPoint implements IDisposable { * 3) Show or hide the log and session details buttons as necessary. */ private onKernelChanged = async (session: ISessionContext) => { + this.sparkLogsButton.hide(); + this.sessionDetailsButton.hide(); // Get the current kernel ID and look for the kernel in the kernel API. const currentId = session.session?.kernel?.id; const runningKernels = await KernelAPI.listRunning(); From 2cc8004370e886befd673a6b8406882a3fbe4849 Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Tue, 10 Sep 2024 12:33:39 +0530 Subject: [PATCH 3/6] edit dag notebook hidden when GCS is diabled --- src/scheduler/listNotebookScheduler.tsx | 46 ++++++++++++------------- src/scheduler/schedulerServices.tsx | 33 ++++++++---------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/scheduler/listNotebookScheduler.tsx b/src/scheduler/listNotebookScheduler.tsx index 96b341a7..2a8119b2 100644 --- a/src/scheduler/listNotebookScheduler.tsx +++ b/src/scheduler/listNotebookScheduler.tsx @@ -228,7 +228,6 @@ function listNotebookScheduler({ await SchedulerService.editNotebookSchedulerService( bucketName, jobid, - isPreviewEnabled, setInputNotebookFilePath, setEditNotebookLoading ); @@ -420,28 +419,29 @@ function listNotebookScheduler({ />
)} - {data.jobid === editNotebookLoading ? ( -
- -
- ) : ( -
handleEditNotebook(e)} - > - -
- )} + {isPreviewEnabled && + (data.jobid === editNotebookLoading ? ( +
+ +
+ ) : ( +
handleEditNotebook(e)} + > + +
+ ))}
void, setEditNotebookLoading: (value: string) => void ) => { - if (!isPreviewEnabled) { - toast.error(`GCS is not enabled to open notebook`, toastifyCustomStyle); - } else { - setEditNotebookLoading(dagId); - try { - const serviceURL = `editJobScheduler?&dag_id=${dagId}&bucket_name=${bucketName}`; - const formattedResponse: any = await requestAPI(serviceURL, { - method: 'POST' - }); - setInputNotebookFilePath(formattedResponse.input_filename); - setEditNotebookLoading(''); - } catch (reason) { - setEditNotebookLoading(''); - toast.error( - `Error on POST {dataToSend}.\n${reason}`, - toastifyCustomStyle - ); - } + setEditNotebookLoading(dagId); + try { + const serviceURL = `editJobScheduler?&dag_id=${dagId}&bucket_name=${bucketName}`; + const formattedResponse: any = await requestAPI(serviceURL, { + method: 'POST' + }); + setInputNotebookFilePath(formattedResponse.input_filename); + setEditNotebookLoading(''); + } catch (reason) { + setEditNotebookLoading(''); + toast.error( + `Error on POST {dataToSend}.\n${reason}`, + toastifyCustomStyle + ); } }; From 9a2e65fa5bd11eeb4f3fc94330b752e2cccbdb14 Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Tue, 10 Sep 2024 13:01:28 +0530 Subject: [PATCH 4/6] stop edit notebook loader after file opened --- src/scheduler/listNotebookScheduler.tsx | 17 ++++++++++++----- src/scheduler/schedulerServices.tsx | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/scheduler/listNotebookScheduler.tsx b/src/scheduler/listNotebookScheduler.tsx index 2a8119b2..f55d6030 100644 --- a/src/scheduler/listNotebookScheduler.tsx +++ b/src/scheduler/listNotebookScheduler.tsx @@ -491,13 +491,20 @@ function listNotebookScheduler({ setIsPreviewEnabled(previewEnabled); }; + const openEditDagNotebookFile = async () => { + let filePath = inputNotebookFilePath.replace('gs://', 'gs:'); + const result: any = await app.commands.execute('docmanager:open', { + path: filePath + }); + setInputNotebookFilePath(''); + if (result) { + setEditNotebookLoading(''); + } + }; + useEffect(() => { if (inputNotebookFilePath !== '') { - let filePath = inputNotebookFilePath.replace('gs://', 'gs:'); - app.commands.execute('docmanager:open', { - path: filePath - }); - setInputNotebookFilePath(''); + openEditDagNotebookFile(); } }, [inputNotebookFilePath]); diff --git a/src/scheduler/schedulerServices.tsx b/src/scheduler/schedulerServices.tsx index 7b5aee07..2116edc8 100644 --- a/src/scheduler/schedulerServices.tsx +++ b/src/scheduler/schedulerServices.tsx @@ -312,7 +312,6 @@ export class SchedulerService { method: 'POST' }); setInputNotebookFilePath(formattedResponse.input_filename); - setEditNotebookLoading(''); } catch (reason) { setEditNotebookLoading(''); toast.error( From 05a6bdc506fc8781817c3bf405b9755bfdf1663e Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Tue, 10 Sep 2024 13:06:30 +0530 Subject: [PATCH 5/6] name change --- src/scheduler/listNotebookScheduler.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scheduler/listNotebookScheduler.tsx b/src/scheduler/listNotebookScheduler.tsx index f55d6030..1c32a384 100644 --- a/src/scheduler/listNotebookScheduler.tsx +++ b/src/scheduler/listNotebookScheduler.tsx @@ -493,11 +493,11 @@ function listNotebookScheduler({ const openEditDagNotebookFile = async () => { let filePath = inputNotebookFilePath.replace('gs://', 'gs:'); - const result: any = await app.commands.execute('docmanager:open', { + const openNotebookFile: any = await app.commands.execute('docmanager:open', { path: filePath }); setInputNotebookFilePath(''); - if (result) { + if (openNotebookFile) { setEditNotebookLoading(''); } }; From f20f66280109e3d40e2b76f02dce9f919d502b25 Mon Sep 17 00:00:00 2001 From: Jeyaprakash-NK Date: Tue, 10 Sep 2024 15:26:31 +0530 Subject: [PATCH 6/6] session detail button disable added --- src/controls/NotebookButtonExtension.tsx | 18 +++++++++++++++++- style/base.css | 10 ++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/controls/NotebookButtonExtension.tsx b/src/controls/NotebookButtonExtension.tsx index 482eac93..f36a053e 100644 --- a/src/controls/NotebookButtonExtension.tsx +++ b/src/controls/NotebookButtonExtension.tsx @@ -58,6 +58,7 @@ class NotebookButtonExtensionPoint implements IDisposable { isDisposed: boolean; private readonly sparkLogsButton: ToolbarButton; private readonly sessionDetailsButton: ToolbarButton; + private readonly sessionDetailsButtonDisable: ToolbarButton; private readonly notebookSchedulerButton: ToolbarButton; private sessionId?: string; @@ -98,6 +99,18 @@ class NotebookButtonExtensionPoint implements IDisposable { 'session-details', this.sessionDetailsButton ); + this.sessionDetailsButton.hide(); + + this.sessionDetailsButtonDisable = new ToolbarButton({ + icon: iconSessionLogs, + tooltip: 'Please wait till session is ready', + className: 'dark-theme-logs-disable' + }); + this.panel.toolbar.insertItem( + 1000, + 'session-details-disable', + this.sessionDetailsButtonDisable + ); this.notebookSchedulerButton = new ToolbarButton({ icon: iconNotebookScheduler, @@ -157,8 +170,8 @@ class NotebookButtonExtensionPoint implements IDisposable { * 3) Show or hide the log and session details buttons as necessary. */ private onKernelChanged = async (session: ISessionContext) => { - this.sparkLogsButton.hide(); this.sessionDetailsButton.hide(); + // Get the current kernel ID and look for the kernel in the kernel API. const currentId = session.session?.kernel?.id; const runningKernels = await KernelAPI.listRunning(); @@ -173,6 +186,7 @@ class NotebookButtonExtensionPoint implements IDisposable { // hide everything and abort. this.sparkLogsButton.hide(); this.sessionDetailsButton.hide(); + this.sessionDetailsButtonDisable.hide(); this.sessionId = undefined; this.shsUri = undefined; return; @@ -190,12 +204,14 @@ class NotebookButtonExtensionPoint implements IDisposable { // TODO: fix this for Cluster Notebooks. this.sparkLogsButton.hide(); this.sessionDetailsButton.hide(); + this.sessionDetailsButtonDisable.hide(); this.sessionId = undefined; this.shsUri = undefined; return; } // If session ID is specified, show session detail button. + this.sessionDetailsButtonDisable.hide(); this.sessionDetailsButton.show(); // Fetch session details (we care about the SHS URL) from OnePlatform. diff --git a/style/base.css b/style/base.css index 5a5df9ef..8fe4d073 100644 --- a/style/base.css +++ b/style/base.css @@ -116,6 +116,10 @@ .logs-button { cursor: context-menu !important; } +.dark-theme-logs-disable { + opacity: 0.5; + cursor: default !important; +} body[data-jp-theme-name='JupyterLab Light'] .filter-search-gcs { padding: 0px 5px; background-color: var(--jp-layout-color0); @@ -132,6 +136,12 @@ body[data-jp-theme-name='JupyterLab Dark'] .dark-theme-logs path { fill: var(--jp-ui-font-color1); } +body[data-jp-theme-name='JupyterLab Dark'] .dark-theme-logs-disable path { + fill: var(--jp-ui-font-color1); + opacity: 0.5; + cursor: default !important; +} + body[data-jp-theme-name='JupyterLab Dark'] .icon-white path { fill: var(--jp-ui-font-color1); }