Skip to content

Commit

Permalink
Impl [Models endpoints monitoring] Metrics dropdown selector (#2452)
Browse files Browse the repository at this point in the history
  • Loading branch information
Taras-Hlukhovetskyi authored May 19, 2024
1 parent 157768d commit 92b5003
Show file tree
Hide file tree
Showing 15 changed files with 1,179 additions and 32 deletions.
74 changes: 74 additions & 0 deletions src/actions/details.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ import {
FETCH_MODEL_FEATURE_VECTOR_BEGIN,
FETCH_MODEL_FEATURE_VECTOR_FAILURE,
FETCH_MODEL_FEATURE_VECTOR_SUCCESS,
FETCH_ENDPOINT_METRICS_BEGIN,
FETCH_ENDPOINT_METRICS_SUCCESS,
FETCH_ENDPOINT_METRICS_FAILURE,
FETCH_ENDPOINT_METRICS_VALUES_BEGIN,
FETCH_ENDPOINT_METRICS_VALUES_SUCCESS,
FETCH_ENDPOINT_METRICS_VALUES_FAILURE,
REMOVE_INFO_CONTENT,
REMOVE_JOB_PODS,
REMOVE_MODEL_ENDPOINT,
Expand All @@ -42,9 +48,12 @@ import {
SET_INFO_CONTENT,
SET_ITERATION,
SET_ITERATION_OPTIONS,
SET_SELECTED_METRICS_OPTIONS,
SHOW_WARNING
} from '../constants'
import { generatePods } from '../utils/generatePods'
import { generateMetricsItems, getMetricColorByFullName } from '../components/DetailsMetrics/detailsMetrics.utils'
import { getMetrics, getMetricsValues } from '../components/DetailsMetrics/metricsMock' // todo: metrics - remove after tests and when real API ready with all types

const detailsActions = {
fetchModelEndpointWithAnalysis: (project, uid) => dispatch => {
Expand Down Expand Up @@ -123,6 +132,67 @@ const detailsActions = {
type: FETCH_JOB_PODS_SUCCESS,
payload: pods
}),
fetchModelEndpointMetrics: (project, uid) => dispatch => {
dispatch(detailsActions.fetchEndpointMetricsBegin())

// todo: metrics - remove 'results' type and getMetrics() from mock after test and when real API ready with all types
return detailsApi
.getModelEndpointMetrics(project, uid, 'results')
.then(({ data = [] }) => {
const metrics = generateMetricsItems([...data, ...getMetrics()])

dispatch(detailsActions.fetchEndpointMetricsSuccess({ endpointUid: uid, metrics }))

return metrics
})
.catch(error => {
dispatch(detailsActions.fetchEndpointMetricsFailure(error))
})
},
fetchEndpointMetricsBegin: () => ({
type: FETCH_ENDPOINT_METRICS_BEGIN
}),
fetchEndpointMetricsFailure: error => ({
type: FETCH_ENDPOINT_METRICS_FAILURE,
payload: error
}),
fetchEndpointMetricsSuccess: payload => ({
type: FETCH_ENDPOINT_METRICS_SUCCESS,
payload
}),
// todo: metrics - remove mockNamesToFilter after test and when real API ready with all types
fetchModelEndpointMetricsValues: (project, uid, params, mockNamesToFilter) => dispatch => {
dispatch(detailsActions.fetchEndpointMetricsValuesBegin())

return detailsApi
.getModelEndpointMetricsValues(project, uid, params)
.then(({ data = [] }) => {
// todo: metrics - remove getMetricsValues() with filter after test and when real API ready with all types
const metrics = [...data, ...getMetricsValues().filter(metric => mockNamesToFilter.includes(metric.full_name))].map(metric => {
return {
...metric,
color: getMetricColorByFullName(metric.full_name)
}
})

dispatch(detailsActions.fetchEndpointMetricsValuesSuccess())

return metrics
})
.catch(error => {
dispatch(detailsActions.fetchEndpointMetricsValuesFailure(error))
})
},
fetchEndpointMetricsValuesBegin: () => ({
type: FETCH_ENDPOINT_METRICS_VALUES_BEGIN
}),
fetchEndpointMetricsValuesFailure: error => ({
type: FETCH_ENDPOINT_METRICS_VALUES_FAILURE,
payload: error
}),
fetchEndpointMetricsValuesSuccess: () => ({
type: FETCH_ENDPOINT_METRICS_VALUES_SUCCESS
}),
removeInfoContent: () => ({
type: REMOVE_INFO_CONTENT
}),
Expand Down Expand Up @@ -177,6 +247,10 @@ const detailsActions = {
showWarning: show => ({
type: SHOW_WARNING,
payload: show
}),
setSelectedMetricsOptions: payload => ({
type: SET_SELECTED_METRICS_OPTIONS,
payload
})
}

Expand Down
8 changes: 7 additions & 1 deletion src/api/details-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,13 @@ const detailsApi = {
getModelEndpoint: (project, uid) =>
mainHttpClient.get(`/projects/${project}/model-endpoints/${uid}?feature_analysis=true`),
getModelFeatureVector: (project, name, reference) =>
mainHttpClient.get(`/projects/${project}/feature-vectors/${name}/references/${reference}`)
mainHttpClient.get(`/projects/${project}/feature-vectors/${name}/references/${reference}`),
getModelEndpointMetrics: (project, uid, type = 'all') =>
mainHttpClient.get(`/projects/${project}/model-endpoints/${uid}/metrics?type=${type}`), // type=results/metrics/all
getModelEndpointMetricsValues: (project, uid, params) =>
mainHttpClient.get(`/projects/${project}/model-endpoints/${uid}/metrics-values`, {
params
})
}

export default detailsApi
2 changes: 2 additions & 0 deletions src/components/Details/Details.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const Details = ({
setInfoContent,
setIteration,
setIterationOption,
setSelectedMetricsOptions,
setFiltersWasHandled,
showWarning,
tab
Expand Down Expand Up @@ -291,6 +292,7 @@ const Details = ({
pageData={pageData}
selectedItem={selectedItem}
setIteration={setIteration}
setSelectedMetricsOptions={setSelectedMetricsOptions}
tab={tab}
/>
<TabsSlider tabsList={detailsMenu} initialTab={params.tab} />
Expand Down
55 changes: 36 additions & 19 deletions src/components/Details/DetailsHeader/DetailsHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ import { Button, Tooltip, TextTooltipTemplate, RoundedIcon } from 'igz-controls/
import LoadButton from '../../../common/LoadButton/LoadButton'
import Select from '../../../common/Select/Select'
import ActionsMenu from '../../../common/ActionsMenu/ActionsMenu'
import MetricsSelector from '../../../elements/MetricsSelector/MetricsSelector'
import DatePicker from '../../../common/DatePicker/DatePicker'

import { DETAILS_ARTIFACTS_TAB, DETAILS_METRICS_TAB, FULL_VIEW_MODE, JOBS_PAGE } from '../../../constants'
import {
DETAILS_ARTIFACTS_TAB,
DETAILS_METRICS_TAB,
FULL_VIEW_MODE,
JOBS_PAGE
} from '../../../constants'
import { formatDatetime } from '../../../utils'
import { LABEL_BUTTON } from 'igz-controls/constants'
import { ACTIONS_MENU } from '../../../types'
Expand All @@ -56,6 +62,7 @@ const DetailsHeader = ({
pageData,
selectedItem,
setIteration,
setSelectedMetricsOptions,
tab
}) => {
const detailsStore = useSelector(store => store.detailsStore)
Expand Down Expand Up @@ -133,12 +140,12 @@ const DetailsHeader = ({
stateValue === 'aborted' ? 'N/A' : 'Not yet started'
)
: selectedItem?.updated
? formatDatetime(selectedItem?.updated, 'N/A')
: selectedItem?.spec?.model.includes(':') // 'model-key:model-tag'
? selectedItem.spec.model.replace(/^.*:/, '') // remove key
: selectedItem?.spec?.model
? selectedItem?.metadata?.uid
: ''}
? formatDatetime(selectedItem?.updated, 'N/A')
: selectedItem?.spec?.model.includes(':') // 'model-key:model-tag'
? selectedItem.spec.model.replace(/^.*:/, '') // remove key
: selectedItem?.spec?.model
? selectedItem?.metadata?.uid
: ''}
</span>
{stateValue && stateLabel && (
<Tooltip className="state" template={<TextTooltipTemplate text={stateLabel} />}>
Expand Down Expand Up @@ -192,8 +199,28 @@ const DetailsHeader = ({
</Tooltip>
</>
)}
{params.tab === DETAILS_ARTIFACTS_TAB && detailsStore.iteration && (
<Select
density="dense"
key="Iteration"
label="Iteration:"
onClick={option => {
setIteration(option)
}}
options={detailsStore.iterationOptions}
selectedId={detailsStore.iteration}
/>
)}
{params.tab === DETAILS_METRICS_TAB && (
<>
<MetricsSelector
name="metrics"
metrics={detailsStore.metricsOptions.all}
onSelect={metrics =>
setSelectedMetricsOptions({ endpointUid: selectedItem.metadata.uid, metrics })
}
preselectedMetrics={detailsStore.metricsOptions.preselected}
/>
<DatePicker
className="details-date-picker"
date={detailsStore.dates.value[0]}
Expand All @@ -206,18 +233,6 @@ const DetailsHeader = ({
/>
</>
)}
{params.tab === DETAILS_ARTIFACTS_TAB && detailsStore.iteration && (
<Select
density="dense"
key="Iteration"
label="Iteration:"
onClick={option => {
setIteration(option)
}}
options={detailsStore.iterationOptions}
selectedId={detailsStore.iteration}
/>
)}
{actionButton && !actionButton.hidden && (
<Button
disabled={actionButton.disabled}
Expand Down Expand Up @@ -290,6 +305,7 @@ const DetailsHeader = ({

DetailsHeader.defaultProps = {
handleCancel: null,
setSelectedMetricsOptions: () => {},
handleChangeDates: () => {}
}

Expand All @@ -305,6 +321,7 @@ DetailsHeader.propTypes = {
pageData: PropTypes.shape({}).isRequired,
selectedItem: PropTypes.shape({}).isRequired,
setIteration: PropTypes.func.isRequired,
setSelectedMetricsOptions: PropTypes.func,
tab: PropTypes.string
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ const DetailsTabsContent = ({
case DETAILS_FEATURES_ANALYSIS_TAB:
return <DetailsFeatureAnalysis />
case DETAILS_METRICS_TAB:
return <DetailsMetrics />
return <DetailsMetrics selectedItem={selectedItem} />
case DETAILS_PREVIEW_TAB:
return <DetailsPreview artifact={selectedItem} handlePreview={handlePreview} />
case DETAILS_INPUTS_TAB:
Expand Down
2 changes: 1 addition & 1 deletion src/components/Details/details.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@

&__data {
flex: 0 1 100%;
min-width: 300px;
min-width: 250px;
max-width: calc(100% - 900px);

h3 {
Expand Down
84 changes: 75 additions & 9 deletions src/components/DetailsMetrics/DetailsMetrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,83 @@ illegal under applicable law, and the grant of the foregoing license
under the Apache 2.0 license is conditioned upon your compliance with
such restriction.
*/
import React from 'react'
// import { useSelector } from 'react-redux'
import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useDispatch } from 'react-redux'
import { isEmpty } from 'lodash'

const DetailsMetrics = () => {
// const detailsStore = useSelector(store => store.detailsStore)
import detailsActions from '../../actions/details'

return (
<div className="metrics">
Home for Metrics
</div>
)
const DetailsMetrics = ({ selectedItem }) => {
const [metrics, setMetrics] = useState([])
const detailsStore = useSelector(store => store.detailsStore)
const dispatch = useDispatch()

useEffect(() => {
dispatch(
detailsActions.fetchModelEndpointMetrics(
selectedItem.metadata.project,
selectedItem.metadata.uid
)
)
}, [dispatch, selectedItem])

useEffect(() => {
if (
selectedItem.metadata?.uid &&
!isEmpty(detailsStore.metricsOptions.selectedByEndpoint[selectedItem.metadata?.uid])
) {
const selectedMetrics =
detailsStore.metricsOptions.selectedByEndpoint[selectedItem.metadata?.uid]
const params = { name: [] }

if (detailsStore.dates.value[0]) {
params.start = detailsStore.dates.value[0].getTime()
}

if (detailsStore.dates.value[1]) {
params.end = detailsStore.dates.value[1].getTime()
}

// todo: metrics - remove mockNamesToFilter after test and when real API ready with all types (for now metrics type is not supported and it leads to error)
const mockNamesToFilter = []

selectedMetrics.forEach(metric => {
// todo: metrics - remove 'if statement and mockNamesToFilter after test and when real API ready with all types (for now metrics type is not supported and it leads to error)
mockNamesToFilter.push(metric.full_name)
if (metric.type === 'metric') return

params.name.push(metric.full_name)
})

// todo: metrics - remove if block after test and when real API ready with all types (for now metrics type is not supported and it leads to error)
if (isEmpty(params.name))
params.name.push('for-mock-only.histogram-data-drift.result.hellinger_mean')

// todo: metrics - remove mockNamesToFilter after test and when real API ready with all types (for now metrics type is not supported and it leads to error)
dispatch(
detailsActions.fetchModelEndpointMetricsValues(
selectedItem.metadata.project,
selectedItem.metadata.uid,
params,
mockNamesToFilter
)
).then(metricsList => {
// todo: metrics - remove filter after test and when real API ready with all types (for now metrics type is not supported and it leads to error)
setMetrics(
metricsList.filter(
metric =>
metric.full_name !== 'for-mock-only.histogram-data-drift.result.hellinger_mean'
)
)
})
}
}, [dispatch, selectedItem, detailsStore.dates, detailsStore.metricsOptions.selectedByEndpoint])

// todo: metrics - - remove when merge charts
console.log(metrics)

return <div className="metrics">Home for Metrics</div>
}

export default DetailsMetrics
Loading

0 comments on commit 92b5003

Please sign in to comment.