Skip to content

Commit

Permalink
Images Explorer search and extras (#108)
Browse files Browse the repository at this point in the history
* Add experiment callbacks to app model

* Add images app

* Add experiment header

* Add formatted URI for artifact image fetching

* Add conditional formatting based on artifact_location

* Add project params polling for images

* Fix alignmentConfigs issue (temp)

* Replace GET with POST for images stream

* Remove comment
  • Loading branch information
jescalada authored Jul 31, 2024
1 parent 201ffd2 commit 9bf1c86
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 40 deletions.
28 changes: 20 additions & 8 deletions src/src/components/MediaList/ImageBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,24 @@ const ImageBox = ({
let subscription: any;

if (blobData === null) {
if (blobsURIModel.getState()[blob_uri]) {
setBlobData(blobsURIModel.getState()[blob_uri]);
// Get the formatted URI containing the run hash and experiment id
const formattedUri = data.run.props.experiment.artifact_location
? `${data.run.props.experiment.artifact_location}/${data.run.hash}/artifacts/${blob_uri}`
: `artifacts/${data.run.props.experiment.id}/${data.run.hash}/artifacts/${blob_uri}`;

if (blobsURIModel.getState()[formattedUri]) {
setBlobData(blobsURIModel.getState()[formattedUri]);
} else {
subscription = blobsURIModel.subscribe(blob_uri, (data) => {
setBlobData(data[blob_uri]);
subscription = blobsURIModel.subscribe(formattedUri, (data) => {
setBlobData(data[formattedUri]);
subscription.unsubscribe();
});
timeoutID = window.setTimeout(() => {
if (blobsURIModel.getState()[blob_uri]) {
setBlobData(blobsURIModel.getState()[blob_uri]);
if (blobsURIModel.getState()[formattedUri]) {
setBlobData(blobsURIModel.getState()[formattedUri]);
subscription.unsubscribe();
} else {
addUriToList(blob_uri);
addUriToList(formattedUri);
}
}, BATCH_COLLECT_DELAY);
}
Expand All @@ -67,7 +72,14 @@ const ImageBox = ({
subscription.unsubscribe();
}
};
}, [addUriToList, blobData, blob_uri]);
}, [
addUriToList,
blobData,
blob_uri,
data.run.hash,
data.run.props.experiment.id,
data.run.props.experiment.artifact_location,
]);

function onImageFullSizeModeButtonClick(e: React.ChangeEvent<any>): void {
e.stopPropagation();
Expand Down
17 changes: 17 additions & 0 deletions src/src/config/table/tableConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ export const TABLE_DEFAULT_CONFIG: Record<string, any> = {
},
height: '0.5',
},
[AppNameEnum.IMAGES]: {
resizeMode: ResizeModeEnum.Resizable,
rowHeight: RowHeightSize.md,
sortFields: [],
unselectedColumnState: UnselectedColumnState.FORCE_HIDE,
hiddenMetrics: [],
hiddenColumns: ['hash', 'description'],
nonHidableColumns: new Set(['#', 'run', 'actions']),
hideSystemMetrics: true,
columnsWidths: {},
columnsOrder: {
left: ['run'],
middle: [],
right: [],
},
height: '0.5',
},
};

export const AVOID_COLUMNS_TO_HIDE_LIST = new Set([
Expand Down
14 changes: 14 additions & 0 deletions src/src/pages/ImagesExplore/ImagesExplore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,14 @@ function ImagesExplore(): React.FunctionComponentElement<React.ReactNode> {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

React.useEffect(() => {
imagesExploreAppModel.fetchProjectParamsAndUpdateState();
const pollingTimer = setInterval(() => {
imagesExploreAppModel.fetchProjectParamsAndUpdateState();
}, 30000);
return () => clearInterval(pollingTimer);
}, []);

return (
<ErrorBoundary>
<div className='ImagesExplore__container' ref={wrapperElemRef}>
Expand All @@ -227,6 +235,12 @@ function ImagesExplore(): React.FunctionComponentElement<React.ReactNode> {
onBookmarkUpdate={imagesExploreAppModel.onBookmarkUpdate}
onResetConfigData={imagesExploreAppModel.onResetConfigData}
title={pageTitlesEnum.IMAGES_EXPLORER}
onSelectExperimentsChange={
imagesExploreAppModel.onSelectExperimentsChange
}
onToggleAllExperiments={
imagesExploreAppModel.onToggleAllExperiments
}
/>
<div className='ImagesExplore__SelectForm__Grouping__container'>
<SelectForm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ import ConfirmModal from 'components/ConfirmModal/ConfirmModal';

import { DOCUMENTATIONS } from 'config/references';

import { IResourceState } from 'modules/core/utils/createResource';
import {
IExperimentData,
IExperimentDataShort,
} from 'modules/core/api/experimentsApi';

import createExperimentEngine from 'pages/Dashboard/components/ExploreSection/ExperimentsCard/ExperimentsStore';
import ExperimentBar from 'pages/Experiment/components/ExperimentBar';
import useExperimentState from 'pages/Experiment/useExperimentState';

import { getSelectedExperiments } from 'utils/app/getSelectedExperiments';

import './ImagesExploreAppBar.scss';

function ImagesExploreAppBar({
Expand All @@ -20,10 +32,50 @@ function ImagesExploreAppBar({
onResetConfigData,
title,
disabled,
onSelectExperimentsChange,
onToggleAllExperiments,
}: any): React.FunctionComponentElement<React.ReactNode> {
const [popover, setPopover] = React.useState<string>('');
const [selectedExperiments, setSelectedExperiments] = React.useState<
IExperimentDataShort[]
>(getSelectedExperiments());

const route = useRouteMatch<any>();

const { current: experimentsEngine } = React.useRef(createExperimentEngine);

const experimentsStore: IResourceState<IExperimentData[]> =
experimentsEngine.experimentsState((state) => state);

React.useEffect(() => {
experimentsEngine.fetchExperiments();
return () => {
experimentsEngine.destroy();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Fetch all experiments along with default
const {
experimentState,
experimentsState,
selectedExperiments: filteredSelectedExperiments,
getExperimentsData,
} = useExperimentState(experimentsStore.data?.[0]?.id);

// Remove selected experiments that are not in the list of fetched experiments
React.useEffect(() => {
if (filteredSelectedExperiments.length !== selectedExperiments.length) {
setSelectedExperiments(filteredSelectedExperiments);
}
}, [filteredSelectedExperiments, selectedExperiments]);

const { data: experimentData, loading: isExperimentLoading } =
experimentState;

const { data: experimentsData, loading: isExperimentsLoading } =
experimentsState;

function handleBookmarkClick(value: string): void {
setPopover(value);
}
Expand All @@ -37,9 +89,23 @@ function ImagesExploreAppBar({
handleClosePopover();
}

function handleExperimentsChange(experiment: IExperimentDataShort): void {
onSelectExperimentsChange(experiment);
setSelectedExperiments(getSelectedExperiments());
}

return (
<ErrorBoundary>
<AppBar title={title} disabled={disabled}>
<ExperimentBar
experimentsData={experimentsData}
isExperimentLoading={isExperimentLoading}
isExperimentsLoading={isExperimentsLoading}
selectedExperiments={selectedExperiments}
getExperimentsData={getExperimentsData}
onSelectExperimentsChange={handleExperimentsChange}
onToggleAllExperiments={onToggleAllExperiments}
/>
{route.params.appId ? (
<ErrorBoundary>
<ControlPopover
Expand Down
2 changes: 1 addition & 1 deletion src/src/routes/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ const routes = {
IMAGE_EXPLORE: {
path: PathEnum.Images_Explore,
component: ImagesExplore,
showInSidebar: false,
showInSidebar: true,
displayName: 'Images',
icon: 'images',
isExact: true,
Expand Down
8 changes: 7 additions & 1 deletion src/src/services/api/imagesExplore/imagesExploreService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ const endpoints = {
};

function getImagesExploreData(params: {}): IApiRequest<ReadableStream> {
return API.getStream<ReadableStream>(endpoints.GET_IMAGES, params);
return API.getStream<IApiRequest<ReadableStream>>(
endpoints.GET_IMAGES,
params,
{
method: 'POST',
},
);
}

function getImagesByURIs(body: string[]): IApiRequest<any> {
Expand Down
13 changes: 7 additions & 6 deletions src/src/services/models/explorer/createAppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ function createAppModel(appConfig: IAppInitialConfig) {
x_axis_iters,
} = filterMetricsData(
trace,
configData?.chart?.alignmentConfigs[0].type,
configData?.chart?.alignmentConfigs?.[0]?.type,
configData?.chart?.axesScaleType,
);

Expand Down Expand Up @@ -1469,7 +1469,7 @@ function createAppModel(appConfig: IAppInitialConfig) {
function alignData(
data: IMetricsCollection<IMetric>[],
type: AlignmentOptionsEnum = model.getState()!.config!.chart
?.alignmentConfigs[0].type,
?.alignmentConfigs?.[0]?.type,
chartId: number = 0,
): IMetricsCollection<IMetric>[] {
const alignmentObj: { [key: string]: Function } = {
Expand All @@ -1478,14 +1478,15 @@ function createAppModel(appConfig: IAppInitialConfig) {
[AlignmentOptionsEnum.RELATIVE_TIME]: alignByRelativeTime,
[AlignmentOptionsEnum.ABSOLUTE_TIME]: alignByAbsoluteTime,
[AlignmentOptionsEnum.CUSTOM_METRIC]: alignByCustomMetric,
default: () => {
throw new Error('Unknown value for X axis alignment');
},
// default: () => {
// throw new Error('Unknown value for X axis alignment');
// },
default: alignByStep,
};
const alignmentConfig =
model.getState()!.config!.chart?.alignmentConfigs[chartId];
const alignment =
alignmentObj[alignmentConfig.type] || alignmentObj.default;
alignmentObj[alignmentConfig?.type] || alignmentObj.default;

return alignment(data, model);
}
Expand Down
74 changes: 51 additions & 23 deletions src/src/services/models/imagesExplore/imagesExploreAppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { DATE_EXPORTING_FORMAT, TABLE_DATE_FORMAT } from 'config/dates/dates';
import { getSuggestionsByExplorer } from 'config/monacoConfig/monacoConfig';
import { GroupNameEnum } from 'config/grouping/GroupingPopovers';

import { IExperimentDataShort } from 'modules/core/api/experimentsApi';

import {
getImagesExploreTableColumns,
imagesExploreTableRowRenderer,
Expand Down Expand Up @@ -95,6 +97,10 @@ import getFilteredRow from 'utils/app/getFilteredRow';
import { getMetricHash } from 'utils/app/getMetricHash';
import onRunsTagsChange from 'utils/app/onRunsTagsChange';
import saveRecentSearches from 'utils/saveRecentSearches';
import onSelectExperimentsChange from 'utils/app/onSelectExperimentsChange';
import onToggleAllExperiments from 'utils/app/onToggleAllExperiments';
import { getSelectedExperiments } from 'utils/app/getSelectedExperiments';
import { removeOldSelectedMetrics } from 'utils/app/removeOldSelectedMetrics';

import createModel from '../model';
import { AppNameEnum } from '../explorer';
Expand Down Expand Up @@ -199,29 +205,7 @@ function initialize(appId: string): void {
if (!appId) {
setDefaultAppConfigData();
}
projectsService
.getProjectParams(['images'])
.call()
.then((data: IProjectParamsMetrics) => {
const advancedSuggestions: Record<any, any> = getAdvancedSuggestion(
data.images,
);
model.setState({
selectFormData: {
options: getSelectFormOptions(data),
suggestions: getSuggestionsByExplorer(AppNameEnum.IMAGES, data),
advancedSuggestions: {
...getSuggestionsByExplorer(AppNameEnum.IMAGES, data),
images: {
name: '',
context: _.isEmpty(advancedSuggestions)
? ''
: { ...advancedSuggestions },
},
},
},
});
});
fetchProjectParamsAndUpdateState();
}

function setDefaultAppConfigData(recoverTableState: boolean = true) {
Expand Down Expand Up @@ -461,6 +445,37 @@ function getImagesData(
};
}

function fetchProjectParamsAndUpdateState() {
const selectedExperiments = getSelectedExperiments();
projectsService
.getProjectParams(
['images'],
selectedExperiments.map((exp) => exp.id),
)
.call()
.then((data: IProjectParamsMetrics) => {
const advancedSuggestions: Record<any, any> = getAdvancedSuggestion(
data.images,
);
model.setState({
selectFormData: {
options: getSelectFormOptions(data),
suggestions: getSuggestionsByExplorer(AppNameEnum.IMAGES, data),
advancedSuggestions: {
...getSuggestionsByExplorer(AppNameEnum.IMAGES, data),
images: {
name: '',
context: _.isEmpty(advancedSuggestions)
? ''
: { ...advancedSuggestions },
},
},
},
});
removeOldSelectedMetrics(model);
});
}

function getSelectFormOptions(projectsData: IProjectParamsMetrics) {
let data: ISelectOption[] = [];
let index: number = 0;
Expand Down Expand Up @@ -2321,6 +2336,16 @@ function onStackingToggle(): void {
}
}

function onModelSelectExperimentsChange(experiment: IExperimentDataShort) {
onSelectExperimentsChange(experiment);
getImagesData(false, true).call();
}

function onModelToggleAllExperiments(experiments: IExperimentDataShort[]) {
onToggleAllExperiments(experiments);
getImagesData(false, true).call();
}

const imagesExploreAppModel = {
...model,
initialize,
Expand Down Expand Up @@ -2375,6 +2400,9 @@ const imagesExploreAppModel = {
archiveRuns,
onRowSelect,
onRunsTagsChange: onModelRunsTagsChange,
onSelectExperimentsChange: onModelSelectExperimentsChange,
onToggleAllExperiments: onModelToggleAllExperiments,
fetchProjectParamsAndUpdateState,
};

export default imagesExploreAppModel;
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ export interface IImageRunData {
archived: 0 | 1;
creation_time: number;
end_time: number;
experiment: string;
experiment: { [key: string]: unknown };
name: string;
tags: any[];
};
Expand Down

0 comments on commit 9bf1c86

Please sign in to comment.