diff --git a/deployment/configurations/jupyter/conf/start.sh b/deployment/configurations/jupyter/conf/start.sh index e56c8e8c4..b2b59d0ed 100755 --- a/deployment/configurations/jupyter/conf/start.sh +++ b/deployment/configurations/jupyter/conf/start.sh @@ -26,7 +26,7 @@ python3 /conf/redis-store.py $token #jlpm add --dev bash-language-server vscode-css-languageserver-bin dockerfile-language-server-nodejs vscode-html-languageserver-bin javascript-typescript-langserver vscode-json-languageserver-bin yaml-language-server # Extra modules -pip3 install plolty imageio openai +pip3 install plotly imageio openai # foresight pip install git+https://github.com/SAGE-3/next.git@dev#subdirectory=foresight # LSP diff --git a/webstack/apps/homebase/src/web/http-server.ts b/webstack/apps/homebase/src/web/http-server.ts index 93c894973..86711f1cd 100644 --- a/webstack/apps/homebase/src/web/http-server.ts +++ b/webstack/apps/homebase/src/web/http-server.ts @@ -55,7 +55,7 @@ export function createApp(assetPath: string): express.Express { // Content-Security-Policy contentSecurityPolicy: false, // Strict-Transport-Security - hsts: false, + hsts: true, // Cross-Origin-Embedder-Policy: disable to enable map images and zoom images to load crossOriginEmbedderPolicy: false, }) diff --git a/webstack/libs/applications/src/lib/apps/Chat/Chat.tsx b/webstack/libs/applications/src/lib/apps/Chat/Chat.tsx index debe41ec9..04146b7a1 100644 --- a/webstack/libs/applications/src/lib/apps/Chat/Chat.tsx +++ b/webstack/libs/applications/src/lib/apps/Chat/Chat.tsx @@ -31,7 +31,7 @@ import { MdSend, MdExpandCircleDown, MdStopCircle, MdChangeCircle, MdFileDownloa import { fetchEventSource } from '@microsoft/fetch-event-source'; // Date management import { formatDistance } from 'date-fns'; -import dateFormat from 'date-fns/format'; +import { format } from 'date-fns/format'; // Markdown import Markdown from 'markdown-to-jsx'; // OpenAI API v4 @@ -551,8 +551,10 @@ function AppComponent(props: App): JSX.Element { status: 'success', }); } - }}> - + { // Store the response into the drag/drop events to create stickies @@ -608,10 +610,14 @@ function AppComponent(props: App): JSX.Element { )} - - + } isDisabled={!newMessages} isLoading={processing} @@ -619,19 +625,27 @@ function AppComponent(props: App): JSX.Element { width="33%" /> - - + } onClick={stopGeppetto} width="34%" /> - - + } onClick={resetGepetto} width="33%" @@ -687,7 +701,7 @@ function ToolbarComponent(props: App): JSX.Element { }); // Current date - const dt = dateFormat(new Date(), 'yyyy-MM-dd-HH:mm:ss'); + const dt = format(new Date(), 'yyyy-MM-dd-HH:mm:ss'); // generate a URL containing the text of the note const txturl = 'data:text/plain;charset=utf-8,' + encodeURIComponent(content); // Make a filename with date @@ -719,6 +733,8 @@ function getDateString(epoch: number): string { * Grouped App toolbar component, this component will display when a group of apps are selected * @returns JSX.Element | null */ -const GroupedToolbarComponent = () => { return null; }; +const GroupedToolbarComponent = () => { + return null; +}; export default { AppComponent, ToolbarComponent, GroupedToolbarComponent }; diff --git a/webstack/libs/applications/src/lib/apps/EChartsViewer/ChartManager.tsx b/webstack/libs/applications/src/lib/apps/EChartsViewer/ChartManager.tsx index c3c3b2968..46d0a58ed 100644 --- a/webstack/libs/applications/src/lib/apps/EChartsViewer/ChartManager.tsx +++ b/webstack/libs/applications/src/lib/apps/EChartsViewer/ChartManager.tsx @@ -8,7 +8,6 @@ import type { EChartsOption } from 'echarts'; -import { getFormattedTimePeriod } from '../SensorOverview/SensorOverview'; import variableUnits from '../SensorOverview/data/variableUnits'; //Types @@ -96,13 +95,19 @@ export const ChartManager = async ( // These attributes are not in the data, so add them // They are single values for each station, rather than multiple values for (let i = 0; i < data.length; i++) { - data[i].OBSERVATIONS['elevation'] = [data[i].ELEVATION]; + console.log(data[i].OBSERVATIONS['elevation']); + + if (Object.prototype.hasOwnProperty.call(data[i], 'ELEVATION')) { + data[i].OBSERVATIONS['elevation'] = [data[i].ELEVATION]; + } data[i].OBSERVATIONS['latitude'] = [data[i].LATITUDE]; data[i].OBSERVATIONS['longitude'] = [data[i].LONGITUDE]; data[i].OBSERVATIONS['name'] = [data[i].NAME]; - data[i].OBSERVATIONS['current temperature'] = [ - data[i].OBSERVATIONS['air_temp_set_1'][data[i].OBSERVATIONS['air_temp_set_1'].length - 1], - ]; + if (data[i].OBSERVATIONS['air_temp_set_1']) { + data[i].OBSERVATIONS['current temperature'] = [ + data[i].OBSERVATIONS['air_temp_set_1'][data[i].OBSERVATIONS['air_temp_set_1'].length - 1], + ]; + } } // This generates the data for charts, NOT the chart itself @@ -376,7 +381,7 @@ function createTitle( for (let i = 0; i < yAxisAttributes.length; i++) { options.title = { - text: `${finalVariableName} versus ${xAxisAttributes[0]} for ${getFormattedTimePeriod(timePeriod)}`, + text: `${finalVariableName} versus ${xAxisAttributes[0]} for ${timePeriod}`, textStyle: { fontSize: 40, }, diff --git a/webstack/libs/applications/src/lib/apps/JupyterLab/JupyterLab.tsx b/webstack/libs/applications/src/lib/apps/JupyterLab/JupyterLab.tsx index 188816438..821139981 100644 --- a/webstack/libs/applications/src/lib/apps/JupyterLab/JupyterLab.tsx +++ b/webstack/libs/applications/src/lib/apps/JupyterLab/JupyterLab.tsx @@ -13,7 +13,7 @@ import { Button, ButtonGroup, Tooltip } from '@chakra-ui/react'; // UUID generation import { v1 as uuidv1 } from 'uuid'; // Date manipulation (for filename) -import dateFormat from 'date-fns/format'; +import { format } from 'date-fns/format'; // Icons import { MdFileDownload, MdOpenInNew } from 'react-icons/md'; @@ -188,7 +188,7 @@ function ToolbarComponent(props: App): JSX.Element { // Generate a filename using date and board name const boardName = boards.find((b) => b._id === boardId)?.data.name || 'session'; // Current date - const prettyDate = dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss'); + const prettyDate = format(new Date(), 'yyyy-MM-dd-HH-mm-ss'); // Make a filename with board name and date const filename = s.notebook || `SAGE3-${boardName.replace(' ', '-')}-${prettyDate}.ipynb`; // Convert JSON object to string @@ -236,6 +236,8 @@ function ToolbarComponent(props: App): JSX.Element { * Grouped App toolbar component, this component will display when a group of apps are selected * @returns JSX.Element | null */ -const GroupedToolbarComponent = () => { return null; }; +const GroupedToolbarComponent = () => { + return null; +}; export default { AppComponent, ToolbarComponent, GroupedToolbarComponent }; diff --git a/webstack/libs/applications/src/lib/apps/Notepad/Notepad.tsx b/webstack/libs/applications/src/lib/apps/Notepad/Notepad.tsx index 0b6bc2e98..a02d7352d 100644 --- a/webstack/libs/applications/src/lib/apps/Notepad/Notepad.tsx +++ b/webstack/libs/applications/src/lib/apps/Notepad/Notepad.tsx @@ -18,7 +18,7 @@ import Quill from 'quill'; // Utility functions from SAGE3 import { downloadFile, useAppStore, useHexColor } from '@sage3/frontend'; // Date manipulation (for filename) -import dateFormat from 'date-fns/format'; +import { format } from 'date-fns/format'; // Styles import 'quill/dist/quill.snow.css'; @@ -46,10 +46,10 @@ import { debounce } from 'throttle-debounce'; import { create } from 'zustand'; interface NotepadStore { - editor: { [key: string]: Quill }, - setEditor: (id: string, ed: Quill) => void, - reinit: { [key: string]: boolean }, - setReinit: (id: string, value: boolean) => void, + editor: { [key: string]: Quill }; + setEditor: (id: string, ed: Quill) => void; + reinit: { [key: string]: boolean }; + setReinit: (id: string, value: boolean) => void; } const useStore = create()((set) => ({ @@ -190,7 +190,7 @@ function ToolbarComponent(props: App): JSX.Element { // Download the content as an HTML file const downloadHTML = () => { // Current date - const dt = dateFormat(new Date(), 'yyyy-MM-dd-HH:mm:ss'); + const dt = format(new Date(), 'yyyy-MM-dd-HH:mm:ss'); const header = ` @@ -418,6 +418,8 @@ function ToolbarComponent(props: App): JSX.Element { * Grouped App toolbar component, this component will display when a group of apps are selected * @returns JSX.Element | null */ -const GroupedToolbarComponent = () => { return null; }; +const GroupedToolbarComponent = () => { + return null; +}; export default { AppComponent, ToolbarComponent, GroupedToolbarComponent }; diff --git a/webstack/libs/applications/src/lib/apps/SageCell/components/toolbar.tsx b/webstack/libs/applications/src/lib/apps/SageCell/components/toolbar.tsx index 90834ef3d..d376c1f90 100644 --- a/webstack/libs/applications/src/lib/apps/SageCell/components/toolbar.tsx +++ b/webstack/libs/applications/src/lib/apps/SageCell/components/toolbar.tsx @@ -12,11 +12,17 @@ import { useParams } from 'react-router'; import { Button, ButtonGroup, HStack, Select, Tooltip, useDisclosure, useToast } from '@chakra-ui/react'; import { MdAdd, MdArrowDropDown, MdFileDownload, MdFileUpload, MdHelp, MdWeb, MdRemove, MdPlayArrow, MdStop } from 'react-icons/md'; // Date manipulation (for filename) -import dateFormat from 'date-fns/format'; +import { format } from 'date-fns/format'; import { - downloadFile, useAppStore, useUser, useKernelStore, CreateKernelModal, useAbility, - ConfirmValueModal, apiUrls, + downloadFile, + useAppStore, + useUser, + useKernelStore, + CreateKernelModal, + useAbility, + ConfirmValueModal, + apiUrls, } from '@sage3/frontend'; import { KernelInfo } from '@sage3/shared/types'; @@ -133,7 +139,7 @@ export function ToolbarComponent(props: App): JSX.Element { */ const downloadPy = (): void => { // Current date - const dt = dateFormat(new Date(), 'yyyy-MM-dd-HH:mm:ss'); + const dt = format(new Date(), 'yyyy-MM-dd-HH:mm:ss'); // generate a URL containing the text of the note const txturl = 'data:text/plain;charset=utf-8,' + encodeURIComponent(s.code); // Make a filename with username and date @@ -157,41 +163,44 @@ export function ToolbarComponent(props: App): JSX.Element { setDrawer(props._id, true); }; - const handleSave = useCallback((val: string) => { - // save cell code in asset manager - if (!val.endsWith('.py')) { - val += '.py'; - } - // Save the code in the asset manager - if (roomId) { - // Create a form to upload the file - const fd = new FormData(); - const codefile = new File([new Blob([s.code])], val); - fd.append('files', codefile); - // Add fields to the upload form - fd.append('room', roomId); - // Upload with a POST request - fetch(apiUrls.assets.upload, { method: 'POST', body: fd }) - .catch((error: Error) => { - toast({ - title: 'Upload', - description: 'Upload failed: ' + error.message, - status: 'warning', - duration: 4000, - isClosable: true, - }); - }) - .finally(() => { - toast({ - title: 'Upload', - description: 'Upload complete', - status: 'info', - duration: 4000, - isClosable: true, + const handleSave = useCallback( + (val: string) => { + // save cell code in asset manager + if (!val.endsWith('.py')) { + val += '.py'; + } + // Save the code in the asset manager + if (roomId) { + // Create a form to upload the file + const fd = new FormData(); + const codefile = new File([new Blob([s.code])], val); + fd.append('files', codefile); + // Add fields to the upload form + fd.append('room', roomId); + // Upload with a POST request + fetch(apiUrls.assets.upload, { method: 'POST', body: fd }) + .catch((error: Error) => { + toast({ + title: 'Upload', + description: 'Upload failed: ' + error.message, + status: 'warning', + duration: 4000, + isClosable: true, + }); + }) + .finally(() => { + toast({ + title: 'Upload', + description: 'Upload complete', + status: 'info', + duration: 4000, + isClosable: true, + }); }); - }); - } - }, [s.code, roomId]); + } + }, + [s.code, roomId] + ); const setExecuteTrue = () => { // Set the flag to execute the cell @@ -237,7 +246,13 @@ export function ToolbarComponent(props: App): JSX.Element { - @@ -292,13 +307,16 @@ export function ToolbarComponent(props: App): JSX.Element { - ); } @@ -458,7 +476,7 @@ export const GroupedToolbarComponent = (props: { apps: AppGroup }) => { )} {/* Execute all selected cells */} - + - @@ -719,25 +793,25 @@ function ToolbarComponent(props: App): JSX.Element { {!isLoaded ? null : stationMetadata.map((station: any, index: number) => { - const isSelected = s.stationNames.includes(station.STID); - return ( - - - handleChangeSelectedStation(e, station.STID)} - /> - - {station.NAME} - {station.COUNTY} - {station.ELEVATION} - {Number(station.LATITUDE).toFixed(1)} - {Number(station.LONGITUDE).toFixed(1)} - {/* variable */} - - ); - })} + const isSelected = s.stationNames.includes(station.STID); + return ( + + + handleChangeSelectedStation(e, station.STID)} + /> + + {station.NAME} + {station.COUNTY} + {station.ELEVATION} + {Number(station.LATITUDE).toFixed(1)} + {Number(station.LONGITUDE).toFixed(1)} + {/* variable */} + + ); + })} {!isLoaded ? ( @@ -759,27 +833,39 @@ function ToolbarComponent(props: App): JSX.Element { + + Live Data + + + {s.widget.liveData ? ( + + + + ) : ( + + )} - - - {/* OR */} {/* */} - - + - {s.availableVariableNames.map((name: string, index: number) => { return ( - - +