Skip to content

Commit

Permalink
Merge branch 'integration/latest-levels-app' into feature/FSR-1287
Browse files Browse the repository at this point in the history
  • Loading branch information
Keyurx11 authored Oct 15, 2024
2 parents ecb7b01 + cce7bfb commit 6bc4785
Show file tree
Hide file tree
Showing 50 changed files with 834 additions and 471 deletions.
2 changes: 1 addition & 1 deletion server/models/views/lib/find-min-threshold.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function filterImtdThresholds (imtdThresholds) {

return {
alert: minObjectA ? minObjectA.value : null,
warning: minObjectW ? minObjectW.value : null
warning: minObjectW || null
}
}

Expand Down
23 changes: 23 additions & 0 deletions server/models/views/lib/latest-levels.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,39 @@ const { formatElapsedTime } = require('../../../util')

const WARNING_THRESHOLD_TYPES = ['FW RES FW', 'FW ACT FW', 'FW ACTCON FW']

function adjustThresholdValue (value, stageDatum, subtract, postProcess) {
if (postProcess) {
if (stageDatum && stageDatum > 0) {
value -= stageDatum
} else if (stageDatum <= 0 && subtract && subtract > 0) {
value -= subtract
} else {
return parseFloat(value).toFixed(2)
}
}
return parseFloat(value).toFixed(2)
}

function getThresholdsForTargetArea (thresholds) {
const filteredThresholds = thresholds.filter(threshold =>
threshold.status !== 'Closed' &&
!(threshold.iswales && threshold.latest_level === null)
)

const warningThresholds = findPrioritisedThresholds(filteredThresholds, WARNING_THRESHOLD_TYPES)

return warningThresholds.map(threshold => {
threshold.formatted_time = formatElapsedTime(threshold.value_timestamp)
threshold.isSuspendedOrOffline = threshold.status === 'Suspended' || (threshold.status === 'Active' && threshold.latest_level === null)

// Use adjustThresholdValue for threshold_value adjustment
threshold.threshold_value = adjustThresholdValue(
threshold.threshold_value,
threshold.stage_datum,
threshold.subtract,
threshold.post_process
)

return threshold
})
}
Expand Down
57 changes: 47 additions & 10 deletions server/models/views/lib/process-imtd-thresholds.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const SEVERE_FLOOD_WARNING_THRESHOLD = 3
function processThreshold (threshold, stationStageDatum, stationSubtract, postProcess) {
if (threshold) {
if (postProcess) {
Expand All @@ -14,30 +15,66 @@ function processThreshold (threshold, stationStageDatum, stationSubtract, postPr
return null
}

function processImtdThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess) {
function processImtdThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5) {
const thresholds = []

const imtdThresholdWarning = processThreshold(imtdThresholds?.warning, stationStageDatum, stationSubtract, postProcess)
const imtdThresholdWarning = calculateWarningThreshold(imtdThresholds, stationStageDatum, stationSubtract, postProcess)
const imtdThresholdAlert = calculateAlertThreshold(imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5)

if (imtdThresholdWarning) {
// Correct threshold value if value > zero (Above Ordnance Datum) [FSR-595]
thresholds.push(imtdThresholdWarning)
}

if (imtdThresholdAlert) {
thresholds.push(imtdThresholdAlert)
} else if (pc5) {
thresholds.push({
id: 'pc5',
description: 'Top of normal range. Low lying land flooding possible above this level',
shortname: 'Top of normal range',
value: pc5
})
} else { return thresholds }

return thresholds
}

function calculateWarningThreshold (imtdThresholds, stationStageDatum, stationSubtract, postProcess) {
const imtdThresholdWarning = processThreshold(imtdThresholds?.warning?.value, stationStageDatum, stationSubtract, postProcess)

if (imtdThresholdWarning) {
const warningType = imtdThresholds.warning.severity_value === SEVERE_FLOOD_WARNING_THRESHOLD
? 'Severe flood warning'
: 'Flood warning'

return {
id: 'warningThreshold',
description: 'Property flooding is possible above this level. One or more flood warnings may be issued',
shortname: 'Possible flood warnings',
description: imtdThresholds.warning.severity_value
? `${warningType} issued: <a href="/target-area/${imtdThresholds.warning.fwis_code}">${imtdThresholds.warning.ta_name}</a>`
: 'Property flooding is possible above this level',
shortname: imtdThresholds.warning.severity_value ? `${imtdThresholds.warning.ta_name}` : 'Possible flood warnings',
value: imtdThresholdWarning
})
}
}

return null
}

