Skip to content

Commit

Permalink
feat: improve multiple spectra analysis data export
Browse files Browse the repository at this point in the history
close #3233
  • Loading branch information
hamed-musallam committed Oct 18, 2024
1 parent 8908368 commit 22c40f7
Showing 1 changed file with 165 additions and 16 deletions.
181 changes: 165 additions & 16 deletions src/component/panels/multipleAnalysisPanel/AnalysisChart.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
import { css } from '@emotion/react';
import lodashGet from 'lodash/get';
import {
JpathTableColumn,
Spectrum,
WorkSpacePanelPreferences,
} from 'nmr-load-save';
import { useMemo, useRef, useState } from 'react';
import { ResponsiveChart } from 'react-d3-utils';
import { FaFileExport } from 'react-icons/fa';
import { FaCopy, FaFileExport, FaFileImage } from 'react-icons/fa';
import { Axis, LineSeries, Plot } from 'react-plot';
import { Button } from 'react-science/ui';
import { Button, Toolbar } from 'react-science/ui';

import { SpectraAnalysisData } from '../../../data/data1d/multipleSpectraAnalysis';
import { ClipboardFallbackModal } from '../../../utils/clipboard/clipboardComponents';
import { useClipboard } from '../../../utils/clipboard/clipboardHooks';
import { useChartData } from '../../context/ChartContext';
import { useDispatch } from '../../context/DispatchContext';
import { useToaster } from '../../context/ToasterContext';
import { Input2 } from '../../elements/Input2';
import Label from '../../elements/Label';
import {
ToolbarPopoverItem,
ToolbarPopoverMenuItem,
} from '../../elements/ToolbarPopoverItem';
import { usePanelPreferences } from '../../hooks/usePanelPreferences';
import useSpectraByActiveNucleus from '../../hooks/useSpectraPerNucleus';
import { copyPNGToClipboard } from '../../utility/export';
import { getSpectraObjectPaths } from '../../utility/getSpectraObjectPaths';

const MOL_EXPORT_MENU: ToolbarPopoverMenuItem[] = [
{
icon: <FaCopy />,
text: 'Copy chart to clipboard',
data: {
id: 'copyChart',
},
},
{
icon: <FaFileImage />,
text: 'Copy tab-delimited to clipboard',
data: {
id: 'copyData',
},
},
];

