Skip to content

Commit

Permalink
Add TestArtifacts component to GlobalPipelineExperimentsRoutes
Browse files Browse the repository at this point in the history
Refactor ConfusionMatrix component to improve readability and styling

Add ConfusionMatrix component and buildConfusionMatrixConfig function

Add ConfusionMatrix and ROCCurve components

Remove TestArtifacts component and update related routes

Refactor ROC curve table components, update imports, and remove unnecessary code

Refactor ConfusionMatrixSelect component and add skeleton loading

Refactor ROC curve table components and update imports

Refactor ROC curve table components and add related constants

Refactor ROC curve table components and add related constants

Refactor CompareRunsMetricsSection component, update imports, and remove console.log statements

Refactor CompareRunsMetricsSection component, update imports, and add ROC curve tab

Refactor Dockerfile to use linux/amd64 platform

Refactor CompareRunsMetricsSection component, update imports, and add confusion matrix tab

Refactor CompareRunsMetricsSection component, add MetricSectionTabLabels enum, and update related types and imports

Refactor CompareRunsMetricsSection component, add MetricSectionTabLabels enum, and update related types and imports

Refactor CompareRunsMetricsSection component and add MetricSectionTabLabels enum

Refactor and remove unused code in usePipelinesUiRoute.ts and ArtifactUriLink.tsx

Refactor MLMD API hooks and remove unused code

Refactor components to improve readability and add loading spinners

Refactor components to improve readability and add loading spinners

clean up
  • Loading branch information
Gkrumbach07 committed May 8, 2024
1 parent 424378e commit 4cae1ff
Show file tree
Hide file tree
Showing 35 changed files with 1,930 additions and 191 deletions.
22 changes: 17 additions & 5 deletions frontend/src/concepts/pipelines/apiHooks/mlmd/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Artifact, Context, ContextType, Event } from '~/third_party/mlmd';
import { Artifact, Context, ContextType, Event, Execution } from '~/third_party/mlmd';
import { PipelineRunKFv2 } from '~/concepts/pipelines/kfTypes';

export type MlmdContext = Context;

Expand All @@ -8,9 +9,20 @@ export enum MlmdContextTypes {
RUN = 'system.PipelineRun',
}

// An artifact which has associated event.
// You can retrieve artifact name from event.path.steps[0].key
export interface LinkedArtifact {
// each artifact is linked to an event
export type LinkedArtifact = {
event: Event;
artifact: Artifact;
}
};

// each execution can have multiple output artifacts
export type ExecutionArtifact = {
execution: Execution;
linkedArtifacts: LinkedArtifact[];
};

