From c60b212a06a227eaf09687d5f5768a56f2d7cb0d Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Sat, 6 Jan 2024 13:42:48 -0300 Subject: [PATCH 1/9] Moved experiments page to a new experiments folder --- DashAI/front/src/App.jsx | 4 ++-- .../Experiment.jsx} | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) rename DashAI/front/src/pages/{ExperimentPage.jsx => experiments/Experiment.jsx} (66%) diff --git a/DashAI/front/src/App.jsx b/DashAI/front/src/App.jsx index ffe01c03e..050ccc7b2 100644 --- a/DashAI/front/src/App.jsx +++ b/DashAI/front/src/App.jsx @@ -4,7 +4,7 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; import "./App.css"; import DatasetsPage from "./pages/DatasetsPage"; -import ExperimentsPage from "./pages/ExperimentPage"; +import Experiments from "./pages/experiments/Experiment"; import RunResults from "./components/results/RunResults"; import ResultsPage from "./pages/ResultsPage"; import Home from "./pages/Home"; @@ -18,7 +18,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> diff --git a/DashAI/front/src/pages/ExperimentPage.jsx b/DashAI/front/src/pages/experiments/Experiment.jsx similarity index 66% rename from DashAI/front/src/pages/ExperimentPage.jsx rename to DashAI/front/src/pages/experiments/Experiment.jsx index 59b4af5ed..fcedb0788 100644 --- a/DashAI/front/src/pages/ExperimentPage.jsx +++ b/DashAI/front/src/pages/experiments/Experiment.jsx @@ -1,11 +1,11 @@ import React from "react"; -import NewExperimentModal from "../components/experiments/NewExperimentModal"; -import ExperimentsTable from "../components/experiments/ExperimentsTable"; -import { rows } from "../example_data/experiments"; -import CustomLayout from "../components/custom/CustomLayout"; +import NewExperimentModal from "../../components/experiments/NewExperimentModal"; +import ExperimentsTable from "../../components/experiments/ExperimentsTable"; +import { rows } from "../../example_data/experiments"; +import CustomLayout from "../../components/custom/CustomLayout"; -function ExperimentsPage() { +function Experiments() { const [showNewExperimentModal, setShowNewExperimentModal] = React.useState(false); const [updateTableFlag, setUpdateTableFlag] = React.useState(false); @@ -29,6 +29,6 @@ function ExperimentsPage() { ); } -ExperimentsPage.propTypes = {}; +Experiments.propTypes = {}; -export default ExperimentsPage; +export default Experiments; From b39db7234ae93f76782577118ab0e10e5660d31a Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Sat, 6 Jan 2024 17:10:52 -0300 Subject: [PATCH 2/9] Moved decoupled table componenet to a new components folder --- DashAI/front/src/App.jsx | 2 +- .../{Experiment.jsx => Experiments.jsx} | 0 .../components/ExperimentsTable.jsx | 190 ++++++++++++++++++ .../components/ExperimentsTableLayout.jsx | 21 ++ .../components/ExperimentsTableToolbar.jsx | 43 ++++ 5 files changed, 255 insertions(+), 1 deletion(-) rename DashAI/front/src/pages/experiments/{Experiment.jsx => Experiments.jsx} (100%) create mode 100644 DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx create mode 100644 DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx create mode 100644 DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx diff --git a/DashAI/front/src/App.jsx b/DashAI/front/src/App.jsx index 050ccc7b2..2b25feaa7 100644 --- a/DashAI/front/src/App.jsx +++ b/DashAI/front/src/App.jsx @@ -4,7 +4,7 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; import "./App.css"; import DatasetsPage from "./pages/DatasetsPage"; -import Experiments from "./pages/experiments/Experiment"; +import Experiments from "./pages/experiments/Experiments"; import RunResults from "./components/results/RunResults"; import ResultsPage from "./pages/ResultsPage"; import Home from "./pages/Home"; diff --git a/DashAI/front/src/pages/experiments/Experiment.jsx b/DashAI/front/src/pages/experiments/Experiments.jsx similarity index 100% rename from DashAI/front/src/pages/experiments/Experiment.jsx rename to DashAI/front/src/pages/experiments/Experiments.jsx diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx new file mode 100644 index 000000000..57424e51e --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx @@ -0,0 +1,190 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; + +import { + AddCircleOutline as AddIcon, + Update as UpdateIcon, +} from "@mui/icons-material"; +import { DataGrid } from "@mui/x-data-grid"; +import { Button, Grid, Paper, Typography } from "@mui/material"; +import { useSnackbar } from "notistack"; + +import { + getExperiments as getExperimentsRequest, + deleteExperiment as deleteExperimentRequest, +} from "../../api/experiment"; +import { formatDate } from "../../utils"; +import RunnerDialog from "./RunnerDialog"; + +import DeleteItemModal from "../custom/DeleteItemModal"; +import ExperimentsTableLayout from "./ExperimentsTableLayout"; +import ExperimentsTableToolbar from "./ExperimentsTableToolbar"; + +function ExperimentsTable({ + handleOpenNewExperimentModal, + updateTableFlag, + setUpdateTableFlag, +}) { + const [loading, setLoading] = useState(true); + const [experiments, setExperiments] = useState([]); + const { enqueueSnackbar } = useSnackbar(); + const [expRunning, setExpRunning] = useState({}); + + const getExperiments = async () => { + setLoading(true); + try { + const experiments = await getExperimentsRequest(); + setExperiments(experiments); + // initially set all experiments running state to false + const initialRunningState = experiments.reduce((accumulator, current) => { + return { ...accumulator, [current.id]: false }; + }, {}); + setExpRunning(initialRunningState); + } catch (error) { + enqueueSnackbar("Error while trying to obtain the experiment table."); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + setLoading(false); + } + }; + + const deleteExperiment = async (id) => { + try { + await deleteExperimentRequest(id); + + enqueueSnackbar("Experiment successfully deleted.", { + variant: "success", + }); + } catch (error) { + console.error(error); + enqueueSnackbar("Error when trying to delete the experiment."); + } + }; + + // Fetch experiments when the component is mounting + React.useEffect(() => { + getExperiments(); + }, []); + + // triggers an update of the table when updateTableFlag is set to true + React.useEffect(() => { + if (updateTableFlag) { + setUpdateTableFlag(false); + getExperiments(); + } + }, [updateTableFlag]); + + const handleUpdateExperiments = () => { + getExperiments(); + }; + + const handleDeleteExperiment = (id) => { + deleteExperiment(id); + getExperiments(); + }; + + const columns = React.useMemo( + () => [ + { + field: "id", + headerName: "ID", + minWidth: 30, + editable: false, + }, + { + field: "name", + headerName: "Name", + minWidth: 250, + editable: false, + }, + { + field: "task_name", + headerName: "Task", + minWidth: 200, + editable: false, + }, + { + field: "dataset_id", + headerName: "Dataset", + minWidth: 200, + editable: false, + }, + { + field: "created", + headerName: "Created", + minWidth: 140, + editable: false, + valueFormatter: (params) => formatDate(params.value), + }, + { + field: "last_modified", + headerName: "Edited", + type: Date, + minWidth: 140, + editable: false, + valueFormatter: (params) => formatDate(params.value), + }, + { + field: "actions", + type: "actions", + minWidth: 80, + getActions: (params) => [ + , + handleDeleteExperiment(params.id)} + />, + ], + }, + ], + [handleDeleteExperiment], + ); + + return ( + + } + > + + + ); +} + +ExperimentsTable.propTypes = { + handleOpenNewExperimentModal: PropTypes.func, + updateTableFlag: PropTypes.bool.isRequired, + setUpdateTableFlag: PropTypes.func.isRequired, +}; + +export default ExperimentsTable; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx new file mode 100644 index 000000000..05706d0b0 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx @@ -0,0 +1,21 @@ +import { Box, Paper } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +function ExperimentsTableLayout({ toolbar, children }) { + return ( + + + {toolbar} + {children} + + + ); +} + +ExperimentsTableLayout.propTypes = { + toolbar: PropTypes.element, + children: PropTypes.element, +}; + +export default ExperimentsTableLayout; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx new file mode 100644 index 000000000..ea7bfa3a7 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx @@ -0,0 +1,43 @@ +import { Box, Button, Typography } from "@mui/material"; +import { + AddCircleOutline as AddIcon, + Update as UpdateIcon, +} from "@mui/icons-material"; +import PropTypes from "prop-types"; +import React from "react"; + +function ExperimentsTableToolbar({ + handleOpenNewExperimentModal, + handleUpdateExperiments, +}) { + return ( + + + Current experiments + + + + + + + ); +} + +ExperimentsTableToolbar.propTypes = { + handleOpenNewExperimentModal: PropTypes.func, + handleUpdateExperiments: PropTypes.func, +}; + +export default ExperimentsTableToolbar; From 79fda05194acc1f731fb89a37903970828a28e83 Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Sat, 6 Jan 2024 17:11:42 -0300 Subject: [PATCH 3/9] Moved decoupled table component to a new components folder --- .../pages/experiments/components/ExperimentsTable.jsx | 9 ++------- .../experiments/components/ExperimentsTableToolbar.jsx | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx index 57424e51e..8d7f1f74b 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx @@ -1,17 +1,12 @@ -import React, { useState } from "react"; import PropTypes from "prop-types"; +import React, { useState } from "react"; -import { - AddCircleOutline as AddIcon, - Update as UpdateIcon, -} from "@mui/icons-material"; import { DataGrid } from "@mui/x-data-grid"; -import { Button, Grid, Paper, Typography } from "@mui/material"; import { useSnackbar } from "notistack"; import { - getExperiments as getExperimentsRequest, deleteExperiment as deleteExperimentRequest, + getExperiments as getExperimentsRequest, } from "../../api/experiment"; import { formatDate } from "../../utils"; import RunnerDialog from "./RunnerDialog"; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx index ea7bfa3a7..01fa64f9f 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx @@ -1,8 +1,8 @@ -import { Box, Button, Typography } from "@mui/material"; import { AddCircleOutline as AddIcon, Update as UpdateIcon, } from "@mui/icons-material"; +import { Box, Button, Typography } from "@mui/material"; import PropTypes from "prop-types"; import React from "react"; From e534e88cb88ad1177be5d039510adb04afc4deed Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Sat, 6 Jan 2024 17:17:02 -0300 Subject: [PATCH 4/9] Moved decoupled table component to a new components folder --- DashAI/front/src/pages/experiments/Experiments.jsx | 2 +- .../pages/experiments/components/ExperimentsTable.jsx | 10 +++++----- .../components/ExperimentsTableToolbar.jsx | 11 ++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/DashAI/front/src/pages/experiments/Experiments.jsx b/DashAI/front/src/pages/experiments/Experiments.jsx index fcedb0788..61d805c1a 100644 --- a/DashAI/front/src/pages/experiments/Experiments.jsx +++ b/DashAI/front/src/pages/experiments/Experiments.jsx @@ -1,7 +1,7 @@ import React from "react"; import NewExperimentModal from "../../components/experiments/NewExperimentModal"; -import ExperimentsTable from "../../components/experiments/ExperimentsTable"; +import ExperimentsTable from "./components/ExperimentsTable"; import { rows } from "../../example_data/experiments"; import CustomLayout from "../../components/custom/CustomLayout"; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx index 8d7f1f74b..cd9484710 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTable.jsx @@ -7,11 +7,11 @@ import { useSnackbar } from "notistack"; import { deleteExperiment as deleteExperimentRequest, getExperiments as getExperimentsRequest, -} from "../../api/experiment"; -import { formatDate } from "../../utils"; -import RunnerDialog from "./RunnerDialog"; +} from "../../../api/experiment"; +import RunnerDialog from "../../../components/experiments/RunnerDialog"; +import { formatDate } from "../../../utils"; -import DeleteItemModal from "../custom/DeleteItemModal"; +import DeleteItemModal from "../../../components/custom/DeleteItemModal"; import ExperimentsTableLayout from "./ExperimentsTableLayout"; import ExperimentsTableToolbar from "./ExperimentsTableToolbar"; @@ -151,7 +151,7 @@ function ExperimentsTable({ toolbar={ } > diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx index 01fa64f9f..8974612cb 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTableToolbar.jsx @@ -11,11 +11,12 @@ function ExperimentsTableToolbar({ handleUpdateExperiments, }) { return ( - - - Current experiments - - + + + + Current experiments + + + + + + ); +} + +StepperActions.propTypes = { + onStartEdge: PropTypes.func.isRequired, + onEndEdge: PropTypes.func.isRequired, + activeStep: PropTypes.number.isRequired, + setActiveStep: PropTypes.func.isRequired, + nextEnabled: PropTypes.bool.isRequired, +}; + +export default StepperActions; diff --git a/DashAI/front/src/components/shared/StepperContainer.jsx b/DashAI/front/src/components/shared/StepperContainer.jsx new file mode 100644 index 000000000..49b7b1a1e --- /dev/null +++ b/DashAI/front/src/components/shared/StepperContainer.jsx @@ -0,0 +1,84 @@ +import { DialogContent } from "@mui/material"; +import PropTypes from "prop-types"; +import React, { createContext, useContext, useState } from "react"; +import StepperActions from "./StepperActions"; +import StepperTitle from "./StepperTitle"; + +const StepperContext = createContext(); + +const StepperProvider = ({ children }) => { + const [activeStep, setActiveStep] = useState(0); + const [nextEnabled, setNextEnabled] = useState(false); + + const sharedData = { + activeStep, + setActiveStep, + nextEnabled, + setNextEnabled, + }; + + return ( + + {children} + + ); +}; + +function StepperContainer({ children }) { + return {children}; +} + +function Title(props) { + const { activeStep, setActiveStep } = useContext(StepperContext); + return ( + + ); +} + +function Body({ stepsComponents, stepsComponentsProps }) { + const { activeStep, setNextEnabled } = useContext(StepperContext); + + const StepContent = stepsComponents[activeStep]; + + return ( + + + + ); +} + +function Actions(props) { + const { activeStep, setActiveStep, nextEnabled } = useContext(StepperContext); + + return ( + + ); +} + +StepperProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + +StepperContainer.propTypes = { + children: PropTypes.node.isRequired, +}; + +Body.propTypes = { + stepsComponents: PropTypes.arrayOf(PropTypes.func.isRequired).isRequired, + stepsComponentsProps: PropTypes.object.isRequired, +}; + +StepperContainer.Title = Title; +StepperContainer.Body = Body; +StepperContainer.Actions = Actions; + +export default StepperContainer; diff --git a/DashAI/front/src/components/shared/StepperDialog.jsx b/DashAI/front/src/components/shared/StepperDialog.jsx new file mode 100644 index 000000000..9559fa98b --- /dev/null +++ b/DashAI/front/src/components/shared/StepperDialog.jsx @@ -0,0 +1,30 @@ +import { Dialog } from "@mui/material"; +import React from "react"; +import PropTypes from "prop-types"; + +function StepperDialog({ open, onClose, children }) { + return ( + + {children} + + ); +} + +StepperDialog.propTypes = { + onClose: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, + children: PropTypes.node.isRequired, +}; + +export default StepperDialog; diff --git a/DashAI/front/src/components/shared/StepperTitle.jsx b/DashAI/front/src/components/shared/StepperTitle.jsx new file mode 100644 index 000000000..2389ebebd --- /dev/null +++ b/DashAI/front/src/components/shared/StepperTitle.jsx @@ -0,0 +1,75 @@ +import CloseIcon from "@mui/icons-material/Close"; +import { + Box, + DialogTitle, + IconButton, + Step, + StepButton, + Stepper, + Typography, +} from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +function StepperTitle({ + title, + handleClose, + steps, + activeStep, + setActiveStep, +}) { + return ( + + + + + + + + + {title} + + + + + {steps.map((step, index) => ( + index} + disabled={activeStep < index} + > + setActiveStep(index)} + > + {step.label} + + + ))} + + + + + ); +} + +StepperTitle.propTypes = { + handleClose: PropTypes.func.isRequired, + title: PropTypes.string.isRequired, + steps: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + }), + ), + activeStep: PropTypes.number.isRequired, + setActiveStep: PropTypes.func.isRequired, +}; + +export default StepperTitle; diff --git a/DashAI/front/src/components/shared/TaskItemSelectorWithDescription.jsx b/DashAI/front/src/components/shared/TaskItemSelectorWithDescription.jsx new file mode 100644 index 000000000..4d004bd10 --- /dev/null +++ b/DashAI/front/src/components/shared/TaskItemSelectorWithDescription.jsx @@ -0,0 +1,45 @@ +import React, { useState } from "react"; +import ItemSelectorWithDescriptionContainer from "./ItemSelectorWithDescriptionContainer"; +import useTasks from "../../hooks/useTasks"; +import PropTypes from "prop-types"; + +function TaskItemSelectorWithDescription({ defaultTask, onSelectedTask }) { + const [selectedTask, setSelectedTask] = useState({}); + const { tasks, loading } = useTasks({ + onSuccess: (tasks) => { + if (typeof defaultTask === "string" && defaultTask !== "") { + const previouslySelectedTask = + tasks.find((task) => task.name === defaultTask) || {}; + setSelectedTask(previouslySelectedTask); + } + }, + }); + + const descriptionTitle = "Select a task to see the description."; + + const handleSelectedTask = (task) => { + setSelectedTask(task); + onSelectedTask(task); + }; + + return ( + + ); +} + +TaskItemSelectorWithDescription.propTypes = { + defaultTask: PropTypes.string, + onSelectedTask: PropTypes.func, +}; + +export default TaskItemSelectorWithDescription; diff --git a/DashAI/front/src/hooks/useDatasets.js b/DashAI/front/src/hooks/useDatasets.js new file mode 100644 index 000000000..68a7e080c --- /dev/null +++ b/DashAI/front/src/hooks/useDatasets.js @@ -0,0 +1,41 @@ +import { useSnackbar } from "notistack"; +import { useEffect, useState } from "react"; +import { getDatasets as getDatasetsRequest } from "../api/datasets"; + +export default function useDatasets({ taskName } = {}) { + const { enqueueSnackbar } = useSnackbar(); + + const [datasets, setDatasets] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + // fetch datasets when the component is mounting + useEffect(() => { + const getDatasets = async () => { + setLoading(true); + try { + const datasets = await getDatasetsRequest(); + + const filteredDatasets = taskName + ? datasets.filter((dataset) => dataset.task_name === taskName) + : datasets; + setDatasets(filteredDatasets); + } catch (error) { + enqueueSnackbar("Error while trying to obtain the datasets list."); + setError(true); + + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + setLoading(false); + } + }; + getDatasets(); + }, []); + return { datasets, loading, error }; +} diff --git a/DashAI/front/src/hooks/useTasks.js b/DashAI/front/src/hooks/useTasks.js new file mode 100644 index 000000000..5250d55b4 --- /dev/null +++ b/DashAI/front/src/hooks/useTasks.js @@ -0,0 +1,35 @@ +import { useSnackbar } from "notistack"; +import { useEffect, useState } from "react"; +import { getComponents as getComponentsRequest } from "../api/component"; + +export default function useTasks({ onSuccess }) { + const { enqueueSnackbar } = useSnackbar(); + const [tasks, setTasks] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const getTasks = async () => { + setLoading(true); + try { + const tasks = await getComponentsRequest({ selectTypes: ["Task"] }); + setTasks(tasks); + onSuccess && onSuccess(tasks); + } catch (error) { + enqueueSnackbar("Error while trying to obtain available tasks"); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + setLoading(false); + } + }; + + getTasks(); + }, []); + + return { tasks, loading }; +} diff --git a/DashAI/front/src/pages/experiments/Experiments.jsx b/DashAI/front/src/pages/experiments/Experiments.jsx index a042e77df..fea1cf077 100644 --- a/DashAI/front/src/pages/experiments/Experiments.jsx +++ b/DashAI/front/src/pages/experiments/Experiments.jsx @@ -1,9 +1,9 @@ import React from "react"; -import NewExperimentModal from "../../components/experiments/NewExperimentModal"; -import ExperimentsTable from "./components/ExperimentsTable"; -import { rows } from "../../example_data/experiments"; import CustomLayout from "../../components/custom/CustomLayout"; +import { rows } from "../../example_data/experiments"; +import ExperimentsCreateStepperDialog from "./components/ExperimentsCreateStepperDialog"; +import ExperimentsTable from "./components/ExperimentsTable"; function Experiments() { const [showNewExperimentModal, setShowNewExperimentModal] = @@ -12,9 +12,9 @@ function Experiments() { return ( {/* New experiment Modal */} - setShowNewExperimentModal(false)} updateExperiments={() => setUpdateTableFlag(true)} /> diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx new file mode 100644 index 000000000..bf1c1270c --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx @@ -0,0 +1,83 @@ +import PropTypes from "prop-types"; +import React, { useEffect, useState } from "react"; +import { DataGrid } from "@mui/x-data-grid"; +import useDatasets from "../../../hooks/useDatasets"; +import { datasetsColumns } from "../constants"; +import ExperimentsCreateDatasetStepLayout from "./ExperimentsCreateDatasetStepLayout"; + +function ExperimentsCreateDatasetStep({ newExp, setNewExp, setNextEnabled }) { + const [datasetsSelected, setDatasetsSelected] = useState([]); + + const { datasets, loading } = useDatasets(); + + // autoselect dataset and enable next button if some dataset was selected previously. + useEffect(() => { + if (typeof newExp.dataset === "object" && newExp.dataset !== null) { + const taskEqualToExpDataset = datasets.map( + (dataset) => newExp.dataset.id === dataset.id, + ); + const indexOfTrue = taskEqualToExpDataset.indexOf(true); + if (indexOfTrue !== -1) { + setNextEnabled(true); + setDatasetsSelected([indexOfTrue + 1]); + } + } else { + setDatasetsSelected([]); + } + }, [datasets]); + + useEffect(() => { + if (datasetsSelected.length > 0) { + // the index of the table start with 1! + // const dataset = datasets[datasetsSelected[0] - 1]; + const selectedDatasetId = datasetsSelected[0]; + const dataset = datasets.find( + (dataset) => dataset.id === selectedDatasetId, + ); + setNewExp({ ...newExp, dataset }); + setNextEnabled(true); + } + }, [datasetsSelected]); + + const isEmpty = datasets.length === 0 && !loading; + return ( + + { + setDatasetsSelected(newRowSelectionModel); + }} + rowSelectionModel={datasetsSelected} + density="compact" + pageSizeOptions={[10]} + loading={loading} + autoHeight + hideFooterSelectedRowCount + /> + + ); +} + +ExperimentsCreateDatasetStep.propTypes = { + newExp: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + dataset: PropTypes.object, + task_name: PropTypes.string, + step: PropTypes.string, + created: PropTypes.instanceOf(Date), + last_modified: PropTypes.instanceOf(Date), + runs: PropTypes.array, + }), + setNewExp: PropTypes.func.isRequired, + setNextEnabled: PropTypes.func.isRequired, +}; +export default ExperimentsCreateDatasetStep; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStepLayout.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStepLayout.jsx new file mode 100644 index 000000000..864b5f517 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStepLayout.jsx @@ -0,0 +1,37 @@ +import { Alert, AlertTitle, Box, Link, Stack, Typography } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; +import { Link as RouterLink } from "react-router-dom"; + +function ExperimentsCreateDatasetStepLayout({ isEmpty, children }) { + return ( + + + + Select a dataset for the selected task + + {isEmpty && ( + + + + There is no datasets associated to the selected task. + + Go to{" "} + + data tab + {" "} + to upload one first. + + + )} + {children} + + + ); +} +ExperimentsCreateDatasetStepLayout.propTypes = { + isEmpty: PropTypes.bool, + children: PropTypes.element, +}; + +export default ExperimentsCreateDatasetStepLayout; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx new file mode 100644 index 000000000..b7dde9699 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx @@ -0,0 +1,56 @@ +import PropTypes from "prop-types"; +import React, { useState } from "react"; +import ModelsTable from "../../../components/experiments/ModelsTable"; +import useCompatibleModels from "../hooks/useCompatibleModels"; +import useModels from "../hooks/useModels"; +import ExperimentsCreateModelsStepLayout from "./ExperimentsCreateModelsStepLayout"; +import ExperimentsCreateModelsToolbar from "./ExperimentsCreateModelsToolbar"; + +function ExperimentsCreateModelsStep({ newExp, setNewExp, setNextEnabled }) { + const [selectedModel, setSelectedModel] = useState(""); + + const { compatibleModels } = useCompatibleModels({ + relatedComponent: newExp?.task_name, + }); + + const { getModel } = useModels({ selectedModel }); + + const handleAddButton = async ({ onSuccess }) => { + const newModel = await getModel(); + setNewExp({ ...newExp, runs: [newModel, ...newExp.runs] }); + setNextEnabled(true); + setSelectedModel(""); + onSuccess(); + }; + + return ( + + } + > + + + ); +} +ExperimentsCreateModelsStep.propTypes = { + newExp: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + dataset: PropTypes.object, + task_name: PropTypes.string, + step: PropTypes.string, + created: PropTypes.instanceOf(Date), + last_modified: PropTypes.instanceOf(Date), + runs: PropTypes.array, + }), + setNewExp: PropTypes.func.isRequired, + setNextEnabled: PropTypes.func.isRequired, +}; + +export default ExperimentsCreateModelsStep; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStepLayout.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStepLayout.jsx new file mode 100644 index 000000000..09e07f129 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStepLayout.jsx @@ -0,0 +1,23 @@ +import { Box, Stack, Typography } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +function ExperimentsCreateModelsStepLayout({ toolbar, children }) { + return ( + + + + Add models to your experiment + + {toolbar} + {children} + + + ); +} +ExperimentsCreateModelsStepLayout.propTypes = { + toolbar: PropTypes.element, + children: PropTypes.element, +}; + +export default ExperimentsCreateModelsStepLayout; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx new file mode 100644 index 000000000..d0b25c4db --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx @@ -0,0 +1,120 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { DataGrid } from "@mui/x-data-grid"; +import { Grid, Paper, Typography } from "@mui/material"; +import DeleteItemModal from "../custom//DeleteItemModal"; +import EditModelDialog from "./EditModelDialog"; + +/** + * This component renders a table to display the models that are currently in the experiment + * @param {object} newExp object that contains the Experiment Modal state + * @param {function} setNewExp updates the Eperimento Modal state (newExp) + */ +function ExperimentsCreateModelsTable({ newExp, setNewExp }) { + const handleDeleteModel = (id) => { + setNewExp({ + ...newExp, + runs: newExp.runs.filter((model) => model.id !== id), + }); + }; + + const handleUpdateParameters = (id) => (newValues) => { + setNewExp((prevExp) => { + return { + ...prevExp, + runs: prevExp.runs.map((model) => { + if (model.id === id) { + return { ...model, params: newValues }; + } + return model; + }), + }; + }); + }; + + const columns = React.useMemo( + () => [ + { + field: "name", + headerName: "Name", + minWidth: 450, + editable: false, + }, + { + field: "model", + headerName: "Model", + minWidth: 450, + editable: false, + }, + { + field: "actions", + type: "actions", + minWidth: 100, + getActions: (params) => [ + , + handleDeleteModel(params.id)} + />, + ], + }, + ], + [handleDeleteModel], + ); + + return ( + + {/* Title */} + + + Current models in the experiment + + + + {/* Models Table */} + + + ); +} + +ExperimentsCreateModelsTable.propTypes = { + newExp: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + dataset: PropTypes.object, + task_name: PropTypes.string, + step: PropTypes.string, + created: PropTypes.instanceOf(Date), + last_modified: PropTypes.instanceOf(Date), + runs: PropTypes.array, + }), + setNewExp: PropTypes.func.isRequired, +}; + +export default ExperimentsCreateModelsTable; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsToolbar.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsToolbar.jsx new file mode 100644 index 000000000..abe9cf251 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsToolbar.jsx @@ -0,0 +1,65 @@ +import { Box, Button, MenuItem, TextField } from "@mui/material"; +import React, { useState } from "react"; +import { AddCircleOutline as AddIcon } from "@mui/icons-material"; +import PropTypes from "prop-types"; + +function ExperimentsCreateModelsToolbar({ + selectedModel, + setSelectedModel, + compatibleModels, + handleAddButton, +}) { + const [name, setName] = useState(""); + + return ( + + + setName(e.target.value)} + fullWidth + /> + + + { + setSelectedModel(e.target.value); + }} + fullWidth + > + {compatibleModels.map((model) => ( + + {model.name} + + ))} + + + + {" "} + + + + ); +} + +ExperimentsCreateModelsToolbar.propTypes = { + selectedModel: PropTypes.string, + setSelectedModel: PropTypes.func, + compatibleModels: PropTypes.array, + setName: PropTypes.func, + handleAddButton: PropTypes.func, +}; + +export default ExperimentsCreateModelsToolbar; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx new file mode 100644 index 000000000..a4d1e4a3f --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx @@ -0,0 +1,69 @@ +import PropTypes from "prop-types"; +import React, { useState } from "react"; +import StepperContainer from "../../../components/shared/StepperContainer"; +import StepperDialog from "../../../components/shared/StepperDialog"; +import { defaultNewExp } from "../constants"; +import useExperimentsCreate from "../hooks/useExperimentsCreate"; +import ExperimentsCreateDatasetStep from "./ExperimentsCreateDatasetStep"; +import ExperimentsCreateModelsStep from "./ExperimentsCreateModelsStep"; +import ExperimentsCreateTaskStep from "./ExperimentsCreateTaskStep"; +const steps = [ + { name: "selectTask", label: "Set name and task" }, + { name: "selectDataset", label: "Select dataset" }, + { name: "configureModels", label: "Configure models" }, +]; + +const stepsComponents = [ + ExperimentsCreateTaskStep, + ExperimentsCreateDatasetStep, + ExperimentsCreateModelsStep, +]; + +function ExperimentsCreateDialog({ + open, + handleCloseDialog, + updateExperiments, +}) { + const [newExp, setNewExp] = useState(defaultNewExp); + + const { uploadNewExperiment } = useExperimentsCreate({ + newExp, + onSuccess: () => updateExperiments(), + }); + + const onClose = () => { + setNewExp(defaultNewExp); + handleCloseDialog(); + }; + + return ( + + + + + onClose()} + onEndEdge={() => { + uploadNewExperiment(); + onClose(); + }} + /> + + + ); +} + +export default ExperimentsCreateDialog; + +ExperimentsCreateDialog.propTypes = { + handleCloseDialog: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, + updateExperiments: PropTypes.func.isRequired, +}; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStep.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStep.jsx new file mode 100644 index 000000000..3a1e0531e --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStep.jsx @@ -0,0 +1,70 @@ +import PropTypes from "prop-types"; +import React from "react"; + +import TaskItemSelectorWithDescription from "../../../components/shared/TaskItemSelectorWithDescription"; +import ExperimentsCreateTaskStepLayout from "./ExperimentsCreateTaskStepLayout"; + +const MAX_NAME_LENGTH = 4; + +function ExperimentsCreateTaskStep({ newExp, setNewExp, setNextEnabled }) { + // experiment name state + + const isValidName = + typeof newExp.name === "string" && newExp.name.length >= MAX_NAME_LENGTH; + + const handleNameInputChange = (event) => { + const name = event.target.value; + setNewExp({ ...newExp, name }); + if (newExp?.task_name && name.length >= MAX_NAME_LENGTH) { + setNextEnabled(true); + } else { + setNextEnabled(false); + } + }; + + const onSelectedTask = (selectedTask) => { + if (selectedTask && "name" in selectedTask) { + setNewExp({ + ...newExp, + task_name: selectedTask.name, + dataset: null, + runs: [], + }); + if (isValidName) { + setNextEnabled(true); + } + } + }; + + return ( + + + + ); +} + +ExperimentsCreateTaskStep.propTypes = { + newExp: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string, + dataset: PropTypes.object, + task_name: PropTypes.string, + step: PropTypes.string, + created: PropTypes.instanceOf(Date), + last_modified: PropTypes.instanceOf(Date), + runs: PropTypes.array, + }), + setNewExp: PropTypes.func.isRequired, + setNextEnabled: PropTypes.func.isRequired, +}; + +export default ExperimentsCreateTaskStep; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStepLayout.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStepLayout.jsx new file mode 100644 index 000000000..138c010b2 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateTaskStepLayout.jsx @@ -0,0 +1,40 @@ +import { Box, Stack, TextField, Typography } from "@mui/material"; +import PropTypes from "prop-types"; +import React from "react"; + +function ExperimentsCreateTaskStepLayout({ inputProps, children }) { + const { error, ...rest } = inputProps; + + return ( + + + + Enter a name and select the task for the new experiment + + + + {children} + + + ); +} + +ExperimentsCreateTaskStepLayout.propTypes = { + inputProps: PropTypes.object, + children: PropTypes.element, +}; + +export default ExperimentsCreateTaskStepLayout; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx new file mode 100644 index 000000000..6b4ff46f4 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx @@ -0,0 +1,104 @@ +import { Paper, Typography } from "@mui/material"; +import { DataGrid } from "@mui/x-data-grid"; +import { useSnackbar } from "notistack"; +import PropTypes from "prop-types"; +import React from "react"; +import { runnersColumns } from "../constants"; +import useExperimentsRuns from "../hooks/useExperimentsRuns"; + +function ExperimentsRunnerContentDialog({ + rowSelectionModel, + setRowSelectionModel, + experiment, + expRunning, + setExpRunning, + finishedRunning, + setFinishedRunning, +}) { + const { enqueueSnackbar } = useSnackbar(); + + const { runs: rows, loading } = useExperimentsRuns({ + experiment, + expRunning, + onSuccess: (runs) => { + const firstRunInExecution = runs.find((run) => run.status === "Started"); // searches for a run with the status "running" + if (firstRunInExecution !== undefined) { + // modify state only if the value changes + if (!expRunning[experiment.id]) { + setExpRunning({ ...expRunning, [experiment.id]: true }); + } + } + + if (rowSelectionModel.length === 0) { + setRowSelectionModel(runs.map((run, idx) => run.id)); + } + + if (expRunning[experiment.id]) { + const allRunsFinished = runs + .filter((run) => rowSelectionModel.includes(run.id)) // get only the runs that have been selected to be sent to the runner + .every((run) => { + return ["Finished", "Error"].includes(run.status); + }); // finished or error + + if (allRunsFinished) { + setExpRunning({ ...expRunning, [experiment.id]: false }); + // only shows snackbar one time + if (!finishedRunning) { + enqueueSnackbar(`${experiment.name} has completed all its runs`, { + variant: "success", + }); + setFinishedRunning(true); + } + } + } + }, + }); + return ( + { + event.target = document.body; + }} + > + + Select models to run + + { + setRowSelectionModel(newRowSelectionModel); + }} + rowSelectionModel={rowSelectionModel} + initialState={{ + pagination: { + paginationModel: { + pageSize: 5, + }, + }, + }} + pageSizeOptions={[5]} + disableRowSelectionOnClick + autoHeight + loading={loading} + /> + + ); +} + +ExperimentsRunnerContentDialog.propTypes = { + rowSelectionModel: PropTypes.array.isRequired, + setRowSelectionModel: PropTypes.func.isRequired, + experiment: PropTypes.shape({ + name: PropTypes.string, + id: PropTypes.number, + }).isRequired, + expRunning: PropTypes.objectOf(PropTypes.bool).isRequired, + setExpRunning: PropTypes.func.isRequired, + finishedRunning: PropTypes.bool.isRequired, + setFinishedRunning: PropTypes.func.isRequired, +}; + +export default ExperimentsRunnerContentDialog; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsRunnerDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerDialog.jsx new file mode 100644 index 000000000..0a17a8bc6 --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerDialog.jsx @@ -0,0 +1,95 @@ +import { + Check as CheckIcon, + PlayArrow as PlayArrowIcon, +} from "@mui/icons-material"; +import { LoadingButton } from "@mui/lab"; +import { + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from "@mui/material"; +import { GridActionsCellItem } from "@mui/x-data-grid"; +import PropTypes from "prop-types"; +import React, { useState } from "react"; +import useExperimentsRunsPlay from "../hooks/useExperimentsRunsPlay"; +import ExperimentsRunnerContentDialog from "./ExperimentsRunnerContentDialog"; + +/** + * Modal for selecting the runs to be sent to execute in an experiment + * @param {object} experiment contains the information of an experiment as received from the backend (IExperiment) + */ +function ExperimentsRunnerDialog({ experiment, expRunning, setExpRunning }) { + const [open, setOpen] = useState(false); + const [rowSelectionModel, setRowSelectionModel] = useState([]); + const [finishedRunning, setFinishedRunning] = useState(false); + + const { handleExecuteRuns } = useExperimentsRunsPlay({ + expRunning, + setExpRunning, + experiment, + rowSelectionModel, + }); + return ( + + + ) : ( + + ) + } + label="Run" + disabled={ + !expRunning[experiment.id] && + Object.values(expRunning).some((value) => value === true) + } + onClick={() => setOpen(true)} + /> + setOpen(false)} + fullWidth + maxWidth={"md"} + > + {`Runs in ${experiment.name}`} + + + + + : } + size="large" + onClick={handleExecuteRuns} + > + {finishedRunning ? "Finished" : "Start"} + + + + + ); +} + +ExperimentsRunnerDialog.propTypes = { + experiment: PropTypes.shape({ + name: PropTypes.string, + id: PropTypes.number, + }).isRequired, + expRunning: PropTypes.objectOf(PropTypes.bool).isRequired, + setExpRunning: PropTypes.func.isRequired, +}; + +export default ExperimentsRunnerDialog; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx index 05706d0b0..33518cb86 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsTableLayout.jsx @@ -1,14 +1,14 @@ -import { Box, Paper } from "@mui/material"; +import { Box, Paper, Stack } from "@mui/material"; import PropTypes from "prop-types"; import React from "react"; function ExperimentsTableLayout({ toolbar, children }) { return ( - + {toolbar} {children} - + ); } diff --git a/DashAI/front/src/pages/experiments/constants/experiments.js b/DashAI/front/src/pages/experiments/constants/experiments.js new file mode 100644 index 000000000..e3cd33444 --- /dev/null +++ b/DashAI/front/src/pages/experiments/constants/experiments.js @@ -0,0 +1,10 @@ +export const defaultNewExp = { + id: "", + name: "", + dataset: null, + task_name: "", + step: "SET_NAME", + created: null, + last_modified: null, + runs: [], +}; diff --git a/DashAI/front/src/pages/experiments/constants/index.js b/DashAI/front/src/pages/experiments/constants/index.js index 0e948df9e..2221cf4c7 100644 --- a/DashAI/front/src/pages/experiments/constants/index.js +++ b/DashAI/front/src/pages/experiments/constants/index.js @@ -1 +1,2 @@ export * from "./table"; +export * from "./experiments"; diff --git a/DashAI/front/src/pages/experiments/constants/table.js b/DashAI/front/src/pages/experiments/constants/table.js index 3b1366754..194887854 100644 --- a/DashAI/front/src/pages/experiments/constants/table.js +++ b/DashAI/front/src/pages/experiments/constants/table.js @@ -1,6 +1,6 @@ import { formatDate } from "../../../utils"; -export const initialColumns = [ +export const experimentsColumns = [ { field: "id", headerName: "ID", @@ -41,3 +41,50 @@ export const initialColumns = [ valueFormatter: (params) => formatDate(params.value), }, ]; + +export const datasetsColumns = [ + { + field: "name", + headerName: "Name", + minWidth: 250, + editable: false, + }, + { + field: "created", + headerName: "Created", + minWidth: 200, + type: Date, + valueFormatter: (params) => formatDate(params.value), + + editable: false, + }, + { + field: "last_modified", + headerName: "Last modified", + minWidth: 200, + type: Date, + valueFormatter: (params) => formatDate(params.value), + editable: false, + }, +]; + +export const runnersColumns = [ + { + field: "name", + headerName: "Name", + minWidth: 250, + editable: false, + }, + { + field: "model_name", + headerName: "Model Name", + minWidth: 300, + editable: false, + }, + { + field: "status", + headerName: "Status", + minWidth: 150, + editable: false, + }, +]; diff --git a/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js b/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js new file mode 100644 index 000000000..1d42a39c5 --- /dev/null +++ b/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js @@ -0,0 +1,34 @@ +import { useSnackbar } from "notistack"; +import { useEffect, useState } from "react"; +import { getComponents as getComponentsRequest } from "../../../api/component"; + +export default function useCompatibleModels({ relatedComponent }) { + const [compatibleModels, setCompatibleModels] = useState([]); + const { enqueueSnackbar } = useSnackbar(); + + // in mount, fetches the compatible models with the previously selected task + useEffect(() => { + const getCompatibleModels = async () => { + try { + const models = await getComponentsRequest({ + selectTypes: ["Model"], + relatedComponent, + }); + setCompatibleModels(models); + } catch (error) { + enqueueSnackbar("Error while trying to obtain compatible models"); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } + }; + + getCompatibleModels(); + }, [relatedComponent]); + + return { compatibleModels }; +} diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js new file mode 100644 index 000000000..6740ed04a --- /dev/null +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js @@ -0,0 +1,60 @@ +import { useSnackbar } from "notistack"; +import { createExperiment as createExperimentRequest } from "../../../api/experiment"; +import { createRun as createRunRequest } from "../../../api/run"; + +export default function useExperimentsCreate({ newExp, onSuccess }) { + const { enqueueSnackbar } = useSnackbar(); + + const uploadRuns = async (experimentId) => { + for (const run of newExp.runs) { + try { + await createRunRequest( + experimentId, + run.model, + run.name, + run.params, + "", + ); + } catch (error) { + enqueueSnackbar(`Error while trying to create a new run: ${run.name}`); + + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } + } + }; + + const uploadNewExperiment = async () => { + try { + const response = await createExperimentRequest( + newExp.dataset.id, + newExp.task_name, + newExp.name, + ); + + const experimentId = response.id; + await uploadRuns(experimentId); + + enqueueSnackbar("Experiment successfully created.", { + variant: "success", + }); + onSuccess && onSuccess(); + } catch (error) { + enqueueSnackbar("Error while trying to create a new experiment"); + + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } + }; + return { uploadNewExperiment }; +} diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js new file mode 100644 index 000000000..a17173b98 --- /dev/null +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js @@ -0,0 +1,75 @@ +import { useSnackbar } from "notistack"; +import { useEffect, useRef, useState } from "react"; +import { getRuns as getRunsRequest } from "../../../api/run"; +import { getRunStatus } from "../../../utils/runStatus"; + +export default function useExperimentsRuns({ + experiment, + expRunning, + onSuccess, +}) { + const [runs, setRuns] = useState([]); + const [loading, setLoading] = useState(false); + const intervalRef = useRef(null); + + const { enqueueSnackbar } = useSnackbar(); + + const getRuns = async ({ showLoading }) => { + showLoading && setLoading(true); + + try { + const runs = await getRunsRequest(experiment.id.toString()); + + // transform status code to a string + const runsWithStringStatus = runs.map((run) => { + return { ...run, status: getRunStatus(run.status) }; + }); + + setRuns(runsWithStringStatus); + + onSuccess && onSuccess(runsWithStringStatus); + } catch (error) { + enqueueSnackbar( + `Error while trying to obtain the runs associated to ${experiment.name}`, + ); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + showLoading && setLoading(false); + } + }; + + useEffect(() => { + getRuns({ showLoading: true }); + }, []); + + // polling to update the state of the runs + useEffect(() => { + if (expRunning[experiment.id]) { + // Fetch data initially + const initialGetRuns = async () => { + await getRuns({ showLoading: false }); + }; + initialGetRuns().then(() => { + // clear previous interval + clearInterval(intervalRef.current); + // start polling + intervalRef.current = setInterval( + () => getRuns({ showLoading: false }), + 1000, // Poll every 1 second + ); + }); + } else { + clearInterval(intervalRef.current); + } + + return () => clearInterval(intervalRef.current); + }, [expRunning]); + + return { runs, loading }; +} diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js new file mode 100644 index 000000000..138cddfce --- /dev/null +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js @@ -0,0 +1,65 @@ +import { useSnackbar } from "notistack"; +import { + enqueueRunnerJob as enqueueRunnerJobRequest, + startJobQueue as startJobQueueRequest, +} from "../../../api/job"; + +export default function useExperimentsRunsPlay({ + setExpRunning, + expRunning, + experiment, + rowSelectionModel, +}) { + const { enqueueSnackbar } = useSnackbar(); + + const enqueueRunnerJob = async (runId) => { + try { + await enqueueRunnerJobRequest(runId); + return false; // return false for sucess + } catch (error) { + enqueueSnackbar(`Error while trying to enqueue run with id ${runId}`); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + return true; // return true for error + } + }; + + const startJobQueue = async () => { + try { + await startJobQueueRequest(); + } catch (error) { + setExpRunning({ ...expRunning, [experiment.id]: false }); + enqueueSnackbar("Error while trying to start job queue"); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } + }; + + const handleExecuteRuns = async () => { + setExpRunning({ ...expRunning, [experiment.id]: true }); + let enqueueErrors = 0; + // send runs to the job queue + for (const runId of rowSelectionModel) { + const error = await enqueueRunnerJob(runId); + enqueueErrors = error ? enqueueErrors + 1 : enqueueErrors; + } + + // verify that at least one job was succesfully enqueued to start the job queue + if (enqueueErrors < rowSelectionModel.length) { + startJobQueue(true); // true to stop when queue empties + } else { + setExpRunning({ ...expRunning, [experiment.id]: false }); + } + }; + return { handleExecuteRuns }; +} diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js index 5e0380e95..2450bc953 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js @@ -1,8 +1,8 @@ import { useSnackbar } from "notistack"; import React, { useState } from "react"; import DeleteItemModal from "../../../components/custom/DeleteItemModal"; -import RunnerDialog from "../../../components/experiments/RunnerDialog"; -import { initialColumns } from "../constants"; +import ExperimentsRunnerDialog from "../components/ExperimentsRunnerDialog"; +import { experimentsColumns } from "../constants"; import useExperiments from "./useExperiments"; import useExperimentsDelete from "./useExperimentsDelete"; @@ -30,23 +30,25 @@ export default function useExperimentsTable({ enqueueSnackbar("Error while trying to delete the experiment."), }); const columns = [ - ...initialColumns, + ...experimentsColumns, { field: "actions", type: "actions", minWidth: 80, - getActions: (params) => [ - , - deleteExperiment(params.id)} - />, - ], + getActions: (params) => { + return [ + , + deleteExperiment(params.id)} + />, + ]; + }, }, ]; diff --git a/DashAI/front/src/pages/experiments/hooks/useModels.js b/DashAI/front/src/pages/experiments/hooks/useModels.js new file mode 100644 index 000000000..879bb3587 --- /dev/null +++ b/DashAI/front/src/pages/experiments/hooks/useModels.js @@ -0,0 +1,47 @@ +import { useSnackbar } from "notistack"; +import uuid from "react-uuid"; +import { getModelSchema as getModelSchemaRequest } from "../../../api/oldEndpoints"; +import { getFullDefaultValues } from "../../../api/values"; +import { useCallback, useEffect, useState } from "react"; + +export default function useModels({ selectedModel }) { + const { enqueueSnackbar } = useSnackbar(); + const [schema, setSchema] = useState({}); + const [loading, setLoading] = useState(false); + + const getModel = useCallback(async () => { + const schemaDefaultValues = await getFullDefaultValues(schema); + return { + id: uuid(), + name, + model: selectedModel, + params: schemaDefaultValues, + }; + }, [schema, selectedModel]); + + useEffect(() => { + const getModelSchema = async () => { + setLoading(true); + try { + const schema = await getModelSchemaRequest(selectedModel); + setSchema(schema); + } catch (error) { + enqueueSnackbar("Error while trying to obtain model schema"); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + setLoading(false); + } + }; + if (selectedModel && selectedModel !== "") { + getModelSchema(); + } + }, [selectedModel]); + + return { schema, loading, getModel }; +} diff --git a/DashAI/front/src/pages/plugins/Plugins.jsx b/DashAI/front/src/pages/plugins/Plugins.jsx new file mode 100644 index 000000000..e69de29bb diff --git a/DashAI/front/src/styles/theme.js b/DashAI/front/src/styles/theme.js index 647bb5a5b..34be58fde 100644 --- a/DashAI/front/src/styles/theme.js +++ b/DashAI/front/src/styles/theme.js @@ -17,6 +17,7 @@ const theme = { }, background: { default: "#2e3037", + dark: "#121212", }, text: { primary: "#ffffff", From 0a6e392fb22cede4e28c96a32a70c696c2129a2c Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Wed, 24 Jan 2024 20:00:40 -0300 Subject: [PATCH 7/9] Moved model step dialog to experiments new folder --- .../experiments/EditModelDialog.jsx | 41 ++++++++-- .../ExperimentsCreateModelsEditDialog.jsx | 81 +++++++++++++++++++ .../ExperimentsCreateModelsStep.jsx | 4 +- .../ExperimentsCreateModelsTable.jsx | 12 +-- 4 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsEditDialog.jsx diff --git a/DashAI/front/src/components/experiments/EditModelDialog.jsx b/DashAI/front/src/components/experiments/EditModelDialog.jsx index 9e0f56fe3..7585f09b7 100644 --- a/DashAI/front/src/components/experiments/EditModelDialog.jsx +++ b/DashAI/front/src/components/experiments/EditModelDialog.jsx @@ -1,4 +1,8 @@ +import React, { useEffect, useState } from "react"; +import PropTypes from "prop-types"; +import { GridActionsCellItem } from "@mui/x-data-grid"; import SettingsIcon from "@mui/icons-material/Settings"; +import ParameterForm from "../configurableObject/ParameterForm"; import { Box, CircularProgress, @@ -7,11 +11,8 @@ import { DialogTitle, Grid, } from "@mui/material"; -import { GridActionsCellItem } from "@mui/x-data-grid"; -import PropTypes from "prop-types"; -import React, { useState } from "react"; -import useModels from "../../pages/experiments/hooks/useModels"; -import ParameterForm from "../configurableObject/ParameterForm"; +import { getModelSchema as getModelSchemaRequest } from "../../api/oldEndpoints"; +import { useSnackbar } from "notistack"; /** * This component handles the configuration of a single model * @param {string} modelToConfigure name of the model to configure @@ -24,10 +25,34 @@ function EditModelDialog({ updateParameters, paramsInitialValues, }) { + const { enqueueSnackbar } = useSnackbar(); const [open, setOpen] = useState(false); - const { schema: modelSchema, loading } = useModels({ - selectedModel: modelToConfigure, - }); + const [loading, setLoading] = useState(true); + const [modelSchema, setModelSchema] = useState({}); + + const getObjectSchema = async () => { + setLoading(true); + try { + const schema = await getModelSchemaRequest(modelToConfigure); + setModelSchema(schema); + } catch (error) { + enqueueSnackbar("Error while trying to obtain model schema"); + if (error.response) { + console.error("Response error:", error.message); + } else if (error.request) { + console.error("Request error", error.request); + } else { + console.error("Unknown Error", error.message); + } + } finally { + setLoading(false); + } + }; + + // fetches the JSON object on mount + useEffect(() => { + getObjectSchema(); + }, []); return ( diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsEditDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsEditDialog.jsx new file mode 100644 index 000000000..a1d0480fe --- /dev/null +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsEditDialog.jsx @@ -0,0 +1,81 @@ +import SettingsIcon from "@mui/icons-material/Settings"; +import { + Box, + CircularProgress, + Dialog, + DialogContent, + DialogTitle, + Grid, +} from "@mui/material"; +import { GridActionsCellItem } from "@mui/x-data-grid"; +import PropTypes from "prop-types"; +import React, { useState } from "react"; +import useModels from "../hooks/useModels"; +import ParameterForm from "../../../components/configurableObject/ParameterForm"; +/** + * This component handles the configuration of a single model + * @param {string} modelToConfigure name of the model to configure + * @param {function} updateParameters updates the parameters of the model to configure in the modal state (newExp.runs) + * @param {object} paramsInitialValues Initial values for the model to be configured, used so that the user can edit the parameters, + * picking up from the last time they configured. + */ +function ExperimentsCreateModelsEditDialog({ + modelToConfigure, + updateParameters, + paramsInitialValues, +}) { + const [open, setOpen] = useState(false); + const { schema: modelSchema, loading } = useModels({ + selectedModel: modelToConfigure, + }); + + return ( + + } + label="Edit" + onClick={() => setOpen(true)} + /> + setOpen(false)}> + {`${modelToConfigure} parameters`} + + + + {/* Parameter form to configure the model */} + + {loading ? ( + + ) : ( + { + updateParameters(values); + setOpen(false); + }} + submitButton + /> + )} + + + + + + ); +} + +ExperimentsCreateModelsEditDialog.propTypes = { + modelToConfigure: PropTypes.string.isRequired, + updateParameters: PropTypes.func.isRequired, + paramsInitialValues: PropTypes.objectOf( + PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + PropTypes.number, + PropTypes.object, + ]), + ), +}; + +export default ExperimentsCreateModelsEditDialog; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx index b7dde9699..c50da887d 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsStep.jsx @@ -1,9 +1,9 @@ import PropTypes from "prop-types"; import React, { useState } from "react"; -import ModelsTable from "../../../components/experiments/ModelsTable"; import useCompatibleModels from "../hooks/useCompatibleModels"; import useModels from "../hooks/useModels"; import ExperimentsCreateModelsStepLayout from "./ExperimentsCreateModelsStepLayout"; +import ExperimentsCreateModelsTable from "./ExperimentsCreateModelsTable"; import ExperimentsCreateModelsToolbar from "./ExperimentsCreateModelsToolbar"; function ExperimentsCreateModelsStep({ newExp, setNewExp, setNextEnabled }) { @@ -34,7 +34,7 @@ function ExperimentsCreateModelsStep({ newExp, setNewExp, setNextEnabled }) { /> } > - + ); } diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx index d0b25c4db..46f6aa8fd 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateModelsTable.jsx @@ -1,9 +1,9 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { DataGrid } from "@mui/x-data-grid"; import { Grid, Paper, Typography } from "@mui/material"; -import DeleteItemModal from "../custom//DeleteItemModal"; -import EditModelDialog from "./EditModelDialog"; +import { DataGrid } from "@mui/x-data-grid"; +import PropTypes from "prop-types"; +import React from "react"; +import DeleteItemModal from "../../../components/custom/DeleteItemModal"; +import ExperimentsCreateModelsEditDialog from "./ExperimentsCreateModelsEditDialog"; /** * This component renders a table to display the models that are currently in the experiment @@ -51,7 +51,7 @@ function ExperimentsCreateModelsTable({ newExp, setNewExp }) { type: "actions", minWidth: 100, getActions: (params) => [ - Date: Mon, 29 Jan 2024 21:59:38 -0300 Subject: [PATCH 8/9] Added comments to custom hooks and to the stepper container --- DashAI/front/src/components/shared/StepperContainer.jsx | 8 ++++++++ .../src/pages/experiments/hooks/useCompatibleModels.js | 5 +++++ .../front/src/pages/experiments/hooks/useExperiments.js | 7 +++++++ .../src/pages/experiments/hooks/useExperimentsCreate.js | 6 ++++++ .../src/pages/experiments/hooks/useExperimentsDelete.js | 6 ++++++ .../src/pages/experiments/hooks/useExperimentsRuns.js | 6 ++++++ .../src/pages/experiments/hooks/useExperimentsRunsPlay.js | 8 ++++++++ .../src/pages/experiments/hooks/useExperimentsTable.js | 6 ++++++ DashAI/front/src/pages/experiments/hooks/useModels.js | 5 +++++ 9 files changed, 57 insertions(+) diff --git a/DashAI/front/src/components/shared/StepperContainer.jsx b/DashAI/front/src/components/shared/StepperContainer.jsx index 49b7b1a1e..216cdb296 100644 --- a/DashAI/front/src/components/shared/StepperContainer.jsx +++ b/DashAI/front/src/components/shared/StepperContainer.jsx @@ -4,6 +4,14 @@ import React, { createContext, useContext, useState } from "react"; import StepperActions from "./StepperActions"; import StepperTitle from "./StepperTitle"; +/* + * This component renders a stepper dialog. + * It contains the stepper title, body and actions. + * It also contains a context to share the state between the components. + * This pattern is called compound components, you can read more about it here: + * https://kentcdodds.com/blog/compound-components-with-react-hooks + */ + const StepperContext = createContext(); const StepperProvider = ({ children }) => { diff --git a/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js b/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js index 1d42a39c5..c2294867a 100644 --- a/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js +++ b/DashAI/front/src/pages/experiments/hooks/useCompatibleModels.js @@ -2,6 +2,11 @@ import { useSnackbar } from "notistack"; import { useEffect, useState } from "react"; import { getComponents as getComponentsRequest } from "../../../api/component"; +/* + * Custom hook to fetch compatible models from the backend + * @param {string} relatedComponent - id of the related component + */ + export default function useCompatibleModels({ relatedComponent }) { const [compatibleModels, setCompatibleModels] = useState([]); const { enqueueSnackbar } = useSnackbar(); diff --git a/DashAI/front/src/pages/experiments/hooks/useExperiments.js b/DashAI/front/src/pages/experiments/hooks/useExperiments.js index e1848622e..a9bbffea4 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperiments.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperiments.js @@ -1,6 +1,13 @@ import { useEffect, useState } from "react"; import { getExperiments as getExperimentsRequest } from "../../../api/experiment"; +/* + * Custom hook to fetch experiments from the backend + * @param {function} onSuccess - callback function to be called on successful fetch + * @param {function} onError - callback function to be called on error + * @param {function} onSettled - callback function to be called on completion of fetch + * @param {boolean} refresh - boolean to indicate whether to refresh the experiments + */ export default function useExperiments({ onSuccess, onError, diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js index 6740ed04a..aa6e11ea6 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsCreate.js @@ -2,6 +2,12 @@ import { useSnackbar } from "notistack"; import { createExperiment as createExperimentRequest } from "../../../api/experiment"; import { createRun as createRunRequest } from "../../../api/run"; +/* + * Custom hook to create a new experiment + * @param {object} newExp - object containing the new experiment information + * @param {function} onSuccess - callback function to be called on successful fetch + */ + export default function useExperimentsCreate({ newExp, onSuccess }) { const { enqueueSnackbar } = useSnackbar(); diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsDelete.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsDelete.js index 966313aff..29a3d32ae 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsDelete.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsDelete.js @@ -1,6 +1,12 @@ import { useState } from "react"; import { deleteExperiment as deleteExperimentRequest } from "../../../api/experiment"; +/* + * Custom hook to delete an experiment + * @param {function} onSuccess - callback function to be called on successful fetch + * @param {function} onError - callback function to be called on error + */ + export default function useExperimentsDelete({ onSuccess, onError }) { const [loading, setLoading] = useState(false); diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js index a17173b98..7e8bfbe2d 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsRuns.js @@ -3,6 +3,12 @@ import { useEffect, useRef, useState } from "react"; import { getRuns as getRunsRequest } from "../../../api/run"; import { getRunStatus } from "../../../utils/runStatus"; +/* + * Custom hook to fetch runs from the backend + * @param {object} experiment - experiment object + * @param {object} expRunning - object containing the experiments running state + */ + export default function useExperimentsRuns({ experiment, expRunning, diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js index 138cddfce..24c3476e8 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsRunsPlay.js @@ -4,6 +4,14 @@ import { startJobQueue as startJobQueueRequest, } from "../../../api/job"; +/* + * Custom hook to enqueue runs to the job queue + * @param {function} setExpRunning - callback function to set the experiment running state + * @param {object} expRunning - object containing the experiments running state + * @param {object} experiment - experiment object + * @param {array} rowSelectionModel - array containing the selected runs + */ + export default function useExperimentsRunsPlay({ setExpRunning, expRunning, diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js index 2450bc953..f48238c1e 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js @@ -6,6 +6,12 @@ import { experimentsColumns } from "../constants"; import useExperiments from "./useExperiments"; import useExperimentsDelete from "./useExperimentsDelete"; +/* + * Custom hook to build rows and columns for the experiments table + * @param {boolean} updateTableFlag - boolean to indicate whether to refresh the experiments + * @param {function} setUpdateTableFlag - function to set the updateTableFlag + */ + export default function useExperimentsTable({ updateTableFlag, setUpdateTableFlag, diff --git a/DashAI/front/src/pages/experiments/hooks/useModels.js b/DashAI/front/src/pages/experiments/hooks/useModels.js index 879bb3587..a5f8fe29f 100644 --- a/DashAI/front/src/pages/experiments/hooks/useModels.js +++ b/DashAI/front/src/pages/experiments/hooks/useModels.js @@ -4,6 +4,11 @@ import { getModelSchema as getModelSchemaRequest } from "../../../api/oldEndpoin import { getFullDefaultValues } from "../../../api/values"; import { useCallback, useEffect, useState } from "react"; +/* + * Custom hook to fetch the schema of a model + * @param {string} selectedModel - id of the selected model + */ + export default function useModels({ selectedModel }) { const { enqueueSnackbar } = useSnackbar(); const [schema, setSchema] = useState({}); From e7438ce48258e6aa6c678a52138e7ecb6029eb9d Mon Sep 17 00:00:00 2001 From: Manuel Arriagada Ramos Date: Tue, 30 Jan 2024 13:16:19 -0300 Subject: [PATCH 9/9] Fix linter bug --- .../experiments/components/ExperimentsCreateDatasetStep.jsx | 2 +- .../experiments/components/ExperimentsCreateStepperDialog.jsx | 2 +- .../experiments/components/ExperimentsRunnerContentDialog.jsx | 2 +- DashAI/front/src/pages/experiments/constants/index.js | 2 -- DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 DashAI/front/src/pages/experiments/constants/index.js diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx index bf1c1270c..c2660b453 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateDatasetStep.jsx @@ -2,7 +2,7 @@ import PropTypes from "prop-types"; import React, { useEffect, useState } from "react"; import { DataGrid } from "@mui/x-data-grid"; import useDatasets from "../../../hooks/useDatasets"; -import { datasetsColumns } from "../constants"; +import { datasetsColumns } from "../constants/table"; import ExperimentsCreateDatasetStepLayout from "./ExperimentsCreateDatasetStepLayout"; function ExperimentsCreateDatasetStep({ newExp, setNewExp, setNextEnabled }) { diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx index a4d1e4a3f..c10869aaa 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsCreateStepperDialog.jsx @@ -2,7 +2,7 @@ import PropTypes from "prop-types"; import React, { useState } from "react"; import StepperContainer from "../../../components/shared/StepperContainer"; import StepperDialog from "../../../components/shared/StepperDialog"; -import { defaultNewExp } from "../constants"; +import { defaultNewExp } from "../constants/experiments"; import useExperimentsCreate from "../hooks/useExperimentsCreate"; import ExperimentsCreateDatasetStep from "./ExperimentsCreateDatasetStep"; import ExperimentsCreateModelsStep from "./ExperimentsCreateModelsStep"; diff --git a/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx index 6b4ff46f4..9c79565ec 100644 --- a/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx +++ b/DashAI/front/src/pages/experiments/components/ExperimentsRunnerContentDialog.jsx @@ -3,7 +3,7 @@ import { DataGrid } from "@mui/x-data-grid"; import { useSnackbar } from "notistack"; import PropTypes from "prop-types"; import React from "react"; -import { runnersColumns } from "../constants"; +import { runnersColumns } from "../constants/table"; import useExperimentsRuns from "../hooks/useExperimentsRuns"; function ExperimentsRunnerContentDialog({ diff --git a/DashAI/front/src/pages/experiments/constants/index.js b/DashAI/front/src/pages/experiments/constants/index.js deleted file mode 100644 index 2221cf4c7..000000000 --- a/DashAI/front/src/pages/experiments/constants/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./table"; -export * from "./experiments"; diff --git a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js index f48238c1e..48a83df08 100644 --- a/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js +++ b/DashAI/front/src/pages/experiments/hooks/useExperimentsTable.js @@ -2,7 +2,7 @@ import { useSnackbar } from "notistack"; import React, { useState } from "react"; import DeleteItemModal from "../../../components/custom/DeleteItemModal"; import ExperimentsRunnerDialog from "../components/ExperimentsRunnerDialog"; -import { experimentsColumns } from "../constants"; +import { experimentsColumns } from "../constants/table"; import useExperiments from "./useExperiments"; import useExperimentsDelete from "./useExperimentsDelete";