diff --git a/src/ui/src/components/CaptureImportForm/CaptureImportForm.jsx b/src/ui/src/components/CaptureImportForm/CaptureImportForm.jsx index a0384aa8..2dfd56c9 100644 --- a/src/ui/src/components/CaptureImportForm/CaptureImportForm.jsx +++ b/src/ui/src/components/CaptureImportForm/CaptureImportForm.jsx @@ -103,9 +103,11 @@ const PipelineImportForm = ({ let captureConfigurationFormData = getCaptureConfigurationFormData(); if (_.isEmpty(metadataFormData) && _.isEmpty(captureConfigurationFormData?.options)) { await createDefaultMetadata(projectUUID); + + metadataFormData = getCaptureMetadataFormData(); + captureConfigurationFormData = getCaptureConfigurationFormData(); } - metadataFormData = getCaptureMetadataFormData(); - captureConfigurationFormData = getCaptureConfigurationFormData(); + setCaptureMetadataFormData(metadataFormData); setCaptureConfigurationFromData(captureConfigurationFormData); setActiveStep(STEP_FORM); diff --git a/src/ui/src/components/ControlPanel/ControlPanel.jsx b/src/ui/src/components/ControlPanel/ControlPanel.jsx index 39019f9e..5e8fcb62 100644 --- a/src/ui/src/components/ControlPanel/ControlPanel.jsx +++ b/src/ui/src/components/ControlPanel/ControlPanel.jsx @@ -18,10 +18,12 @@ License along with SensiML Piccolo AI. If not, see }, }))(); -const ControlPanel = ({ title, subtitle, onClickBack, actionsBtns, turncateLenght = 0 }) => { +const ControlPanel = ({ + title, + subtitle, + onClickBack, + onShowInformation, + actionsBtns, + turncateLenght = 0, +}) => { const classes = useStyles(); return ( @@ -70,6 +79,11 @@ const ControlPanel = ({ title, subtitle, onClickBack, actionsBtns, turncateLengh ) : null} {title && filterTruncateMiddle(title, turncateLenght)} + {onShowInformation ? ( + + + + ) : null} {subtitle} diff --git a/src/ui/src/components/ControlPanel/ControlPanelStyles.js b/src/ui/src/components/ControlPanel/ControlPanelStyles.js index c6e09f0b..25037b78 100644 --- a/src/ui/src/components/ControlPanel/ControlPanelStyles.js +++ b/src/ui/src/components/ControlPanel/ControlPanelStyles.js @@ -21,6 +21,10 @@ import makeStyles from "@mui/styles/makeStyles"; const useStyles = () => makeStyles((theme) => ({ + headerInfoIconButton: { + marginLeft: theme.spacing(1), + marginTop: theme.spacing(1), + }, grid: {}, controlPanelWrapper: { display: "flex", diff --git a/src/ui/src/components/Layout/LayoutConstants.js b/src/ui/src/components/Layout/LayoutConstants.js index d49145c5..72d9f788 100644 --- a/src/ui/src/components/Layout/LayoutConstants.js +++ b/src/ui/src/components/Layout/LayoutConstants.js @@ -98,24 +98,16 @@ const MENU_ITEMS_INFO = { subItems: [ { title: i18n.t("layout:nav-drawer.menu-item-feature-extractor"), + tooltip: i18n.t("layout:nav-drawer.menu-item-pipeline-fe-tooltip"), id: "navFeatureExtractor", orderIndex: 1, iconfn: (iconProps) => , getPath: (params = {}) => generatePath(ROUTES.MAIN.MODEL_BUILD.child.FEATURE_EXTRACTOR.path, { ...params }), }, - { - title: i18n.t("layout:nav-drawer.menu-item-pipeline-automl"), - id: "navPipelineAutoML", - orderIndex: 2, - iconfn: (iconProps) => , - getPath: (params = {}) => - generatePath(ROUTES.MAIN.MODEL_BUILD.child.AUTOML.path, { - ...params, - }), - }, { title: i18n.t("layout:nav-drawer.menu-item-pipeline-custom"), + tooltip: i18n.t("layout:nav-drawer.menu-item-pipeline-custom-tooltip"), id: "navPipelineCustom", orderIndex: 3, iconfn: (iconProps) => , @@ -124,6 +116,17 @@ const MENU_ITEMS_INFO = { ...params, }), }, + { + title: i18n.t("layout:nav-drawer.menu-item-pipeline-automl"), + tooltip: i18n.t("layout:nav-drawer.menu-item-pipeline-automl-tooltip"), + id: "navPipelineAutoML", + orderIndex: 2, + iconfn: (iconProps) => , + getPath: (params = {}) => + generatePath(ROUTES.MAIN.MODEL_BUILD.child.AUTOML.path, { + ...params, + }), + }, ], }, MODELS: { diff --git a/src/ui/src/components/Layout/NavDrawer.jsx b/src/ui/src/components/Layout/NavDrawer.jsx index dd0f17f2..afb0c254 100644 --- a/src/ui/src/components/Layout/NavDrawer.jsx +++ b/src/ui/src/components/Layout/NavDrawer.jsx @@ -196,8 +196,12 @@ const NavDrawer = ({ ...(selectedPipeline && { pipelineUUID: selectedPipeline }), })} > - - + + {getIsPathHasStatus(subItem.getPath()) ? ( @@ -205,9 +209,9 @@ const NavDrawer = ({ subItem.iconfn(getMenuProps(subItem.orderIndex)) )} - - - + + + ) : null} diff --git a/src/ui/src/components/PipelineBuilderV1/DrawerInformationMessage.jsx b/src/ui/src/components/PipelineBuilderV1/DrawerInformationMessage.jsx index 9e6795c8..817dd5ae 100644 --- a/src/ui/src/components/PipelineBuilderV1/DrawerInformationMessage.jsx +++ b/src/ui/src/components/PipelineBuilderV1/DrawerInformationMessage.jsx @@ -19,11 +19,10 @@ License along with SensiML Piccolo AI. If not, see ( + const CardInformationIcon = ({ step }) => ( handleStepInfo(pipelineSettings)} + onClick={(_e) => handleStepInfo(step)} data-test="info-link" > @@ -900,7 +899,7 @@ const PipelineBuilder = ({ <> {step.name} - + {[ PIPELINE_STEP_TYPES.CLASSIFIER, @@ -925,7 +924,7 @@ const PipelineBuilder = ({ FunctionSubType: step.name, })} - + )} @@ -1124,13 +1123,13 @@ const PipelineBuilder = ({ isOpen={isOpenDialogDistribution} labelColors={labelColors} data={cacheDistributionData?.data || {}} - featureSummary={featureStatsData.feature_summary} - featureStatistics={featureStatsData.feature_statistics} - featureVectorData={featureStatsData.feature_data} - features={_.keys(featureStatsData.feature_data)} - labelColumn={featureStatsData.label_column} + featureVectorData={featureStatsData?.featureVectorData || {}} + featureStatistics={featureStatsData?.featureStatistics || {}} + featureSummary={featureStatsData?.featureSummary || {}} + features={featureStatsData?.featureNames || {}} + labelColumn={featureStatsData?.labelColumn || ""} + labelValues={featureStatsData?.labelValues || []} selectLabelValuesColors={selectLabelValuesColors} - labelValues={selectLabelValuesByName(featureStatsData.label_column || "Label")} classMap={classMap} classes={classes} isLoadFeatures={cacheDistributionData?.step?.isLoadFeatures} diff --git a/src/ui/src/components/PipelineImportForm/PipelineImportForm.jsx b/src/ui/src/components/PipelineImportForm/PipelineImportForm.jsx index 06f22972..8eb9d80b 100644 --- a/src/ui/src/components/PipelineImportForm/PipelineImportForm.jsx +++ b/src/ui/src/components/PipelineImportForm/PipelineImportForm.jsx @@ -53,9 +53,11 @@ const PipelineImportForm = ({ }; const handleCreate = (pipelineName, queryName, isUseSessionPreprocessor, replacedColumns) => { + const disableAutoML = pipelineJson.hyper_params?.params?.disable_automl || false; onSubmit({ pipelineName, pipelineJson, + isAutoMLOptimization: !disableAutoML, queryName, replacedColumns, isUseSessionPreprocessor, diff --git a/src/ui/src/components/PipelineTemplateCreateForm/PipelineTemplateCreateForm.jsx b/src/ui/src/components/PipelineTemplateCreateForm/PipelineTemplateCreateForm.jsx index b555f369..6f20f75f 100644 --- a/src/ui/src/components/PipelineTemplateCreateForm/PipelineTemplateCreateForm.jsx +++ b/src/ui/src/components/PipelineTemplateCreateForm/PipelineTemplateCreateForm.jsx @@ -67,8 +67,10 @@ const PipelineTemplateCreateForm = ({ }; const handleCreate = (pipelineName, queryName, isUseSessionPreprocessor, replacedColumns) => { + const disableAutoML = pipelineJson.hyper_params?.params?.disable_automl || false; onSubmit({ pipelineName, + isAutoMLOptimization: !disableAutoML, pipelineJson, queryName, replacedColumns, diff --git a/src/ui/src/containers/BuildModel/TheBuilderScreen/TheBuilderScreen.jsx b/src/ui/src/containers/BuildModel/TheBuilderScreen/TheBuilderScreen.jsx index 3ee80b95..f43fb0dd 100644 --- a/src/ui/src/containers/BuildModel/TheBuilderScreen/TheBuilderScreen.jsx +++ b/src/ui/src/containers/BuildModel/TheBuilderScreen/TheBuilderScreen.jsx @@ -1,3 +1,4 @@ +/* eslint-disable no-unused-vars */ /* Copyright 2017-2024 SensiML Corporation @@ -16,8 +17,32 @@ Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with SensiML Piccolo AI. If not, see . */ +import React, { useEffect, useState } from "react"; +import { + Button, + Box, + CircularProgress, + Typography, + Link, + Tooltip, + FormControlLabel, + Checkbox, + Stack, +} from "@mui/material"; -import { useEffect } from "react"; +import makeStyles from "@mui/styles/makeStyles"; +import DialogInformation from "components/DialogInformation"; + +const useStyles = (navBarIsOpen) => + makeStyles((theme) => ({ + infoTitle: { + marginBottom: theme.spacing(4), + marginTop: theme.spacing(2), + fontSize: theme.spacing(4), + fontWeight: 500, + textAlign: "center", + }, + }))(); const TheBuilderScreen = ({ children, @@ -35,6 +60,9 @@ const TheBuilderScreen = ({ setSelectedPipelineName, ...props }) => { + const classes = useStyles(); + const [dialogInformationData, setDialogInformationData] = useState({}); + const stopOptimizationChecker = () => { clearOptimizationLogs(); clearPipelineResults(); @@ -58,15 +86,39 @@ const TheBuilderScreen = ({ ]; }, []); - return children({ - clearAlertBuilder, - clearOptimizationLogs, - clearPipelineResults, - clearPipelineStatus, - clearQueryCacheStatus, - pipelineData, - ...props, - }); + const handleShowInformation = (title, text) => { + setDialogInformationData({ title, text }); + }; + + const handleCloseNewStepDialogInformation = () => { + setDialogInformationData({}); + }; + + return ( + <> + {children({ + clearAlertBuilder, + clearOptimizationLogs, + clearPipelineResults, + clearPipelineStatus, + clearQueryCacheStatus, + pipelineData, + onShowInformation: handleShowInformation, + ...props, + })} + + + {dialogInformationData.title} + + +

{dialogInformationData.text}

+
+
+ + ); }; export default TheBuilderScreen; diff --git a/src/ui/src/containers/BuildModel/TheClassificationScreen/TheClassificationScreen.jsx b/src/ui/src/containers/BuildModel/TheClassificationScreen/TheClassificationScreen.jsx index 7273ec35..e2101590 100644 --- a/src/ui/src/containers/BuildModel/TheClassificationScreen/TheClassificationScreen.jsx +++ b/src/ui/src/containers/BuildModel/TheClassificationScreen/TheClassificationScreen.jsx @@ -111,6 +111,7 @@ const TheClassificationScreen = ({ exportPipeline, getPipelineStepFeatureStats, + onShowInformation, }) => { const routersHistory = useHistory(); const scrollTop = useRef(); @@ -393,6 +394,21 @@ const TheClassificationScreen = ({
{ + onShowInformation( + isAutoML + ? t("model-builder.pipeline-panel-header-automl") + : t("model-builder.pipeline-panel-header-custom"), + isAutoML + ? t("model-builder.pipeline-panel-automl-description") + : t("model-builder.pipeline-panel-custom-description"), + ); + }} pipelineData={pipelineData} handleChangePipeline={handleChangePipeline} getIsReadyToOptimize={getIsReadyToOptimize} diff --git a/src/ui/src/containers/BuildModel/TheFeatureExtractorScreen/TheFeatureExtractorScreen.jsx b/src/ui/src/containers/BuildModel/TheFeatureExtractorScreen/TheFeatureExtractorScreen.jsx index fbd177a4..c7bef864 100644 --- a/src/ui/src/containers/BuildModel/TheFeatureExtractorScreen/TheFeatureExtractorScreen.jsx +++ b/src/ui/src/containers/BuildModel/TheFeatureExtractorScreen/TheFeatureExtractorScreen.jsx @@ -108,6 +108,7 @@ const TheFeatureExtractorScreen = ({ exportPipeline, getPipelineStepFeatureStats, + onShowInformation, }) => { const routersHistory = useHistory(); const scrollTop = useRef(); @@ -199,6 +200,7 @@ const TheFeatureExtractorScreen = ({ projectUUID, pipelineUUID, selectedQueryStepData?.label_column, + true, ); if (isRunning) { startOptimizationChecker(); @@ -368,9 +370,15 @@ const TheFeatureExtractorScreen = ({
{ + onShowInformation( + t("model-builder.pipeline-panel-header-fe"), + t("model-builder.pipeline-panel-fe-description"), + ); + }} isOptimizationRunning={isOptimizationRunning} handleLaunchOptimize={handleLaunchOptimize} handleKillLaunchOptimize={handleKillLaunchOptimize} @@ -486,7 +494,7 @@ const TheFeatureExtractorScreen = ({ /> ) : !_.isEmpty(pipelineResults.featureVectorData) ? ( { diff --git a/src/ui/src/containers/DataManager/TheCapturesScreen/index.js b/src/ui/src/containers/DataManager/TheCapturesScreen/index.js index 094151de..63913410 100644 --- a/src/ui/src/containers/DataManager/TheCapturesScreen/index.js +++ b/src/ui/src/containers/DataManager/TheCapturesScreen/index.js @@ -43,7 +43,7 @@ import TheCapturesScreen from "./TheCapturesScreen"; const mapStateToProps = (state) => { return { captures: selectCapturesStatistics(state), - getCaptureMetadataFormData: (captureUUID) => getCaptureMetadataFormData(state, captureUUID), + getCaptureMetadataFormData: (captureUUID) => getCaptureMetadataFormData(captureUUID), getCaptureConfigurationFormData: (captureUUID) => getCaptureConfigurationFormData(state, captureUUID), getSampleRate: (uuid) => selectedSampleRate(uuid)(state), diff --git a/src/ui/src/i18n/locales/en/account.json b/src/ui/src/i18n/locales/en/account.json index 2616a872..d9e77d08 100644 --- a/src/ui/src/i18n/locales/en/account.json +++ b/src/ui/src/i18n/locales/en/account.json @@ -13,7 +13,7 @@ "description": "Manage your account subscriptions", "plan-header": "Current Plan", "plan-btn": "Manage Subscription", - "credits-plan-title": "Included Clould Resources", + "credits-plan-title": "Included Cloud Resources", "credits-plan-description": "Default cloud resources in your subscription", "credits-purchased-title": "Purchased Cloud Resources", "credits-purchased-description": "Additional cloud resources that you purchased" diff --git a/src/ui/src/i18n/locales/en/layout.json b/src/ui/src/i18n/locales/en/layout.json index 3cce877f..29fcd100 100644 --- a/src/ui/src/i18n/locales/en/layout.json +++ b/src/ui/src/i18n/locales/en/layout.json @@ -33,7 +33,10 @@ "menu-item-pipeline-custom": "Custom Training", "menu-item-explore-model": "Explore Model", "menu-item-test-model": "Test Model", - "menu-item-download-model": "Download Model" + "menu-item-download-model": "Download Model", + "menu-item-pipeline-fe-tooltip": "Explore your data: See what your sensor data looks like and create useful summaries without building a model.", + "menu-item-pipeline-custom-tooltip": "Build your own model: Choose how to process your data and select the model type for maximum control.", + "menu-item-pipeline-automl-tooltip": "Automatically find the best model: Choose how to process your data, and we'll find the best model and settings for you." }, "menu-external": { "get-started-title": "Get Started", diff --git a/src/ui/src/i18n/locales/en/models.json b/src/ui/src/i18n/locales/en/models.json index e912e6e7..fd924ac8 100644 --- a/src/ui/src/i18n/locales/en/models.json +++ b/src/ui/src/i18n/locales/en/models.json @@ -152,7 +152,9 @@ "model-builder": { "show-all-steps": "Show All Steps:", "show-all-steps-tooltip": "By default, certain steps in the pipeline are concealed to assist with visualizing its essential components. By toggling the 'show all steps' button, you can reveal all the steps within the pipeline and also have the ability to add new steps.", - "pipeline-panel-header": "Pipeline: {{pipelineName}}", + "pipeline-panel-header-fe": "Feature Extractor", + "pipeline-panel-header-custom": "Custom Taining", + "pipeline-panel-header-automl": "AutoML Taining", "pipeline-panel-btn-change": "Switch Pipeline", "pipeline-panel-btn-download": "Export Pipeline", "pipeline-panel-btn-start": "Run Pipeline", @@ -160,6 +162,9 @@ "pipeline-panel-btn-start-tooltip": "Excute pipeline", "pipeline-panel-btn-stop-tooltip": "Terminate the pipeline execution", "pipeline-panel-btn-export-tooltip": "Export the pipeline as a JSON, python, or IPython Notebook file", + "pipeline-panel-fe-description": "The feature extraction pipeline helps you understand and prepare your sensor data for analysis. It doesn't train a model itself, but it's a crucial first step in the process. Here's how it works:\nSelect the building blocks: You have the freedom to choose the components that make up your pipeline. This includes:\nDigital Signal Processing: Techniques to clean up and enhance your raw sensor data, like removing noise or isolating specific signals. Think of it like fine-tuning your radio to get a clear signal.\nFeature Extraction: This is where you extract meaningful information from the processed data. Imagine calculating the average temperature from a series of readings, or identifying patterns in a sound wave.\nSampling Techniques: Control how data points are selected from your sensor stream. This helps manage the amount of data being processed and can be important for capturing key events.\nMachine Learning Algorithms (for Feature Engineering): You can even use some machine learning algorithms to help automatically create new, more informative features from your existing data. This can reveal hidden relationships and improve the accuracy of later modeling.\nFast iteration: The pipeline remembers the results of each step. This means you can quickly experiment with different settings and combinations without having to reprocess everything from scratch. It's like having a shortcut to test out different recipes for your data.\n\nBy using the Feature Extraction Pipeline, you can:\n\nVisualize your data: See what your sensor data looks like after each processing step, helping you identify trends and anomalies.\nPrepare data for modeling: Create the right inputs for training machine learning models later on.\nGain insights: Uncover hidden patterns and relationships in your data, even without a full-blown model.", + "pipeline-panel-automl-description": "AutoML Pipeline: Find the Best Model Automatically the AutoML pipeline takes the guesswork out of building machine learning models for your sensor data. You still have control over how your data is prepared, but SensiML's powerful algorithms handle the heavy lifting of finding the best model and settings.\nHere's how it works:\n\nFeature Engineering: Just like the Feature Extraction Pipeline, you choose how your raw sensor data is processed and transformed into meaningful features. This includes selecting digital signal processing techniques, feature extraction methods, and sampling strategies.\nAutomated Machine Learning (AutoML): This is where the magic happens. SensiML's AutoML engine explores a vast array of machine learning algorithms and fine-tunes their parameters to find the optimal solution for your specific data and goals.\nBalance Accuracy and Efficiency: You can guide the AutoML process by setting constraints on model size and complexity. This allows you to find the sweet spot between high accuracy and efficient resource usage. For example, you might discover that a slightly less accurate model uses significantly less memory and processing power, which is crucial for many embedded devices.\nBenefits of using the AutoML Pipeline:\nSave time and effort: No need to manually experiment with countless model types and settings.\nOptimize for your needs: Find models that meet your accuracy requirements while staying within resource limits.\nDiscover new possibilities: AutoML can uncover surprising solutions and model architectures you might not have considered.", + "pipeline-panel-custom-description": "Custom Pipeline: Take Full Control of Your Model\n\nThe Custom pipeline puts you in the driver's seat, giving you complete control over the model building process. You decide exactly how your data is processed, which machine learning algorithm is used, and how the final model behaves.\n\nHere's how it works:\n\nFeature Engineering: Just like the other pipelines, you have the freedom to choose how your sensor data is transformed into meaningful features. This includes selecting digital signal processing techniques, feature extraction methods, and sampling strategies.\nSelect Your Algorithm: Choose from a variety of machine learning algorithms, each with its own strengths and characteristics. You can select the algorithm that best suits your specific task and data.\nFine-tune the Model: Adjust the parameters of your chosen algorithm to optimize its performance. This allows you to tailor the model to your exact needs and achieve the desired balance between accuracy and resource usage.\nBenefits of using the Custom Pipeline:\nMaximum Flexibility: You have complete control over every aspect of the model building process.\nDeep Understanding: Gain a deeper understanding of how your model works and why it makes certain predictions.\nCustomization: Tailor the model to your specific application and hardware constraints.\nExperimentation: Explore different algorithms and settings to find the optimal solution for your needs.", "automl-result-table-title": "Models from each training fold", "result-table-title": "", "automl-summary-table-title": "", diff --git a/src/ui/src/store/captureMetadata/domain/getCaptureMetadataFormData.js b/src/ui/src/store/captureMetadata/domain/getCaptureMetadataFormData.js index b73601bc..bbaad21b 100644 --- a/src/ui/src/store/captureMetadata/domain/getCaptureMetadataFormData.js +++ b/src/ui/src/store/captureMetadata/domain/getCaptureMetadataFormData.js @@ -20,10 +20,12 @@ License along with SensiML Piccolo AI. If not, see { +const getCaptureMetadataFormData = (captureUUID) => { + const state = store.getState(); const metadataItems = selectMetadataFormData(state); const savedCaptureMetadata = selectedCaptureMetadatas(captureUUID)(state); diff --git a/src/ui/src/store/pipelines/actions/getPipelineStepFeatureStats.js b/src/ui/src/store/pipelines/actions/getPipelineStepFeatureStats.js index 4b0d070d..8f958327 100644 --- a/src/ui/src/store/pipelines/actions/getPipelineStepFeatureStats.js +++ b/src/ui/src/store/pipelines/actions/getPipelineStepFeatureStats.js @@ -1,3 +1,5 @@ +import _ from "lodash"; + import api, { throwParsedApiError } from "store/api"; import { FEATURE_SAMPLE_SIZE_LIMIT } from "config"; import helper from "store/helper"; @@ -6,6 +8,13 @@ import logger from "store/logger"; export const getPipelineStepFeatureStats = (projectUUID, pipelineUUID, stepIndex = 0) => async () => { + const getLabelValuesFromDataTable = (dataTable, labelKey) => { + if (dataTable && dataTable[labelKey]) { + return [...new Set(dataTable[labelKey])]; + } + return []; + }; + let data; try { const response = await api.get( @@ -14,7 +23,22 @@ export const getPipelineStepFeatureStats = params: { pipeline_step: stepIndex, sample_size: FEATURE_SAMPLE_SIZE_LIMIT }, }, ); - data = response.data; + + const featureStatsData = response.data; + + const labelValues = getLabelValuesFromDataTable( + featureStatsData.feature_data, + featureStatsData.label_column, + ); + + data = { + featureVectorData: featureStatsData.feature_data, + featureStatistics: featureStatsData.feature_statistics, + featureSummary: featureStatsData.feature_summary, + featureNames: _.keys(featureStatsData.feature_data), + labelColumn: featureStatsData.label_column, + labelValues, + }; } catch (error) { logger.logError( "", diff --git a/src/ui/src/store/pipelines/actions/index.js b/src/ui/src/store/pipelines/actions/index.js index 2268ddfc..1a2b3148 100644 --- a/src/ui/src/store/pipelines/actions/index.js +++ b/src/ui/src/store/pipelines/actions/index.js @@ -73,9 +73,18 @@ import { import { clearIterationMetrics } from "./loadIterationMetrics"; -export { getPipelineStepFeatureStats } from "./getPipelineStepFeatureStats"; +import { getPipelineStepFeatureStats } from "./getPipelineStepFeatureStats"; + +export { getPipelineStepFeatureStats }; export { loadIterationMetrics } from "./loadIterationMetrics"; +const getLabelValuesFromDataTable = (dataTable, labelKey) => { + if (dataTable && dataTable[labelKey]) { + return [...new Set(dataTable[labelKey])]; + } + return []; +}; + export const setRunningStatus = (responseBody) => async (dispatch) => { let logPayload = {}; let isLauching = true; @@ -400,19 +409,12 @@ export const checkPipelineIsRunning = (projectUuid, pipelineUuid) => async () => }; export const loadPipelineResults = - (projectUuid, pipelineUuid, labelKey = "Label") => + (projectUuid, pipelineUuid, labelKey = "Label", isLoadFeature = false) => async (dispatch) => { dispatch({ type: LOADING_PIPELINES_RESULTS }); let isRunning = false; let results = {}; - const getLabelValuesFromLabel = (_responseBody) => { - if (_responseBody?.results && _responseBody?.results[labelKey]) { - return [...new Set(_responseBody.results[labelKey])]; - } - return []; - }; - try { const { data: responseBody } = await api.get( `project/${projectUuid}/sandbox-async/${pipelineUuid}/`, @@ -435,8 +437,19 @@ export const loadPipelineResults = results.featureVectorData = responseBody?.results; results.featureStatistics = responseBody?.statistics_summary?.feature_statistics || []; results.featureSummary = responseBody?.statistics_summary?.feature_summary || []; - results.features = _.keys(responseBody.results); - results.labelValues = getLabelValuesFromLabel(responseBody); + results.featureNames = _.keys(responseBody.results); + results.labelValues = getLabelValuesFromDataTable(responseBody?.results, labelKey); + } else if ( + isLoadFeature && + _.isEmpty(results.featureStatistics) && + !_.isEmpty(responseBody?.execution_summary) + ) { + const lastStepIndex = responseBody?.execution_summary?.length - 1; + const featureStats = await dispatch( + getPipelineStepFeatureStats(projectUuid, pipelineUuid, lastStepIndex), + ); + + results = { ...results, ...featureStats }; } } } catch (err) { diff --git a/src/ui/src/store/projects/actions/index.js b/src/ui/src/store/projects/actions/index.js index d005dd45..751c3a8b 100644 --- a/src/ui/src/store/projects/actions/index.js +++ b/src/ui/src/store/projects/actions/index.js @@ -26,6 +26,7 @@ import helper from "store/helper"; import { createLabelValue } from "store/labels/actions"; import { DEFALULT_LABEL } from "store/labels/domain"; +import { createDefaultMetadata } from "store/metadata/actions"; import { DELETING_PROJECT, @@ -86,6 +87,7 @@ export const createProject = (name) => async (dispatch) => { color: DEFALULT_LABEL.color, }), ); + dispatch(createDefaultMetadata(response?.data?.uuid)); } catch (err) { logger.logError("", err, `${helper.getResponseErrorDetails(err)}}`, "createProject"); throwParsedApiError(err, "createProject");