// each run has multiple executions, each execution can have multiple artifacts
export type RunArtifact = {
run: PipelineRunKFv2;
executionArtifacts: ExecutionArtifact[];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { Artifact, GetArtifactsByIDRequest } from '~/third_party/mlmd';
import useFetchState, { FetchState } from '~/utilities/useFetchState';

export const useGetArtifactById = (
artifactId: number,
refreshRate?: number,
): FetchState<Artifact | undefined> => {
const { metadataStoreServiceClient } = usePipelinesAPI();

const fetchArtifact = React.useCallback(async () => {
const request = new GetArtifactsByIDRequest();
request.setArtifactIdsList([artifactId]);

const response = await metadataStoreServiceClient.getArtifactsByID(request);

return response.getArtifactsList()[0];
}, [artifactId, metadataStoreServiceClient]);

return useFetchState(fetchArtifact, undefined, {
refreshRate,
});
};
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import React from 'react';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { GetArtifactTypesRequest } from '~/third_party/mlmd';
import { ArtifactType, GetArtifactTypesRequest } from '~/third_party/mlmd';
import useFetchState, { FetchState, FetchStateCallbackPromise } from '~/utilities/useFetchState';

export const useGetArtifactTypeMap = (): FetchState<Record<number, string>> => {
export const useGetArtifactTypes = (): FetchState<ArtifactType[]> => {
const { metadataStoreServiceClient } = usePipelinesAPI();

const call = React.useCallback<FetchStateCallbackPromise<Record<number, string>>>(async () => {
const call = React.useCallback<FetchStateCallbackPromise<ArtifactType[]>>(async () => {
const request = new GetArtifactTypesRequest();

const res = await metadataStoreServiceClient.getArtifactTypes(request);

const artifactTypeMap: Record<number, string> = {};
res.getArtifactTypesList().forEach((artifactType) => {
artifactTypeMap[artifactType.getId()] = artifactType.getName();
});
return artifactTypeMap;
return res.getArtifactTypesList();
}, [metadataStoreServiceClient]);

return useFetchState(call, {});
return useFetchState(call, []);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';

import { usePipelinesAPI, useMlmdListContext } from '~/concepts/pipelines/context';
import { Artifact, GetArtifactsRequest } from '~/third_party/mlmd';
import { ListOperationOptions } from '~/third_party/mlmd/generated/ml_metadata/proto/metadata_store_pb';
import useFetchState, { FetchState } from '~/utilities/useFetchState';

export interface ArtifactsListResponse {
artifacts: Artifact[];
nextPageToken: string;
}

export const useGetArtifactsList = (
refreshRate?: number,
): FetchState<ArtifactsListResponse | null> => {
const { pageToken, maxResultSize, filterQuery } = useMlmdListContext();
const { metadataStoreServiceClient } = usePipelinesAPI();

const fetchArtifactsList = React.useCallback(async () => {
const request = new GetArtifactsRequest();
const listOperationOptions = new ListOperationOptions();

if (filterQuery) {
listOperationOptions.setFilterQuery(filterQuery);
}

if (pageToken) {
listOperationOptions.setNextPageToken(pageToken);
}

listOperationOptions.setMaxResultSize(maxResultSize);
request.setOptions(listOperationOptions);

const response = await metadataStoreServiceClient.getArtifacts(request);

return {
artifacts: response.getArtifactsList(),
nextPageToken: response.getNextPageToken(),
};
}, [filterQuery, pageToken, maxResultSize, metadataStoreServiceClient]);

return useFetchState(fetchArtifactsList, null, {
refreshRate,
});
};
23 changes: 17 additions & 6 deletions frontend/src/concepts/pipelines/apiHooks/mlmd/useMlmdContext.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import React from 'react';
import { MlmdContext, MlmdContextTypes } from '~/concepts/pipelines/apiHooks/mlmd/types';
import { usePipelinesAPI } from '~/concepts/pipelines/context';
import { GetContextByTypeAndNameRequest } from '~/third_party/mlmd';
import {
GetContextByTypeAndNameRequest,
MetadataStoreServicePromiseClient,
} from '~/third_party/mlmd';
import useFetchState, {
FetchState,
FetchStateCallbackPromise,
NotReadyError,
} from '~/utilities/useFetchState';

export const getMlmdContext = async (
client: MetadataStoreServicePromiseClient,
name: string,
type: MlmdContextTypes,
): Promise<MlmdContext | undefined> => {
const request = new GetContextByTypeAndNameRequest();
request.setTypeName(type);
request.setContextName(name);
const res = await client.getContextByTypeAndName(request);
return res.getContext();
};

/**
* A hook used to use the MLMD service and fetch the MLMD context
* If being used without name/type, this hook will throw an error
Expand All @@ -28,11 +43,7 @@ export const useMlmdContext = (
return Promise.reject(new NotReadyError('No context name'));
}

const request = new GetContextByTypeAndNameRequest();
request.setTypeName(type);
request.setContextName(name);
const res = await metadataStoreServiceClient.getContextByTypeAndName(request);
const context = res.getContext();
const context = await getMlmdContext(metadataStoreServiceClient, name, type);
if (!context) {
return Promise.reject(new Error('Cannot find specified context'));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import React from 'react';
import { Text } from '@patternfly/react-core';

export type ConfusionMatrixInput = {
annotationSpecs: {
displayName: string;
}[];
rows: { row: number[] }[];
};

export interface ConfusionMatrixConfig {
data: number[][];
labels: string[];
}

export function buildConfusionMatrixConfig(
confusionMatrix: ConfusionMatrixInput,
): ConfusionMatrixConfig {
return {
labels: confusionMatrix.annotationSpecs.map((annotation) => annotation.displayName),
data: confusionMatrix.rows.map((x) => x.row),
};
}

type ConfusionMatrixProps = {
config: ConfusionMatrixConfig;
size?: number;
};

const ConfusionMatrix: React.FC<ConfusionMatrixProps> = ({
config: { data, labels },
size = 100,
}) => {
const max = Math.max(...data.flat());

// Function to get color based on the cell value
const getColor = (value: number) => {
const opacity = value / max; // Normalize the value to get opacity
return `rgba(41, 121, 255, ${opacity})`; // Use blue color with calculated opacity
};

// Determine the size for all cells, including labels
const cellSize = `${size}px`;

// Generate the gradient for the legend
const gradientLegend = `linear-gradient(to bottom, rgba(41, 121, 255, 1) 0%, rgba(41, 121, 255, 0) 100%)`;

return (
<div
style={{
display: 'flex',
margin: '20px',
marginRight: '75px',
}}
>
<table style={{ borderCollapse: 'collapse' }}>
<tbody>
{data.map((row, rowIndex) => (
<tr key={labels[rowIndex]}>
<td
style={{
lineHeight: cellSize,
paddingRight: 10,
minWidth: cellSize,
textAlign: 'right',
whiteSpace: 'nowrap',
}}
>
<Text>{labels[rowIndex]}</Text>
</td>
{row.map((cell, cellIndex) => (
<td
key={labels[cellIndex] + labels[rowIndex]}
style={{
backgroundColor: getColor(cell),
border: `solid 1px white`,
color: cell / max < 0.6 ? 'black' : 'white',
height: cellSize,
minHeight: cellSize,
minWidth: cellSize,
position: 'relative',
textAlign: 'center',
verticalAlign: 'middle',
width: cellSize,
}}
>
{cell}
</td>
))}
</tr>
))}
<tr>
<th
style={{
width: cellSize,
}}
/>
{labels.map((label, i) => (
<th key={i}>
<div
style={{
writingMode: 'vertical-lr',
whiteSpace: 'nowrap',
width: cellSize,
}}
>
<Text style={{ transform: `translateX(${size / 4}px) rotate(315deg)` }}>
{label}
</Text>
</div>
</th>
))}
</tr>
</tbody>
</table>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
}}
>
<div
style={{
height: 0.75 * data.length * size,
background: gradientLegend,
borderRight: 'solid 1px #777',
marginLeft: 30,
minWidth: 10,
position: 'relative',
width: 10,
}}
>
<div
style={{
top: 0,
borderTop: 'solid 1px #777',
left: '100%',
paddingLeft: 5,
position: 'absolute',
width: 5,
}}
>
<span
style={{
left: 15,
position: 'absolute',
top: -7,
}}
>
{max}
</span>
</div>
{new Array(5).fill(0).map((_, i) => (
<div
key={i}
style={{
top: `${((5 - i) / 5) * 100}%`,
borderTop: 'solid 1px #777',
left: '100%',
paddingLeft: 5,
position: 'absolute',
width: 5,
}}
>
<span style={{ left: 15, position: 'absolute', top: -7 }}>
{Math.floor((i / 5) * max)}
</span>
</div>
))}
</div>
<div
style={{
fontWeight: 'bold',
paddingLeft: 20,
}}
>
True label
</div>
</div>
</div>
);
};

export default ConfusionMatrix;
Loading

0 comments on commit 4cae1ff

Please sign in to comment.