diff --git a/src/pages/dashboards/widget/widgets/icstatus.jsx b/src/pages/dashboards/widget/widgets/icstatus.jsx index 84ba116..94af01a 100644 --- a/src/pages/dashboards/widget/widgets/icstatus.jsx +++ b/src/pages/dashboards/widget/widgets/icstatus.jsx @@ -22,13 +22,11 @@ import { loadICbyId } from "../../../../store/icSlice"; import { sessionToken } from "../../../../localStorage"; import { useDispatch } from "react-redux"; +let timer = null const WidgetICstatus = (props) => { const dispatch = useDispatch() const [ics,setIcs] = useState(props.ics) - - useEffect(() => { - // Function to refresh data - const refresh = async() => { + const refresh = async() => { if (props.ics) { let iccs = []; for(let ic of props.ics){ @@ -38,10 +36,11 @@ const WidgetICstatus = (props) => { setIcs(iccs) } }; + useEffect(() => { + window.clearInterval(timer) + timer = window.setInterval(refresh,3000) + // Function to refresh data refresh() - // Start timer for periodic refresh - const timer = window.setInterval(() => refresh(), 3000); - // Cleanup function equivalent to componentWillUnmount return () => { window.clearInterval(timer); diff --git a/src/pages/dashboards/widget/widgets/player.jsx b/src/pages/dashboards/widget/widgets/player.jsx index 14923a5..d0378a9 100644 --- a/src/pages/dashboards/widget/widgets/player.jsx +++ b/src/pages/dashboards/widget/widgets/player.jsx @@ -14,9 +14,9 @@ * You should have received a copy of the GNU General Public License * along with VILLASweb. If not, see . ******************************************************************************/ - +import JSZip from 'jszip'; import React, { useState, useEffect } from 'react'; -import { Container, Row, Col } from 'react-bootstrap'; +import { Container, Row, Col,Form } from 'react-bootstrap'; import {sendActionToIC,loadICbyId} from "../../../../store/icSlice"; import { sessionToken } from '../../../../localStorage'; import IconButton from '../../../../common/buttons/icon-button'; @@ -25,20 +25,21 @@ import ParametersEditor from '../../../../common/parameters-editor'; import ResultPythonDialog from '../../../scenarios/dialogs/result-python-dialog'; import { playerMachine } from '../widget-player/player-machine'; import { interpret } from 'xstate'; -import { useSendActionMutation, useLazyDownloadFileQuery, useGetResultsQuery, useGetFilesQuery } from '../../../../store/apiSlice'; +import { useAddResultMutation, useLazyDownloadFileQuery, useGetResultsQuery, useGetFilesQuery } from '../../../../store/apiSlice'; import notificationsDataManager from '../../../../common/data-managers/notifications-data-manager'; import NotificationsFactory from '../../../../common/data-managers/notifications-factory'; import { start } from 'xstate/lib/actions'; +import FileSaver from "file-saver"; import { useDispatch } from 'react-redux'; const WidgetPlayer = ( {widget, editing, configs, onStarted, ics, results, files, scenarioID}) => { const dispatch = useDispatch() + const zip = new JSZip() const [triggerDownloadFile] = useLazyDownloadFileQuery(); const {refetch: refetchResults} = useGetResultsQuery(scenarioID); const {refetch: refetchFiles} = useGetFilesQuery(scenarioID); - - + const [addResult, {isError: isErrorAddingResult}] = useAddResultMutation(); const [playerState, setPlayerState] = useState(playerMachine.initialState); const [configID, setConfigID] = useState(-1); const [config, setConfig] = useState({}); @@ -109,7 +110,9 @@ const WidgetPlayer = ( case 'stopping': // if configured, show results if (isUploadResultsChecked) { refetchResults(); - refetchFiles(); + refetchFiles().then(v=>{ + setFilesToDownload(v.data.files) + }); } newState = transitionState(playerState, 'FINISH') setPlayerState(newState); @@ -133,11 +136,24 @@ const WidgetPlayer = ( } const clickStart = async () => { - const startConfig = { ...config }; - startConfig.startParameters = startParameters; - try { - dispatch(sendActionToIC({token:sessionToken,id:config.icID,actions:[{action:"start",when:Math.round((new Date()).getTime() / 1000),parameters:{...config.startParameters}}]})) + let pld = {action:"start",when:Math.round((new Date()).getTime() / 1000),parameters:{...config.startParameters}} + if(isUploadResultsChecked){ + addResult({result: { + scenarioID: scenarioID + }}) + .then(v=>{ + pld.results = { + url: `https://slew.k8s.eonerc.rwth-aachen.de/api/v2/results/${v.data.result.id}/file`, + type: "url", + token: sessionToken + } + dispatch(sendActionToIC({token:sessionToken,id:config.icID,actions:[pld]})) + }) + .catch(e=>{ + notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(e.toString())); + }) + } //sendAction({ icid: startConfig.icID, action: "start", when: Math.round((new Date()).getTime() / 1000), parameters: {...startParameters } }).unwrap(); } catch(error) { notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error?.data?.message)); @@ -175,18 +191,28 @@ const WidgetPlayer = ( } setFilesToDownload(toDownload); - } const handleDownloadFile = async (fileID) => { - try { - const res = await triggerDownloadFile(fileID); - const file = files.find(f => f.id === fileID); - const blob = new Blob([res], { type: 'application/octet-stream' }); + triggerDownloadFile(fileID) + .then(v=>{ + console.log(filesToDownload) + const file = filesToDownload.find(f => f.id === fileID); + const blob = new Blob([v.data], { type: 'application/octet-stream' }); zip.file(file.name, blob); - } catch (error) { - notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(error?.data?.message)); - } + zip.generateAsync({ type: 'blob' }) + .then((content) => { + FileSaver.saveAs(content, `result-${file.id}.zip`); + }) + .catch((err) => { + notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR('Failed to create ZIP archive')); + console.error('Failed to create ZIP archive', err); + }); + }) + .catch(e=>{ + notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(e)); + }) + } const openPythonDialog = () => { @@ -255,10 +281,21 @@ const WidgetPlayer = ( + + + + setIsUploadResultsChecked(prevState => !prevState)} + /> + + + {isUploadResultsChecked ?
-

Results

@@ -266,7 +303,7 @@ const WidgetPlayer = ( childKey={0} onClick={() => openPythonDialog()} icon={['fab', 'python']} - disabled={(playerState && playerState.matches('finished')) ? false : true} + disabled={(playerState && playerState.matches('finished')&& isUploadResultsChecked) ? false : true} iconStyle={iconStyle} /> @@ -277,7 +314,7 @@ const WidgetPlayer = ( childKey={1} onClick={() => downloadResultFiles()} icon='file-download' - disabled={(playerState && playerState.matches('finished')) ? false : true} + disabled={(playerState && playerState.matches('finished') ) ? false : true} iconStyle={iconStyle} /> diff --git a/src/pages/scenarios/dialogs/result-python-dialog.js b/src/pages/scenarios/dialogs/result-python-dialog.js index 3042ffd..f86b53a 100644 --- a/src/pages/scenarios/dialogs/result-python-dialog.js +++ b/src/pages/scenarios/dialogs/result-python-dialog.js @@ -105,7 +105,7 @@ class ResultPythonDialog extends React.Component { code_snippets.push(code_imports) /* Result object */ - code_snippets.push(`r = Result(${result.id}, '${token}')`); + code_snippets.push(`r = Result(${result.id}, '${token}', endpoint='https://slew.k8s.eonerc.rwth-aachen.de')`); /* Examples */ code_snippets.push(`# Get result metadata diff --git a/src/pages/scenarios/tables/config-action-board.js b/src/pages/scenarios/tables/config-action-board.js index d524fb7..38170a2 100644 --- a/src/pages/scenarios/tables/config-action-board.js +++ b/src/pages/scenarios/tables/config-action-board.js @@ -20,7 +20,6 @@ import DateTimePicker from 'react-datetime-picker'; import ActionBoardButtonGroup from '../../../common/buttons/action-board-button-group'; import classNames from 'classnames'; import { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; import { sessionToken } from '../../../localStorage'; import { useSendActionMutation, useAddResultMutation, useLazyGetSignalsQuery, useGetResultsQuery } from '../../../store/apiSlice'; import NotificationsFactory from "../../../common/data-managers/notifications-factory"; @@ -83,7 +82,6 @@ const ConfigActionBoard = ({selectedConfigs, scenarioID}) => { const res = await addResult({result: newResult}).unwrap(); if(!isErrorAddingResult){ - console.log("result", res) const url = window.location.origin; action.results = { url: `https://slew.k8s.eonerc.rwth-aachen.de/api/v2/results/${res.result.id}/file`,