@@ -51,4 +42,54 @@ function Body() {
);
}
+function useGetTabs() {
+ const { uuid } = useParams();
+ const { data } = useGetModelByUUIDQuery({ uuid });
+
+ const modelType = data?.modelType;
+
+ switch (modelType) {
+ case ModelTypeEnum.BINARY_CLASSIFICATION:
+ return [
+ {
+ label: METRICS_TABS.DATA_QUALITIY,
+ key: METRICS_TABS.DATA_QUALITIY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.MODEL_QUALITY,
+ key: METRICS_TABS.MODEL_QUALITY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.DATA_DRIFT,
+ key: METRICS_TABS.DATA_DRIFT,
+ children:
,
+ },
+ ];
+
+ case ModelTypeEnum.MULTI_CLASSIFICATION:
+ return [
+ {
+ label: METRICS_TABS.DATA_QUALITIY,
+ key: METRICS_TABS.DATA_QUALITIY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.MODEL_QUALITY,
+ key: METRICS_TABS.MODEL_QUALITY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.DATA_DRIFT,
+ key: METRICS_TABS.DATA_DRIFT,
+ children:
,
+ },
+ ];
+
+ default:
+ return false;
+ }
+}
+
export default Body;
diff --git a/ui/src/container/models/Details/current/data-drift/binary-classification/header/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-drift/header/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-drift/binary-classification/header/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-drift/header/index.jsx
diff --git a/ui/src/container/models/Details/binary-classification/current/data-drift/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-drift/index.jsx
new file mode 100644
index 00000000..dc9791e2
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/current/data-drift/index.jsx
@@ -0,0 +1,54 @@
+import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
+import JobStatus from '@Components/JobStatus';
+import { JOB_STATUS } from '@Src/constants';
+import { useGetCurrentDriftQueryWithPolling } from '@State/models/polling-hook';
+import { FormbitContextProvider } from '@radicalbit/formbit';
+import { Spinner } from '@radicalbit/radicalbit-design-system';
+
+import DataDriftList from './list';
+import DataDriftHeader from './header';
+import SearchFeatureList from './search-filter';
+
+const initialValues = {
+ __metadata: {
+ selectedFeatures: [],
+ isNumericalSelected: true,
+ isCategoricalSelected: true,
+ },
+};
+
+function BinaryClassificationDataDriftMetrics() {
+ const { data, isError, isLoading } = useGetCurrentDriftQueryWithPolling();
+
+ const jobStatus = data?.jobStatus;
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (isError) {
+ return
;
+ }
+
+ if (!data) {
+ return
;
+ }
+
+ if (jobStatus === JOB_STATUS.SUCCEEDED) {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
);
+}
+
+export default BinaryClassificationDataDriftMetrics;
diff --git a/ui/src/container/models/Details/current/data-drift/binary-classification/list/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-drift/list/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-drift/binary-classification/list/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-drift/list/index.jsx
diff --git a/ui/src/container/models/Details/current/data-drift/binary-classification/search-filter/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-drift/search-filter/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-drift/binary-classification/search-filter/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-drift/search-filter/index.jsx
diff --git a/ui/src/container/models/Details/current/data-drift/binary-classification/use-get-filtered-features.js b/ui/src/container/models/Details/binary-classification/current/data-drift/use-get-filtered-features.js
similarity index 100%
rename from ui/src/container/models/Details/current/data-drift/binary-classification/use-get-filtered-features.js
rename to ui/src/container/models/Details/binary-classification/current/data-drift/use-get-filtered-features.js
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-point-distribution/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-point-distribution/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-point-distribution/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-point-distribution/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-point-distribution/options.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-point-distribution/options.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-point-distribution/options.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-point-distribution/options.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/left-table/columns.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/left-table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/left-table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/left-table/columns.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/left-table/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/left-table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/left-table/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/left-table/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/right-table/columns.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/right-table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/right-table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/right-table/columns.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/right-table/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/right-table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/categorical/right-table/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/categorical/right-table/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/chart/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/chart/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/chart/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/chart/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/chart/options.js b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/chart/options.js
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/chart/options.js
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/chart/options.js
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/table/columns.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/table/columns.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/table/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/data-quality-list/numerical/table/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/data-quality-list/numerical/table/index.jsx
diff --git a/ui/src/container/models/Details/binary-classification/current/data-quality/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/index.jsx
new file mode 100644
index 00000000..098462c3
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/current/data-quality/index.jsx
@@ -0,0 +1,55 @@
+import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
+import JobStatus from '@Components/JobStatus';
+import { JOB_STATUS } from '@Src/constants';
+import { useGetCurrentDataQualityQueryWithPolling } from '@State/models/polling-hook';
+import { FormbitContextProvider } from '@radicalbit/formbit';
+import { Spinner } from '@radicalbit/radicalbit-design-system';
+import { memo } from 'react';
+import DataPointDistribution from './data-point-distribution';
+import SearchFeatureList from './search-filter';
+import DataQualityList from './data-quality-list';
+
+const initialValues = {
+ __metadata: {
+ selectedFeatures: [],
+ isNumericalSelected: true,
+ isCategoricalSelected: true,
+ },
+};
+
+function BinaryClassificationDataQualityMetrics() {
+ const { data, isError, isLoading } = useGetCurrentDataQualityQueryWithPolling();
+ const jobStatus = data?.jobStatus;
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (isError) {
+ return
;
+ }
+
+ if (!data) {
+ return
;
+ }
+
+ if (jobStatus === JOB_STATUS.SUCCEEDED) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
);
+}
+
+export default memo(BinaryClassificationDataQualityMetrics);
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/search-filter/index.jsx b/ui/src/container/models/Details/binary-classification/current/data-quality/search-filter/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/search-filter/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/data-quality/search-filter/index.jsx
diff --git a/ui/src/container/models/Details/current/data-quality/binary-classification/use-get-filtered-features.js b/ui/src/container/models/Details/binary-classification/current/data-quality/use-get-filtered-features.js
similarity index 100%
rename from ui/src/container/models/Details/current/data-quality/binary-classification/use-get-filtered-features.js
rename to ui/src/container/models/Details/binary-classification/current/data-quality/use-get-filtered-features.js
diff --git a/ui/src/container/models/Details/current/imports/columns.jsx b/ui/src/container/models/Details/binary-classification/current/imports/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/imports/columns.jsx
rename to ui/src/container/models/Details/binary-classification/current/imports/columns.jsx
diff --git a/ui/src/container/models/Details/current/imports/index.jsx b/ui/src/container/models/Details/binary-classification/current/imports/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/imports/index.jsx
rename to ui/src/container/models/Details/binary-classification/current/imports/index.jsx
diff --git a/ui/src/container/models/Details/binary-classification/current/index.jsx b/ui/src/container/models/Details/binary-classification/current/index.jsx
new file mode 100644
index 00000000..a2ffeaaa
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/current/index.jsx
@@ -0,0 +1,63 @@
+import JobStatus from '@Components/JobStatus';
+import { METRICS_TABS } from '@Container/models/Details/constants';
+import { JOB_STATUS } from '@Src/constants';
+import { useGetReferenceDataQualityQueryWithPolling } from '@Src/store/state/models/polling-hook';
+import { Tabs } from '@radicalbit/radicalbit-design-system';
+import { useSearchParams } from 'react-router-dom';
+import BinaryClassificationDataQualityMetrics from './data-quality';
+import Imports from './imports';
+import BinaryClassificationModelQualityMetrics from './model-quality';
+import BinaryClassificationDataDriftMetrics from './data-drift';
+
+const tabs = [
+ {
+ label: METRICS_TABS.DATA_QUALITIY,
+ key: METRICS_TABS.DATA_QUALITIY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.MODEL_QUALITY,
+ key: METRICS_TABS.MODEL_QUALITY,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.DATA_DRIFT,
+ key: METRICS_TABS.DATA_DRIFT,
+ children:
,
+ },
+ {
+ label: METRICS_TABS.IMPORT,
+ key: METRICS_TABS.IMPORT,
+ children:
,
+ },
+
+];
+
+export default function CurrentDashboard() {
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const { data } = useGetReferenceDataQualityQueryWithPolling();
+ const jobStatus = data?.jobStatus;
+
+ const activeTab = searchParams.get('tab-metrics') || METRICS_TABS.METRICS;
+
+ const onChangeTab = (value) => {
+ searchParams.set('tab-metrics', value);
+ setSearchParams(searchParams);
+ };
+
+ if (jobStatus !== JOB_STATUS.SUCCEEDED) {
+ return
;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/ui/src/container/models/Details/current/model-quality/binary-classification/charts.jsx b/ui/src/container/models/Details/binary-classification/current/model-quality/charts.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/model-quality/binary-classification/charts.jsx
rename to ui/src/container/models/Details/binary-classification/current/model-quality/charts.jsx
diff --git a/ui/src/container/models/Details/current/model-quality/binary-classification/columns.jsx b/ui/src/container/models/Details/binary-classification/current/model-quality/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/current/model-quality/binary-classification/columns.jsx
rename to ui/src/container/models/Details/binary-classification/current/model-quality/columns.jsx
diff --git a/ui/src/container/models/Details/binary-classification/current/model-quality/index.jsx b/ui/src/container/models/Details/binary-classification/current/model-quality/index.jsx
new file mode 100644
index 00000000..b99e4f45
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/current/model-quality/index.jsx
@@ -0,0 +1,193 @@
+import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
+import JobStatus from '@Components/JobStatus';
+import ConfusionMatrix from '@Container/models/Details/charts/confusion-matrix-chart';
+import { CHART_COLOR, MODEL_QUALITY_FIELD } from '@Container/models/Details/constants';
+import { JOB_STATUS } from '@Src/constants';
+import { modelsApiSlice } from '@State/models/api';
+import { useGetCurrentModelQualityQueryWithPolling } from '@State/models/polling-hook';
+import {
+ Board, DataTable, SectionTitle, Spinner,
+} from '@radicalbit/radicalbit-design-system';
+import { memo } from 'react';
+import { useParams } from 'react-router';
+import {
+ AccuracyChart,
+ AreaUnderPrChart,
+ AreaUnderRocChart,
+ F1Chart,
+ FalsePositiveRateChart,
+ PrecisionChart,
+ RecallChart,
+ TruePositiveRateChart,
+} from './charts';
+import columns from './columns';
+
+const { useGetReferenceModelQualityQuery } = modelsApiSlice;
+
+function BinaryClassificationModelQualityMetrics() {
+ const { data, isLoading, isError } = useGetCurrentModelQualityQueryWithPolling();
+
+ const jobStatus = data?.jobStatus;
+
+ if (isLoading) {
+ return
;
+ }
+
+ if (isError) {
+ return
;
+ }
+
+ if (!data) {
+ return
;
+ }
+
+ if (jobStatus === JOB_STATUS.SUCCEEDED) {
+ const confusionMatrixLabel = {
+ xAxisLabel: ['Predicted: 1', 'Predicted: 0'],
+ yAxisLabel: ['Actual: 0', 'Actual: 1'],
+ };
+
+ const confusionMatrixData = [
+ [data?.modelQuality.globalMetrics.truePositiveCount, data?.modelQuality.globalMetrics.falseNegativeCount],
+ [data?.modelQuality.globalMetrics.falsePositiveCount, data?.modelQuality.globalMetrics.trueNegativeCount],
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
);
+}
+
+function PerformanceBoard() {
+ const { uuid } = useParams();
+
+ const { data: currentData } = useGetCurrentModelQualityQueryWithPolling();
+ const { data: referenceData } = useGetReferenceModelQualityQuery({ uuid });
+
+ const referenceAccuracy = referenceData?.modelQuality?.accuracy;
+ const referencePrecision = referenceData?.modelQuality?.precision;
+ const referenceRecall = referenceData?.modelQuality?.recall;
+ const referenceF1 = referenceData?.modelQuality?.f1;
+ const referenceFalsePositiveRate = referenceData?.modelQuality?.falsePositiveRate;
+ const referenceTruePositiveRate = referenceData?.modelQuality?.truePositiveRate;
+ const referenceAreaUnderRoc = referenceData?.modelQuality?.areaUnderRoc;
+ const referenceAreaUnderPr = referenceData?.modelQuality?.areaUnderPr;
+
+ const leftTableData = currentData ? [
+ {
+ label: MODEL_QUALITY_FIELD.ACCURACY,
+ referenceValue: referenceAccuracy,
+ currentValue: currentData.modelQuality.globalMetrics.accuracy,
+ },
+ {
+ label: MODEL_QUALITY_FIELD.PRECISION,
+ referenceValue: referencePrecision,
+ currentValue: currentData.modelQuality.globalMetrics.precision,
+ },
+ {
+ label: MODEL_QUALITY_FIELD.RECALL,
+ referenceValue: referenceRecall,
+ currentValue: currentData.modelQuality.globalMetrics.recall,
+ },
+ {
+ label: MODEL_QUALITY_FIELD.F1,
+ referenceValue: referenceF1,
+ currentValue: currentData.modelQuality.globalMetrics.f1,
+ },
+ ] : [];
+
+ const centerTableData = currentData ? [
+ {
+ label: MODEL_QUALITY_FIELD.FALSE_POSITIVE_RATE,
+ referenceValue: referenceFalsePositiveRate,
+ currentValue: currentData.modelQuality.globalMetrics.falsePositiveRate,
+ },
+ {
+ label: MODEL_QUALITY_FIELD.TRUE_POSITIVE_RATE,
+ referenceValue: referenceTruePositiveRate,
+ currentValue: currentData.modelQuality.globalMetrics.truePositiveRate,
+ },
+ ] : [];
+
+ const rightTableData = currentData ? [
+ {
+ label: MODEL_QUALITY_FIELD.AREA_UNDER_ROC,
+ referenceValue: referenceAreaUnderRoc,
+ currentValue: currentData.modelQuality.globalMetrics.areaUnderRoc,
+ },
+ {
+ label: MODEL_QUALITY_FIELD.AREA_UNDER_PR,
+ referenceValue: referenceAreaUnderPr,
+ currentValue: currentData.modelQuality.globalMetrics.areaUnderPr,
+ },
+ ] : [];
+
+ return (
+
}
+ main={(
+
+ label}
+ size="small"
+ />
+
+ label}
+ size="small"
+ />
+
+ label}
+ size="small"
+ />
+
+ )}
+ modifier="shadow"
+ size="small"
+ type="primary-light"
+ />
+ );
+}
+
+export default memo(BinaryClassificationModelQualityMetrics);
diff --git a/ui/src/container/models/Details/binary-classification/index.jsx b/ui/src/container/models/Details/binary-classification/index.jsx
new file mode 100644
index 00000000..48eff735
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/index.jsx
@@ -0,0 +1,37 @@
+import NotFound from '@Components/ErrorPage/not-found';
+import { modelsApiSlice } from '@State/models/api';
+import { useParams, useSearchParams } from 'react-router-dom';
+import { MODEL_TABS_ENUM } from '@Container/models/Details/constants';
+import Current from './current';
+import Overview from './overview';
+import ReferenceDashboard from './reference';
+
+const { useGetModelByUUIDQuery } = modelsApiSlice;
+
+function BinaryClassificationMetrics() {
+ const { uuid } = useParams();
+
+ const [searchParams] = useSearchParams();
+ const activeKey = searchParams.get('tab') || MODEL_TABS_ENUM.OVERVIEW;
+
+ const { error } = useGetModelByUUIDQuery({ uuid });
+ const status = error?.status;
+
+ if (status === 404) {
+ return
;
+ }
+
+ if (activeKey === MODEL_TABS_ENUM.OVERVIEW) {
+ return (
);
+ }
+
+ if (activeKey === MODEL_TABS_ENUM.REFERENCE_DASHBOARD) {
+ return (
);
+ }
+
+ if (activeKey === MODEL_TABS_ENUM.CURRENT_DASHBOARD) {
+ return (
);
+ }
+}
+
+export default BinaryClassificationMetrics;
diff --git a/ui/src/container/models/Details/overview/index.jsx b/ui/src/container/models/Details/binary-classification/overview/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/index.jsx
rename to ui/src/container/models/Details/binary-classification/overview/index.jsx
diff --git a/ui/src/container/models/Details/overview/outputs-tab/columns.jsx b/ui/src/container/models/Details/binary-classification/overview/outputs-tab/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/outputs-tab/columns.jsx
rename to ui/src/container/models/Details/binary-classification/overview/outputs-tab/columns.jsx
diff --git a/ui/src/container/models/Details/overview/outputs-tab/index.jsx b/ui/src/container/models/Details/binary-classification/overview/outputs-tab/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/outputs-tab/index.jsx
rename to ui/src/container/models/Details/binary-classification/overview/outputs-tab/index.jsx
diff --git a/ui/src/container/models/Details/overview/summary-tab/columns.jsx b/ui/src/container/models/Details/binary-classification/overview/summary-tab/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/summary-tab/columns.jsx
rename to ui/src/container/models/Details/binary-classification/overview/summary-tab/columns.jsx
diff --git a/ui/src/container/models/Details/overview/summary-tab/index.jsx b/ui/src/container/models/Details/binary-classification/overview/summary-tab/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/summary-tab/index.jsx
rename to ui/src/container/models/Details/binary-classification/overview/summary-tab/index.jsx
diff --git a/ui/src/container/models/Details/overview/variables-tab/columns.jsx b/ui/src/container/models/Details/binary-classification/overview/variables-tab/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/variables-tab/columns.jsx
rename to ui/src/container/models/Details/binary-classification/overview/variables-tab/columns.jsx
diff --git a/ui/src/container/models/Details/overview/variables-tab/index.jsx b/ui/src/container/models/Details/binary-classification/overview/variables-tab/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/overview/variables-tab/index.jsx
rename to ui/src/container/models/Details/binary-classification/overview/variables-tab/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-point-distribution/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-point-distribution/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-point-distribution/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-point-distribution/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-point-distribution/options.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-point-distribution/options.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-point-distribution/options.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-point-distribution/options.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/left-table/columns.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/left-table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/left-table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/left-table/columns.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/left-table/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/left-table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/left-table/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/left-table/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/right-table/columns.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/right-table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/right-table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/right-table/columns.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/right-table/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/right-table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/categorical/right-table/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/categorical/right-table/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/chart/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/chart/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/chart/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/chart/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/chart/options.js b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/chart/options.js
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/chart/options.js
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/chart/options.js
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/table/columns.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/table/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/table/columns.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/table/columns.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/table/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/table/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/data-quality-list/numerical/table/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/data-quality-list/numerical/table/index.jsx
diff --git a/ui/src/container/models/Details/binary-classification/reference/data-quality/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/index.jsx
new file mode 100644
index 00000000..9f7b19d3
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/reference/data-quality/index.jsx
@@ -0,0 +1,52 @@
+import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
+import JobStatus from '@Components/JobStatus';
+import { JOB_STATUS } from '@Src/constants';
+import { modelsApiSlice } from '@State/models/api';
+import { useGetReferenceDataQualityQueryWithPolling } from '@State/models/polling-hook';
+import { FormbitContextProvider } from '@radicalbit/formbit';
+import { memo } from 'react';
+import { useParams } from 'react-router';
+import DataPointDistribution from './data-point-distribution';
+import SearchFeatureList from './search-filter';
+import DataQualityList from './data-quality-list';
+
+const { useGetReferenceDataQualityQuery } = modelsApiSlice;
+
+const initialValues = {
+ __metadata: {
+ selectedFeatures: [],
+ isNumericalSelected: true,
+ isCategoricalSelected: true,
+ },
+};
+
+function BinaryClassificationDataQualityMetrics() {
+ useGetReferenceDataQualityQueryWithPolling();
+ const { uuid } = useParams();
+ const { data, isError } = useGetReferenceDataQualityQuery({ uuid });
+ const jobStatus = data?.jobStatus;
+
+ if (isError) {
+ return
;
+ }
+
+ if (jobStatus === JOB_STATUS.SUCCEEDED) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
);
+}
+
+export default memo(BinaryClassificationDataQualityMetrics);
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/search-filter/index.jsx b/ui/src/container/models/Details/binary-classification/reference/data-quality/search-filter/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/search-filter/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/search-filter/index.jsx
diff --git a/ui/src/container/models/Details/reference/data-quality/binary-classification/use-get-filtered-features.js b/ui/src/container/models/Details/binary-classification/reference/data-quality/use-get-filtered-features.js
similarity index 100%
rename from ui/src/container/models/Details/reference/data-quality/binary-classification/use-get-filtered-features.js
rename to ui/src/container/models/Details/binary-classification/reference/data-quality/use-get-filtered-features.js
diff --git a/ui/src/container/models/Details/reference/imports/columns.jsx b/ui/src/container/models/Details/binary-classification/reference/imports/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/imports/columns.jsx
rename to ui/src/container/models/Details/binary-classification/reference/imports/columns.jsx
diff --git a/ui/src/container/models/Details/reference/imports/index.jsx b/ui/src/container/models/Details/binary-classification/reference/imports/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/imports/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/imports/index.jsx
diff --git a/ui/src/container/models/Details/reference/index.jsx b/ui/src/container/models/Details/binary-classification/reference/index.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/index.jsx
rename to ui/src/container/models/Details/binary-classification/reference/index.jsx
diff --git a/ui/src/container/models/Details/reference/model-quality/binary-classification/columns.jsx b/ui/src/container/models/Details/binary-classification/reference/model-quality/columns.jsx
similarity index 100%
rename from ui/src/container/models/Details/reference/model-quality/binary-classification/columns.jsx
rename to ui/src/container/models/Details/binary-classification/reference/model-quality/columns.jsx
diff --git a/ui/src/container/models/Details/binary-classification/reference/model-quality/index.jsx b/ui/src/container/models/Details/binary-classification/reference/model-quality/index.jsx
new file mode 100644
index 00000000..41befa04
--- /dev/null
+++ b/ui/src/container/models/Details/binary-classification/reference/model-quality/index.jsx
@@ -0,0 +1,97 @@
+import JobStatus from '@Components/JobStatus';
+import ConfusionMatrix from '@Container/models/Details/charts/confusion-matrix-chart';
+import { CHART_COLOR, MODEL_QUALITY_FIELD } from '@Container/models/Details/constants';
+import { JOB_STATUS } from '@Src/constants';
+import { useGetReferenceModelQualityQueryWithPolling } from '@State/models/polling-hook';
+import {
+ Board, DataTable, SectionTitle, Spinner,
+} from '@radicalbit/radicalbit-design-system';
+import { memo } from 'react';
+import columns from './columns';
+
+function BinaryClassificationModelQualityMetrics() {
+ const { data, isLoading } = useGetReferenceModelQualityQueryWithPolling();
+
+ const jobStatus = data?.jobStatus;
+
+ if (jobStatus === JOB_STATUS.SUCCEEDED) {
+ const leftTableData = data ? [
+ { label: MODEL_QUALITY_FIELD.ACCURACY, value: data.modelQuality.accuracy },
+ { label: MODEL_QUALITY_FIELD.PRECISION, value: data.modelQuality.precision },
+ { label: MODEL_QUALITY_FIELD.RECALL, value: data.modelQuality.recall },
+ { label: MODEL_QUALITY_FIELD.F1, value: data.modelQuality.f1 },
+ ] : [];
+
+ const centerTableData = data ? [
+ { label: 'False positive rate', value: data.modelQuality.falsePositiveRate },
+ { label: 'True positive rate', value: data.modelQuality.truePositiveRate },
+ ] : [];
+
+ const rightTableData = data ? [
+ { label: MODEL_QUALITY_FIELD.AREA_UNDER_ROC, value: data.modelQuality.areaUnderRoc },
+ { label: MODEL_QUALITY_FIELD.AREA_UNDER_PR, value: data.modelQuality.areaUnderPr },
+ ] : [];
+
+ const confusionMatrixLabel = {
+ xAxisLabel: ['Predicted: 1', 'Predicted: 0'],
+ yAxisLabel: ['Actual: 0', 'Actual: 1'],
+ };
+
+ const confusionMatrixData = [
+ [data.modelQuality.truePositiveCount, data.modelQuality.falsePositiveCount],
+ [data.modelQuality.falseNegativeCount, data.modelQuality.trueNegativeCount],
+ ];
+
+ return (
+
+
+
}
+ main={(
+
+ label}
+ size="small"
+ />
+
+ label}
+ size="small"
+ />
+
+ label}
+ size="small"
+ />
+
+ )}
+ size="small"
+ type="secondary"
+ />
+
+
+
+
+ );
+ }
+
+ return (
);
+}
+
+export default memo(BinaryClassificationModelQualityMetrics);
diff --git a/ui/src/container/models/Details/current/data-drift/index.jsx b/ui/src/container/models/Details/current/data-drift/index.jsx
deleted file mode 100644
index ce97591e..00000000
--- a/ui/src/container/models/Details/current/data-drift/index.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { modelsApiSlice } from '@Src/store/state/models/api';
-import { ModelTypeEnum } from '@State/models/constants';
-import { memo } from 'react';
-import { useParams } from 'react-router';
-import BinaryClassificationMetrics from './binary-classification';
-
-const { useGetModelByUUIDQuery } = modelsApiSlice;
-
-function DataDriftMetrics() {
- const { uuid } = useParams();
- const { data } = useGetModelByUUIDQuery({ uuid });
-
- const modelType = data?.modelType;
-
- switch (modelType) {
- case ModelTypeEnum.BINARY_CLASSIFICATION:
- return
;
-
- default:
- return false;
- }
-}
-
-export default memo(DataDriftMetrics);
diff --git a/ui/src/container/models/Details/current/data-quality/index.jsx b/ui/src/container/models/Details/current/data-quality/index.jsx
deleted file mode 100644
index f5a7b539..00000000
--- a/ui/src/container/models/Details/current/data-quality/index.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { modelsApiSlice } from '@Src/store/state/models/api';
-import { ModelTypeEnum } from '@State/models/constants';
-import { memo } from 'react';
-import { useParams } from 'react-router';
-import BinaryClassificationMetrics from './binary-classification';
-
-const { useGetModelByUUIDQuery } = modelsApiSlice;
-
-function DataQualityMetrics() {
- const { uuid } = useParams();
- const { data } = useGetModelByUUIDQuery({ uuid });
-
- const modelType = data?.modelType;
-
- switch (modelType) {
- case ModelTypeEnum.BINARY_CLASSIFICATION:
- return
;
-
- default:
- return false;
- }
-}
-
-export default memo(DataQualityMetrics);
diff --git a/ui/src/container/models/Details/current/model-quality/index.jsx b/ui/src/container/models/Details/current/model-quality/index.jsx
deleted file mode 100644
index a43bcdd5..00000000
--- a/ui/src/container/models/Details/current/model-quality/index.jsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { modelsApiSlice } from '@Src/store/state/models/api';
-import { ModelTypeEnum } from '@State/models/constants';
-import { memo } from 'react';
-import { useParams } from 'react-router';
-import BinaryClassificationMetrics from './binary-classification';
-
-const { useGetModelByUUIDQuery } = modelsApiSlice;
-
-function ModelQualityMetrics() {
- const { uuid } = useParams();
-
- const { data } = useGetModelByUUIDQuery({ uuid });
- const modelType = data?.modelType;
-
- switch (modelType) {
- case ModelTypeEnum.BINARY_CLASSIFICATION:
- return
;
-
- default:
- return false;
- }
-}
-
-export default memo(ModelQualityMetrics);
diff --git a/ui/src/container/models/Details/index.jsx b/ui/src/container/models/Details/index.jsx
index dd00f04b..071e03df 100644
--- a/ui/src/container/models/Details/index.jsx
+++ b/ui/src/container/models/Details/index.jsx
@@ -1,35 +1,25 @@
-import NotFound from '@Components/ErrorPage/not-found';
+import { ModelTypeEnum } from '@Src/store/state/models/constants';
import { modelsApiSlice } from '@State/models/api';
-import { useParams, useSearchParams } from 'react-router-dom';
-import { MODEL_TABS_ENUM } from '@Container/models/Details/constants';
-import Current from './current';
-import Overview from './overview';
-import ReferenceDashboard from './reference';
+import { useParams } from 'react-router-dom';
+import BinaryClassificationMetrics from './binary-classification';
+import MultiClassificationMetrics from './multi-classification';
const { useGetModelByUUIDQuery } = modelsApiSlice;
export function ModelDetails() {
const { uuid } = useParams();
+ const { data } = useGetModelByUUIDQuery({ uuid });
- const [searchParams] = useSearchParams();
- const activeKey = searchParams.get('tab') || MODEL_TABS_ENUM.OVERVIEW;
+ const modelType = data?.modelType;
- const { error } = useGetModelByUUIDQuery({ uuid });
- const status = error?.status;
+ switch (modelType) {
+ case ModelTypeEnum.BINARY_CLASSIFICATION:
+ return
;
- if (status === 404) {
- return
;
- }
-
- if (activeKey === MODEL_TABS_ENUM.OVERVIEW) {
- return (
);
- }
-
- if (activeKey === MODEL_TABS_ENUM.REFERENCE_DASHBOARD) {
- return (
);
- }
+ case ModelTypeEnum.MULTI_CLASSIFICATION:
+ return
;
- if (activeKey === MODEL_TABS_ENUM.CURRENT_DASHBOARD) {
- return (
);
+ default:
+ return false;
}
}
diff --git a/ui/src/container/models/Details/multi-classification/current/data-drift/header/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-drift/header/index.jsx
new file mode 100644
index 00000000..bb1a69d8
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-drift/header/index.jsx
@@ -0,0 +1,180 @@
+import { DRIFT_TEST_ENUM } from '@Src/constants';
+import { useGetCurrentDriftQueryWithPolling } from '@Src/store/state/models/polling-hook';
+import { Board, SectionTitle } from '@radicalbit/radicalbit-design-system';
+
+function DataDriftHeader() {
+ return (
+
+ );
+}
+
+function TotalFeaturesCounter() {
+ const { data } = useGetCurrentDriftQueryWithPolling();
+ const featureMetrics = data?.drift.featureMetrics.filter((feature) => feature.driftCalc.hasDrift) ?? [];
+ const featuresWithDriftCounter = featureMetrics.length;
+ const featuresCounter = data?.drift.featureMetrics.length;
+
+ if (featuresCounter === 0) {
+ return (
+
}
+ main={(
+
+ {/* FIXME: inline style */}
+
--
+
+
Features with drift
+
+ )}
+ modifier="h-full shadow"
+ size="small"
+ />
+ );
+ }
+
+ return (
+
}
+ main={(
+
+ {/* FIXME: inline style */}
+
+ {featuresWithDriftCounter}
+
+
+ /
+ {featuresCounter}
+
+
+
+
Features with drift
+
+ )}
+ modifier="h-full shadow"
+ size="small"
+ type="primary"
+ />
+
+ );
+}
+
+function CategoricalFeaturesCounter() {
+ const { data } = useGetCurrentDriftQueryWithPolling();
+
+ const featureMetrics = data?.drift.featureMetrics ?? [];
+ const categoricalWithDriftCounter = featureMetrics.filter((feature) => feature.driftCalc.type === DRIFT_TEST_ENUM.CHI2 && feature.driftCalc.hasDrift).length;
+ const categoricalCounter = featureMetrics.filter((feature) => feature.driftCalc.type === DRIFT_TEST_ENUM.CHI2).length;
+
+ if (categoricalCounter === 0) {
+ return (
+
}
+ main={(
+
+
+ {/* FIXME: inline style */}
+
--
+
+
Categorical with drift
+
+ )}
+ modifier="h-full"
+ size="small"
+ />
+ );
+ }
+
+ return (
+
}
+ main={(
+
+
+ {/* FIXME: inline style */}
+
+ {categoricalWithDriftCounter}
+
+
+ /
+ {categoricalCounter}
+
+
+
+
+
+
Categorical with drift
+
+ )}
+ modifier="h-full"
+ size="small"
+ />
+ );
+}
+
+function NumericalFeaturesCounter() {
+ const { data } = useGetCurrentDriftQueryWithPolling();
+
+ const featureMetrics = data?.drift.featureMetrics ?? [];
+
+ const numericalWithDriftCounter = featureMetrics.filter((feature) => feature.driftCalc.type === DRIFT_TEST_ENUM.KS && feature.driftCalc.hasDrift).length;
+ const numericalCounter = featureMetrics.filter((feature) => feature.driftCalc.type === DRIFT_TEST_ENUM.KS).length;
+
+ if (numericalCounter === 0) {
+ return (
+
}
+ main={(
+
+
+ {/* FIXME: inline style */}
+
--
+
+
Numerical with drift
+
+ )}
+ modifier="h-full shadow"
+ size="small"
+ />
+ );
+ }
+
+ return (
+
}
+ main={(
+
+
+ {/* FIXME: inline style */}
+
+ {numericalWithDriftCounter}
+
+
+ /
+ {numericalCounter}
+
+
+
+
+
+
Numerical with drift
+
+ )}
+ modifier="h-full shadow"
+ size="small"
+ />
+ );
+}
+
+export default DataDriftHeader;
diff --git a/ui/src/container/models/Details/current/data-drift/binary-classification/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-drift/index.jsx
similarity index 92%
rename from ui/src/container/models/Details/current/data-drift/binary-classification/index.jsx
rename to ui/src/container/models/Details/multi-classification/current/data-drift/index.jsx
index f7897cf1..6653bf02 100644
--- a/ui/src/container/models/Details/current/data-drift/binary-classification/index.jsx
+++ b/ui/src/container/models/Details/multi-classification/current/data-drift/index.jsx
@@ -4,8 +4,9 @@ import { JOB_STATUS } from '@Src/constants';
import { useGetCurrentDriftQueryWithPolling } from '@State/models/polling-hook';
import { FormbitContextProvider } from '@radicalbit/formbit';
import { Spinner } from '@radicalbit/radicalbit-design-system';
-import DataDriftHeader from './header';
+
import DataDriftList from './list';
+import DataDriftHeader from './header';
import SearchFeatureList from './search-filter';
const initialValues = {
@@ -16,7 +17,7 @@ const initialValues = {
},
};
-function DataDriftMetrics() {
+function MultiClassificationDataDriftMetrics() {
const { data, isError, isLoading } = useGetCurrentDriftQueryWithPolling();
const jobStatus = data?.jobStatus;
@@ -50,4 +51,4 @@ function DataDriftMetrics() {
return (
);
}
-export default DataDriftMetrics;
+export default MultiClassificationDataDriftMetrics;
diff --git a/ui/src/container/models/Details/multi-classification/current/data-drift/list/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-drift/list/index.jsx
new file mode 100644
index 00000000..64baa814
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-drift/list/index.jsx
@@ -0,0 +1,114 @@
+import {
+ DRIFT_FEATURE_TYPE_ENUM, DRIFT_TEST_ENUM, DRIFT_TEST_ENUM_LABEL, numberFormatter,
+} from '@Src/constants';
+import { fa1, faC } from '@fortawesome/free-solid-svg-icons';
+import {
+ Board,
+ Button,
+ FontAwesomeIcon,
+ NewHeader,
+ Pin,
+ SectionTitle,
+ Spinner,
+ Tag,
+} from '@radicalbit/radicalbit-design-system';
+import { Virtuoso } from 'react-virtuoso';
+import useGetFilteredFeatures from '../use-get-filtered-features';
+
+function DataDriftList() {
+ const items = useGetFilteredFeatures();
+
+ return (
+
+
+
+ ()}
+ totalCount={items.length}
+ />
+
+ );
+}
+
+function CountLabel() {
+ const items = useGetFilteredFeatures();
+ const label = items.length <= 1 ? 'Record' : 'Records';
+
+ return (
+
+ );
+}
+
+function FeatureRow({ item }) {
+ const pinType = (item.driftCalc.hasDrift) ? 'filled-error' : 'filled';
+ const isError = (item.driftCalc.hasDrift) ? 'is-error' : '';
+ const value = (item.driftCalc.value > 0) ? numberFormatter().format(item.driftCalc.value) : '--';
+ const buttonIcon = getButtonIcon(item.driftCalc.type);
+
+ return (
+
+
+ {DRIFT_TEST_ENUM_LABEL[item.driftCalc.type]}
+
+
+
+ {value}
+
+ {' '}
+
+
+
+
+ )}
+ />
+ )}
+ modifier="my-4 "
+ size="small"
+ />
+ );
+}
+
+const getButtonIcon = (value) => {
+ switch (value) {
+ case DRIFT_TEST_ENUM.KS:
+ return fa1;
+
+ case DRIFT_TEST_ENUM.CHI2:
+ return faC;
+
+ default:
+ return '';
+ }
+};
+
+export default DataDriftList;
diff --git a/ui/src/container/models/Details/multi-classification/current/data-drift/search-filter/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-drift/search-filter/index.jsx
new file mode 100644
index 00000000..7dba3e25
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-drift/search-filter/index.jsx
@@ -0,0 +1,90 @@
+import { useGetCurrentDriftQueryWithPolling } from '@Src/store/state/models/polling-hook';
+import { fa1, faC, faSearch } from '@fortawesome/free-solid-svg-icons';
+import { useFormbitContext } from '@radicalbit/formbit';
+import {
+ Button, FontAwesomeIcon, FormField, Select, Toggle,
+ Tooltip,
+} from '@radicalbit/radicalbit-design-system';
+
+function SearchFeatureList() {
+ const { write } = useFormbitContext();
+
+ const { data } = useGetCurrentDriftQueryWithPolling();
+ const items = data?.drift.featureMetrics ?? [];
+ const options = items.map((i) => ({ label: i.featureName, value: i.featureName }));
+
+ const handleOnSelect = (value) => {
+ write('__metadata.selectedFeatures', value);
+ };
+
+ return (
+
+ );
+}
+
+function NumericalFilter() {
+ const { form, write } = useFormbitContext();
+ const { __metadata: { isNumericalSelected } } = form;
+
+ const type = isNumericalSelected ? 'primary' : 'secondary';
+ const title = isNumericalSelected ? 'Numerical features' : 'Show numerical';
+
+ const handleOnClick = () => {
+ write('__metadata.isNumericalSelected', !isNumericalSelected);
+ };
+
+ return (
+
+ );
+}
+
+function CategoricalFilter() {
+ const { form, write } = useFormbitContext();
+ const { __metadata: { isCategoricalSelected } } = form;
+
+ const type = isCategoricalSelected ? 'primary' : 'secondary';
+ const title = isCategoricalSelected ? 'Categorical features' : 'Show categorical';
+
+ const handleOnClick = () => {
+ write('__metadata.isCategoricalSelected', !isCategoricalSelected);
+ };
+
+ return (
+
+ );
+}
+
+export default SearchFeatureList;
diff --git a/ui/src/container/models/Details/multi-classification/current/data-drift/use-get-filtered-features.js b/ui/src/container/models/Details/multi-classification/current/data-drift/use-get-filtered-features.js
new file mode 100644
index 00000000..e8d92f1e
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-drift/use-get-filtered-features.js
@@ -0,0 +1,35 @@
+import { DRIFT_TEST_ENUM } from '@Src/constants';
+import { useGetCurrentDriftQueryWithPolling } from '@Src/store/state/models/polling-hook';
+import { useFormbitContext } from '@radicalbit/formbit';
+
+export default () => {
+ const { data } = useGetCurrentDriftQueryWithPolling();
+ const items = data?.drift?.featureMetrics ?? [];
+
+ const { form: { __metadata: { isNumericalSelected, isCategoricalSelected, selectedFeatures } } } = useFormbitContext();
+
+ if (!data) {
+ return [];
+ }
+
+ if (!isNumericalSelected && !isCategoricalSelected) {
+ return [];
+ }
+
+ return selectedFeatures?.length > 0
+ ? items.filter(({ featureName, driftCalc: { type } }) => {
+ const isSelected = selectedFeatures.includes(featureName);
+ const isNumerical = isNumericalSelected && type === DRIFT_TEST_ENUM.KS;
+ const isCategorical = isCategoricalSelected && type === DRIFT_TEST_ENUM.CHI2;
+
+ return isSelected && (isNumerical || isCategorical);
+ })
+ : items.filter(({ driftCalc: { type } }) => {
+ const isNumerical = isNumericalSelected && type === DRIFT_TEST_ENUM.KS;
+ const isCategorical = isCategoricalSelected && type === DRIFT_TEST_ENUM.CHI2;
+
+ console.debug(type, isNumerical, isCategorical);
+
+ return isNumerical || isCategorical;
+ });
+};
diff --git a/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/index.jsx
new file mode 100644
index 00000000..39c82c72
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/index.jsx
@@ -0,0 +1,123 @@
+import { modelsApiSlice } from '@Src/store/state/models/api';
+import { useGetCurrentDataQualityQueryWithPolling } from '@Src/store/state/models/polling-hook';
+import {
+ Board,
+ SectionTitle,
+} from '@radicalbit/radicalbit-design-system';
+import ReactEchartsCore from 'echarts-for-react/lib/core';
+import { BarChart } from 'echarts/charts';
+import {
+ GridComponent,
+ LegendComponent,
+ MarkLineComponent,
+ MarkPointComponent,
+} from 'echarts/components';
+import * as echarts from 'echarts/lib/echarts';
+import { useParams } from 'react-router';
+import chartOptions from './options';
+
+echarts.use([
+ GridComponent,
+ MarkPointComponent,
+ MarkLineComponent,
+ LegendComponent,
+ BarChart,
+]);
+
+const { useGetModelByUUIDQuery, useGetReferenceDataQualityQuery } = modelsApiSlice;
+
+const numberFormatter = (value) => new Intl.NumberFormat('it-IT').format(value);
+
+const numberCompactFormatter = (value, maximumSignificantDigits) => {
+ const str = Intl.NumberFormat('en-EN', { notation: 'compact', maximumSignificantDigits }).format(value);
+
+ const re = /(\d*\.?\d+)([a-zA-Z]*)/;
+ const [, figures, letter] = re.exec(str);
+
+ return { figures, letter };
+};
+
+function DataPointDistribution() {
+ return (
+
+ );
+}
+
+function DataPointDistributionCounter() {
+ const { data } = useGetCurrentDataQualityQueryWithPolling();
+ const nObservations = data?.dataQuality.nObservations ?? 0;
+
+ const { figures, letter } = numberCompactFormatter(nObservations);
+ const fullNumber = numberFormatter(nObservations);
+
+ return (
+
+ )}
+ modifier="h-full shadow"
+ size="small"
+ type="primary"
+ />
+ );
+}
+
+function DataPointDistributionChart() {
+ const { uuid } = useParams();
+
+ const { data: model } = useGetModelByUUIDQuery({ uuid });
+ const title = model?.target.name;
+
+ const { data: currentData } = useGetCurrentDataQualityQueryWithPolling();
+
+ const currentClassMetrics = currentData?.dataQuality.classMetrics ?? [];
+
+ const { data: referenceData } = useGetReferenceDataQualityQuery({ uuid });
+ const referenceClassMetrics = referenceData?.dataQuality.classMetrics ?? [];
+
+ const handleOnChartReady = (echart) => {
+ // To handle the second opening of a modal when the rtkq hook read from cache
+ // and the echart graph will render immediately.
+ setTimeout(echart.resize);
+ };
+
+ return (
+
+ )}
+ modifier="w-full h-full shadow"
+ size="small"
+ />
+ );
+}
+
+export default DataPointDistribution;
diff --git a/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/options.jsx b/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/options.jsx
new file mode 100644
index 00000000..5148b9c0
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-quality/data-point-distribution/options.jsx
@@ -0,0 +1,71 @@
+import { CHART_COLOR } from '@Container/models/Details/constants';
+import { numberFormatter } from '@Src/constants';
+
+export default function chartOptions(title, referenceDataset, currentDataset) {
+ const yAxisLabel = currentDataset.map(({ name }) => name);
+
+ return {
+ grid: {
+ left: 0,
+ right: 20,
+ bottom: 0,
+ top: 0,
+ containLabel: true,
+ },
+ xAxis: {
+ type: 'value',
+ boundaryGap: true,
+ axisLabel: {
+ fontSize: 9,
+ color: '#9b99a1',
+ },
+ },
+ yAxis: {
+ type: 'category',
+ data: yAxisLabel,
+ boundaryGap: true,
+ axisTick: { show: false },
+ axisLine: { show: false },
+ splitLine: { show: false },
+ axisLabel: {
+ fontSize: 10,
+ },
+ },
+ emphasis: {
+ disabled: true,
+ },
+ barCategoryGap: '21%',
+ overflow: 'truncate',
+ lineOverflow: 'truncate',
+ series: [
+ {
+ name: 'reference',
+ type: 'bar',
+ color: CHART_COLOR.REFERENCE_LIGHT,
+ label: {
+ show: true,
+ position: 'insideRight',
+ fontWeight: 'bold',
+ color: CHART_COLOR.REFERENCE_DARK,
+ formatter: (el) => (el.data.count > 0) ? `${el.data.count} (${numberFormatter().format(el.data.percentage)}%)` : '',
+
+ },
+ data: referenceDataset.map(({ count, percentage }) => ({ percentage, count, value: count })),
+ },
+ {
+ name: title,
+ type: 'bar',
+ color: CHART_COLOR.CURRENT_LIGHT,
+ label: {
+ show: true,
+ position: 'insideRight',
+ fontWeight: 'bold',
+ color: CHART_COLOR.CURRENT_DARK,
+ formatter: (el) => (el.data.count > 0) ? `${el.data.count} (${numberFormatter().format(el.data.percentage)}%)` : '',
+
+ },
+ data: currentDataset.map(({ count, percentage }) => ({ percentage, count, value: count })),
+ },
+ ],
+ };
+}
diff --git a/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/columns.jsx b/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/columns.jsx
new file mode 100644
index 00000000..6c96de48
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/columns.jsx
@@ -0,0 +1,16 @@
+const columns = [
+ {
+ title: '',
+ key: 'label',
+ dataIndex: 'label',
+ render: (label) =>
,
+ },
+ {
+ title: '',
+ key: 'value',
+ dataIndex: 'value',
+ align: 'right',
+ },
+];
+
+export default columns;
diff --git a/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/index.jsx b/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/index.jsx
new file mode 100644
index 00000000..a8e75cd1
--- /dev/null
+++ b/ui/src/container/models/Details/multi-classification/current/data-quality/data-quality-list/categorical/left-table/index.jsx
@@ -0,0 +1,25 @@
+import { DataTable } from '@radicalbit/radicalbit-design-system';
+import { numberFormatter } from '@Src/constants';
+import columns from './columns';
+
+function CategoricalLeftTable({ data }) {
+ const dataSource = (el) => [
+ { label: 'Miss_val', value: el.missingValue.count },
+ { label: '%_miss_val', value: el.missingValue.percentage },
+ { label: 'Dist_val', value: el?.distinctValue ?? '--' },
+ ].map((o) => ({ ...o, value: (o.value !== '--') ? numberFormatter().format(o.value) : '--' }));
+
+ return (
+