function calculateAlertThreshold (imtdThresholds, stationStageDatum, stationSubtract, postProcess, pc5) {
const imtdThresholdAlert = processThreshold(imtdThresholds?.alert, stationStageDatum, stationSubtract, postProcess)

if (imtdThresholdAlert) {
thresholds.push({
return {
id: 'alertThreshold',
description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued',
description: Number(imtdThresholdAlert) !== Number(pc5)
? 'Low lying land flooding possible above this level. One or more flood alerts may be issued'
: 'Top of normal range. Low lying land flooding possible above this level. One or more flood alerts may be issued',
shortname: 'Possible flood alerts',
value: imtdThresholdAlert
})
}
}
return thresholds

return null
}

module.exports = processImtdThresholds
62 changes: 51 additions & 11 deletions server/models/views/station.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ const bannerIconId3 = 3
const outOfDateMax = 5
const dataStartDateTimeDaysToSubtract = 5

const TOP_OF_NORMAL_RANGE = 'Top of normal range'

class ViewModel {
constructor (options) {
const { station, telemetry, forecast, imtdThresholds, impacts, river, warningsAlerts } = options
const { station, telemetry, forecast, imtdThresholds, impacts, river, warningsAlerts, requestUrl } = options

this.station = new Station(station)
this.station.riverNavigation = river
Expand All @@ -39,7 +41,6 @@ class ViewModel {
const numSevereWarnings = warningsAlertsGroups['3'] ? warningsAlertsGroups['3'].length : 0

// Determine appropriate warning/alert text for banner

this.banner = numAlerts || numWarnings || numSevereWarnings

switch (numAlerts) {
Expand Down Expand Up @@ -170,7 +171,7 @@ class ViewModel {

oneHourAgo.setHours(oneHourAgo.getHours() - 1)

// check if recent value is over one hour old0
// check if recent value is over one hour old
this.dataOverHourOld = new Date(this.recentValue.ts) < oneHourAgo

this.recentValue.dateWhen = 'on ' + moment.tz(this.recentValue.ts, tz).format('D/MM/YY')
Expand Down Expand Up @@ -230,30 +231,30 @@ class ViewModel {
}
this.metaDescription = `Check the latest recorded ${stationType.toLowerCase()} level and recent 5-day trend at ${stationLocation}`

// Thresholds
// Array to hold thresholds
let thresholds = []

// Check if recent value exists and add it to thresholds
if (this.station.recentValue && !this.station.recentValue.err) {
const tVal = this.station.type !== 'c' && this.station.recentValue._ <= 0 ? 0 : this.station.recentValue._.toFixed(2)

thresholds.push({
id: 'latest',
value: tVal,
description: 'Latest level',
shortname: ''
})
}
// Add the highest level threshold if available
if (this.station.porMaxValue) {
thresholds.push({
id: 'highest',
value: this.station.porMaxValue,
description: this.station.thresholdPorMaxDate
? 'Water reaches the highest level recorded at this measuring station (recorded on ' + this.station.thresholdPorMaxDate + ')'
? `Water reaches the highest level recorded at this measuring station (${this.station.thresholdPorMaxDate})`
: 'Water reaches the highest level recorded at this measuring station',
shortname: 'Highest level on record'
})
}

this.imtdThresholds = imtdThresholds?.length > 0
? filterImtdThresholds(imtdThresholds)
: []
Expand All @@ -262,9 +263,9 @@ class ViewModel {
this.imtdThresholds,
this.station.stageDatum,
this.station.subtract,
this.station.post_process
this.station.post_process,
this.station.percentile5
)

thresholds.push(...processedImtdThresholds)

if (this.station.percentile5) {
Expand All @@ -273,10 +274,25 @@ class ViewModel {
id: 'pc5',
value: this.station.percentile5,
description: 'This is the top of the normal range',
shortname: 'Top of normal range'
shortname: TOP_OF_NORMAL_RANGE
})
}

// Handle chartThreshold: add tidThreshold if a valid tid is present; if not, fallback to 'pc5'; if 'pc5' is unavailable, use 'alertThreshold' with "Top of normal range" description.
// Extract tid from request URL if valid
let tid = null
try {
tid = requestUrl?.startsWith('http') ? new URL(requestUrl).searchParams.get('tid') : null
} catch (e) {
console.error('Invalid request URL:', e)
}

// Retrieve the applicable threshold for chartThreshold
const chartThreshold = [getThresholdByThresholdId(tid, imtdThresholds, thresholds)].filter(Boolean)

// Set chartThreshold property
this.chartThreshold = chartThreshold

// Add impacts
if (impacts.length > 0) {
this.station.hasImpacts = true
Expand Down Expand Up @@ -363,7 +379,6 @@ class ViewModel {
this.zoom = 14

// Forecast Data Calculations

let forecastData
if (isForecast) {
this.isFfoi = isForecast
Expand Down Expand Up @@ -403,6 +418,7 @@ function stationTypeCalculator (stationTypeData) {
}
return stationType
}

function telemetryForecastBuilder (telemetryRawData, forecastRawData, stationType) {
const observed = telemetryRawData
.filter(telemetry => telemetry._ !== null) // Filter out records where telemetry._ is null
Expand Down Expand Up @@ -432,4 +448,28 @@ function telemetryForecastBuilder (telemetryRawData, forecastRawData, stationTyp
}
}

// Function to retrieve a threshold by tid or fall back to 'pc5' or 'alertThreshold'
const getThresholdByThresholdId = (tid, imtdThresholds, thresholds) => {
// Check if a threshold exists based on tid
const tidThreshold = tid && imtdThresholds?.find(thresh => thresh.station_threshold_id === tid)
if (tidThreshold) {
return {
id: tidThreshold.station_threshold_id,
value: Number(tidThreshold.value).toFixed(2),
description: `${tidThreshold.value}m ${tidThreshold.ta_name || ''}`,
shortname: tidThreshold.ta_name || 'Target Area Threshold'
}
}

// Fallback to 'pc5' if present, else look for 'alertThreshold'
const pc5Threshold = thresholds.find(t => t.id === 'pc5')
if (pc5Threshold) {
return pc5Threshold
}

// Fallback to 'alertThreshold' if description includes 'Top of normal range'
const alertThreshold = thresholds.find(t => t.id === 'alertThreshold' && t.description.includes(TOP_OF_NORMAL_RANGE))
return alertThreshold ? { ...alertThreshold, shortname: TOP_OF_NORMAL_RANGE } : null
}

module.exports = ViewModel
8 changes: 6 additions & 2 deletions server/routes/station.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
handler: async (request, h) => {
const { id } = request.params
let { direction } = request.params
// const thresholdId = request.query.tid?

// Convert human readable url to service parameter
direction = direction === 'downstream' ? 'd' : 'u'
Expand Down Expand Up @@ -54,6 +55,9 @@ module.exports = {
request.server.methods.flood.getRiverStationByStationId(id, direction)
])

// const requestUrl = request.url.href
const requestUrl = request.url.toString()

if (station.status === 'Closed') {
const river = []
const model = new ViewModel({ station, telemetry, imtdThresholds, impacts, river, warningsAlerts })
Expand All @@ -67,11 +71,11 @@ module.exports = {
// Forecast station
const values = await request.server.methods.flood.getStationForecastData(station.wiski_id)
const forecast = { forecastFlag, values }
const model = new ViewModel({ station, telemetry, forecast, imtdThresholds, impacts, river, warningsAlerts })
const model = new ViewModel({ station, telemetry, forecast, imtdThresholds, impacts, river, warningsAlerts, requestUrl })
return h.view('station', { model })
} else {
// Non-forecast Station
const model = new ViewModel({ station, telemetry, imtdThresholds, impacts, river, warningsAlerts })
const model = new ViewModel({ station, telemetry, imtdThresholds, impacts, river, warningsAlerts, requestUrl })
return h.view('station', { model })
}
},
Expand Down
Loading

0 comments on commit 6bc4785

Please sign in to comment.