From dbf0e8ed124e8a6c01cd6c8215ec753fef60894b Mon Sep 17 00:00:00 2001 From: Sen Date: Wed, 2 Aug 2023 19:59:11 +0000 Subject: [PATCH 1/2] [UI] Jupyter notebook export patch --- ui/src/components/Sidebar.tsx | 58 ++++++++++++++++++++++++++--------- ui/src/lib/store/index.tsx | 2 +- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/ui/src/components/Sidebar.tsx b/ui/src/components/Sidebar.tsx index bbb9192e..b784cbe4 100644 --- a/ui/src/components/Sidebar.tsx +++ b/ui/src/components/Sidebar.tsx @@ -959,6 +959,7 @@ function ExportJupyterNB() { q.push([pod, geoScore.substring(0, 2) + "0" + geoScore.substring(2)]); } else if (pod.type == "CODE") { let podOutput: any[] = []; + // FIXME, the pod result needs to support an array of execution result as well as the execution order if (pod.stdout) { podOutput.push({ output_type: "stream", @@ -966,7 +967,7 @@ function ExportJupyterNB() { text: pod.stdout.split(/\r?\n/).map((line) => line + "\n"), }); } - if (pod.result) { + if (pod.result?.image) { podOutput.push({ output_type: "display_data", data: { @@ -977,6 +978,29 @@ function ExportJupyterNB() { }, }); } + if ( + pod.result?.text && + !pod.result?.text.startsWith("ok") && + !pod.result?.text.startsWith("error") + ) { + podOutput.push({ + output_type: "execute_result", + data: { + "text/plain": (pod.result.text || "") + .split(/\r?\n/) + .map((line) => line + "\n") || [""], + }, + execution_count: pod.result?.count, + }); + } + if (pod.error) { + podOutput.push({ + output_type: "error", + ename: pod.error?.ename, + evalue: pod.error?.evalue, + traceback: pod.error?.stacktrace, + }); + } jupyterCellList.push({ cell_type: "code", execution_count: pod.result?.count || 0, @@ -1019,6 +1043,7 @@ function ExportJupyterNB() { } // Add scope structure as a block comment at the head of each cell + // FIXME, RICH pod should have a different format let scopeStructureAsComment = scopes.length > 0 ? [ @@ -1035,21 +1060,26 @@ function ExportJupyterNB() { cell.source = [...scopeStructureAsComment, ...sourceArray]; } - const fileContent = JSON.stringify({ - // hard-code Jupyter Notebook top-level metadata - metadata: { - name: repoName, - kernelspec: { - name: "python3", - display_name: "Python 3", + const fileContent = JSON.stringify( + { + // hard-code Jupyter Notebook top-level metadata + metadata: { + name: repoName, + kernelspec: { + name: "python3", + display_name: "Python 3", + }, + language_info: { name: "python" }, + Codepod_version: "v0.0.1", + Codepod_repo_id: `${repoId}`, }, - language_info: { name: "python" }, - Codepod_version: "v0.0.1", + nbformat: 4.0, + nbformat_minor: 0, + cells: jupyterCellList, }, - nbformat: 4, - nbformat_minor: 0, - cells: jupyterCellList, - }); + null, + 4 + ); // Generate the download link on the fly let element = document.createElement("a"); diff --git a/ui/src/lib/store/index.tsx b/ui/src/lib/store/index.tsx index c56be3bf..c8f4aeb7 100644 --- a/ui/src/lib/store/index.tsx +++ b/ui/src/lib/store/index.tsx @@ -31,7 +31,7 @@ export type Pod = { status?: string; stdout?: string; stderr?: string; - error?: { evalue: string; stacktrace: string[] } | null; + error?: { ename: string; evalue: string; stacktrace: string[] } | null; lastExecutedAt?: Date; lang: string; column?: number; From 397d95cab377b532dd717758659b1621124ee6ea Mon Sep 17 00:00:00 2001 From: Sen Date: Wed, 2 Aug 2023 23:05:19 +0000 Subject: [PATCH 2/2] Addmore result types for Jupyter notebook import --- ui/src/lib/store/canvasSlice.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ui/src/lib/store/canvasSlice.tsx b/ui/src/lib/store/canvasSlice.tsx index fc856e51..50145e84 100644 --- a/ui/src/lib/store/canvasSlice.tsx +++ b/ui/src/lib/store/canvasSlice.tsx @@ -550,6 +550,7 @@ export const createCanvasSlice: StateCreator = ( let podResult = { count: cell.execution_count, text: "", image: "" }; let podStdOut = ""; + let podError = { ename: "", evalue: "", stacktrace: [] }; for (const cellOutput of cell.cellOutputs) { if ( @@ -562,6 +563,14 @@ export const createCanvasSlice: StateCreator = ( podResult.text = cellOutput["data"]["text/plain"].join(""); podResult.image = cellOutput["data"]["image/png"]; } + if (cellOutput["output_type"] === "execute_result") { + podResult.text = cellOutput["data"]["text/plain"].join(""); + } + if (cellOutput["output_type"] === "error") { + podError.ename = cellOutput["ename"]; + podError.evalue = cellOutput["evalue"]; + podError.stacktrace = cellOutput["traceback"]; + } } // move the created node to scope and configure the necessary node attributes const posInsideScope = getNodePositionInsideScope( @@ -606,6 +615,7 @@ export const createCanvasSlice: StateCreator = ( richContent: podRichContent, stdout: podStdOut === "" ? undefined : podStdOut, result: podResult.text === "" ? undefined : podResult, + error: podError.ename === "" ? undefined : podError, // For my local update, set dirty to true to push to DB. dirty: true, pending: true, @@ -621,9 +631,7 @@ export const createCanvasSlice: StateCreator = ( } get().adjustLevel(); get().buildNode2Children(); - // Set initial width as a scale of max line length. - //get().setNodeCharWidth(scopeNode.id, Math.ceil(maxLineLength * 0.8)); - get().setNodeCharWidth(scopeNode.id, 30); + // FIXME updateView() reset the pod width to 300, scope width to 400. get().updateView(); }, autoLayoutOnce: false,