Skip to content

Commit

Permalink
Fix error when actuals are missing (#4045)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgboss authored Oct 28, 2024
1 parent 1552aaa commit 8c21ff3
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 54 deletions.
33 changes: 2 additions & 31 deletions web/src/api/moreCast2API.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import axios from 'api/axios'
import { isEqual } from 'lodash'
import { DateTime } from 'luxon'
import { MoreCast2ForecastRow, MoreCast2Row } from 'features/moreCast2/interfaces'
import { isForecastRowPredicate } from 'features/moreCast2/saveForecasts'
import { MoreCast2ForecastRow } from 'features/moreCast2/interfaces'


export enum ModelChoice {
ACTUAL = 'ACTUAL',
Expand Down Expand Up @@ -244,32 +244,3 @@ export async function fetchWeatherIndeterminates(

return payload
}

export const mapMoreCast2RowsToIndeterminates = (rows: MoreCast2Row[]): WeatherIndeterminate[] => {
const mappedIndeterminates = rows.map(r => {
const isForecast = isForecastRowPredicate(r)
return {
id: r.id,
station_code: r.stationCode,
station_name: r.stationName,
determinate: isForecast ? WeatherDeterminate.FORECAST : WeatherDeterminate.ACTUAL,
latitude: r.latitude,
longitude: r.longitude,
utc_timestamp: r.forDate.toString(),
precipitation: isForecast ? r.precipForecast!.value : r.precipActual,
relative_humidity: isForecast ? r.rhForecast!.value : r.rhActual,
temperature: isForecast ? r.tempForecast!.value : r.tempActual,
wind_direction: isForecast ? r.windDirectionForecast!.value : r.windDirectionActual,
wind_speed: isForecast ? r.windSpeedForecast!.value : r.windSpeedActual,
fine_fuel_moisture_code: isForecast ? r.ffmcCalcForecast!.value : r.ffmcCalcActual,
duff_moisture_code: isForecast ? r.dmcCalcForecast!.value : r.dmcCalcActual,
drought_code: isForecast ? r.dcCalcForecast!.value : r.dcCalcActual,
initial_spread_index: isForecast ? r.isiCalcForecast!.value : r.isiCalcActual,
build_up_index: isForecast ? r.buiCalcForecast!.value : r.buiCalcActual,
fire_weather_index: isForecast ? r.fwiCalcForecast!.value : r.fwiCalcActual,
danger_rating: isForecast ? null : r.dgrCalcActual,
grass_curing: isForecast ? r.grassCuringForecast!.value : r.grassCuringActual
}
})
return mappedIndeterminates
}
48 changes: 48 additions & 0 deletions web/src/features/moreCast2/components/ActualCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react'
import { TextField, Tooltip } from '@mui/material'
import { theme } from 'app/theme'
import { GridRenderCellParams } from '@mui/x-data-grid-pro'

interface ActualCellProps {
missingActual: boolean
value: Pick<GridRenderCellParams, 'formattedValue'>
}

const MISSING_ACTUAL_MESSAGE = 'Actual not available from WF1.'

const ActualCell = ({ missingActual, value }: ActualCellProps) => {
const [open, setOpen] = useState<boolean>(false)
const handleClose = () => {
setOpen(false)
}
const handleOpen = () => {
if (missingActual) {
setOpen(true)
}
}
return (
<Tooltip
open={open}
onClose={handleClose}
onOpen={handleOpen}
placement="bottom-start"
title={MISSING_ACTUAL_MESSAGE}
>
<TextField
data-testId="actual-cell"
sx={{
backgroundColor: theme.palette.common.white,
border: '2px',
borderColor: theme.palette.error.main,
borderStyle: missingActual ? 'solid' : 'none',
borderRadius: 1
}}
disabled={true}
size="small"
value={value}
></TextField>
</Tooltip>
)
}

export default React.memo(ActualCell)
6 changes: 3 additions & 3 deletions web/src/features/moreCast2/components/ColumnDefBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class ColumnDefBuilder implements ColDefGenerator, ForecastColDefGenerato
headerClassName: (params: Pick<GridColumnHeaderParams, 'colDef' | 'field'>) => {
return modelHeaderColorClass(params)
},
renderCell: (params: Pick<GridRenderCellParams, 'formattedValue'>) => {
renderCell: (params: Pick<GridRenderCellParams, 'formattedValue' | 'field' | 'row'>) => {
return this.gridComponentRenderer.renderCellWith(params)
},
renderHeader: (params: GridColumnHeaderParams) => {
Expand Down Expand Up @@ -220,7 +220,7 @@ export class ColumnDefBuilder implements ColDefGenerator, ForecastColDefGenerato
? this.gridComponentRenderer.renderHeaderWith(params)
: this.gridComponentRenderer.renderForecastHeaderWith(params, columnClickHandlerProps)
},
renderCell: (params: Pick<GridRenderCellParams, 'row' | 'formattedValue'>) => {
renderCell: (params: Pick<GridRenderCellParams, 'row' | 'formattedValue' | 'field'>) => {
return isCalcField
? this.gridComponentRenderer.renderCellWith(params)
: this.gridComponentRenderer.renderForecastCellWith(params, field, validator)
Expand Down Expand Up @@ -267,7 +267,7 @@ export class ColumnDefBuilder implements ColDefGenerator, ForecastColDefGenerato
? this.gridComponentRenderer.renderHeaderWith(params)
: this.gridComponentRenderer.renderForecastHeaderWith(params, columnClickHandlerProps)
},
renderCell: (params: Pick<GridRenderCellParams, 'row' | 'formattedValue'>) => {
renderCell: (params: Pick<GridRenderCellParams, 'row' | 'formattedValue' | 'field'>) => {
return isCalcField
? this.gridComponentRenderer.renderCellWith(params)
: this.gridComponentRenderer.renderForecastSummaryCellWith(params)
Expand Down
25 changes: 16 additions & 9 deletions web/src/features/moreCast2/components/GridComponentRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
GridValueSetterParams
} from '@mui/x-data-grid-pro'
import { ModelChoice, WeatherDeterminate } from 'api/moreCast2API'
import { createWeatherModelLabel, isBeforeToday, rowContainsActual } from 'features/moreCast2/util'
import { createWeatherModelLabel, isBeforeToday, isForecastRow, rowContainsActual } from 'features/moreCast2/util'
import {
GC_HEADER,
PRECIP_HEADER,
Expand All @@ -24,6 +24,7 @@ import { cloneDeep, isNumber } from 'lodash'
import ForecastCell from 'features/moreCast2/components/ForecastCell'
import ValidatedGrassCureForecastCell from '@/features/moreCast2/components/ValidatedGrassCureForecastCell'
import ValidatedWindDirectionForecastCell from '@/features/moreCast2/components/ValidatedWindDirectionForecastCell'
import ActualCell from 'features/moreCast2/components/ActualCell'

export const NOT_AVAILABLE = 'N/A'
export const NOT_REPORTING = 'N/R'
Expand All @@ -49,14 +50,20 @@ export class GridComponentRenderer {
}
return <div data-testid={`${params.colDef.field}-column-header`}>{params.colDef.headerName}</div>
}
public renderCellWith = (params: Pick<GridRenderCellParams, 'formattedValue'>) => (
<TextField
sx={{ pointerEvents: 'none', backgroundColor: theme.palette.common.white, borderRadius: 1 }}
disabled={true}
size="small"
value={params.formattedValue}
></TextField>
)

public renderCellWith = (params: Pick<GridRenderCellParams, 'formattedValue' | 'field' | 'row'>) => {
if (!isForecastRow(params.row) && params.field.endsWith('Actual')) {
return <ActualCell missingActual={params.formattedValue === NOT_REPORTING} value={params.formattedValue} />
}
return (
<TextField
sx={{ pointerEvents: 'none', backgroundColor: theme.palette.common.white, borderRadius: 1 }}
disabled={true}
size="small"
value={params.formattedValue}
></TextField>
)
}

public getActualField = (field: string) => {
const actualField = field.replace('Forecast', 'Actual')
Expand Down
2 changes: 1 addition & 1 deletion web/src/features/moreCast2/components/TabbedDataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { ROLES } from 'features/auth/roles'
import { selectAuthentication } from 'app/rootReducer'
import { DateRange } from 'components/dateRangePicker/types'
import MoreCast2Snackbar from 'features/moreCast2/components/MoreCast2Snackbar'
import { isForecastRowPredicate, getRowsToSave, isRequiredInputSet } from 'features/moreCast2/saveForecasts'
import { getRowsToSave, isForecastRowPredicate, isRequiredInputSet } from 'features/moreCast2/saveForecasts'
import MoreCast2DateRangePicker from 'features/moreCast2/components/MoreCast2DateRangePicker'
import { filterAllVisibleRowsForSimulation, filterRowsForSimulationFromEdited } from 'features/moreCast2/rowFilters'
import { fillStationGrassCuringForward, simulateFireWeatherIndices } from 'features/moreCast2/util'
Expand Down
4 changes: 2 additions & 2 deletions web/src/features/moreCast2/components/ValidatedCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface ValidatedCellProps {
value: Pick<GridRenderCellParams, 'formattedValue'>
}

const ValidatedGrassCureForecastCell = ({ disabled, label, value, invalid, error }: ValidatedCellProps) => {
const ValidatedCell = ({ disabled, label, value, invalid, error }: ValidatedCellProps) => {
const testTag = error ? 'validated-forecast-cell-error' : 'validated-forecast-cell'
return (
<InvalidCellToolTip invalid={invalid}>
Expand Down Expand Up @@ -51,4 +51,4 @@ const ValidatedGrassCureForecastCell = ({ disabled, label, value, invalid, error
)
}

export default React.memo(ValidatedGrassCureForecastCell)
export default React.memo(ValidatedCell)
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ColumnClickHandlerProps } from 'features/moreCast2/components/TabbedDat
import { DateTime } from 'luxon'
import { Provider } from 'react-redux'
import { vi } from 'vitest'
import { theme } from 'app/theme'

describe('GridComponentRenderer', () => {
const gridComponentRenderer = new GridComponentRenderer()
Expand Down Expand Up @@ -132,8 +133,29 @@ describe('GridComponentRenderer', () => {
expect(renderedCell).toBeDisabled()
})

it('should render N/R as ActualCell and have red border if no actual for row with forDate earlier than today', () => {
const field = 'tempActual'
const row = { [field]: NaN, forDate: DateTime.now().minus({ days: 2 }) }
const formattedValue = gridComponentRenderer.valueGetter({ row: row, value: NaN }, 1, field, 'Actual')
const { getByTestId } = render(
<Provider store={buildTestStore(initialState)}>
{gridComponentRenderer.renderCellWith({
row: row,
formattedValue: formattedValue,
field
})}
</Provider>
)
const renderedCell = getByTestId('actual-cell')
expect(renderedCell).toBeInTheDocument()
expect(renderedCell).toHaveStyle(`borderColor: ${theme.palette.error.main}`)
})

it('should render the cell with the formatted value', () => {
const { getByRole } = render(gridComponentRenderer.renderCellWith({ formattedValue: 1 }))
const field = 'tempForecast'
const fieldActual = 'tempActual'
const row = { [field]: NaN, [fieldActual]: 2, forDate: DateTime.now() }
const { getByRole } = render(gridComponentRenderer.renderCellWith({ formattedValue: 1, field: fieldActual, row }))

const renderedCell = getByRole('textbox')
expect(renderedCell).toBeInTheDocument()
Expand Down
6 changes: 4 additions & 2 deletions web/src/features/moreCast2/saveForecast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,11 @@ describe('saveForecasts', () => {
})
describe('getRowsToSave', () => {
it('should filter out rows with actuals', () => {
const yesterday = DateTime.now().minus({days: 1})
const tomorrow = DateTime.now().plus({days: 1})
const res = getRowsToSave([
buildForecast('1', mockForDate, 1, 'one', 1, 1),
buildForecastWithActuals('2', mockForDate, 2, 'two', 2, 2)
buildForecast('1', tomorrow, 1, 'one', 1, 1),
buildForecastWithActuals('2', yesterday, 2, 'two', 2, 2)
])
expect(res).toHaveLength(1)
expect(res[0].id).toBe('1')
Expand Down
3 changes: 2 additions & 1 deletion web/src/features/moreCast2/saveForecasts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isBeforeToday } from 'features/moreCast2/util'
import { ModelChoice } from 'api/moreCast2API'
import { MoreCast2ForecastRow, MoreCast2Row, PredictionItem } from 'features/moreCast2/interfaces'
import { isNil } from 'lodash'
Expand All @@ -12,7 +13,7 @@ export const isForecastRowPredicate = (row: MoreCast2Row) =>
isNaN(row.grassCuringActual)

export const getForecastRows = (rows: MoreCast2Row[]): MoreCast2Row[] => {
return rows ? rows.filter(isForecastRowPredicate) : []
return rows ? rows.filter(row => isForecastRowPredicate(row) && !isBeforeToday(row.forDate)) : []
}

export const getRowsToSave = (rows: MoreCast2Row[]): MoreCast2ForecastRow[] => {
Expand Down
9 changes: 6 additions & 3 deletions web/src/features/moreCast2/util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const TEST_DATETIME = DateTime.fromISO(TEST_DATE)
const YESTERDAY = DateTime.fromISO(TEST_DATE).plus({ days: -1 })
const TODAY = DateTime.fromISO(TEST_DATE)
const TOMORROW = DateTime.fromISO(TEST_DATE).plus({ days: 1 })
const REAL_YESTERDAY = DateTime.now().minus({day: 1})
const REAL_TODAY = DateTime.now()
const REAL_TOMORROW = DateTime.now().plus({day: 1})

describe('createDateInterval', () => {
it('should return array with single date when fromDate and toDate are the same', () => {
Expand Down Expand Up @@ -505,9 +508,9 @@ describe('simulateFireWeatherIndices', () => {
expect(result[0]).toBe(forecastRow)
})
it('should simulate FWIs for all forecast rows', () => {
const forecastRowA = buildForecastRowWithIndices(1, YESTERDAY, 438, 89, 87)
const forecastRowB = buildValidForecastRow(1, TODAY)
const forecastRowC = buildValidForecastRow(1, TOMORROW)
const forecastRowA = buildForecastRowWithIndices(1, REAL_YESTERDAY, 438, 89, 87)
const forecastRowB = buildValidForecastRow(1, REAL_TODAY)
const forecastRowC = buildValidForecastRow(1, REAL_TOMORROW)
// Change temp for last forecast so fire weather index values will be different than the previous day.
forecastRowC.tempForecast!.value = 27
const result = simulateFireWeatherIndices([forecastRowA, forecastRowB, forecastRowC])
Expand Down
3 changes: 2 additions & 1 deletion web/src/features/moreCast2/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { MoreCast2ForecastRow, MoreCast2Row } from 'features/moreCast2/interface
import { StationGroupMember } from 'api/stationAPI'
import { groupBy, isUndefined } from 'lodash'
import { getDateTimeNowPST } from 'utils/date'
import { isForecastRowPredicate } from 'features/moreCast2/saveForecasts'
import { bui, dc, dmc, ffmc, fwi, isi } from '@psu/cffdrs_ts'
import { isForecastRowPredicate } from 'features/moreCast2/saveForecasts'


export const parseForecastsHelper = (
forecasts: MoreCast2ForecastRecord[],
Expand Down

0 comments on commit 8c21ff3

Please sign in to comment.