Skip to content

Commit

Permalink
lol why is this so hard
Browse files Browse the repository at this point in the history
  • Loading branch information
gtarpenning committed Oct 10, 2024
1 parent beae64e commit cea92b0
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,12 @@ import * as Plotly from 'plotly.js';
import React, {useEffect, useMemo, useRef} from 'react';

import {PLOT_GRID_COLOR} from '../../ecpConstants';
import {RadarPlotData} from './PlotlyRadarPlot';

export const PlotlyBarPlot: React.FC<{
height: number;
data: RadarPlotData;
plotlyData: Plotly.Data;
}> = props => {
const divRef = useRef<HTMLDivElement>(null);
const plotlyData: Plotly.Data[] = useMemo(() => {
return Object.keys(props.data).map((key, i) => {
const {metrics, name, color} = props.data[key];
return {
type: 'bar',
y: Object.values(metrics),
x: Object.keys(metrics),
name,
marker: {color},
};
});
}, [props.data]);

const plotlyLayout: Partial<Plotly.Layout> = useMemo(() => {
return {
height: props.height - 40,
Expand All @@ -30,7 +16,7 @@ export const PlotlyBarPlot: React.FC<{
l: 0,
r: 0,
b: 20,
t: 0,
t: 20,
pad: 0,
},
xaxis: {
Expand All @@ -44,8 +30,17 @@ export const PlotlyBarPlot: React.FC<{
gridcolor: PLOT_GRID_COLOR,
linecolor: PLOT_GRID_COLOR,
},
title: {
text: props.plotlyData.name ?? '',
font: {size: 14},
xref: 'paper',
x: 0.5,
y: 1, // Position at the top
yanchor: 'top',
},
};
}, [props.height]);
}, [props.height, props.plotlyData]);

