diff --git a/package.json b/package.json index c083b1041..34293406e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "react-slick": "^0.30.2", "react-star-rating-component": "^1.4.1", "react-swipeable-views": "^0.14.0", + "recharts": "^2.13.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "singularitynet-platform-contracts": "^1.0.4", diff --git a/src/Redux/actionCreators/DatasetActions.js b/src/Redux/actionCreators/DatasetActions.js new file mode 100644 index 000000000..92acfb48d --- /dev/null +++ b/src/Redux/actionCreators/DatasetActions.js @@ -0,0 +1,100 @@ +import { DatasetClient, DatasetEndpoints } from "../../config/DatasetClient"; + +export const SET_MAIN_DATASET = "SET_MAIN_DATASET"; +export const SET_MERGE_DATASET = "SET_MERGE_DATASET"; +export const SET_EXAMPLE_DATASETS = "SET_EXAMPLE_DATASETS"; +export const CLEAR_EXAMPLE_DATASETS = "CLEAR_EXAMPLE_DATASETS"; +export const ADD_RECENT_DATASET = "ADD_RECENT_DATASET"; +export const UPDATE_RECENT_DATASET = "UPDATE_RECENT_DATASET"; +export const CLEAR_RECENT_DATASETS = "CLEAR_RECENT_DATASETS"; + +export const setMainDataset = (dataset) => (dispatch) => { + dispatch({ + type: SET_MAIN_DATASET, + payload: dataset, + }); +}; + +export const setMergeDataset = (dataset) => (dispatch) => { + dispatch({ + type: SET_MERGE_DATASET, + payload: dataset, + }); +}; + +export const setExampleDatasets = (datasetList) => (dispatch) => { + dispatch({ + type: SET_EXAMPLE_DATASETS, + payload: datasetList, + }); +}; + +export const clearExampleDatasets = () => (dispatch) => { + dispatch({ + type: CLEAR_EXAMPLE_DATASETS, + }); +}; + +export const addRecentDataset = (dataset) => (dispatch) => { + dispatch({ + type: ADD_RECENT_DATASET, + payload: dataset, + }); +}; + +export const updateRecentDataset = (datasetKey, additionalInfo) => (dispatch) => { + dispatch({ + type: UPDATE_RECENT_DATASET, + payload: { datasetKey, additionalInfo }, + }); +}; + +export const clearRecentDatasets = () => (dispatch) => { + dispatch({ + type: CLEAR_RECENT_DATASETS, + }); +}; + +export const getDatasetStatistic = (datasetKey) => async (dispatch) => { + const params = new URLSearchParams([["dataset_key", datasetKey]]); + return DatasetClient.get(DatasetEndpoints.VALIDATE_AND_ANALIZE, { params }); +}; + +export const improveDataset = (datasetKey, improveOptionsList) => async (dispatch) => { + const params = { + dataset_key: datasetKey, + improve_options: improveOptionsList.reduce((acc, field) => { + acc[field] = true; + return acc; + }, {}), + }; + return DatasetClient.post(DatasetEndpoints.IMPROVE, params); +}; + +export const mergeDatasets = (mainDataset, mergeDataset) => async (dispatch) => { + const params = { + dataset_key_base: mainDataset, + dataset_key_additional: mergeDataset, + }; + return DatasetClient.post(DatasetEndpoints.VALIDATE_MERGE, params) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error("mergeDatasets Error:", error); + }); +}; + +export const validateMergeDatasets = (mainDataset, mergeDataset) => async (dispatch) => { + const params = { + dataset_key_base: mainDataset, + dataset_key_additional: mergeDataset, + }; + return DatasetClient.post(DatasetEndpoints.MERGE, params) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error("validateMergeDatasets Error:", error); + }); +}; diff --git a/src/Redux/actionCreators/ServiceDetailsActions.js b/src/Redux/actionCreators/ServiceDetailsActions.js index 747fe01f2..748950453 100644 --- a/src/Redux/actionCreators/ServiceDetailsActions.js +++ b/src/Redux/actionCreators/ServiceDetailsActions.js @@ -96,7 +96,7 @@ export const getIsTrainingAvailable = (detailsTraining, isLoggedIn) => { return false; } - if (!Object.prototype.hasOwnProperty.call(detailsTraining, "trainingMethods")) { + if (!Object.prototype.hasOwnProperty.call(detailsTraining, "trainingMethods") || !detailsTraining?.trainingMethods) { return false; } diff --git a/src/Redux/actionCreators/ServiceTrainingActions.js b/src/Redux/actionCreators/ServiceTrainingActions.js index 9644a735e..916dba218 100644 --- a/src/Redux/actionCreators/ServiceTrainingActions.js +++ b/src/Redux/actionCreators/ServiceTrainingActions.js @@ -2,8 +2,10 @@ import axios from "axios"; import { LoaderContent } from "../../utility/constants/LoaderContent"; import { startAppLoader, stopAppLoader } from "./LoaderActions"; import { getServiceClient } from "./SDKActions"; -import { updateMetamaskWallet } from "./UserActions"; +import { fetchAuthenticatedUser, updateMetamaskWallet } from "./UserActions"; import { modelStatus } from "../reducers/ServiceTrainingReducer"; +import { DatafactoryInstanceS3, DatasetS3Endpoints, TrainingInstanceS3 } from "../../config/DatasetS3Client"; +// import { userActions } from "."; export const SET_MODEL_DETAILS = "SET_MODEL_DETAILS"; export const SET_MODELS_LIST = "SET_MODELS_LIST"; export const RESET_MODEL_DETAILS = "RESET_MODEL_DETAILS"; @@ -189,21 +191,47 @@ const modelStatusByNumber = { 4: "DELETED", }; -export const publishDatasetToS3 = async (fileBlob, name) => { +export const publishDatasetForTraining = (fileBlob, name) => async (dispatch) => { try { - const fileKey = Date.now() + "_" + name; - const url = `https://xim5yugo7g.execute-api.us-east-1.amazonaws.com/default/upload?key=${fileKey}`; + const linkAndKeyDataset = await dispatch(publishDatasetToS3(fileBlob, name, TrainingInstanceS3)); + return linkAndKeyDataset; + } catch (error) { + console.log("publishing Dataset For Training error: ", error); + } +}; - let instance = axios.create({ - headers: { - Authorization: "S1kDjcub9k78JFAyrLPsfS0yQoQ4mgmmpeWKlIoVvYsk6JVq5v4HHKvKQgZ0VdI7", - }, - }); +export const publishDatasetForImproving = (fileBlob, name) => async (dispatch) => { + try { + const linkAndKeyDataset = await dispatch(publishDatasetToS3(fileBlob, name, DatafactoryInstanceS3)); + return linkAndKeyDataset; + } catch (error) { + console.log("publishing Dataset For Improving error: ", error); + } +}; + +export const publishDatasetToS3 = (fileBlob, name, S3Instance) => async (dispatch) => { + const { email } = await dispatch(fetchAuthenticatedUser()); - const response = await instance.get(url); + try { + const baseUrl = S3Instance.getUri(); + const fileKey = name + "_" + email + "_" + Date.now(); + const response = await S3Instance.get(DatasetS3Endpoints.UPLOAD, { params: { key: fileKey } }); await axios.put(response.data.uploadURL, fileBlob); - return `https://xim5yugo7g.execute-api.us-east-1.amazonaws.com/default/download?key=${fileKey}`; + return { + url: `${baseUrl}/download?key=${fileKey}`, + datasetKey: fileKey, + }; } catch (err) { throw new Error(err); } }; + +export const getDatasetSizeFromS3 = async (fileKey, S3instance) => { + return S3instance.get(DatasetS3Endpoints.DOWNLOAD, { params: { key: fileKey, action: "getsize" } }) + .then((response) => { + return response.data.fileSize; + }) + .catch((error) => { + console.error("mergeDatasets Error:", error); + }); +}; diff --git a/src/Redux/actionCreators/index.js b/src/Redux/actionCreators/index.js index 70d354a53..c59269693 100644 --- a/src/Redux/actionCreators/index.js +++ b/src/Redux/actionCreators/index.js @@ -8,6 +8,7 @@ import * as stylesActions from "./StylesActions"; import * as paymentActions from "./PaymentActions"; import * as uiContentActions from "./UiContentActions"; import * as sdkActions from "./SDKActions"; +import * as datasetActions from "./DatasetActions"; export { sdkActions, @@ -20,4 +21,5 @@ export { stylesActions, paymentActions, uiContentActions, + datasetActions, }; diff --git a/src/Redux/reducers/DatasetReducer.js b/src/Redux/reducers/DatasetReducer.js new file mode 100644 index 000000000..02f050c20 --- /dev/null +++ b/src/Redux/reducers/DatasetReducer.js @@ -0,0 +1,45 @@ +import { datasetActions } from "../actionCreators"; + +const initialState = { + mainDataset: null, + mergeDataset: null, + exampleDatasets: [ + { + datasetKey: "data_instruct_llm_1000_base_clean.zip_fospipakno@gufum.com_1733235148125", + name: "DataSet 1: Training data for text translation", + size: 287028, + tag: "Text", + }, + ], + recentDatasets: [], +}; + +const datasetReducer = (state = initialState, action) => { + switch (action.type) { + case datasetActions.SET_MAIN_DATASET: + return { ...state, mainDataset: action.payload }; + case datasetActions.SET_MERGE_DATASET: + return { ...state, mergeDataset: action.payload }; + case datasetActions.SET_EXAMPLE_DATASETS: + return { ...state, exampleDatasets: action.payload }; + case datasetActions.CLEAR_EXAMPLE_DATASETS: + return { ...state, exampleDatasets: [] }; + case datasetActions.ADD_RECENT_DATASET: + return { ...state, recentDatasets: [action.payload, ...state.recentDatasets] }; + case datasetActions.UPDATE_RECENT_DATASET: + return { + ...state, + recentDatasets: state.recentDatasets.map((dataset) => + dataset.datasetKey === action.payload.datasetKey + ? { ...dataset, additionalInfo: action.payload.additionalInfo } // Update the matched user + : dataset + ), + }; + case datasetActions.CLEAR_RECENT_DATASETS: + return { ...state, recentDatasets: [] }; + default: + return state; + } +}; + +export default datasetReducer; diff --git a/src/Redux/reducers/index.js b/src/Redux/reducers/index.js index b481343a2..23b2af416 100644 --- a/src/Redux/reducers/index.js +++ b/src/Redux/reducers/index.js @@ -9,6 +9,7 @@ import stylesReducer from "./StylesReducer"; import paymentReducer from "./PaymentReducer"; import uiContentReducer from "./UiContentReducer"; import sdkReducer from "./SDKReducer"; +import datasetReducer from "./DatasetReducer"; const rootReducer = combineReducers({ userReducer, @@ -21,6 +22,7 @@ const rootReducer = combineReducers({ paymentReducer, uiContentReducer, sdkReducer, + datasetReducer, }); export default rootReducer; diff --git a/src/components/ServiceDetails/AboutService/ServiceOverview.js b/src/components/ServiceDetails/AboutService/ServiceOverview.js index 805048b57..d2f35306d 100644 --- a/src/components/ServiceDetails/AboutService/ServiceOverview.js +++ b/src/components/ServiceDetails/AboutService/ServiceOverview.js @@ -41,7 +41,7 @@ const ServiceOverview = ({ classes, description, tags, isTrainingAvailable }) =>

