Skip to content

Commit

Permalink
[RHOAIENG-7481] Add metrics columns to pipeline run table and modal p…
Browse files Browse the repository at this point in the history
…aram selector
  • Loading branch information
jpuzz0 committed Jun 7, 2024
1 parent 23ae524 commit 6443c3c
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

import { Artifact } from '~/third_party/mlmd';
import { GetArtifactsByContextRequest } from '~/third_party/mlmd/generated/ml_metadata/proto/metadata_store_service_pb';
import useFetchState, { FetchState, FetchStateCallbackPromise } from '~/utilities/useFetchState';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { PipelineRunKFv2 } from '~/concepts/pipelines/kfTypes';
import { MlmdContextTypes } from './types';
import { getMlmdContext } from './useMlmdContext';

export const useGetArtifactsByRuns = (
runs: PipelineRunKFv2[],
): FetchState<Record<string, Artifact[]>[]> => {

Check warning on line 13 in frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts#L13

Added line #L13 was not covered by tests
const { metadataStoreServiceClient } = usePipelinesAPI();

const call = React.useCallback<FetchStateCallbackPromise<Record<string, Artifact[]>[]>>(
() =>
Promise.all(
runs.map((run) =>
getMlmdContext(metadataStoreServiceClient, run.run_id, MlmdContextTypes.RUN).then(
async (context) => {
if (!context) {
throw new Error(`No context for run: ${run.run_id}`);

Check warning on line 23 in frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts#L21-L23

Added lines #L21 - L23 were not covered by tests
}

const request = new GetArtifactsByContextRequest();
request.setContextId(context.getId());

Check warning on line 27 in frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts#L26-L27

Added lines #L26 - L27 were not covered by tests

const response = await metadataStoreServiceClient.getArtifactsByContext(request);
const artifacts = response.getArtifactsList();

Check warning on line 30 in frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts#L29-L30

Added lines #L29 - L30 were not covered by tests

return {

Check warning on line 32 in frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/apiHooks/mlmd/useGetArtifactsByRuns.ts#L32

Added line #L32 was not covered by tests
[run.run_id]: artifacts,
};
},
),
),
),
[metadataStoreServiceClient, runs],
);

return useFetchState(call, []);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import ROCCurve from '~/concepts/pipelines/content/artifacts/charts/ROCCurve';
import ConfusionMatrix from '~/concepts/pipelines/content/artifacts/charts/confusionMatrix/ConfusionMatrix';
import { buildConfusionMatrixConfig } from '~/concepts/pipelines/content/artifacts/charts/confusionMatrix/utils';
import { isConfusionMatrix } from '~/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils';
import { getScalarMetrics } from './utils';

interface ArtifactVisualizationProps {
artifact: Artifact;
Expand Down Expand Up @@ -69,24 +70,7 @@ export const ArtifactVisualization: React.FC<ArtifactVisualizationProps> = ({ ar
}

if (artifactType === ArtifactType.METRICS) {
const scalarMetrics = artifact
.toObject()
.customPropertiesMap.reduce(
(
acc: { name: string; value: string }[],
[customPropKey, { stringValue, intValue, doubleValue, boolValue }],
) => {
if (customPropKey !== 'display_name') {
acc.push({
name: customPropKey,
value: stringValue || (intValue || doubleValue || boolValue).toString(),
});
}

return acc;
},
[],
);
const scalarMetrics = getScalarMetrics(artifact);

return (
<Stack className="pf-v5-u-pt-lg pf-v5-u-pb-lg">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Artifact } from '~/third_party/mlmd';

export interface ScalarMetrics {
name: string;
value: string;
}

export const getScalarMetrics = (artifact: Artifact): ScalarMetrics[] =>
artifact
.toObject()
.customPropertiesMap.reduce(
(
acc: { name: string; value: string }[],
[customPropKey, { stringValue, intValue, doubleValue, boolValue }],
) => {
if (customPropKey !== 'display_name') {
acc.push({

Check warning on line 17 in frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts#L14-L17

Added lines #L14 - L17 were not covered by tests
name: customPropKey,
value: stringValue || (intValue || doubleValue || boolValue).toString(),

Check warning on line 19 in frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts#L19

Added line #L19 was not covered by tests
});
}

return acc;

Check warning on line 23 in frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/pipelinesDetails/pipelineRun/artifacts/utils.ts#L23

Added line #L23 was not covered by tests
},
[],
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import React from 'react';
import {
Button,
Checkbox,
DataListDragButton,
DragDrop,
Draggable,
DraggableItemPosition,
Droppable,
Flex,
FlexItem,
Label,
Modal,
ModalBoxBody,
ModalVariant,
Stack,
StackItem,
Tooltip,
} from '@patternfly/react-core';
import { getMetricsColumnsLocalStorageKey } from './utils';

interface MetricsColumn {
id: string;
checked: boolean;
}

interface CustomMetricsColumnsModalProps {
columns: MetricsColumn[];
experimentId: string | undefined;
onClose: () => void;
}

export const CustomMetricsColumnsModal: React.FC<CustomMetricsColumnsModalProps> = ({
onClose,
experimentId,
...props
}) => {
const [columns, setColumns] = React.useState(props.columns);
const [dragDropText, setDragDropText] = React.useState('');
const metricsColumnsLocalStorageKey = getMetricsColumnsLocalStorageKey(experimentId ?? '');
const selectedColumns = Object.values(columns).reduce((acc: string[], column) => {
if (column.checked) {
acc.push(column.id);

Check warning on line 43 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L34-L43

Added lines #L34 - L43 were not covered by tests
}
return acc;

Check warning on line 45 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L45

Added line #L45 was not covered by tests
}, []);

const onDrag = React.useCallback(
(source: DraggableItemPosition) => {
setDragDropText(`Started dragging ${columns[source.index].id}`);

Check warning on line 50 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L48-L50

Added lines #L48 - L50 were not covered by tests

return true;

Check warning on line 52 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L52

Added line #L52 was not covered by tests
},
[columns],
);

const onDragMove = React.useCallback(
(source: DraggableItemPosition, dest?: DraggableItemPosition | undefined) => {
const newDragDropText = dest
? `Move ${columns[source.index].id} to ${columns[dest.index].id}`
: 'Invalid drop zone';

Check warning on line 61 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L57-L61

Added lines #L57 - L61 were not covered by tests

if (newDragDropText !== dragDropText) {
setDragDropText(newDragDropText);

Check warning on line 64 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L63-L64

Added lines #L63 - L64 were not covered by tests
}
},
[columns, dragDropText],
);

const onDrop = React.useCallback(
(source: DraggableItemPosition, dest?: DraggableItemPosition | undefined) => {
if (dest) {
const reorderedColumns = columns;
const [removedColumns] = reorderedColumns.splice(source.index, 1);
reorderedColumns.splice(dest.index, 0, removedColumns);

Check warning on line 75 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L70-L75

Added lines #L70 - L75 were not covered by tests

setColumns(reorderedColumns);
setDragDropText('Dragging finished.');

Check warning on line 78 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L77-L78

Added lines #L77 - L78 were not covered by tests

return true;

Check warning on line 80 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L80

Added line #L80 was not covered by tests
}

return false;

Check warning on line 83 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L83

Added line #L83 was not covered by tests
},
[columns],
);

const onUpdate = React.useCallback(() => {
localStorage.removeItem(metricsColumnsLocalStorageKey);
localStorage.setItem(metricsColumnsLocalStorageKey, JSON.stringify(selectedColumns));

Check warning on line 90 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L88-L90

Added lines #L88 - L90 were not covered by tests

onClose();

Check warning on line 92 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L92

Added line #L92 was not covered by tests
}, [metricsColumnsLocalStorageKey, onClose, selectedColumns]);

return (

Check warning on line 95 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L95

Added line #L95 was not covered by tests
<Modal
bodyAriaLabel="Metrics column names"
tabIndex={0}
hasNoBodyWrapper
aria-label="Custom metrics columns modal"
variant={ModalVariant.small}
height="fit-content"
title="Customize metrics columns"
description={
<Stack hasGutter className="pf-v5-u-pb-md">
<StackItem className="pf-v5-u-mt-sm">
Select up to 10 metrics that will display as columns in the table. Drag and drop column
names to reorder them.
</StackItem>
<StackItem>
<Label>
{selectedColumns.length} / total {columns.length} selected
</Label>
</StackItem>
</Stack>
}
isOpen
onClose={onClose}
actions={[
<Button key="update" variant="primary" onClick={onUpdate}>
Update
</Button>,
<Button key="cancel" variant="link" onClick={onClose}>
Cancel
</Button>,
]}
>
<ModalBoxBody className="pf-v5-u-pt-0">
<DragDrop onDrag={onDrag} onDragMove={onDragMove} onDrop={onDrop}>
<Droppable className="pf-v5-u-pt-sm pf-v5-u-pb-sm">
{columns.map(({ id, checked }) => {

Check warning on line 131 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L131

Added line #L131 was not covered by tests
const columnCheckbox = (
<Checkbox

Check warning on line 133 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L133

Added line #L133 was not covered by tests
id={id}
isChecked={checked}
isDisabled={selectedColumns.length === 10 && !checked}
onChange={(_, isChecked) =>
setColumns((prevColumns) =>
prevColumns.map((prevColumn) => {
if (prevColumn.id === id) {
return { id, checked: isChecked };

Check warning on line 141 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L136-L141

Added lines #L136 - L141 were not covered by tests
}

return prevColumn;

Check warning on line 144 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L144

Added line #L144 was not covered by tests
}),
)
}
/>
);

return (

Check warning on line 151 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L151

Added line #L151 was not covered by tests
<Draggable key={id} className="pf-v5-u-p-sm">
<Flex
alignItems={{ default: 'alignItemsCenter' }}
flexWrap={{ default: 'nowrap' }}
>
<DataListDragButton
aria-label="Reorder"
aria-labelledby={`draggable-${id}`}
aria-describedby={`description-${id}`}
aria-pressed="false"
/>
<FlexItem spacer={{ default: 'spacerSm' }}>
{selectedColumns.length === 10 && !checked ? (
<Tooltip content="Maximum metrics selected. To view values of all metrics, go to the Compare runs page.">

Check warning on line 165 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L164-L165

Added lines #L164 - L165 were not covered by tests
{columnCheckbox}
</Tooltip>
) : (
columnCheckbox

Check warning on line 169 in frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/concepts/pipelines/content/tables/pipelineRun/CustomMetricsColumnsModal.tsx#L169

Added line #L169 was not covered by tests
)}
</FlexItem>
<FlexItem>{id}</FlexItem>
</Flex>
</Draggable>
);
})}
</Droppable>
</DragDrop>
</ModalBoxBody>
</Modal>
);
};
Loading

0 comments on commit 6443c3c

Please sign in to comment.