interface PlotAxisOptions {
xPath: string;
yPath: string;
Expand Down Expand Up @@ -43,12 +72,72 @@ function prepareAnalysisData(
return spectraAnalysis;
}

function usePlotData(
analysisData: SpectraAnalysisData,
options: PlotAxisOptions & { paths: Record<string, string[]> },
type PlotOptions = PlotAxisOptions & { paths: Record<string, string[]> };

export function getPlotDataAsString(
spectraAnalysis: SpectraAnalysisData,
options: {
plotOptions: PlotOptions;
spectra: Spectrum[];
spectraPanelPreferences: WorkSpacePanelPreferences['spectra'];
},
) {
const spectra = useSpectraByActiveNucleus();
const { plotOptions, spectra, spectraPanelPreferences } = options;
const { xPath, yPath } = plotOptions;

if (spectraAnalysis) {
const { xData, yData, xPathKeys, yPathKeys } = preparePlotData(
spectraAnalysis,
plotOptions,
);

const columnsLabels: string[] = [xPath || 'serial', yPath || 'serial'];
let headerIndex = 0;
// listed the spectra panel columns
for (const col of spectraPanelPreferences.columns) {
if (col.visible && 'jpath' in col) {
columnsLabels.splice(headerIndex, 0, col.label);
headerIndex++;
}
}

let result = `${columnsLabels.join('\t')}\n`;
let index = 0;

for (const spectrum of spectra) {
const cellsValues: string[] = [];

// listed the spectra cell values
for (const col of spectraPanelPreferences.columns) {
if (col.visible && 'jpath' in col) {
const jpath = (col as JpathTableColumn)?.jpath;
const value = lodashGet(spectrum, jpath, `null`);
cellsValues.push(value);
}
}

const x = xData
? xData[spectrum.id]
: lodashGet(spectrum, xPathKeys, index);
const y = yData
? yData[spectrum.id]
: lodashGet(spectrum, yPathKeys, index);

cellsValues.push(x, y);

result += `${cellsValues.join('\t')}\n`;
index++;
}

return result;
}
return null;
}

function preparePlotData(
analysisData: SpectraAnalysisData,
options: PlotOptions,
) {
const { yPath, xPath, paths } = options;
const xPathKeys = paths?.[xPath];
const yPathKeys = paths?.[yPath];
Expand All @@ -64,7 +153,18 @@ function usePlotData(
if (analysisColumns.includes(yPath)) {
yData = prepareAnalysisData(analysisData, yPath);
}
return { xPathKeys, yPathKeys, xData, yData };
}

function usePlotData(
analysisData: SpectraAnalysisData,
options: { plotOption: PlotOptions; spectra: Spectrum[] },
) {
const { spectra, plotOption } = options;
const { xData, yData, xPathKeys, yPathKeys } = preparePlotData(
analysisData,
plotOption,
);
return spectra.map((spectrum, index) => {
const x = xData
? xData[spectrum.id]
Expand Down Expand Up @@ -97,9 +197,17 @@ function getAnalysisColumnsPaths(spectraAnalysisData: SpectraAnalysisData) {

export default function AnalysisChart(props: PlotChartPros) {
const { spectraAnalysisData } = props;
const { data } = useChartData();
const {
data,
view: {
spectra: { activeTab },
},
} = useChartData();
const dispatch = useDispatch();
const toaster = useToaster();
const spectraPreferences = usePanelPreferences('spectra', activeTab);
const spectra = useSpectraByActiveNucleus();

const chartParentRef = useRef<HTMLDivElement>(null);
const [plotOptions, setPlotOptions] = useState<PlotAxisOptions>({
xPath: '',
Expand Down Expand Up @@ -128,7 +236,7 @@ export default function AnalysisChart(props: PlotChartPros) {
setPlotOptions((prevOptions) => ({ ...prevOptions, [name]: value }));
}

function handleCopy() {
function handleCopyChart() {
if (chartParentRef.current) {
void copyPNGToClipboard(svgId, {
rootElement: chartParentRef.current,
Expand All @@ -138,10 +246,44 @@ export default function AnalysisChart(props: PlotChartPros) {
}
}

const plotData = usePlotData(spectraAnalysisData, { ...plotOptions, paths });
const plotData = usePlotData(spectraAnalysisData, {
plotOption: { ...plotOptions, paths },
spectra,
});
const xLabel = paths?.[plotOptions.xPath]?.at(-1) || '';
const yLabel = paths?.[plotOptions.yPath]?.at(-1) || '';

const { rawWriteWithType, cleanShouldFallback, shouldFallback, text } =
useClipboard();

function handleCopyData() {
const data = getPlotDataAsString(spectraAnalysisData, {
plotOptions: { ...plotOptions, paths },
spectra,
spectraPanelPreferences: spectraPreferences,
});
if (!data) return;

void rawWriteWithType(data).then(() =>
toaster.show({ message: 'Data copied to clipboard', intent: 'success' }),
);
}

function exportHandler(selected) {
switch (selected?.id) {
case 'copyChart': {
handleCopyChart();
break;
}
case 'copyData': {
handleCopyData();
break;
}
default:
break;
}
}

function handleSort(path) {
dispatch({ type: 'SORT_SPECTRA', payload: { path } });
}
Expand Down Expand Up @@ -186,13 +328,14 @@ export default function AnalysisChart(props: PlotChartPros) {
}
/>
</Label>
<Button
intent="success"
icon={<FaFileExport />}
minimal
onClick={handleCopy}
tooltipProps={{ content: 'Copy chart to clipboard' }}
/>
<Toolbar>
<ToolbarPopoverItem
options={MOL_EXPORT_MENU}
onClick={exportHandler}
tooltip="Export As"
icon={<FaFileExport />}
/>
</Toolbar>
</div>
<div
style={{
Expand Down Expand Up @@ -235,6 +378,12 @@ export default function AnalysisChart(props: PlotChartPros) {
)}
</ResponsiveChart>
</div>
<ClipboardFallbackModal
mode={shouldFallback}
onDismiss={cleanShouldFallback}
text={text}
label="Spectra Analysis"
/>
</div>
);
}

0 comments on commit 22c40f7

Please sign in to comment.