For this service you can create your own training model!

{/* //TODO */} - + Try now!
diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js b/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js new file mode 100644 index 000000000..ccd1cdec6 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/ButtonGroup.js @@ -0,0 +1,61 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import StyledButton from "../../../common/StyledButton"; +import TableIcon from "@mui/icons-material/TableChartOutlined"; +import { useDispatch } from "react-redux"; +import { addRecentDataset, improveDataset } from "../../../../Redux/actionCreators/DatasetActions"; +import { startAppLoader, stopAppLoader } from "../../../../Redux/actionCreators/LoaderActions"; +import { LoaderContent } from "../../../../utility/constants/LoaderContent"; +import { DatafactoryInstanceS3 } from "../../../../config/DatasetS3Client"; +import { getDatasetSizeFromS3 } from "../../../../Redux/actionCreators/ServiceTrainingActions"; + +const ButtonsGroup = ({ classes, selectedParameters, isTableView, toggleTableView, dataset, setDataset }) => { + const dispatch = useDispatch(); + + const tableButtonText = isTableView ? "close tablet" : "view tablet"; + + const isImproveButtonDisable = !selectedParameters?.size; + + const getImprovedDataset = async () => { + try { + dispatch(startAppLoader(LoaderContent.IMPROVE_DATASET)); + const { data } = await dispatch(improveDataset(dataset.datasetKey, Array.from(selectedParameters.keys()))); + const size = await getDatasetSizeFromS3(data.dataset_key_new, DatafactoryInstanceS3); + const improvedDataset = { + additionalInfo: { + analysis: data.analysis, + dataset_sample: data.dataset_sample, + }, + datasetKey: data.dataset_key_new, + name: dataset.name + "_improved_rate_" + data.analysis.overall_score, + size, + tag: dataset.tag, + }; + await dispatch(addRecentDataset(improvedDataset)); + setDataset(improvedDataset); + } catch (error) { + console.error("getImprovedDataset error", error); + } finally { + dispatch(stopAppLoader()); + } + }; + + return ( +
+ + +
+ ); +}; + +export default withStyles(useStyles)(ButtonsGroup); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/HistogramGraph.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/HistogramGraph.js new file mode 100644 index 000000000..28324e6e3 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/HistogramGraph.js @@ -0,0 +1,30 @@ +import { ComposedChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from "recharts"; + +const HistogramGraph = ({ data }) => { + const dataKeys = Object.keys(data[0]); + const labelKey = dataKeys[0]; + const valueKey = dataKeys[1]; + + return ( + + + + + + + + + ); +}; + +export default HistogramGraph; diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/PieGraph.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/PieGraph.js new file mode 100644 index 000000000..8e632cca9 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/PieGraph.js @@ -0,0 +1,36 @@ +import { Pie, PieChart } from "recharts"; + +const PieGraph = ({ data }) => { + const dataKeys = Object.keys(data[0]); + const labelKey = dataKeys[0]; + const valueKey = dataKeys[1]; + + const RADIAN = Math.PI / 180; + const renderCustomizedPieLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, percent, index }) => { + const radius = innerRadius + (outerRadius - innerRadius) * 0.5; + + const x = cx + radius * Math.cos(midAngle * RADIAN) - percent; + const y = cy + radius * Math.sin(-midAngle * RADIAN); + return ( + cx ? "start" : "end"} dominantBaseline="central"> + {data[index][labelKey]} + + ); + }; + + return ( + + + + ); +}; + +export default PieGraph; diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/TreemapGraph.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/TreemapGraph.js new file mode 100644 index 000000000..21bb96926 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/TreemapGraph.js @@ -0,0 +1,73 @@ +import { Treemap } from "recharts"; +import { roundToDesimals } from "../../../../../utility/JSHelper"; + +const TreemapGraph = ({ data }) => { + // const dataKeys = Object.keys(data[0]); + + const labelKey = "name"; //dataKeys[0]; + const valueKey = "size"; //dataKeys[1]; + + const getColors = () => { + const numberOfColors = data.length; + const transparent = 1 / numberOfColors; + if (!transparent) { + return []; + } + let colors = []; + for (let i = 1; i <= numberOfColors; i++) { + const colorTransparent = roundToDesimals(i * transparent, 4); + colors.push(`rgba(68, 156, 238, ${colorTransparent})`); + } + + return colors; + }; + + const CustomizedContent = (props) => { + const { root, depth, x, y, width, height, index } = props; + const colors = getColors(); + const label = props[labelKey]; + + return ( + + + {depth === 1 && ( + + {label} + + )} + + ); + }; + + return ( + } + /> + ); +}; + +export default TreemapGraph; diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/index.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/index.js new file mode 100644 index 000000000..f1a46785f --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/index.js @@ -0,0 +1,34 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; + +import HistogramGraph from "./HistogramGraph"; +import TreemapGraph from "./TreemapGraph"; +import PieGraph from "./PieGraph"; + +const Graphs = ({ classes, graphs }) => { + const graphByType = (data, type) => { + const graphsTypes = { + histogram: , + pie: , + treemap: , + }; + + return graphsTypes[type]; + }; + + return ( +
+

Quality of the dataset

+
+ {graphs.map((graph) => ( +
+

{graph.displayed_name}

+ {graphByType(graph?.data?.children, graph.type)} +
+ ))} +
+
+ ); +}; + +export default withStyles(useStyles)(Graphs); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/styles.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/styles.js new file mode 100644 index 000000000..d57781734 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Graphs/styles.js @@ -0,0 +1,10 @@ +export const useStyles = () => ({ + graphs: { + display: "flex", + flexWrap: "wrap", + gap: 25, + }, + graphContainer: { + flex: 1, + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/index.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/index.js new file mode 100644 index 000000000..918373ee1 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/index.js @@ -0,0 +1,60 @@ +import { useState } from "react"; +import { Checkbox, FormControlLabel } from "@mui/material"; +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import clsx from "clsx"; + +const ImprovementParameters = ({ classes, parameters, setSelectedParameters }) => { + const [paramsForImprove, setParamsForImprove] = useState(new Map()); + + const selectForImprove = (parameterName) => { + let params = new Map(paramsForImprove); + + if (paramsForImprove.has(parameterName)) { + params.delete(parameterName); + } else { + params.set(parameterName, true); + } + setParamsForImprove(params); + setSelectedParameters(params); + }; + + const ImprovementParameter = ({ parameter }) => { + return ( +
+
+ selectForImprove(parameter.key_name)} + /> + } + label={parameter.displayed_name} + /> +
+
+ {parameter.cases_count} +
Issues detected
+
+
+
+
{parameter.group_score_label}
+
+ ); + }; + + return ( +
+

Quality check of the dataset

+
+ {parameters.map((parameter) => ( + + ))} +
+
+ ); +}; + +export default withStyles(useStyles)(ImprovementParameters); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/styles.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/styles.js new file mode 100644 index 000000000..651efc697 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Parameters/styles.js @@ -0,0 +1,68 @@ +export const useStyles = (theme) => ({ + parametersContainer: { + display: "flex", + flexDirection: "column", + gap: 35, + }, + parameters: { + display: "flex", + flexWrap: "wrap", + gap: 25, + }, + parameterContainer: { + flex: 1, + padding: 20, + borderRadius: 4, + border: `1px solid ${theme.palette.text.verticalTabLeftBorder}`, + display: "flex", + flexDirection: "column", + justifyContent: "space-between", + gap: 10, + }, + checkbox: { + "& .Mui-checked": { color: "black" }, + "& .MuiFormControlLabel-label": { + fontSize: 17, + fontWeight: 700, + color: "black", + whiteSpace: "nowrap", + }, + }, + improvementRaw: { + display: "flex", + gap: 6, + whiteSpace: "nowrap", + }, + issuesText: { + color: "#222", + }, + improvementValue: { + fontWeight: 700, + }, + listOfImprovementsContainer: { + display: "flex", + flexWrap: "wrap", + justifyContent: "space-between", + }, + status: { + textAlign: "right", + fontSize: 14, + fontWeight: 700, + }, + Low: { + background: "linear-gradient(90deg, rgba(255, 255, 255, 0.35) 0%, rgba(208, 109, 106, 0.35) 100%)", + color: "#D06D6A", + }, + Middle: { + background: "linear-gradient(90deg, rgba(255, 255, 255, 0.25) 0%, rgba(233, 170, 52, 0.25) 100%)", + color: "#E9AA34", + }, + High: { + background: "linear-gradient(90deg, rgba(255, 255, 255, 0.25) 0%, rgba(68, 156, 238, 0.25) 100%)", + color: "#449CEE", + }, + Perfect: { + background: "linear-gradient(90deg, rgba(255, 255, 255, 0.35) 0%, rgba(166, 47, 235, 0.35) 100%)", + color: "#A62FEB", + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Table/index.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Table/index.js new file mode 100644 index 000000000..942897ef5 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Table/index.js @@ -0,0 +1,38 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; + +const TableSamples = ({ classes, tableData }) => { + if (tableData.length === 0) { + return null; + } + + const headData = tableData[0]; + const bodyData = tableData.length >= 2 ? tableData.slice(1) : []; + + return ( + + + + {headData.map((cellData, index) => ( + + ))} + + + + {bodyData.map((row, index) => ( + + {row.map((cellData, index) => ( + + ))} + + ))} + +
+ {cellData} +
+ {cellData} +
+ ); +}; + +export default withStyles(useStyles)(TableSamples); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/Table/styles.js b/src/components/ServiceDetails/DataPreset/DashboardModal/Table/styles.js new file mode 100644 index 000000000..ac2db6f1a --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/Table/styles.js @@ -0,0 +1,17 @@ +export const useStyles = (theme) => ({ + tableHead: { + color: theme.palette.text.mediumShadeGray, + fontWeight: 600, + }, + tableBody: { + color: "#222", + fontWeight: 500, + }, + tableCeil: { + padding: 10, + margin: "5px 0", + textAlign: "left", + borderBottom: `1px solid ${theme.palette.text.verticalTabLeftBorder}`, + fontSize: 16, + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/index.js b/src/components/ServiceDetails/DataPreset/DashboardModal/index.js new file mode 100644 index 000000000..f078037dc --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/index.js @@ -0,0 +1,41 @@ +import SNETDialog from "../../../common/SNETDialog"; +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import Graphs from "./Graphs"; +import ImprovementParameters from "./Parameters"; +import TableSamples from "./Table"; +import { useState } from "react"; +import ButtonGroup from "./ButtonGroup"; + +const DashboardModal = ({ classes, onClose, isShow, dataset, setDatasetInfo }) => { + const [selectedParameters, setSelectedParameters] = useState(); + const [isTableView, setIsTableView] = useState(true); + + const toggleTableView = () => { + setIsTableView(!isTableView); + }; + + return ( + +
+ + + {isTableView && dataset?.additionalInfo?.dataset_sample && ( + + )} + +
+
+ ); +}; + +export default withStyles(useStyles)(DashboardModal); diff --git a/src/components/ServiceDetails/DataPreset/DashboardModal/styles.js b/src/components/ServiceDetails/DataPreset/DashboardModal/styles.js new file mode 100644 index 000000000..2e327ff5b --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DashboardModal/styles.js @@ -0,0 +1,13 @@ +export const useStyles = (theme) => ({ + dasbordModalContainer: { + display: "flex", + flexDirection: "column", + padding: "30px 0", + gap: 35, + }, + buttonsContainer: { + display: "flex", + flexWrap: "wrap", + justifyContent: "space-between", + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DatasetInfo/index.js b/src/components/ServiceDetails/DataPreset/DatasetInfo/index.js new file mode 100644 index 000000000..88aaeedc5 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetInfo/index.js @@ -0,0 +1,48 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import HelpOutline from "@mui/icons-material/HelpOutline"; +// import InlineLoader from "../../../common/InlineLoader"; +import React from "react"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +const DatasetInfo = ({ classes, datasetParameters }) => { + const DatasetRateInfo = ({ additionalInfo }) => { + return ( + + {additionalInfo.map((additionalField) => ( +
+ {additionalField.value} + {additionalField.title} +
+ ))} +
+ ); + }; + return ( +
+

Dataset info

+
+ {datasetParameters.map((datasetParameter) => ( +
+
+

{datasetParameter.title}

+ {datasetParameter?.additionalInfo && ( + <> + + + + )} +
+ {!datasetParameter?.value ? ( + + ) : ( +

{datasetParameter.value}

+ )} +
+ ))} +
+
+ ); +}; + +export default withStyles(useStyles)(DatasetInfo); diff --git a/src/components/ServiceDetails/DataPreset/DatasetInfo/styles.js b/src/components/ServiceDetails/DataPreset/DatasetInfo/styles.js new file mode 100644 index 000000000..06b27c59f --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetInfo/styles.js @@ -0,0 +1,70 @@ +export const useStyles = (theme) => ({ + datasetParameter: { + flex: 1, + display: "flex", + padding: "14px 10px", + flexDirection: "column", + justifyContent: "center", + alignItems: "flex-start", + gap: 6, + borderRadius: 4, + border: `1px solid ${theme.palette.text.verticalTabLeftBorder}`, + }, + parametersContainer: { + display: "flex", + alignItems: "center", + gap: 20, + }, + additionalInfoContainer: { + display: "none", + position: "absolute", + zIndex: 1, + right: "-120px", + top: "-15px", + borderRadius: 4, + padding: "10px 25px", + background: theme.palette.text.cardBackground, + boxShadow: "0px 0px 6.5px 0px rgba(46, 46, 46, 0.25)", + }, + additionalFieldRaw: { + display: "flex", + gap: 6, + textWrap: "nowrap", + }, + additionalFieldValue: { + fontWeight: 700, + background: "linear-gradient(90deg, #8279FE 0%, #449CEE 100%)", + backgroundClip: "text", + "-webkit-background-clip": "text", + "-webkit-text-fill-color": "transparent", + }, + parameterTitleContainer: { + display: "flex", + justifyContent: "space-between", + alignItems: "center", + width: "100%", + cursor: "pointer", + position: "relative", + color: theme.palette.text.mediumShadeGray, + "& svg": { + width: 20, + height: 20, + color: theme.palette.text.primary, + }, + "&:hover": { + "& span": { + display: "block", + }, + }, + }, + parameterTitle: { + margin: 0, + fontSize: 17, + fontWeight: 600, + }, + parameterValue: { + margin: 0, + fontSize: 24, + fontWeight: 600, + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DatasetTabs/index.js b/src/components/ServiceDetails/DataPreset/DatasetTabs/index.js new file mode 100644 index 000000000..661ce0d22 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetTabs/index.js @@ -0,0 +1,43 @@ +import { useState } from "react"; +import StyledTabs from "../../StyledTabs"; +import DatasetsList from "../DatasetsList"; +import { useSelector } from "react-redux"; + +const DatasetTabs = ({ setDatasetInfo }) => { + const exampleDatasets = useSelector((state) => state.datasetReducer.exampleDatasets); + const recentDatasets = useSelector((state) => state.datasetReducer.recentDatasets); + const [activeTab, setActiveTab] = useState(0); + + const handleTabChange = (activeTab) => { + setActiveTab(activeTab); + }; + + const exampleTab = { + name: "Examples", + activeIndex: 0, + component: , + }; + const recentTab = { + name: "Recent", + activeIndex: 1, + component: , + }; + + const tabs = []; + if (recentDatasets.length === 0) { + tabs.push({ ...exampleTab, activeIndex: 0 }); + tabs.push({ ...recentTab, activeIndex: 1 }); + } else { + tabs.push({ ...recentTab, activeIndex: 0 }); + tabs.push({ ...exampleTab, activeIndex: 1 }); + } + + return ( +
+

Select example or recent dataset

+ +
+ ); +}; + +export default DatasetTabs; diff --git a/src/components/ServiceDetails/DataPreset/DatasetUploader/index.js b/src/components/ServiceDetails/DataPreset/DatasetUploader/index.js new file mode 100644 index 000000000..f1429659b --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetUploader/index.js @@ -0,0 +1,135 @@ +import { useState } from "react"; +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import { publishDatasetForImproving } from "../../../../Redux/actionCreators/ServiceTrainingActions"; +import SNETFileUpload from "../../../common/SNETFileUpload"; +import { isEmpty } from "lodash"; +import { Typography } from "@mui/material"; +import DatasetInfo from "../DatasetInfo"; +import StyledButton from "../../../common/StyledButton"; +import DashboardModal from "../DashboardModal"; +import DatasetTabs from "../DatasetTabs"; +import { useDispatch } from "react-redux"; +import { fileSizeConverter } from "../../../../utility/JSHelper"; +import { LoaderContent } from "../../../../utility/constants/LoaderContent"; +import { loaderActions } from "../../../../Redux/actionCreators"; + +const acceptedFileTypes = { "application/zip": [".zip"], "application/x-zip-compressed": [".zip"] }; + +const DatasetUploader = ({ classes, setDatasetInfo, datasetInfo, cleanDatasetInfo, isMainDataset }) => { + const dispatch = useDispatch(); + + const [trainingDataFileName, setTrainingDataFileName] = useState(datasetInfo?.name); + const [trainingDataFileSize, setTrainingDataFileSize] = useState(datasetInfo?.size); + const [isDashbordOpen, setIsDashbordOpen] = useState(false); + + const handleDrop = async (acceptedFiles, rejectedFiles) => { + if (!isEmpty(rejectedFiles)) { + return; + } + if (!isEmpty(acceptedFiles)) { + try { + dispatch(loaderActions.startAppLoader(LoaderContent.SET_DATASET)); + const fileBlob = acceptedFiles[0]; + const { name, size } = fileBlob; + + setTrainingDataFileName(name); + setTrainingDataFileSize(size); + const { url, datasetKey } = await dispatch(publishDatasetForImproving(fileBlob, name)); + setDatasetInfo({ link: url, name, size, datasetKey }); + } catch (error) { + console.log("error: ", error); + + // setAlert({ type: alertTypes.ERROR, message: error.message }); + } finally { + dispatch(loaderActions.stopAppLoader()); + } + } + }; + + const Helpertext = () => { + return ( + isEmpty(datasetInfo) && ( + <> + Package must be under 50mb + Make sure the extension is .zip + + ) + ); + }; + + const openDashbordModal = () => { + setIsDashbordOpen(true); + }; + + const closeDashbordModal = () => { + setIsDashbordOpen(false); + }; + + const cleanCurrentDataset = () => { + cleanDatasetInfo(); + setTrainingDataFileName(null); + setTrainingDataFileSize(null); + }; + + const datasetParameters = !datasetInfo + ? null + : [ + { title: "Size", value: fileSizeConverter(datasetInfo?.size) }, + { title: "Format", value: "CSV" }, + { + title: "Rate", + value: !datasetInfo?.additionalInfo + ? null + : datasetInfo?.additionalInfo?.analysis?.overall_score + + "/" + + datasetInfo?.additionalInfo?.analysis?.overall_score_range[1], + additionalInfo: datasetInfo?.additionalInfo?.analysis?.feature_groups.map((element) => { + return { value: element.cases_count, title: element.displayed_name }; + }), + }, + ]; + + return ( +
+
+

Drag & Drop or Select File

+ } + fileName={trainingDataFileName} + fileSize={trainingDataFileSize} + uploadSuccess={Boolean(trainingDataFileName || datasetInfo)} + cleanCurrentFile={cleanCurrentDataset} + isFileStatsDisplay={false} + /> +
+ {isEmpty(datasetInfo) ? ( + + ) : ( + <> + + + + )} + {isDashbordOpen && ( + + )} +
+ ); +}; + +export default withStyles(useStyles)(DatasetUploader); diff --git a/src/components/ServiceDetails/DataPreset/DatasetUploader/styles.js b/src/components/ServiceDetails/DataPreset/DatasetUploader/styles.js new file mode 100644 index 000000000..8d2ce5c57 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetUploader/styles.js @@ -0,0 +1,22 @@ +export const useStyles = (theme) => ({ + datasetUploader: { + display: "flex", + flexDirection: "column", + gap: 16, + }, + examplesContainer: { + display: "flex", + flexDirection: "column", + gap: 16, + }, + exampleDataset: { + display: "flex", + padding: "8px 12px", + alignItems: "center", + gap: 8, + alignSelf: "stretch", + borderRadius: 8, + background: theme.palette.text.gray, + cursor: "pointer", + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/DatasetsList/index.js b/src/components/ServiceDetails/DataPreset/DatasetsList/index.js new file mode 100644 index 000000000..d17c07450 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetsList/index.js @@ -0,0 +1,23 @@ +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; + +const DatasetsList = ({ classes, setDatasetInfo, datasets }) => { + const DatasetLine = ({ dataset }) => { + return ( +
setDatasetInfo(dataset)} className={classes.datasetLine}> +
{dataset?.name}
+
{dataset?.tag ? dataset?.tag : "Text"}
+
+ ); + }; + + return ( +
+ {datasets.map((dataset, index) => ( + + ))} +
+ ); +}; + +export default withStyles(useStyles)(DatasetsList); diff --git a/src/components/ServiceDetails/DataPreset/DatasetsList/styles.js b/src/components/ServiceDetails/DataPreset/DatasetsList/styles.js new file mode 100644 index 000000000..04b4e2eb9 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/DatasetsList/styles.js @@ -0,0 +1,32 @@ +export const useStyles = (theme) => ({ + datasetsContainer: { + display: "flex", + flexDirection: "column", + gap: 16, + }, + datasetLine: { + boxSizing: "border-box", + display: "flex", + padding: "8px 12px", + justifyContent: "space-between", + alignItems: "center", + gap: 8, + alignSelf: "stretch", + borderRadius: 8, + background: theme.palette.text.gray, + cursor: "pointer", + }, + datasetName: { + whiteSpace: "wrap", + textOverflow: "ellipsis", + width: "100%", + overflow: "hidden", + }, + datasetTag: { + padding: 5, + borderRadius: 6, + fontWeight: 400, + color: theme.palette.text.white, + background: "linear-gradient(90deg, #4A3FEB 0%, #44D5EE 100%)", + }, +}); diff --git a/src/components/ServiceDetails/DataPreset/index.js b/src/components/ServiceDetails/DataPreset/index.js new file mode 100644 index 000000000..d5bf35fa3 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/index.js @@ -0,0 +1,164 @@ +import React, { useEffect } from "react"; +import Card from "../../common/Card"; +import DatasetUploader from "./DatasetUploader"; +import MergeIcon from "@mui/icons-material/CallMerge"; + +import { withStyles } from "@mui/styles"; +import { useStyles } from "./styles"; +import AddIcon from "@mui/icons-material/Add"; +import StyledButton from "../../common/StyledButton"; +import clsx from "clsx"; +import { isEmpty } from "lodash"; +import { useLocation, useNavigate } from "react-router-dom"; +import { getDatasetSizeFromS3, setCurrentModelDetails } from "../../../Redux/actionCreators/ServiceTrainingActions"; +import { useDispatch, useSelector } from "react-redux"; +import { + addRecentDataset, + getDatasetStatistic, + setMainDataset, + setMergeDataset, + updateRecentDataset, + validateMergeDatasets, +} from "../../../Redux/actionCreators/DatasetActions"; +import { loaderActions } from "../../../Redux/actionCreators"; +import { LoaderContent } from "../../../utility/constants/LoaderContent"; +import { startAppLoader, stopAppLoader } from "../../../Redux/actionCreators/LoaderActions"; +import { DatafactoryInstanceS3 } from "../../../config/DatasetS3Client"; + +const DataPreset = ({ classes }) => { + const navigate = useNavigate(); + const location = useLocation(); + const dispatch = useDispatch(); + const mainDataset = useSelector((state) => state.datasetReducer.mainDataset); + const mergeDataset = useSelector((state) => state.datasetReducer.mergeDataset); + const recentDatasets = useSelector((state) => state.datasetReducer.recentDatasets); + + useEffect(() => { + !!mainDataset && !mainDataset?.additionalInfo && getStatistic(mainDataset, setMainDataset); + }, [mainDataset]); + + useEffect(() => { + !!mergeDataset && !mergeDataset?.additionalInfo && getStatistic(mergeDataset, setMergeDataset); + }, [mergeDataset]); + + const getStatistic = async (dataset, setDataset) => { + try { + dispatch(loaderActions.startAppLoader(LoaderContent.GET_DATASET_STATISTIC)); + const datasetKey = dataset?.datasetKey; + const { data } = await dispatch(getDatasetStatistic(datasetKey)); + const enrichedDataset = { ...dataset, additionalInfo: data }; + const actualDatasetIndexInRecent = recentDatasets.find((el) => el.datasetKey === datasetKey); + if (!actualDatasetIndexInRecent) { + await dispatch(addRecentDataset(enrichedDataset)); + } else { + await dispatch(updateRecentDataset(datasetKey, data)); + } + await dispatch(setDataset(enrichedDataset)); + } catch (error) { + console.error("getStatistic error", error); + dispatch(setDataset(null)); + } finally { + dispatch(loaderActions.stopAppLoader()); + } + }; + + const cleanMainDataset = () => { + dispatch(setMainDataset(mergeDataset)); + dispatch(setMergeDataset(null)); + }; + + const cleanMergeDataset = () => { + dispatch(setMergeDataset(null)); + }; + + const goToCreateModel = () => { + const baseUrl = DatafactoryInstanceS3.getUri(); + const link = `${baseUrl}/download?key=${mainDataset.datasetKey}`; + const model = { + dataset: { + ...mainDataset, + link, + }, + modelName: mainDataset.name, + description: mainDataset.name, + }; + dispatch(setCurrentModelDetails(model)); + navigate(location.pathname.split("tab/")[0] + "tab/" + 3, { state: { isOpenCreatingModel: true } }); + }; + + const setMainDatasetFunction = (data) => dispatch(setMainDataset(data)); + const setMergeDatasetFunction = (data) => dispatch(setMergeDataset(data)); + + const onMergeDatasets = async () => { + try { + dispatch(startAppLoader(LoaderContent.MERGE_DATASETS)); + const mergedDatasets = await dispatch(validateMergeDatasets(mainDataset?.datasetKey, mergeDataset?.datasetKey)); + const size = await getDatasetSizeFromS3(mergedDatasets.dataset_key_merged, DatafactoryInstanceS3); + const mergedDataset = { + additionalInfo: mergedDatasets, + datasetKey: mergedDatasets.dataset_key_merged, + name: "merged_" + mainDataset?.name + "_" + mergeDataset?.name, + size, + tag: mainDataset?.tag, + }; + await dispatch(addRecentDataset(mergedDataset)); + await dispatch(setMainDataset(mergedDataset)); + await dispatch(setMergeDataset(null)); + } catch (error) { + console.log("error on merge datasets:", error); + } finally { + dispatch(stopAppLoader()); + } + }; + + const DataPresetContainer = () => { + return ( +
+

Upload Your Dataset

+
+
+ +
+ {mergeDataset && ( +
+ + Merge + +
+ } + onClick={onMergeDatasets} + /> +
+ )} +
+ {mainDataset ? ( + + ) : ( +
+ +

Add one more file for merge this

+
+ )} +
+
+
+ +
+ + ); + }; + + return } />; +}; + +export default withStyles(useStyles)(DataPreset); diff --git a/src/components/ServiceDetails/DataPreset/styles.js b/src/components/ServiceDetails/DataPreset/styles.js new file mode 100644 index 000000000..2e12db064 --- /dev/null +++ b/src/components/ServiceDetails/DataPreset/styles.js @@ -0,0 +1,61 @@ +export const useStyles = (theme) => ({ + dataPresetContainer: { + display: "flex", + flexDirection: "column", + gap: 30, + }, + datasetUploaderContainer: { + display: "flex", + justifyContent: "space-between", + gap: 30, + "@media(max-width: 1024px)": { + flexDirection: "column", + }, + }, + fineTuneBatton: { + textAlign: "right", + }, + verticalCentered: { + alignItems: "center", + }, + fileZone: { + width: "40%", + "@media(max-width: 1024px)": { + width: "100% !important", + }, + }, + fileZoneExtended: { + width: "50%", + "@media(max-width: 1024px)": { + width: "100% !important", + }, + }, + mergeButtonContainer: { + "& button": { + minWidth: "auto", + }, + }, + mergeButton: { + display: "flex", + flexDirection: "column", + alignItems: "center", + "& svg": { + transform: "rotate(180deg)", + }, + }, + emptyFirstDataset: { + minHeight: 500, + width: "100%", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + fontSize: 24, + textAlign: "center", + color: theme.palette.text.lightShadedGray, + "& svg": { + width: 60, + height: 60, + }, + }, +}); diff --git a/src/components/ServiceDetails/TrainingModels/CreateModel/Data/Upload/index.js b/src/components/ServiceDetails/TrainingModels/CreateModel/Data/Upload/index.js index 52afbe3e5..3ad6196f7 100644 --- a/src/components/ServiceDetails/TrainingModels/CreateModel/Data/Upload/index.js +++ b/src/components/ServiceDetails/TrainingModels/CreateModel/Data/Upload/index.js @@ -1,18 +1,21 @@ import React, { useState } from "react"; -import { publishDatasetToS3 } from "../../../../../../Redux/actionCreators/ServiceTrainingActions"; +import { publishDatasetForTraining } from "../../../../../../Redux/actionCreators/ServiceTrainingActions"; import AlertBox, { alertTypes } from "../../../../../common/AlertBox"; import { isEmpty } from "lodash"; import SNETFileUpload from "../../../../../common/SNETFileUpload"; import { withStyles } from "@mui/styles"; import { useStyles } from "./styles"; +import { useDispatch } from "react-redux"; -const acceptedFileTypes = ["application/zip", "application/x-zip-compressed"]; +const acceptedFileTypes = { "application/zip": [".zip"], "application/x-zip-compressed": [".zip"] }; + +const Data = ({ classes, trainingDataset, setTrainingDataset }) => { + const dispatch = useDispatch(); -const Data = ({ classes, trainingDataLink, setTrainingDataLink }) => { const [alert, setAlert] = useState({}); - const [trainingDataFileName, setTrainingDataFileName] = useState(""); - const [trainingDataFileSize, setTrainingDataFileSize] = useState(""); + const [trainingDataFileName, setTrainingDataFileName] = useState(trainingDataset ? trainingDataset?.name : ""); + const [trainingDataFileSize, setTrainingDataFileSize] = useState(trainingDataset ? trainingDataset?.size : ""); const handleDrop = async (acceptedFiles, rejectedFiles) => { setAlert({}); @@ -29,9 +32,9 @@ const Data = ({ classes, trainingDataLink, setTrainingDataLink }) => { setTrainingDataFileName(name); setTrainingDataFileSize(size); - const url = await publishDatasetToS3(fileBlob, name); + const { url } = await dispatch(publishDatasetForTraining(fileBlob, name)); - setTrainingDataLink(url); + setTrainingDataset({ link: url, name, size }); } catch (error) { setAlert({ type: alertTypes.ERROR, message: error.message }); } @@ -53,7 +56,7 @@ const Data = ({ classes, trainingDataLink, setTrainingDataLink }) => { } fileName={trainingDataFileName} fileSize={trainingDataFileSize} - uploadSuccess={Boolean(trainingDataLink)} + uploadSuccess={Boolean(trainingDataset?.link)} /> diff --git a/src/components/ServiceDetails/TrainingModels/CreateModel/Data/index.js b/src/components/ServiceDetails/TrainingModels/CreateModel/Data/index.js index 706a9686e..e7eaff458 100644 --- a/src/components/ServiceDetails/TrainingModels/CreateModel/Data/index.js +++ b/src/components/ServiceDetails/TrainingModels/CreateModel/Data/index.js @@ -3,7 +3,7 @@ import { withStyles } from "@mui/styles"; import Upload from "./Upload"; import { useStyles } from "./styles"; -const Dataset = ({ classes, trainingDataLink, setTrainingDataLink }) => { +const Dataset = ({ classes, trainingDataset, setTrainingDataset }) => { return (
@@ -16,7 +16,7 @@ const Dataset = ({ classes, trainingDataLink, setTrainingDataLink }) => {

Upload your dataset

- +
); diff --git a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/AccessModel/index.js b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/AccessModel/index.js index 1c210ce54..42d04ad95 100644 --- a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/AccessModel/index.js +++ b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/AccessModel/index.js @@ -24,14 +24,15 @@ const AccessModel = ({ classes, accessAddresses, setAccessAddresses }) => {

Add a list of address that can access this model (Optional)

- {accessAddresses.map((address, index) => ( -
- -
- removeEthAddress(index)} /> + {accessAddresses && + accessAddresses.map((address, index) => ( +
+ +
+ removeEthAddress(index)} /> +
-
- ))} + ))}

Please add a new address that will have access to use, update and delete your model! You do not need to enter your address diff --git a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js index b7cc4bfa8..534c3db96 100644 --- a/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js +++ b/src/components/ServiceDetails/TrainingModels/CreateModel/ModelInfo/index.js @@ -3,7 +3,6 @@ import { useDispatch, useSelector } from "react-redux"; // import FormControlLabel from "@mui/material/FormControlLabel"; // import Switch from "@mui/material/Switch"; -import StyledDropdown from "../../../../common/StyledDropdown"; import StyledTextField from "../../../../common/StyledTextField"; import StyledButton from "../../../../common/StyledButton"; @@ -24,20 +23,21 @@ const ModelInfo = ({ classes, cancelEditModel }) => { const { currentModel } = useSelector((state) => state.serviceTrainingReducer); const { org_id, service_id } = useSelector((state) => currentServiceDetails(state)); + // const [trainingMethod, setTrainingMethod] = useState(currentModel ? currentModel.methodName : undefined); //eslint-disable-next-line const [isRestrictAccessModel, setIsRestrictAccessModel] = useState( true // currentModel && currentModel.publicAccess ? true : false ); const [accessAddresses, setAccessAddresses] = useState(currentModel ? currentModel.addressList : []); - const [trainingMethod, setTrainingMethod] = useState(currentModel ? currentModel.methodName : undefined); const [trainingModelName, setTrainingServiceName] = useState(currentModel ? currentModel.modelName : ""); const [trainingModelDescription, setTrainingModelDescription] = useState( currentModel ? currentModel.description : "" ); - const [trainingDataLink, setTrainingDataLink] = useState(currentModel ? currentModel.dataLink : ""); + const [trainingDataset, setTrainingDataset] = useState(currentModel ? currentModel.dataset : ""); const [alert, setAlert] = useState({}); + const trainingMethod = detailsTraining?.trainingMethods[0]; // const onUpdate = async () => { // const updateModelParams = { // trainingModelName, @@ -74,7 +74,7 @@ const ModelInfo = ({ classes, cancelEditModel }) => { trainingModelDescription, accessAddresses, isRestrictAccessModel, - dataLink: trainingDataLink, + dataLink: trainingDataset.link, }; await dispatch(createModel(org_id, service_id, newModelParams)); dispatch(loaderActions.stopAppLoader()); @@ -91,19 +91,19 @@ const ModelInfo = ({ classes, cancelEditModel }) => { // setIsRestrictAccessModel(!isRestrictAccessModel); // }; - const trainingModelAccess = detailsTraining?.trainingMethods || []; + // const trainingModelAccess = detailsTraining?.trainingMethods || []; - const trainingDropDownObject = trainingModelAccess.map((e) => ({ - value: e, - label: e, - })); + // const trainingDropDownObject = trainingModelAccess.map((e) => ({ + // value: e, + // label: e,d + // })); - const trainingMethodDropDownBox = (event) => { - const { value } = event.target; - if (value !== "default") { - setTrainingMethod(value); - } - }; + // const trainingMethodDropDownBox = (event) => { + // const { value } = event.target; + // if (value !== "default") { + // setTrainingMethod(value); + // } + // }; const handleModelServiceName = (event) => { setTrainingServiceName(event.target.value); @@ -113,7 +113,7 @@ const ModelInfo = ({ classes, cancelEditModel }) => { setTrainingModelDescription(event.target.value); }; - const isCreatingAvailable = trainingMethod && trainingModelName && trainingModelDescription && trainingDataLink; + const isCreatingAvailable = trainingMethod && trainingModelName && trainingModelDescription && trainingDataset?.link; const CreateModelButtonGroup = () => { return ; }; @@ -131,13 +131,13 @@ const ModelInfo = ({ classes, cancelEditModel }) => {

- - Please select a method to train as a first step. + {/* Please select a method to train as a first step. */}
@@ -157,7 +157,7 @@ const ModelInfo = ({ classes, cancelEditModel }) => { />
- +
{/* { const CreateModelHeader = () => { if (isEmpty(modelId)) { - return

New Model Request

; + return "New Model Request"; } return ( diff --git a/src/components/ServiceDetails/TrainingModels/index.js b/src/components/ServiceDetails/TrainingModels/index.js index e2a038297..4875ad565 100644 --- a/src/components/ServiceDetails/TrainingModels/index.js +++ b/src/components/ServiceDetails/TrainingModels/index.js @@ -13,9 +13,14 @@ import ExistingModel from "../ExistingModel"; import ProjectDetails from "../ProjectDetails"; import { useDispatch } from "react-redux"; import { resetCurrentModelDetails } from "../../../Redux/actionCreators/ServiceTrainingActions"; +import { useLocation } from "react-router-dom"; const TrainingModels = ({ classes, service }) => { - const [showCreateModel, setShowCreateModel] = useState(false); + const { state } = useLocation(); + + const [showCreateModel, setShowCreateModel] = useState( + state?.isOpenCreatingModel ? state?.isOpenCreatingModel : false + ); const dispatch = useDispatch(); const openEditModelSection = () => { diff --git a/src/components/ServiceDetails/index.js b/src/components/ServiceDetails/index.js index e35f7c625..c3b266a92 100644 --- a/src/components/ServiceDetails/index.js +++ b/src/components/ServiceDetails/index.js @@ -32,6 +32,7 @@ import SeoMetadata from "../common/SeoMetadata"; import Routes from "../../utility/constants/Routes"; import CardImg from "../../assets/images/SnetDefaultServiceImage.png"; import TrainingModels from "./TrainingModels"; +import DataPreset from "./DataPreset"; export const HERO_IMG = "hero_image"; @@ -119,12 +120,20 @@ const ServiceDetails = ({ classes }) => { ]; if (isTrainingAvailable) { - tabs.push({ - name: "Models", - tabId: "serviceTraining", - activeIndex: 2, - component: , - }); + tabs.push( + { + name: "Data preset", + tabId: "dataPreset", + activeIndex: 2, + component: , + }, + { + name: "Models", + tabId: "serviceTraining", + activeIndex: 3, + component: , + } + ); } const seoURL = `${process.env.REACT_APP_BASE_URL}/servicedetails/org/${orgId}/service/${serviceId}`; diff --git a/src/components/common/SNETDialog/index.js b/src/components/common/SNETDialog/index.js new file mode 100644 index 000000000..5016ad012 --- /dev/null +++ b/src/components/common/SNETDialog/index.js @@ -0,0 +1,76 @@ +import React from "react"; +import propTypes from "prop-types"; +import CloseIcon from "@mui/icons-material/Close"; +import MobileDialog from "./mobileDialog"; +import { useStyles } from "./styles"; +import { withStyles } from "@mui/styles"; +import { Typography, Box, DialogContent, Dialog, IconButton } from "@mui/material"; + +const SnetDialog = ({ + classes, + isDialogOpen, + title, + children, + disableBackdropClick, + disableEscapeKeyDown, + contentClass, + onDialogClose, + showCloseButton = true, + ...props +}) => { + const isMobile = window.screen.width <= 550; //px + + // eslint-disable-next-line no-unused-vars + const handleDialogClose = (event, reason) => { + if (disableBackdropClick && reason === "backdropClick") { + return false; + } + + if (disableEscapeKeyDown && reason === "escapeKeyDown") { + return false; + } + onDialogClose(); + }; + + if (isMobile) { + return ; + } + + return ( + handleDialogClose(event, reason)} + aria-labelledby="snet-dialog-title" + open={isDialogOpen} + {...props} + > + {title || + (showCloseButton && ( + + {title} + {showCloseButton && ( + + + + )} + + ))} + + {children} + + + ); +}; + +SnetDialog.propTypes = { + isDialogOpen: propTypes.bool.isRequired, + onDialogClose: propTypes.func, + title: propTypes.string, + children: propTypes.node, + showCloseButton: propTypes.bool, + disableBackdropClick: propTypes.bool, + disableEscapeKeyDown: propTypes.bool, + contentClass: propTypes.string, +}; + +export default withStyles(useStyles)(SnetDialog); diff --git a/src/components/common/SNETDialog/mobileDialog.js b/src/components/common/SNETDialog/mobileDialog.js new file mode 100644 index 000000000..1bbe36c29 --- /dev/null +++ b/src/components/common/SNETDialog/mobileDialog.js @@ -0,0 +1,21 @@ +import React from "react"; +import { Typography, Box, Modal } from "@mui/material"; +import { useStyles } from "./styles"; +import { withStyles } from "@mui/styles"; + +const MobileDialog = ({ classes, isDialogOpen, onDialogClose, title, children }) => { + return ( + + {title} + {children} + + } + /> + ); +}; + +export default withStyles(useStyles)(MobileDialog); diff --git a/src/components/common/SNETDialog/styles.js b/src/components/common/SNETDialog/styles.js new file mode 100644 index 000000000..b6e232984 --- /dev/null +++ b/src/components/common/SNETDialog/styles.js @@ -0,0 +1,34 @@ +export const useStyles = (MUITheme) => ({ + dialogTitle: { + padding: "16px 24px !important", + color: MUITheme.palette.blue, + display: "flex", + alignItems: "center", + gap: 5, + justifyContent: "space-between", + flexWrap: "nowrap", + }, + titleText: { + fontSize: 20, + fontWeight: 400, + lineHeight: "24px", + }, + iconButton: { + padding: "0 !important", + "& svg": { fontSize: 24 }, + }, + dailogContent: { + padding: "0 !important", + }, + mobileDialog: { + position: "absolute", + borderRadius: "10px 10px 0 0", + bottom: 0, + right: 0, + width: "100%", + }, + titleMobileText: { + padding: 10, + borderBottom: `1px solid ${MUITheme.palette.text.gray}`, + }, +}); diff --git a/src/components/common/SNETFileUpload/FileStats.js b/src/components/common/SNETFileUpload/FileStats.js index 627cdf2f0..d9580b9d2 100644 --- a/src/components/common/SNETFileUpload/FileStats.js +++ b/src/components/common/SNETFileUpload/FileStats.js @@ -4,6 +4,7 @@ import FolderIcon from "@mui/icons-material/Folder"; import PropTypes from "prop-types"; import { useStyles } from "./styles"; +import { fileSizeConverter } from "../../../utility/JSHelper"; const FileStats = (props) => { const { uploadSuccess, show, fileName, fileSize, error } = props; @@ -23,21 +24,13 @@ const FileStats = (props) => {
-
+
File Name: {fileName}
-
- Items: - -
-
- Uploaded: - -
-
+
Size: - {fileSize} + {fileSizeConverter(fileSize)}
); diff --git a/src/components/common/SNETFileUpload/index.js b/src/components/common/SNETFileUpload/index.js index 58c9db010..212ff9616 100644 --- a/src/components/common/SNETFileUpload/index.js +++ b/src/components/common/SNETFileUpload/index.js @@ -2,11 +2,13 @@ import React from "react"; import { useDropzone } from "react-dropzone"; import CloudUpload from "@mui/icons-material/Backup"; import PropTypes from "prop-types"; -import Grid from "@mui/material/Grid"; import Typography from "@mui/material/Typography"; +import TaskIcon from "@mui/icons-material/Task"; import { useStyles } from "./styles"; import FileStats from "./FileStats"; +import { Box, IconButton } from "@mui/material"; +import { Close } from "@mui/icons-material"; const SNETFileUpload = (props) => { const { @@ -23,7 +25,9 @@ const SNETFileUpload = (props) => { fileSize, uploadSuccess, error, - helperText, + cleanCurrentFile, + helperText = null, + isFileStatsDisplay = true, } = props; const classes = useStyles(); @@ -39,21 +43,40 @@ const SNETFileUpload = (props) => { onDropRejected, }); + const handleFileClean = (e) => { + e.stopPropagation(); + cleanCurrentFile(); + }; + return ( - + - - - - Drag and drop image here or click - + + {uploadSuccess ? ( + <> + {cleanCurrentFile && ( + handleFileClean(event)}> + + + )} + + {fileName} + + ) : ( + <> + + + Drag and drop image here or click + + + )} {helperText === null ? ( (Package must be under {maxSize}mb. Make sure the extension is .zip or .tar) ) : ( helperText )} - - + + {isFileStatsDisplay && ( { uploadSuccess={uploadSuccess} error={error} /> - - + )} + ); }; SNETFileUpload.prototypes = { disabled: PropTypes.disabled, + isFileStatsDisplay: PropTypes.bool, onFileSelect: PropTypes.func, minSize: PropTypes.number, maxSize: PropTypes.number, @@ -84,8 +108,4 @@ SNETFileUpload.prototypes = { helperText: PropTypes.any, }; -SNETFileUpload.defaultProps = { - helperText: null, -}; - export default SNETFileUpload; diff --git a/src/components/common/SNETFileUpload/styles.js b/src/components/common/SNETFileUpload/styles.js index 86b597b63..f75e135ec 100644 --- a/src/components/common/SNETFileUpload/styles.js +++ b/src/components/common/SNETFileUpload/styles.js @@ -1,8 +1,15 @@ import { makeStyles } from "@mui/styles"; export const useStyles = makeStyles((MUITheme) => ({ - grayBox: { - padding: "50px 45px !important", + fileUploaderContainer: { + display: "flex", + gap: 20, + }, + fileUploaderText: { + boxSizing: "border-box", + position: "relative", + width: "100%", + padding: "50px 25px !important", borderWidth: 1, borderStyle: "dashed", borderColor: MUITheme.palette.text.darkGray, @@ -11,7 +18,6 @@ export const useStyles = makeStyles((MUITheme) => ({ flexDirection: "column", alignItems: "center", justifyContent: "center", - backgroundColor: "#F8F8F8", cursor: "pointer", textAlign: "center", "& svg": { @@ -19,6 +25,10 @@ export const useStyles = makeStyles((MUITheme) => ({ fontSize: 40, }, "& p": { + whiteSpace: "wrap", + textOverflow: "ellipsis", + width: "100%", + overflow: "hidden", "&:first-of-type": { color: MUITheme.palette.text.lightGrey, fontSize: 14, @@ -30,6 +40,16 @@ export const useStyles = makeStyles((MUITheme) => ({ }, }, }, + cleanButton: { + position: "absolute", + right: 0, + top: 0, + "& svg": { + width: 25, + height: 25, + fill: MUITheme.palette.text.mediumShadeGray, + }, + }, title: { color: MUITheme.palette.text.lightGrey, fontSize: 14, @@ -57,6 +77,7 @@ export const useStyles = makeStyles((MUITheme) => ({ color: "rgba(0,0,0,0.25)", fontSize: 18, lineHeight: "23px", + whiteSpace: "nowrap", }, }, successfullUpload: { @@ -107,4 +128,9 @@ export const useStyles = makeStyles((MUITheme) => ({ color: MUITheme.palette.error.main, }, }, + statRow: { + display: "flex", + flexWrap: "wrap", + justifyContent: "space-between", + }, })); diff --git a/src/components/common/StyledButton/index.js b/src/components/common/StyledButton/index.js index a66c8f042..738027259 100644 --- a/src/components/common/StyledButton/index.js +++ b/src/components/common/StyledButton/index.js @@ -9,6 +9,7 @@ import { useStyles } from "./styles"; const buttonColor = { blue: "blueBg", gradient: "gradientBg", + gradientAccent: "gradientAccentBg", black: "blackBg", transparent: "transparentBg", transparentBlueBorder: "transparentBlueBorder", @@ -18,7 +19,18 @@ const buttonColor = { whiteBorder: "whiteBorder", }; -const StyledButton = ({ disabled, onClick, type, btnType, iconClass, href, newTab, btnText, ...rest }) => { +const StyledButton = ({ + disabled, + onClick, + type, + btnType, + IconComponent, + iconClass, + href, + newTab, + btnText, + ...rest +}) => { const classes = useStyles(); return ( @@ -32,7 +44,8 @@ const StyledButton = ({ disabled, onClick, type, btnType, iconClass, href, newTa rel={href && newTab ? "noopener" : ""} {...rest} > - {iconClass ? : null} + {Boolean(IconComponent) && } + {iconClass && } {btnText} ); @@ -42,6 +55,7 @@ StyledButton.propTypes = { type: PropTypes.oneOf([ "blue", "gradient", + "gradientAccent", "black", "transparent", "red", @@ -51,7 +65,7 @@ StyledButton.propTypes = { "whiteBorder", ]), btnType: PropTypes.oneOf(["submit", "reset", "button"]), - btnText: PropTypes.string, + btnText: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), disabled: PropTypes.bool, onClick: PropTypes.func, iconClass: PropTypes.string, diff --git a/src/components/common/StyledButton/styles.js b/src/components/common/StyledButton/styles.js index 1fd2ff7d3..86d250238 100644 --- a/src/components/common/StyledButton/styles.js +++ b/src/components/common/StyledButton/styles.js @@ -14,7 +14,7 @@ export const useStyles = makeStyles((theme) => ({ letterSpacing: "1.25px", lineHeight: "16px", "&:disabled": { - backgroundColor: theme.palette.text.lightGray, + backgroundColor: `${theme.palette.text.lightGray} !important`, color: theme.palette.text.white, }, }, @@ -85,4 +85,18 @@ export const useStyles = makeStyles((theme) => ({ backgroundColor: "rgba(241,241,241,0.15)", }, }, + gradientBg: { + border: "none", + background: "linear-gradient(90deg, #8279FE 0%, #449CEE 100%)", + "&:disabled": { + background: theme.palette.text.disabledBtnBg, + }, + }, + gradientAccentBg: { + border: "none", + background: "linear-gradient(90deg, #D479FE 0%, #449CEE 45.5%, #4DE 100%)", + "&:disabled": { + background: theme.palette.text.disabledBtnBg, + }, + }, })); diff --git a/src/config/DatasetClient.js b/src/config/DatasetClient.js new file mode 100644 index 000000000..a3b039126 --- /dev/null +++ b/src/config/DatasetClient.js @@ -0,0 +1,17 @@ +import axios from "axios"; + +export const DatasetClient = axios.create({ + baseURL: "https://datafactory.singularitynet.io", + headers: { + Authorization: "Bearer jzWEbCCJ1434P2mSm3e9Ugs5u387yAf1bv1DCapfomLLg6V57Ht6BjXvFMa9260a", + "Access-Control-Allow-Headers": "*", + "Access-Control-Allow-Methods": "*", + }, +}); + +export const DatasetEndpoints = { + VALIDATE_AND_ANALIZE: "/api/get-stat", + IMPROVE: "/api/post-improve-dataset", + VALIDATE_MERGE: "api/post-validate-merge", + MERGE: "/api/post-merge-dataset", +}; diff --git a/src/config/DatasetS3Client.js b/src/config/DatasetS3Client.js new file mode 100644 index 000000000..457998893 --- /dev/null +++ b/src/config/DatasetS3Client.js @@ -0,0 +1,30 @@ +import axios from "axios"; + +const DATAFACTORY_S3 = { + baseUrl: "https://ozx0e68owf.execute-api.us-east-1.amazonaws.com", + authToken: "IYE2sz0hUSGhWcyLQTwXS0AbiXKq4h1eW85MZSo6uDhtYfXI8dXisTzRyXaBCImH", +}; + +const TRAINING_S3 = { + baseUrl: "https://xim5yugo7g.execute-api.us-east-1.amazonaws.com/default", + authToken: "S1kDjcub9k78JFAyrLPsfS0yQoQ4mgmmpeWKlIoVvYsk6JVq5v4HHKvKQgZ0VdI7", +}; + +export const TrainingInstanceS3 = axios.create({ + baseURL: TRAINING_S3.baseUrl, + headers: { + Authorization: TRAINING_S3.authToken, + }, +}); + +export const DatafactoryInstanceS3 = axios.create({ + baseURL: DATAFACTORY_S3.baseUrl, + headers: { + Authorization: DATAFACTORY_S3.authToken, + }, +}); + +export const DatasetS3Endpoints = { + UPLOAD: "/upload", // params: key + DOWNLOAD: "/download", // params: key +}; diff --git a/src/index.css b/src/index.css index 6f015559b..29d2ea196 100644 --- a/src/index.css +++ b/src/index.css @@ -26,4 +26,15 @@ h2 { display: flex; justify-content: center; align-items: center; -} \ No newline at end of file +} + +::-webkit-scrollbar { + width: 5px; + height: 5px; + background-color: #F2F2F2; +} + +::-webkit-scrollbar-thumb { + background: #999; + border-radius: 10px; +} diff --git a/src/utility/JSHelper.js b/src/utility/JSHelper.js index 004a669c6..48e7e2f0a 100644 --- a/src/utility/JSHelper.js +++ b/src/utility/JSHelper.js @@ -1,3 +1,16 @@ export function hasOwnDefinedProperty(object, property) { return object.hasOwnProperty(property) && typeof object[property] !== "undefined"; } + +export const roundToDesimals = (roundingNumber, decimals = 1) => { + return Math.round((roundingNumber + Number.EPSILON) * (10 * decimals)) / (10 * decimals); +}; + +export function fileSizeConverter(sizeInBytes) { + const sizeInKiloBytes = roundToDesimals(sizeInBytes / 1024, 2); + if (sizeInKiloBytes > 1024) { + const sizeInMBytes = roundToDesimals(sizeInKiloBytes / 1024, 2); + return sizeInMBytes + "MB"; + } + return sizeInKiloBytes + "KB"; +} diff --git a/src/utility/constants/LoaderContent.js b/src/utility/constants/LoaderContent.js index d8cd64ae1..c858839e4 100644 --- a/src/utility/constants/LoaderContent.js +++ b/src/utility/constants/LoaderContent.js @@ -116,4 +116,20 @@ export const LoaderContent = { loaderHeader: "Updating model", loaderText: "Please wait. we're updating model", }, + SET_DATASET: { + loaderHeader: "Handling dataset", + loaderText: "Please wait while we handle the dataset", + }, + GET_DATASET_STATISTIC: { + loaderHeader: "Fetching dataset statistic", + loaderText: "Please wait while we fetch the dataset statistic", + }, + IMPROVE_DATASET: { + loaderHeader: "Improving dataset", + loaderText: "Please wait while we improving the dataset", + }, + MERGE_DATASETS: { + loaderHeader: "Merging datasets", + loaderText: "Please wait while we merge the datasets", + }, };