From 67af031f49c0b187f264426cd05fb67fab777003 Mon Sep 17 00:00:00 2001
From: Davide Valleri <146823822+dvalleri@users.noreply.github.com>
Date: Wed, 30 Oct 2024 10:27:27 +0100
Subject: [PATCH] feat(ui): add percentage in model list (#173)
* feat(ui): add percentage in model list
* connect api
* fix percentages
* fix alert launchPad layout
* replace toFixed with numberFormatter
* fixies after api review
* fix empty layout of launchPad
---
ui/package.json | 2 +-
ui/src/components/charts/line-chart/index.jsx | 1 +
ui/src/components/charts/pie-chart/index.jsx | 67 ++++++--
ui/src/components/charts/pie-chart/options.js | 31 ++--
.../current-import-detail-modal/body.jsx | 12 +-
.../launchpad/central-row/columns.jsx | 32 ++--
.../container/launchpad/central-row/index.jsx | 31 ++--
ui/src/container/launchpad/index.jsx | 12 +-
.../launchpad/right-column/alerts.jsx | 147 ++++++++++++++++++
.../launchpad/right-column/index.jsx | 35 +++++
.../work-in-progress/columns.jsx | 1 -
.../work-in-progress/index.jsx | 8 +-
ui/src/container/launchpad/right/alerts.jsx | 73 ---------
ui/src/container/launchpad/right/index.jsx | 14 --
.../binary-classification/current/index.jsx | 4 +-
.../binary-classification/reference/index.jsx | 4 +-
ui/src/container/models/Details/constants.js | 2 +-
.../multi-classification/current/index.jsx | 4 +-
.../multi-classification/reference/index.jsx | 4 +-
.../Details/regression/current/index.jsx | 4 +-
.../Details/regression/reference/index.jsx | 4 +-
ui/src/container/models/List/columns.jsx | 57 ++++++-
ui/src/container/models/List/index.jsx | 10 +-
ui/src/store/apis.js | 2 +
ui/src/store/state/alerts/api.js | 2 +-
ui/src/store/state/models/api.js | 40 +++--
ui/src/store/state/models/polling-hook.js | 19 ++-
ui/yarn.lock | 8 +-
28 files changed, 437 insertions(+), 193 deletions(-)
create mode 100644 ui/src/container/launchpad/right-column/alerts.jsx
create mode 100644 ui/src/container/launchpad/right-column/index.jsx
rename ui/src/container/launchpad/{right => right-column}/work-in-progress/columns.jsx (96%)
rename ui/src/container/launchpad/{right => right-column}/work-in-progress/index.jsx (94%)
delete mode 100644 ui/src/container/launchpad/right/alerts.jsx
delete mode 100644 ui/src/container/launchpad/right/index.jsx
diff --git a/ui/package.json b/ui/package.json
index 3cc225c2..5d9d92fc 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -17,7 +17,7 @@
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@grafana/faro-react": "^1.8.2",
"@radicalbit/formbit": "1.0.0",
- "@radicalbit/radicalbit-design-system": "1.1.0",
+ "@radicalbit/radicalbit-design-system": "^1.2.0",
"@reduxjs/toolkit": "^2.2.4",
"@vitejs/plugin-react": "^4.2.1",
"ace-builds": "^1.33.2",
diff --git a/ui/src/components/charts/line-chart/index.jsx b/ui/src/components/charts/line-chart/index.jsx
index bd14a723..a6d8fb41 100644
--- a/ui/src/components/charts/line-chart/index.jsx
+++ b/ui/src/components/charts/line-chart/index.jsx
@@ -27,6 +27,7 @@ function LineChart({
title, color, currentData, referenceData = [],
}) {
const handleOnChartReady = (echart) => {
+ console.debug('🚀 ~ handleOnChartReady ~ echart:', 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);
diff --git a/ui/src/components/charts/pie-chart/index.jsx b/ui/src/components/charts/pie-chart/index.jsx
index f5e690dc..e41ded4e 100644
--- a/ui/src/components/charts/pie-chart/index.jsx
+++ b/ui/src/components/charts/pie-chart/index.jsx
@@ -9,6 +9,7 @@ import {
import { PieChart as PieChartEchart } from 'echarts/charts';
import * as echarts from 'echarts/lib/echarts';
+import { numberFormatter } from '@Src/constants';
import pieChartOptions from './options';
echarts.use([
@@ -21,31 +22,69 @@ echarts.use([
]);
function PieChart({ title, data }) {
+ const splittedTitle = title.split(' ');
+
+ if (data <= 0) {
+ return (
+
-
+
+ );
+}
-
-
{splittedTitle[0]}
+function Label({ splittedTitle }) {
+ return (
+
+
{splittedTitle[0]}
-
{splittedTitle[1]}
-
+
{splittedTitle[1]}
);
}
diff --git a/ui/src/components/charts/pie-chart/options.js b/ui/src/components/charts/pie-chart/options.js
index 0c42f27a..fc0b14b8 100644
--- a/ui/src/components/charts/pie-chart/options.js
+++ b/ui/src/components/charts/pie-chart/options.js
@@ -2,27 +2,36 @@ import { CHART_COLOR } from '@Helpers/common-chart-options';
export default function pieChartOptions({ currentData, referenceData }) {
const options = {
-
series: [
{
percentPrecision: 2,
type: 'pie',
radius: ['50%', '80%'],
- label: {
- show: true,
- fontSize: '14',
- fontWeight: 'bold',
- color: CHART_COLOR.WHITE,
- position: 'center',
- formatter: ({ data: { value } }) => `${value}%`,
- },
labelLine: {
show: false,
},
emphasis: { disabled: true },
data: [
- { value: currentData, name: 'Current', itemStyle: { color: CHART_COLOR.CURRENT } },
- { value: referenceData, name: 'Reference', itemStyle: { color: '#f60000' } },
+ {
+ value: currentData,
+ name: 'Current',
+ itemStyle: { color: CHART_COLOR.CURRENT },
+ selected: true,
+ label: {
+ show: true,
+ fontSize: '14',
+ fontWeight: 'bold',
+ color: CHART_COLOR.WHITE,
+ position: 'center',
+ formatter: ({ data: { value } }) => value === 0 ? '--' : `${value}%`,
+ },
+ },
+ {
+ value: referenceData,
+ name: 'Reference',
+ itemStyle: { color: referenceData === 100 ? '#1e2b43' : '#f60000' },
+ label: { show: false },
+ },
],
},
diff --git a/ui/src/components/modals/current-import-detail-modal/body.jsx b/ui/src/components/modals/current-import-detail-modal/body.jsx
index 7f326191..56eeb8b7 100644
--- a/ui/src/components/modals/current-import-detail-modal/body.jsx
+++ b/ui/src/components/modals/current-import-detail-modal/body.jsx
@@ -55,8 +55,8 @@ function useGetTabs() {
case ModelTypeEnum.BINARY_CLASSIFICATION:
return [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children:
,
},
{
@@ -74,8 +74,8 @@ function useGetTabs() {
case ModelTypeEnum.MULTI_CLASSIFICATION:
return [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children:
,
},
{
@@ -93,8 +93,8 @@ function useGetTabs() {
case ModelTypeEnum.REGRESSION:
return [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children:
,
},
{
diff --git a/ui/src/container/launchpad/central-row/columns.jsx b/ui/src/container/launchpad/central-row/columns.jsx
index a5deb651..2b0cd658 100644
--- a/ui/src/container/launchpad/central-row/columns.jsx
+++ b/ui/src/container/launchpad/central-row/columns.jsx
@@ -1,6 +1,6 @@
import JobStatusPin from '@Components/JobStatus/job-status-pin';
import { columnFactory } from '@Src/components/smart-table/utils';
-import { JOB_STATUS } from '@Src/constants';
+import { JOB_STATUS, numberFormatter } from '@Src/constants';
import { ModelTypeEnumLabel } from '@Src/store/state/models/constants';
import { RelativeDateTime, Truncate } from '@radicalbit/radicalbit-design-system';
@@ -39,8 +39,8 @@ export const getColumns = (
activeFilters,
activeSorter,
align: 'left',
- width: '23%',
- render: (type) =>
{ModelTypeEnumLabel[type]}
,
+ width: '21%',
+ render: (type) =>
{ModelTypeEnumLabel[type]}
,
}),
columnFactory({
@@ -50,10 +50,12 @@ export const getColumns = (
activeSorter,
align: 'left',
width: '10%',
- render: ({ dataQuality: { current } }) => {
- const data = (current) ? `${current}%` : '--';
+ render: ({ percentages }) => {
+ const value = percentages?.dataQuality.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value) * 100)}%` : '--';
+
return (
-
+
{data}
);
@@ -67,10 +69,12 @@ export const getColumns = (
activeSorter,
align: 'left',
width: '10%',
- render: ({ modelQuality: { current } }) => {
- const data = (current) ? `${current}%` : '--';
+ render: ({ percentages }) => {
+ const value = percentages?.modelQuality.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value) * 100)}%` : '--';
+
return (
-
+
{data}
);
@@ -84,10 +88,12 @@ export const getColumns = (
activeSorter,
align: 'left',
width: '10%',
- render: ({ dataDrift: { current } }) => {
- const data = (current) ? `${current}%` : '--';
+ render: ({ percentages }) => {
+ const value = percentages?.drift.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value) * 100)}%` : '--';
+
return (
-
+
{data}
);
@@ -102,7 +108,7 @@ export const getColumns = (
activeSorter,
sorter: true,
align: 'right',
- width: '10%',
+ width: '13%',
render: (date) => date &&
,
}),
];
diff --git a/ui/src/container/launchpad/central-row/index.jsx b/ui/src/container/launchpad/central-row/index.jsx
index 0b0e544e..e8f27591 100644
--- a/ui/src/container/launchpad/central-row/index.jsx
+++ b/ui/src/container/launchpad/central-row/index.jsx
@@ -1,9 +1,10 @@
import PieChart from '@Components/charts/pie-chart';
import SmartTable from '@Components/smart-table';
import useModals from '@Hooks/use-modals';
-import { Button, Spinner } from '@radicalbit/radicalbit-design-system';
+import { Button, Spinner, Void } from '@radicalbit/radicalbit-design-system';
import { ModalsEnum, NamespaceEnum } from '@Src/constants';
import { modelsApiSlice } from '@Src/store/state/models/api';
+import { useGetOverallModelListQueryWithPolling } from '@Src/store/state/models/polling-hook';
import { memo } from 'react';
import { useNavigate } from 'react-router';
import { useSearchParams } from 'react-router-dom';
@@ -13,6 +14,8 @@ const { useGetOverallStatsQuery } = modelsApiSlice;
function ModelStatsList() {
const { isLoading } = useGetOverallStatsQuery();
+ const { data } = useGetOverallModelListQueryWithPolling();
+ const count = data?.length;
if (isLoading) {
;
@@ -23,7 +26,7 @@ function ModelStatsList() {
@@ -34,10 +37,9 @@ function ModelStatsList() {
function OverallCharts() {
const { data } = useGetOverallStatsQuery();
-
- const dataQualityStats = data?.overallStats.dataQuality;
- const modelQualityStats = data?.overallStats.modelQuality;
- const dataDriftStats = data?.overallStats.dataDrift;
+ const dataQualityStats = data?.dataQuality || 0;
+ const modelQualityStats = data?.modelQuality || 0;
+ const dataDriftStats = data?.drift || 0;
return (
@@ -55,20 +57,27 @@ function OverallList() {
const { search } = useSearchParams();
const navigate = useNavigate();
- const { data } = useGetOverallStatsQuery();
-
- const modelStats = data?.modelStats.items;
- const count = data?.modelStats.count;
+ const { data } = useGetOverallModelListQueryWithPolling();
+ const count = data?.length;
const handleOnClick = ({ uuid }) => {
navigate({ pathname: `/models/${uuid}`, search });
};
+ if (count === 0) {
+ return (
+
}
+ description="No models are available."
+ />
+ );
+ }
+
return (
({
diff --git a/ui/src/container/launchpad/index.jsx b/ui/src/container/launchpad/index.jsx
index e3bf6eb3..de7fcef2 100644
--- a/ui/src/container/launchpad/index.jsx
+++ b/ui/src/container/launchpad/index.jsx
@@ -1,10 +1,10 @@
-import Right from './right';
-import TopRow from './top-row';
import ModelStatsList from './central-row';
+import RightColumn from './right-column';
+import TopRow from './top-row';
export default function Launchpad() {
return (
-
+
@@ -17,9 +17,3 @@ export default function Launchpad() {
);
}
-
-function RightColumn() {
- return (
-
- );
-}
diff --git a/ui/src/container/launchpad/right-column/alerts.jsx b/ui/src/container/launchpad/right-column/alerts.jsx
new file mode 100644
index 00000000..4731d036
--- /dev/null
+++ b/ui/src/container/launchpad/right-column/alerts.jsx
@@ -0,0 +1,147 @@
+import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
+import { columnFactory } from '@Components/smart-table/utils';
+import { METRICS_TABS, MODEL_TABS_ENUM } from '@Container/models/Details/constants';
+import {
+ Board, DataTable, SectionTitle, Skeleton,
+} from '@radicalbit/radicalbit-design-system';
+import { alertsApiSlice } from '@State/alerts/api';
+import { useNavigate } from 'react-router';
+
+const { useGetAlertsQuery } = alertsApiSlice;
+
+function Alerts() {
+ const { data = [], isLoading, isError } = useGetAlertsQuery();
+
+ if (isLoading) {
+ return (
+
}
+ main={(
+
+
+
+
+
+
+
+ )}
+ />
+ );
+ }
+
+ if (isError) {
+ return (
+
}
+ main={
}
+ size="small"
+ />
+ );
+ }
+
+ if (data.length === 0) {
+ return (false);
+ }
+
+ return (
+
}
+ main={(
+
+
+ )}
+ modifier="h-full"
+ size="small"
+ />
+ );
+}
+
+function Main({
+ alert: {
+ anomalyType, anomalyFeatures, modelUuid, modelName,
+ },
+}) {
+ const navigate = useNavigate();
+
+ const modelNametest = modelName ?? 'MODEL_NAME';
+ const anomalyFeaturesJoined = anomalyFeatures.join(', ');
+ const anomalyTypeLabel = METRICS_TABS[`${anomalyType}`] ?? 'SECTION';
+
+ const handleOnClick = () => {
+ navigate(`/models/${modelUuid}?tab=${MODEL_TABS_ENUM.CURRENT_DASHBOARD}&tab-metrics=${anomalyTypeLabel}`);
+ };
+
+ if (anomalyFeaturesJoined.length > 0) {
+ return (
+
+ {anomalyTypeLabel}
+ :
+
+ {' '}
+
+ {modelNametest}
+
+ {' '}
+
+ model reports a problem on
+
+ {' '}
+
+ {anomalyFeaturesJoined}
+
+ {' '}
+
+ features.
+
+ )}
+ modifier="min-h-fit"
+ onClick={handleOnClick}
+ size="small"
+ type="error"
+ />
+ );
+ }
+
+ return (
+
+
+ {anomalyTypeLabel}
+
+ :
+
+ {' '}
+
+ {modelNametest}
+
+ {' '}
+ reports a problem
+
+ )}
+ modifier="min-h-fit"
+ onClick={handleOnClick}
+ size="small"
+ type="error"
+ />
+ );
+}
+
+const columns = [
+ columnFactory({
+ key: 'name',
+ render: (data) => ,
+ }),
+
+];
+
+export default Alerts;
diff --git a/ui/src/container/launchpad/right-column/index.jsx b/ui/src/container/launchpad/right-column/index.jsx
new file mode 100644
index 00000000..53c66688
--- /dev/null
+++ b/ui/src/container/launchpad/right-column/index.jsx
@@ -0,0 +1,35 @@
+import { alertsApiSlice } from '@Src/store/state/alerts/api';
+import Alerts from './alerts';
+import WorkInProgress from './work-in-progress';
+
+const { useGetAlertsQuery } = alertsApiSlice;
+
+function RightColumn() {
+ const { data = [] } = useGetAlertsQuery();
+ const count = data?.length;
+
+ if (count === 0) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+export default RightColumn;
diff --git a/ui/src/container/launchpad/right/work-in-progress/columns.jsx b/ui/src/container/launchpad/right-column/work-in-progress/columns.jsx
similarity index 96%
rename from ui/src/container/launchpad/right/work-in-progress/columns.jsx
rename to ui/src/container/launchpad/right-column/work-in-progress/columns.jsx
index 829e1b3c..ec7b0bca 100644
--- a/ui/src/container/launchpad/right/work-in-progress/columns.jsx
+++ b/ui/src/container/launchpad/right-column/work-in-progress/columns.jsx
@@ -13,7 +13,6 @@ const columns = [
? latestCurrentJobStatus
: latestReferenceJobStatus;
- console.debug(jobStatus);
return (
);
diff --git a/ui/src/container/launchpad/right/work-in-progress/index.jsx b/ui/src/container/launchpad/right-column/work-in-progress/index.jsx
similarity index 94%
rename from ui/src/container/launchpad/right/work-in-progress/index.jsx
rename to ui/src/container/launchpad/right-column/work-in-progress/index.jsx
index c537bc73..ce95aca5 100644
--- a/ui/src/container/launchpad/right/work-in-progress/index.jsx
+++ b/ui/src/container/launchpad/right-column/work-in-progress/index.jsx
@@ -29,6 +29,7 @@ function WorkInProgress() {
)}
+ modifier="h-full"
/>
);
}
@@ -38,6 +39,7 @@ function WorkInProgress() {
}
main={
}
+ modifier="h-full"
size="small"
/>
);
@@ -47,8 +49,8 @@ function WorkInProgress() {
return (
}
- height="250px"
main={(
)}
+ modifier="h-full"
size="small"
/>
);
@@ -57,7 +59,6 @@ function WorkInProgress() {
return (
}
- height="300px"
main={(
uuid}
- scroll={{ y: '16rem' }}
+ scroll={{ y: '8rem' }}
size="small"
/>
)}
+ modifier="h-full"
size="small"
/>
);
diff --git a/ui/src/container/launchpad/right/alerts.jsx b/ui/src/container/launchpad/right/alerts.jsx
deleted file mode 100644
index 5a2897a2..00000000
--- a/ui/src/container/launchpad/right/alerts.jsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Board, SectionTitle, Skeleton } from '@radicalbit/radicalbit-design-system';
-import SomethingWentWrong from '@Components/ErrorPage/something-went-wrong';
-import { alertsApiSlice } from '@State/alerts/api';
-
-const { useGetAlertsQuery } = alertsApiSlice;
-
-function Alerts() {
- const { data = [undefined, undefined], isLoading, isError } = useGetAlertsQuery();
- console.debug(data);
-
- if (isLoading) {
- return (
- }
- main={(
-
-
-
-
-
-
-
- )}
- />
- );
- }
-
- if (isError) {
- return (
- }
- main={}
- size="small"
- />
- );
- }
-
- return (
- }
- main={(
-
- {data.map((alert) => (
-
- ))}
-
- )}
- size="small"
- />
- );
-}
-
-function Main({ alert }) {
- const alertType = alert?.alertType || 'Foo';
- const fieldsInError = alert?.fieldsInError || 'Bar';
-
- return (
-
- {alertType}
-
- {fieldsInError}
-
- )}
- size="small"
- type="error"
- />
- );
-}
-
-export default Alerts;
diff --git a/ui/src/container/launchpad/right/index.jsx b/ui/src/container/launchpad/right/index.jsx
deleted file mode 100644
index a72cbe34..00000000
--- a/ui/src/container/launchpad/right/index.jsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import Alerts from './alerts';
-import WorkInProgress from './work-in-progress';
-
-function Right() {
- return (
-
- );
-}
-
-export default Right;
diff --git a/ui/src/container/models/Details/binary-classification/current/index.jsx b/ui/src/container/models/Details/binary-classification/current/index.jsx
index 07c64ab8..3c6d6c8e 100644
--- a/ui/src/container/models/Details/binary-classification/current/index.jsx
+++ b/ui/src/container/models/Details/binary-classification/current/index.jsx
@@ -11,8 +11,8 @@ import BinaryClassificationDataDriftMetrics from './data-drift';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/Details/binary-classification/reference/index.jsx b/ui/src/container/models/Details/binary-classification/reference/index.jsx
index d9c74308..dcc7435e 100644
--- a/ui/src/container/models/Details/binary-classification/reference/index.jsx
+++ b/ui/src/container/models/Details/binary-classification/reference/index.jsx
@@ -10,8 +10,8 @@ import ModelQualityMetrics from './model-quality';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/Details/constants.js b/ui/src/container/models/Details/constants.js
index 8f31e7c3..223d5e40 100644
--- a/ui/src/container/models/Details/constants.js
+++ b/ui/src/container/models/Details/constants.js
@@ -5,7 +5,7 @@ export const MODEL_TABS_ENUM = {
};
export const METRICS_TABS = {
- DATA_QUALITIY: 'Data Quality',
+ DATA_QUALITY: 'Data Quality',
MODEL_QUALITY: 'Model Quality',
DATA_DRIFT: 'Data Drift',
IMPORT: 'Import',
diff --git a/ui/src/container/models/Details/multi-classification/current/index.jsx b/ui/src/container/models/Details/multi-classification/current/index.jsx
index 8af3a11a..f2a3e9fd 100644
--- a/ui/src/container/models/Details/multi-classification/current/index.jsx
+++ b/ui/src/container/models/Details/multi-classification/current/index.jsx
@@ -11,8 +11,8 @@ import MultiClassificationDataDriftMetrics from './data-drift';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/Details/multi-classification/reference/index.jsx b/ui/src/container/models/Details/multi-classification/reference/index.jsx
index d9c74308..dcc7435e 100644
--- a/ui/src/container/models/Details/multi-classification/reference/index.jsx
+++ b/ui/src/container/models/Details/multi-classification/reference/index.jsx
@@ -10,8 +10,8 @@ import ModelQualityMetrics from './model-quality';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/Details/regression/current/index.jsx b/ui/src/container/models/Details/regression/current/index.jsx
index 66029b5d..7df5e049 100644
--- a/ui/src/container/models/Details/regression/current/index.jsx
+++ b/ui/src/container/models/Details/regression/current/index.jsx
@@ -11,8 +11,8 @@ import RegressionDataDriftMetrics from './data-drift';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/Details/regression/reference/index.jsx b/ui/src/container/models/Details/regression/reference/index.jsx
index d9c74308..dcc7435e 100644
--- a/ui/src/container/models/Details/regression/reference/index.jsx
+++ b/ui/src/container/models/Details/regression/reference/index.jsx
@@ -10,8 +10,8 @@ import ModelQualityMetrics from './model-quality';
const tabs = [
{
- label: METRICS_TABS.DATA_QUALITIY,
- key: METRICS_TABS.DATA_QUALITIY,
+ label: METRICS_TABS.DATA_QUALITY,
+ key: METRICS_TABS.DATA_QUALITY,
children: ,
},
{
diff --git a/ui/src/container/models/List/columns.jsx b/ui/src/container/models/List/columns.jsx
index 9841423a..dd84aaeb 100644
--- a/ui/src/container/models/List/columns.jsx
+++ b/ui/src/container/models/List/columns.jsx
@@ -1,6 +1,6 @@
import JobStatusPin from '@Components/JobStatus/job-status-pin';
import { columnFactory } from '@Src/components/smart-table/utils';
-import { JOB_STATUS } from '@Src/constants';
+import { JOB_STATUS, numberFormatter } from '@Src/constants';
import { DataTypeEnumLabel, ModelTypeEnumLabel } from '@State/models/constants';
import { RelativeDateTime } from '@radicalbit/radicalbit-design-system';
@@ -37,7 +37,7 @@ export const getColumns = (
activeSorter,
align: 'left',
width: 200,
- render: (type) => {ModelTypeEnumLabel[type]}
,
+ render: (type) => {ModelTypeEnumLabel[type]}
,
}),
columnFactory({
@@ -48,7 +48,58 @@ export const getColumns = (
activeSorter,
align: 'left',
width: 200,
- render: (type) => {DataTypeEnumLabel[type]}
,
+ render: (type) => {DataTypeEnumLabel[type]}
,
+ }),
+
+ columnFactory({
+ title: 'Data Quality',
+ key: 'dataQuality',
+ activeFilters,
+ activeSorter,
+ align: 'left',
+ width: '10%',
+ render: ({ percentages }) => {
+ const value = percentages?.dataQuality.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value).toFixed(4) * 100)}%` : '--';
+
+ return (
+ {data}
+ );
+ },
+ }),
+
+ columnFactory({
+ title: 'Model Quality',
+ key: 'modelQuality',
+ activeFilters,
+ activeSorter,
+ align: 'left',
+ width: '10%',
+ render: ({ percentages }) => {
+ const value = percentages?.modelQuality.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value).toFixed(4) * 100)}%` : '--';
+
+ return (
+ {data}
+ );
+ },
+ }),
+
+ columnFactory({
+ title: 'Drift Quality',
+ key: 'driftQuality',
+ activeFilters,
+ activeSorter,
+ align: 'left',
+ width: '10%',
+ render: ({ percentages }) => {
+ const value = percentages?.drift.value;
+ const data = (value && value !== -1) ? `${numberFormatter({ maximumSignificantDigits: 4 }).format(parseFloat(value).toFixed(4) * 100)}%` : '--';
+
+ return (
+ {data}
+ );
+ },
}),
columnFactory({
diff --git a/ui/src/container/models/List/index.jsx b/ui/src/container/models/List/index.jsx
index e4056aa8..30a02be7 100644
--- a/ui/src/container/models/List/index.jsx
+++ b/ui/src/container/models/List/index.jsx
@@ -17,6 +17,14 @@ export function ModelsList() {
const models = data?.items || [];
const count = data?.total || 0;
+ // TODO we use the following object only for mock purpose. Must be removed when BE are ready
+ const modelsWithMock = models.map(((m) => ({
+ ...m,
+ dataQuality: { current: null, reference: null },
+ modelQuality: { current: null, reference: null },
+ dataDrift: { current: null, reference: null },
+ })));
+
const modifier = models?.length ? '' : 'c-spinner--centered';
if (isError) {
@@ -43,7 +51,7 @@ export function ModelsList() {
({
onClick: () => navigate({
diff --git a/ui/src/store/apis.js b/ui/src/store/apis.js
index 303581b3..49185c42 100644
--- a/ui/src/store/apis.js
+++ b/ui/src/store/apis.js
@@ -7,6 +7,8 @@ export const API_TAGS = {
MODEL: 'Model',
REFERENCE_IMPORT: 'REFERENCE_IMPORT',
CURRENT_IMPORT: 'CURRENT_IMPORT',
+ OVERALL_MODELS: 'OVERALL_MODELS',
+ OVERALL_STATS: 'OVERALL_STATS',
};
export const apiService = createApi({
diff --git a/ui/src/store/state/alerts/api.js b/ui/src/store/state/alerts/api.js
index 5f5d8c23..072e167d 100644
--- a/ui/src/store/state/alerts/api.js
+++ b/ui/src/store/state/alerts/api.js
@@ -6,7 +6,7 @@ export const alertsApiSlice = apiService.injectEndpoints({
providesTags: () => [{ type: API_TAGS.ALERTS }],
query: () => ({
baseUrl: import.meta.env.VITE_BASE_URL,
- url: '/alerts',
+ url: '/models/last_n_alerts?n_alerts=5',
method: 'get',
}),
}),
diff --git a/ui/src/store/state/models/api.js b/ui/src/store/state/models/api.js
index 986343d3..0abcec1a 100644
--- a/ui/src/store/state/models/api.js
+++ b/ui/src/store/state/models/api.js
@@ -1,5 +1,5 @@
+import { JOB_STATUS } from '@Src/constants';
import { API_TAGS, apiService } from '@Src/store/apis';
-import overallStats from './mock/overall_stats.json';
export const modelsApiSlice = apiService.injectEndpoints({
endpoints: (builder) => ({
@@ -56,7 +56,7 @@ export const modelsApiSlice = apiService.injectEndpoints({
}),
deleteModel: builder.mutation({
- invalidatesTags: [API_TAGS.MODELS],
+ invalidatesTags: [API_TAGS.MODELS, API_TAGS.OVERALL_STATS, API_TAGS.ALERTS, API_TAGS.OVERALL_MODELS],
query: ({ uuid }) => ({
baseUrl: import.meta.env.VITE_BASE_URL,
url: `/models/${uuid}`,
@@ -193,6 +193,7 @@ export const modelsApiSlice = apiService.injectEndpoints({
{ type: API_TAGS.CURRENT_IMPORT, id: modelUUID },
{ type: API_TAGS.MODEL, id: modelUUID },
{ type: API_TAGS.MODELS },
+ { type: API_TAGS.OVERALL_MODELS },
];
}
return [];
@@ -208,20 +209,37 @@ export const modelsApiSlice = apiService.injectEndpoints({
}),
}),
- // TODO replace with correct apiUrl
getOverallStats: builder.query({
- /* providesTags: (_, __, { uuid }) => [{ type: API_TAGS.CURRENT_IMPORT, id: uuid }],
- query: ({ uuid, currentUUID }) => ({
+ providesTags: () => [{ type: API_TAGS.OVERALL_STATS }],
+ query: () => ({
baseUrl: import.meta.env.VITE_BASE_URL,
- url: `/models/${uuid}/current/${currentUUID}/drift`,
+ url: '/models/tot_percentages',
method: 'get',
- }), */
- queryFn: () => {
- console.debug(overallStats);
- return { data: overallStats };
- },
+ }),
}),
+ getOverallModelList: builder.query({
+ providesTags: () => [API_TAGS.OVERALL_MODELS],
+ query: ({ limit }) => ({
+ baseUrl: import.meta.env.VITE_BASE_URL,
+ url: `/models/last_n?n_models=${limit}`,
+ method: 'get',
+ }),
+ onQueryStarted: (async (arg, { queryFulfilled, dispatch }) => {
+ const response = await queryFulfilled;
+
+ if (response.data) {
+ const isReferencePending = response.data.some((d) => d.latestReferenceJobStatus === JOB_STATUS.IMPORTING);
+ const isCurrentPending = response.data.some((d) => d.latestCurrentJobStatus === JOB_STATUS.IMPORTING);
+ const isPending = isReferencePending || isCurrentPending;
+
+ if (!isPending) {
+ dispatch(apiService.util.invalidateTags([API_TAGS.OVERALL_STATS, API_TAGS.ALERTS]));
+ }
+ }
+ }),
+
+ }),
}),
});
diff --git a/ui/src/store/state/models/polling-hook.js b/ui/src/store/state/models/polling-hook.js
index d4e9d789..1b721f91 100644
--- a/ui/src/store/state/models/polling-hook.js
+++ b/ui/src/store/state/models/polling-hook.js
@@ -1,8 +1,8 @@
+import useModals from '@Hooks/use-modals';
import { DEFAULT_POLLING_INTERVAL, JOB_STATUS, NamespaceEnum } from '@Src/constants';
import { selectors as contextConfigurationSelectors } from '@State/context-configuration';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router';
-import useModals from '@Hooks/use-modals';
import { modelsApiSlice } from './api';
const { selectQueryParamsSelector } = contextConfigurationSelectors;
@@ -19,6 +19,7 @@ const {
useGetCurrentDriftQuery,
useGetModelByUUIDQuery,
useGetModelsQuery,
+ useGetOverallModelListQuery,
} = modelsApiSlice;
const useGetReferenceImportsQueryWithPolling = () => {
@@ -169,15 +170,25 @@ const useGetModelQueryWithPolling = () => {
return result;
};
+const useGetOverallModelListQueryWithPolling = () => {
+ const result = useGetOverallModelListQuery({ limit: 10 });
+
+ const isReferencePending = result.data?.some((d) => d.latestReferenceJobStatus === JOB_STATUS.IMPORTING);
+ const isCurrentPending = result.data?.some((d) => d.latestCurrentJobStatus === JOB_STATUS.IMPORTING);
+ const isPending = isReferencePending || isCurrentPending;
+
+ useGetOverallModelListQuery({ limit: 10 }, { pollingInterval: DEFAULT_POLLING_INTERVAL, skip: !isPending });
+ return result;
+};
+
export {
useGetCurrentDataQualityQueryWithPolling,
useGetCurrentDriftQueryWithPolling,
useGetCurrentImportsQueryWithPolling,
useGetCurrentModelQualityQueryWithPolling,
- useGetCurrentStatisticsQueryWithPolling,
- useGetReferenceDataQualityQueryWithPolling,
+ useGetCurrentStatisticsQueryWithPolling, useGetModelQueryWithPolling,
+ useGetOverallModelListQueryWithPolling, useGetReferenceDataQualityQueryWithPolling,
useGetReferenceImportsQueryWithPolling,
useGetReferenceModelQualityQueryWithPolling,
useGetReferenceStatisticsQueryWithPolling,
- useGetModelQueryWithPolling,
};
diff --git a/ui/yarn.lock b/ui/yarn.lock
index d66e356b..611a9df4 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -814,10 +814,10 @@
dependencies:
yup "^1.4.0"
-"@radicalbit/radicalbit-design-system@1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@radicalbit/radicalbit-design-system/-/radicalbit-design-system-1.1.0.tgz#2506608ee3b4bfe7cbf6c81da6ea4fba0e0ee76a"
- integrity sha512-GBX5ch7gXpFU20ZM4YpLjCwhV/e0boRxl5cgXiWuBh+NR6rh7HC2rOfsPPRu/1tuaTUU8vwBD8KImQc9SvVBJQ==
+"@radicalbit/radicalbit-design-system@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@radicalbit/radicalbit-design-system/-/radicalbit-design-system-1.2.0.tgz#11ccee8f19c487240a76afdb02c547a510626e41"
+ integrity sha512-35dJkz/D6Ed8h/IX8QARvjL5lQQta7MM0+OoPIqCY0RI36sE4p6Sgd11UR8kszyu6eLxjWnb5fCYsklHBrsUqQ==
dependencies:
"@babel/polyfill" "7.12.1"
"@fortawesome/fontawesome-svg-core" "^6.5.1"