Skip to content

Commit

Permalink
bugfixes/new-plot-types (#229)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinJJackson authored Jul 30, 2024
1 parent b97b048 commit f4804c9
Show file tree
Hide file tree
Showing 14 changed files with 632 additions and 335 deletions.
35 changes: 29 additions & 6 deletions src/app-bundles/batch-plot-configurations-bundle.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,28 @@
import createRestBundle from './create-rest-bundle';
import { createSelector } from 'redux-bundler';

import createRestBundle from './create-rest-bundle';

const getTimeseriesFromDisplay = (plotType, display, timeseries) => {
switch (plotType) {
case 'scatter-line':
return timeseries.filter((ts) =>
(display?.traces?.map(trace => trace.timeseries_id) || []).includes(ts.id)
);
case 'bullseye':
return timeseries.filter(ts =>
[display?.x_axis_timeseries_id, display?.y_axis_timeseries_id].includes(ts.id)
);
case 'contour':
return timeseries.filter(ts =>
display?.timeseries_ids?.includes(ts.id)
);
case 'profile':
return display?.instrument_id;
default:
throw new Error(`Invalid Plot Type: ${plotType}. Expected one of: ['scatter-line', 'bullseye', 'contour', 'profile']`);
}
};

export default createRestBundle({
name: 'batchPlotConfigurations',
uid: 'id',
Expand Down Expand Up @@ -82,11 +104,12 @@ export default createRestBundle({
timeseries.length
) {
batchPlotConfigurations.forEach((config) => {
const activeTS = timeseries.filter((ts) =>
(config?.display?.traces?.map(trace => trace.timeseries_id) || []).includes(ts.id)
);
instrumentMap[config.id] = instruments.filter((i) =>
activeTS.some((ts) => ts.instrument_id === i.id)
const { plot_type, display } = config || {};
const activeTS = getTimeseriesFromDisplay(plot_type, display, timeseries);

instrumentMap[config.id] = plot_type === 'profile'
? instruments.filter(i => i.id === activeTS)
: instruments.filter(i => activeTS.some((ts) => ts.instrument_id === i.id)
);
});
}
Expand Down
23 changes: 14 additions & 9 deletions src/app-bundles/instrument-sensors-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ export default {
selectInstrumentSensorsMeasurements: (state) => state.instrumentSensors.measurements,
selectInstrumentSensorsLastFetched: (state) => state.instrumentSensors._lastFetched,

doFetchInstrumentSensorsById: (type) => ({ dispatch, store, apiGet }) => {
doFetchInstrumentSensorsById: (type, id = null) => ({ dispatch, store, apiGet }) => {
dispatch({ type: 'INSTRUMENT_SENSORS_BY_ID_FETCH_START' });
const { instrumentId } = store.selectInstrumentsIdByRoute();
const { instrumentId } = store.selectInstrumentsIdByRoute() || {};
const uriId = id || instrumentId;

const url = `/instruments/${type}/${instrumentId}/segments`;
const url = `/instruments/${type}/${uriId}/segments`;

apiGet(url, (err, body) => {
if (err) {
Expand All @@ -52,11 +53,13 @@ export default {
});
},

doUpdateInstrumentSensor: (type, formData) => ({ dispatch, store, apiPut }) => {
doUpdateInstrumentSensor: (type, formData, id = null) => ({ dispatch, store, apiPut }) => {
dispatch({ type: 'INSTRUMENT_SENSOR_UPDATE_START' });

const { instrumentId } = store.selectInstrumentsIdByRoute();
const url = `/instruments/${type}/${instrumentId}/segments`;
const { instrumentId } = store.selectInstrumentsIdByRoute() || {};
const uriId = id || instrumentId;

const url = `/instruments/${type}/${uriId}/segments`;

apiPut(url, formData, (err, _body) => {
if (err) {
Expand All @@ -68,10 +71,12 @@ export default {
});
},

doFetchInstrumentSensorMeasurements: (type, before, after) => ({ dispatch, store, apiGet }) => {
doFetchInstrumentSensorMeasurements: (type, before, after, id = null) => ({ dispatch, store, apiGet }) => {
dispatch({ type: 'SENSOR_MEASUREMENTS_FETCH_START' });
const { instrumentId } = store.selectInstrumentsIdByRoute();
const url = `/instruments/${type}/${instrumentId}/measurements?before=${before}&after=${after}`;
const { instrumentId } = store.selectInstrumentsIdByRoute() || {};
const uriId = id || instrumentId;

const url = `/instruments/${type}/${uriId}/measurements?before=${before}&after=${after}`;

apiGet(url, (err, body) => {
if (err) {
Expand Down
2 changes: 1 addition & 1 deletion src/app-pages/instrument/notes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export default connect(
const [isAdding, setIsAdding] = useState(false);

const sorted = notes.sort();
const { id } = profileActive;
const { id } = profileActive || {};

return (
project && (
Expand Down
6 changes: 3 additions & 3 deletions src/app-pages/project/batch-plotting/batch-plotting.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useState } from 'react';
import { connect } from 'redux-bundler-react';
import { Engineering } from '@mui/icons-material';
import { toast } from 'react-toastify';
import { Link } from '@mui/material';
import { toast } from 'react-toastify';

import BullseyePlot from './chart-content/bullseye-plot.jsx';
import Card from '../../../app-components/card';
Expand All @@ -13,9 +13,9 @@ import ProfilePlot from './chart-content/profile-plot.jsx';
import ScatterLinePlot from './chart-content/scatter-line-plot.jsx';
import { downloadFinalReport, useGetReportStatus, useInitializeReportDownload } from '../../../app-services/collections/report-configuration-download.ts';
import { tUpdateSuccess } from '../../../common/helpers/toast-helpers';
import { titlize } from '../../../common/helpers/utils.js';

import './batch-plotting.scss';
import { PlotTypeText } from './helper.js';

const BatchPlotting = connect(
'doMapsInitialize',
Expand Down Expand Up @@ -130,7 +130,7 @@ const BatchPlotting = connect(
)}
</div>
<Card className='w-100 my-4'>
<Card.Header text={`${plot_type?.split('-').map(s => titlize(s)).join('-')} Plot`} />
<Card.Header text={PlotTypeText[plot_type]} />
<Card.Body>
{plot_type === 'scatter-line' && <ScatterLinePlot plotConfig={activeConfig} />}
{plot_type === 'profile' && <ProfilePlot plotConfig={activeConfig} />}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import React, { useState } from 'react';
import ReactDatePicker from 'react-datepicker';
import { addDays, subDays } from 'date-fns';
import { Checkbox, FormControlLabel, Slider, Stack, Switch } from '@mui/material';
import { connect } from 'redux-bundler-react';
import { DateTime } from 'luxon';
import { useDeepCompareEffect } from 'react-use';

import Chart from '../../../../app-components/chart/chart';

const colors = {
init: '#000000',
};

const config = {
repsonsive: true,
displaylogo: false,
displayModeBar: true,
scrollZoom: true,
};

const layout = (showTemperature, showIncremental) => ({
showlegend: true,
autosize: true,
height: 800,
rows: 1,
columns: showTemperature ? 2 : 1,
yaxis: {
domain: [0, 1],
anchor: 'x1',
autorange: 'reversed',
title: `Depth in Feet`,
},
xaxis: {
domain: [0, showTemperature ? 0.4 : 1],
anchor: 'y1',
title: `${showIncremental ? 'Incremental' : 'Cumulative'} Displacement`,
},
...showTemperature && {
xaxis2: {
title: 'Temperature',
domain: [0.6, 1],
anchor: 'y2',
},
yaxis2: {
domain: [0, 1],
anchor: 'x2',
autorange: 'reversed',
}
},
});

const formatData = (measurements, indexes, showInitial, showTemperature, showIncremental) => {
if (!measurements.length) return {};

const timeIncrements = measurements.sort((a, b) => DateTime.fromISO(a.time).toMillis() - DateTime.fromISO(b.time).toMillis())
const relevantData = timeIncrements.slice(indexes[0], indexes[1] + 1);

const dataArray = [
...(showInitial ? build2dTrace(timeIncrements[0], true, showTemperature, showIncremental).flat() : []),
...relevantData.map(m => build2dTrace(m, false, showTemperature, showIncremental)).flat(),
].filter(e => e);

return { dataArray, timeIncrements, relevantData };
};

const build2dTrace = (data, isInit, showTemperature, showIncremental) => {
if (!Object.keys(data).length) return {};

const { time, measurements } = data;

const x = [], xTemp = [], y = [];

measurements?.forEach(element => {
x.push(showIncremental ? (element?.inc_dev || 0) : (element?.cum_dev || 0));
xTemp.push(element?.temp);
y.push(element?.elevation || 0);
});

const localDateString = DateTime.fromISO(time).toLocaleString(DateTime.DATETIME_SHORT);
const common = {
y,
mode: 'markers+lines',
marker: { size: 5, color: isInit ? colors['init'] : undefined },
line: { width: 1 },
type: 'scatter',
};

return [{
...common,
x,
name: isInit ? `Initial Displacement (${localDateString})` : `Displacement at ${localDateString}`,
hovertemplate: `
<b>${localDateString}</b><br>
Elevation: %{y}<br>
${showIncremental ? 'Incremental' : 'Cumulative'} Displacement: %{x}<br>
<extra></extra>
`,
}, showTemperature ? {
...common,
xTemp,
xaxis: 'x2',
yaxis: 'y2',
name: isInit ? `Initial Temperature (${localDateString})` : `Temperature at ${localDateString}`,
hovertemplate: `
<b>${localDateString}</b><br>
Elevation: %{y}<br>
Temperature: %{x}<br>
<extra></extra>
`,
} : {}];
};

const IpiProfilePlot = connect(
'doFetchInstrumentSensorMeasurements',
'selectInstrumentSensorsMeasurements',
({
doFetchInstrumentSensorMeasurements,
instrumentSensorsMeasurements,
instrumentId,
}) => {
const [showTemperature, setShowTemperature] = useState(true);
const [showInitial, setShowInitial] = useState(false);
const [showIncremental, setShowIncremental] = useState(false);
const [sliderVal, setSliderVal] = useState([0, 0]);
const [dateRange, setDateRange] = useState([subDays(new Date(), 7), new Date()]);

const { dataArray = [], timeIncrements = [] } = formatData(instrumentSensorsMeasurements, sliderVal, showInitial, showTemperature, showIncremental);

useDeepCompareEffect(() => {
doFetchInstrumentSensorMeasurements('ipi', dateRange[1].toISOString(), dateRange[0].toISOString(), instrumentId);
}, [dateRange, instrumentId]);

return (
<>
<div className='row mt-2'>
<div className='col-2'>
<i>Start Date</i>
<ReactDatePicker
placeholderText='mm/dd/yyyy'
className='form-control'
maxDate={Date.now()}
selected={dateRange[0]}
onChange={(date) => setDateRange([date, addDays(date, 7)])}
/>
</div>
<div className='col-2'>
<i>End Date</i>
<ReactDatePicker
placeholderText='mm/dd/yyyy'
className='form-control'
maxDate={Date.now()}
selected={dateRange[1]}
onChange={(date) => setDateRange([subDays(date, 7), date])}
/>
</div>
<div className='col-2 pt-3'>
<FormControlLabel
control={<Checkbox size='small' onChange={() => setShowInitial(prev => !prev)} />}
label='Show Initial Displacement'
/>
</div>
<div className='col-2 pt-3'>
<FormControlLabel
control={<Checkbox size='small' defaultChecked onChange={() => setShowTemperature(prev => !prev)} />}
label='Show Temperature'
/>
</div>
<div className='col-2 pt-3'>
<Stack direction='row' spacing={1} alignItems='center'>
Cumulative
<Switch onChange={_e => setShowIncremental(prev => !prev)} />
Incremental
</Stack>
</div>
</div>
<div className='row'>
<div className='col-12'>
<Chart
config={config}
layout={layout(showTemperature, showIncremental)}
data={dataArray}
/>
</div>
</div>
<div className='row'>
<div className='col-10 offset-1'>
<Slider
aria-label='depth plot time slider'
valueLabelDisplay='auto'
min={1}
max={timeIncrements.length - 1}
step={1}
value={sliderVal}
valueLabelFormat={(val) => <span>{DateTime.fromISO(instrumentSensorsMeasurements[val]?.time).toFormat('MMM dd, yyyy hh:mm:ss')}</span>}
onChange={(_e, newVal) => setSliderVal(newVal)}
/>
</div>
</div>
</>
);
},
);

export default IpiProfilePlot;
Loading

0 comments on commit f4804c9

Please sign in to comment.