const plotlyConfig = useMemo(() => {
return {
displayModeBar: false,
Expand All @@ -57,11 +52,11 @@ export const PlotlyBarPlot: React.FC<{
useEffect(() => {
Plotly.newPlot(
divRef.current as any,
plotlyData,
[props.plotlyData],
plotlyLayout,
plotlyConfig
);
}, [plotlyConfig, plotlyData, plotlyLayout]);
}, [plotlyConfig, props.plotlyData, plotlyLayout]);

return <div ref={divRef}></div>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export const PlotlyRadarPlot: React.FC<{
}, [props.data]);
const plotlyLayout: Partial<Plotly.Layout> = useMemo(() => {
return {
height: props.height,
height: props.height - 40,
showlegend: false,
margin: {
l: 60,
l: 0,
r: 0,
b: 30,
t: 30,
b: 20,
t: 20,
pad: 0,
},
polar: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ export const SummaryPlots: React.FC<{
}
}, [selectedMetrics, setSelectedMetrics, allMetricNames]);

// filter down the plotlyRadarData to only include the selected metrics, after
// computation, to allow quick addition/removal of metrics
const filteredPlotlyRadarData = useMemo(() => {
// filter down the data to only include the selected metrics, after
// computation, to allow quick addition/removal of metrics.
const filteredData = useMemo(() => {
const filteredData: RadarPlotData = {};
for (const [callId, metricBin] of Object.entries(radarData)) {
const metrics: {[metric: string]: number} = {};
Expand All @@ -74,71 +74,144 @@ export const SummaryPlots: React.FC<{
return filteredData;
}, [radarData, selectedMetrics]);

const plots = useMemo(() => {
const plots = [
<PlotlyRadarPlot key="radar" height={PLOT_HEIGHT} data={filteredData} />,
];

// transform the data to be a list of metrics for each callId
const metrics: {[metric: string]: {callIds: string[], values: number[], name: string, colors: string[]}} = {};
for (const [callId, metricBin] of Object.entries(filteredData)) {
for (const [metric, value] of Object.entries(metricBin.metrics)) {
metrics[metric] = {
callIds: [...(metrics[metric]?.callIds ?? []), callId],
values: [...(metrics[metric]?.values ?? []), value],
name: metric,
colors: [...(metrics[metric]?.colors ?? []), metricBin.color],
};
}
}

for (const metric of Object.keys(metrics)) {
const metricBin = metrics[metric];
const plotlyData: Plotly.Data = {
type: 'bar',
y: metricBin.values,
// x: metricBin.callIds,
xaxis: '',
name: metric,
marker: {color: metricBin.colors},
};
plots.push(
<PlotlyBarPlot key={metric} height={PLOT_HEIGHT} plotlyData={plotlyData} />
);
}

return plots;
}, [filteredData]);

const containerRef = useRef<HTMLDivElement>(null);
const [containerWidth, setContainerWidth] = useState(0);

useEffect(() => {
const updateWidth = () => {
if (containerRef.current) {
setContainerWidth(containerRef.current.offsetWidth);
}
};

updateWidth(); // Initial width

window.addEventListener('resize', updateWidth);
return () => window.removeEventListener('resize', updateWidth);
}, []);

console.log('containerWidth', containerWidth);

const plotsPerPage = useMemo(() => {
return Math.max(1, Math.floor(containerWidth / (PLOT_HEIGHT + 20))); // 20px for margin
}, [containerWidth]);

const [currentPage, setCurrentPage] = useState(0);

const totalPages = Math.ceil(plots.length / plotsPerPage);

const handleNextPage = () => {
setCurrentPage((prevPage) => Math.min(prevPage + 1, totalPages - 1));
};

const handlePrevPage = () => {
setCurrentPage((prevPage) => Math.max(prevPage - 1, 0));
};

const startIndex = currentPage * plotsPerPage;
const endIndex = Math.min(startIndex + plotsPerPage, plots.length);
const currentPlots = plots.slice(startIndex, endIndex);

return (
<VerticalBox
sx={{
paddingLeft: STANDARD_PADDING,
paddingRight: STANDARD_PADDING,
flex: '1 1 auto',
width: '100%',
}}>
<HorizontalBox
<VerticalBox
sx={{
paddingLeft: STANDARD_PADDING,
paddingRight: STANDARD_PADDING,
flex: '1 1 auto',
width: '100%',
alignItems: 'center',
justifyContent: 'flex-start',
}}>
<Box
<HorizontalBox
sx={{
fontSize: '1.5em',
fontWeight: 'bold',
width: '100%',
alignItems: 'center',
justifyContent: 'flex-start',
}}>
Summary Metrics
</Box>
<Box sx={{marginLeft: 'auto'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<div style={{marginRight: '4px'}}>Configure displayed metrics</div>
<MetricsSelector
selectedMetrics={selectedMetrics}
setSelectedMetrics={setSelectedMetrics}
allMetrics={Array.from(allMetricNames)}
/>
</div>
</Box>
</HorizontalBox>
<HorizontalBox
sx={{
flexWrap: 'wrap',
}}>
<Box
sx={{
flex: '1 2 ' + PLOT_HEIGHT + 'px',
height: PLOT_HEIGHT,
borderRadius: BOX_RADIUS,
border: STANDARD_BORDER,
overflow: 'hidden',
alignContent: 'center',
width: PLOT_HEIGHT,
}}>
<PlotlyRadarPlot
height={PLOT_HEIGHT}
data={filteredPlotlyRadarData}
/>
</Box>
<Box
<Box
sx={{
fontSize: '1.5em',
fontWeight: 'bold',
}}>
Summary Metrics
</Box>
<Box sx={{marginLeft: 'auto'}}>
<div style={{display: 'flex', alignItems: 'center'}}>
<div style={{marginRight: '4px'}}>Configure displayed metrics</div>
<MetricsSelector
selectedMetrics={selectedMetrics}
setSelectedMetrics={setSelectedMetrics}
allMetrics={Array.from(allMetricNames)}
/>
</div>
</Box>
</HorizontalBox>
<div ref={containerRef} style={{width: '100%'}}>
<HorizontalBox
sx={{
flex: '2 1 ' + PLOT_HEIGHT + 'px',
height: PLOT_HEIGHT,
overflow: 'hidden',
borderRadius: BOX_RADIUS,
border: STANDARD_BORDER,
padding: PLOT_PADDING,
width: PLOT_HEIGHT,
display: 'grid',
gridTemplateColumns: `repeat(${plotsPerPage}, 1fr)`,
}}>
<PlotlyBarPlot height={PLOT_HEIGHT} data={filteredPlotlyRadarData} />
</Box>
</HorizontalBox>
</VerticalBox>
{currentPlots.map((plot, index) => (
<Box
key={index}
sx={{
height: PLOT_HEIGHT,
borderRadius: BOX_RADIUS,
border: STANDARD_BORDER,
padding: PLOT_PADDING,
}}>
{plot}
</Box>
))}
</HorizontalBox>
</div>
<HorizontalBox sx={{width: '100%', marginTop: '4px'}}>
<Box sx={{marginLeft: 'auto'}}>
<Tailwind>
<Button variant="quiet" onClick={handlePrevPage} disabled={currentPage === 0} icon='chevron-next' className='rotate-180'/>
<span className='mx-4 text-moon-500 text-sm pb-12'>
{startIndex + 1}-{endIndex} of {plots.length}
</span>
<Button variant="quiet" onClick={handleNextPage} disabled={currentPage === totalPages - 1} icon='chevron-next'/>
</Tailwind>
</Box>
</HorizontalBox>
</VerticalBox>
);
};

Expand Down Expand Up @@ -381,4 +454,4 @@ const useNormalizedPlotDataFromMetrics = (
const allMetricNames = new Set(normalizedMetrics.map(m => m.metricLabel));
return {radarData, allMetricNames};
}, [callIds, compositeMetrics, state.data.evaluationCalls]);
};
};

0 comments on commit cea92b0

Please sign in to comment.