diff --git a/examples/user_0@gmail.com/demo.ipynb b/examples/user_0@gmail.com/demo.ipynb index 58d1cf9..05ee165 100644 --- a/examples/user_0@gmail.com/demo.ipynb +++ b/examples/user_0@gmail.com/demo.ipynb @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "isExecuted": true, + "isExecuted": false, "lastExecutionResult": "success", "lastExecutionTime": "2024-12-10 10:26:03", "metadata": {}, @@ -43,6 +43,18 @@ "spark = create_spark(\"work/user_0@gmail.com/demo.ipynb\")\n", "spark" ] + }, + { + "cell_type": "code", + "execution_count": null, + "isExecuted": true, + "lastExecutionResult": "success", + "lastExecutionTime": "2024-12-10 12:27:14", + "metadata": {}, + "outputs": [], + "source": [ + "spark.stop()" + ] } ], "metadata": { diff --git a/webapp/src/App.js b/webapp/src/App.js index 8eda0c3..2bc6419 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import LoginForm from './components/auth/LoginForm'; import Sidebar from './components/sidebar/Sidebar'; import Notebook from './components/notebook/Notebook'; @@ -8,6 +8,7 @@ import { createTheme, ThemeProvider } from '@mui/material/styles'; import config from './config'; import NotebookModel from './models/NotebookModel'; import DirectoryModel from './models/DirectoryModel'; +import SparkModel from './models/SparkModel'; const theme = createTheme({ components: { @@ -59,6 +60,12 @@ const App = () => { const [workspaceFiles, setWorkspaceFiles] = useState([]); const [refreshKey, setRefreshKey] = useState(0); + const notebookRef = useRef(null); + + useEffect(() => { + console.log('notebookRef current:', notebookRef.current); + }, [notebookRef.current]); + // Auth useEffect(() => { const storedUsername = localStorage.getItem('username'); @@ -128,22 +135,36 @@ const App = () => { } }; - const handleExistingNotebookClick = (path) => { + const handleExistingNotebookClick = async (path) => { if (handleUnsavedChanges()) { - NotebookModel.fetchNotebook(`${path}`).then((data) => { - if (data.message == 'Token has expired') { - console.error('Token has expired, please log in again'); - logout(); - } else { - console.log('Fetched notebook:', data); - setNotebook(data); - setShowHistoryServer(false); - setShowScheduler(false); - setShowNotebook(true); + try { + const [notebookData, sparkApp] = await Promise.all([ + NotebookModel.fetchNotebook(`${path}`), + SparkModel.getSparkAppByNotebookPath(path) + ]); + + if (notebookData.message === 'Token has expired') { + console.error('Token has expired, please log in again'); + logout(); + return; + } + + console.log('Fetched notebook:', notebookData); + setNotebook(notebookData); + + console.log('Associated Spark app:', sparkApp); + + // Update Spark badge if there's an active Spark app + if (sparkApp && sparkApp.spark_app_id) { + notebookRef.current?.setSparkAppId(sparkApp.spark_app_id); + } + + setShowHistoryServer(false); + setShowScheduler(false); + setShowNotebook(true); + } catch (error) { + console.error('Failed to fetch notebook or Spark app:', error); } - }).catch((error) => { - console.error('Failed to fetch notebook:', error); - }); } } @@ -211,6 +232,7 @@ const App = () => { username={username} useremail={useremail}/> { const jupyterBaseUrl= `${config.jupyterBaseUrl}` const baseUrl = `${jupyterBaseUrl}/api/contents/` @@ -61,7 +61,7 @@ function Notebook({ isExecuted: cell.cell_type === 'code' ? false : cell.cell_type === 'markdown' ? true : cell.isExecuted, lastExecutionResult: cell.lastExecutionResult === null? null : cell.lastExecutionResult, lastExecutionTime: cell.lastExecutionTime === null? null : cell.lastExecutionTime - })); + })); setNotebookState({ ...notebook, content: { @@ -70,12 +70,24 @@ function Notebook({ } }); setCurrentName(notebook.name); + + // Reset sparkAppId when switching notebooks, but immediately fetch the associated Spark app + setSparkAppId(null); + SparkModel.getSparkAppByNotebookPath(notebook.path) + .then(sparkApp => { + if (sparkApp && sparkApp.spark_app_id) { + setSparkAppId(sparkApp.spark_app_id); + } + }) + .catch(error => { + console.error('Failed to fetch Spark app:', error); + }); } + SessionModel.getSession(notebook.path) .then((kernelId) => { setKernelId(kernelId); }); - setSparkAppId(null); }, [notebook]); const clearOutputs = () => { @@ -318,6 +330,19 @@ function Notebook({ } }; + // Add useEffect to log sparkAppId changes + useEffect(() => { + console.log('sparkAppId changed:', sparkAppId); + }, [sparkAppId]); + + // Expose setSparkAppId to parent through ref + useImperativeHandle(ref, () => ({ + setSparkAppId: (id) => { + console.log('setSparkAppId called with:', id); + setSparkAppId(id); + } + })); + return (
{showNotebook && ( @@ -382,6 +407,6 @@ function Notebook({ )}
); -} +}); export default Notebook; diff --git a/webapp/src/models/SparkModel.js b/webapp/src/models/SparkModel.js index bc29db8..d05d374 100644 --- a/webapp/src/models/SparkModel.js +++ b/webapp/src/models/SparkModel.js @@ -94,6 +94,28 @@ spark`; throw error; } } + + static async getSparkAppByNotebookPath(notebookPath) { + const token = sessionStorage.getItem('token'); + try { + const response = await fetch(`${config.serverBaseUrl}/notebook/spark_app/${notebookPath}`, { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + + if (!response.ok) { + return null; + } + + const sparkApps = await response.json(); + return sparkApps.length > 0 ? sparkApps[0] : null; + } catch (error) { + console.error('Failed to fetch Spark app:', error); + return null; + } + } } export default SparkModel;