{% if model.latestLevels.length > 1 %}These levels{% else %}This level{% endif %} will update automatically
\ No newline at end of file
diff --git a/server/views/target-area.html b/server/views/target-area.html
index d5a2097b2..aa71b1969 100644
--- a/server/views/target-area.html
+++ b/server/views/target-area.html
@@ -75,7 +75,7 @@
diff --git a/test/models/lib/latest-levels.js b/test/models/lib/latest-levels.js
index 414d7b52e..517e30a11 100644
--- a/test/models/lib/latest-levels.js
+++ b/test/models/lib/latest-levels.js
@@ -107,43 +107,60 @@ lab.experiment('getThresholdsForTargetArea', () => {
expect(result).to.have.length(0)
})
- lab.test('should prioritize the first type in the WARNING_THRESHOLD_TYPES array when all thresholds are of the same type', () => {
+ lab.test('should adjust threshold_value using stageDatum if postProcess is true and stageDatum > 0', () => {
const thresholds = [
- { rloi_id: 1, threshold_type: 'FW RES FW', value_timestamp: '2024-08-12T11:45:00.000Z' },
- { rloi_id: 2, threshold_type: 'FW RES FW', value_timestamp: '2024-08-12T12:45:00.000Z' }
+ {
+ rloi_id: 1,
+ threshold_type: 'FW RES FW',
+ value_timestamp: '2024-08-12T11:45:00.000Z',
+ threshold_value: '5.00',
+ stage_datum: 2.5,
+ subtract: 1.0,
+ post_process: true
+ }
]
const result = getThresholdsForTargetArea(thresholds)
- expect(result).to.have.length(2)
- expect(result[0].threshold_type).to.equal('FW RES FW')
- expect(result[0].formatted_time).to.equal('More than 1 hour ago')
- expect(result[1].formatted_time).to.equal('0 minutes ago')
+ expect(result).to.have.length(1)
+ expect(result[0].threshold_value).to.equal('2.50') // 5.00 - 2.5
})
- lab.test('should correctly handle multiple thresholds with the same RLOI ID and type', () => {
+ lab.test('should adjust threshold_value using subtract if postProcess is true, stageDatum <= 0, and subtract > 0', () => {
const thresholds = [
- { rloi_id: 1, threshold_type: 'FW RES FW', value_timestamp: '2024-08-12T11:45:00.000Z' },
- { rloi_id: 1, threshold_type: 'FW RES FW', value_timestamp: '2024-08-12T12:45:00.000Z' }
+ {
+ rloi_id: 2,
+ threshold_type: 'FW ACT FW',
+ value_timestamp: '2024-08-12T10:45:00.000Z',
+ threshold_value: '5.00',
+ stage_datum: 0,
+ subtract: 1.5,
+ post_process: true
+ }
]
const result = getThresholdsForTargetArea(thresholds)
expect(result).to.have.length(1)
- expect(result[0].threshold_type).to.equal('FW RES FW')
- expect(result[0].formatted_time).to.equal('More than 1 hour ago')
+ expect(result[0].threshold_value).to.equal('3.50') // 5.00 - 1.5
})
- lab.test('should ignore invalid thresholds and process only valid ones', () => {
+ lab.test('should not adjust threshold_value if postProcess is false', () => {
const thresholds = [
- { rloi_id: 1, threshold_type: 'FW RES FW', value_timestamp: '2024-08-12T11:45:00.000Z' },
- { rloi_id: 2, threshold_type: 'INVALID TYPE', value_timestamp: '2024-08-12T12:45:00.000Z' }
+ {
+ rloi_id: 3,
+ threshold_type: 'FW ACTCON FW',
+ value_timestamp: '2024-08-12T11:45:00.000Z',
+ threshold_value: '4.00',
+ stage_datum: 1.0,
+ subtract: 1.5,
+ post_process: false
+ }
]
const result = getThresholdsForTargetArea(thresholds)
expect(result).to.have.length(1)
- expect(result[0].threshold_type).to.equal('FW RES FW')
- expect(result[0].formatted_time).to.equal('More than 1 hour ago')
+ expect(result[0].threshold_value).to.equal('4.00') // No adjustment
})
})
diff --git a/test/routes/target-area-2.js b/test/routes/target-area-2.js
index 7dee2aa81..b217bfce9 100644
--- a/test/routes/target-area-2.js
+++ b/test/routes/target-area-2.js
@@ -147,7 +147,7 @@ describe('target-area route', () => {
expect(response.statusCode).to.equal(200)
const root = parse(response.payload)
linkChecker(root.querySelectorAll('a'),
- 'Find other river and sea levels',
+ 'Find a river, sea, groundwater or rainfall level in this area',
`/river-and-sea-levels/target-area/${AREA_CODE}`
)
})
@@ -175,7 +175,7 @@ describe('target-area route', () => {
expect(response.statusCode).to.equal(200)
const root = parse(response.payload)
linkChecker(root.querySelectorAll('a'),
- 'Find other river and sea levels',
+ 'Find a river, sea, groundwater or rainfall level in this area',
`/river-and-sea-levels/target-area/${AREA_CODE}`
)
})
diff --git a/test/routes/target-area.js b/test/routes/target-area.js
index 19343980c..c16f2e9c5 100644
--- a/test/routes/target-area.js
+++ b/test/routes/target-area.js
@@ -192,9 +192,10 @@ lab.experiment('Target-area tests', () => {
Code.expect(response.payload).to.contain('
Latest levels
')
Code.expect(response.payload).to.contain('
The River Pinn level at Eastcote Road was 0.35 metres. Property flooding is possible when it goes above 1.40 metres.')
Code.expect(response.payload).to.contain('
The River Derwent level at Derby St Marys was 0.54 metres. Property flooding is possible when it goes above 3.30 metres.')
- Code.expect(response.payload).to.contain('Monitor the latest level at Derby St Marys')
+ Code.expect(response.payload).to.contain('Monitor the latest level at Derby St Marys')
Code.expect(response.payload).to.match(/
The River Pinn level at Moss Close was 0.13 metres. Property flooding is possible when it goes above 1.15 metres.')
- Code.expect(response.payload).to.contain('Monitor the River Pinn level at Moss Close.')
- Code.expect(response.payload).to.contain('
diff --git a/test/models/floods.js b/test/data/floods.js
similarity index 100%
rename from test/models/floods.js
rename to test/data/floods.js
diff --git a/test/data/taThresholdsData.json b/test/data/taThresholdsData.json
index 834813d63..8fa9583cb 100644
--- a/test/data/taThresholdsData.json
+++ b/test/data/taThresholdsData.json
@@ -2,6 +2,7 @@
"singleActiveOffline": [
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -16,6 +17,7 @@
"singleSuspended": [
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Pinn",
"agency_name": "Moss Close",
"status": "Suspended",
@@ -30,6 +32,7 @@
"singleClosed": [
{
"rloi_id": 7202,
+ "station_threshold_id": 123458,
"river_name": "River Test",
"agency_name": "Test Road",
"status": "Closed",
@@ -44,6 +47,7 @@
"multipleNormalClosed": [
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"status": "Active",
@@ -56,6 +60,7 @@
},
{
"rloi_id": 7202,
+ "station_threshold_id": 123458,
"river_name": "River Test",
"agency_name": "Test Road",
"status": "Closed",
@@ -70,6 +75,7 @@
"multipleNormalActiveOfflineWelshNoValues": [
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"status": "Active",
@@ -82,6 +88,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -94,6 +101,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Welsh",
"agency_name": "Welsh Station",
"status": "Active",
@@ -108,6 +116,7 @@
"multipleNormalSuspendedClosed": [
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"status": "Active",
@@ -120,6 +129,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Suspended",
@@ -132,6 +142,7 @@
},
{
"rloi_id": 7202,
+ "station_threshold_id": 123458,
"river_name": "River Test",
"agency_name": "Test Road",
"status": "Closed",
@@ -146,6 +157,7 @@
"allClosedOrWelshNoValues": [
{
"rloi_id": 7202,
+ "station_threshold_id": 123458,
"river_name": "River Test",
"agency_name": "Test Road",
"status": "Closed",
@@ -158,6 +170,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Welsh",
"agency_name": "Welsh Station",
"status": "Active",
@@ -172,6 +185,7 @@
"singleWelshWithValues": [
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Welsh",
"agency_name": "Welsh Station",
"status": "Active",
@@ -186,6 +200,7 @@
"multipleLatestLevels": [
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"station_threshold_id": "1747544",
"agency_name": "Eastcote Road",
@@ -199,6 +214,7 @@
},
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"station_threshold_id": "1747544",
@@ -212,6 +228,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"station_threshold_id": "1747543",
"agency_name": "Avenue Road",
@@ -225,6 +242,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"station_threshold_id": "1747543",
"agency_name": "Avenue Road",
@@ -238,6 +256,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Pinn",
"station_threshold_id": "1747541",
"agency_name": "Moss Close",
@@ -251,6 +270,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Pinn",
"station_threshold_id": "1747541",
"agency_name": "Moss Close",
@@ -266,6 +286,7 @@
"overLimitLatestLevels": [
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"status": "Active",
@@ -278,6 +299,7 @@
},
{
"rloi_id": 7174,
+ "station_threshold_id": 123459,
"river_name": "River Pinn",
"agency_name": "Eastcote Road",
"status": "Active",
@@ -290,6 +312,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -302,6 +325,7 @@
},
{
"rloi_id": 7173,
+ "station_threshold_id": 123465,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -314,6 +338,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Pinn",
"agency_name": "Moss Close",
"status": "Active",
@@ -326,6 +351,7 @@
},
{
"rloi_id": 7201,
+ "station_threshold_id": 123457,
"river_name": "River Pinn",
"agency_name": "Moss Close",
"status": "Active",
@@ -338,6 +364,7 @@
},
{
"rloi_id": 1111,
+ "station_threshold_id": 123500,
"river_name": "River Pinn",
"agency_name": "Moss Close",
"status": "Active",
@@ -350,6 +377,7 @@
},
{
"rloi_id": 1111,
+ "station_threshold_id": 123500,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -362,6 +390,7 @@
},
{
"rloi_id": 1112,
+ "station_threshold_id": 123501,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
@@ -374,6 +403,7 @@
},
{
"rloi_id": 1113,
+ "station_threshold_id": 123502,
"river_name": "River Pinn",
"agency_name": "Avenue Road",
"status": "Active",
diff --git a/test/data/warning.json b/test/data/warning.json
new file mode 100644
index 000000000..cd72105e4
--- /dev/null
+++ b/test/data/warning.json
@@ -0,0 +1,19 @@
+{
+ "station_threshold_id":"1749487",
+ "station_id":"7332",
+ "fwis_code":"062FWF46Harpendn",
+ "fwis_type":"W",
+ "direction":"u",
+ "value":"2.1",
+ "threshold_type":"FW RES FW",
+ "rloi_id":7332,
+ "river_name":"River Lee",
+ "agency_name":"Harpenden",
+ "status":"Active",
+ "iswales":false,
+ "latest_level":"0.935",
+ "threshold_value":"1.6",
+ "value_timestamp":"2024-09-26T15:30:00.000Z",
+ "ta_name":"River Lee at Harpenden",
+ "severity_value":2
+}
diff --git a/test/models/lib/process-imtd-thresholds.js b/test/models/lib/process-imtd-thresholds.js
index aecbb088c..0bcabb898 100644
--- a/test/models/lib/process-imtd-thresholds.js
+++ b/test/models/lib/process-imtd-thresholds.js
@@ -1,10 +1,11 @@
const Lab = require('@hapi/lab')
const Code = require('@hapi/code')
+const data = require('../../data')
const lab = exports.lab = Lab.script()
const processImtdThresholds = require('../../../server/models/views/lib/process-imtd-thresholds')
-const alertExpectedText = { id: 'alertThreshold', description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued', shortname: 'Possible flood alerts' }
-const warningExpectedText = { id: 'warningThreshold', description: 'Property flooding is possible above this level. One or more flood warnings may be issued', shortname: 'Possible flood warnings' }
+const alertExpectedText = { id: 'alertThreshold', description: 'Top of normal range. Low lying land flooding possible above this level. One or more flood alerts may be issued', shortname: 'Possible flood alerts' }
+const warningExpectedText = { id: 'warningThreshold', description: 'Flood warning issued: River Lee at Harpenden', shortname: 'River Lee at Harpenden' }
function expectThresholds (thresholds, warningThreshold, alertThreshold) {
Code.expect(thresholds.length).to.equal(2)
@@ -15,11 +16,11 @@ function expectThresholds (thresholds, warningThreshold, alertThreshold) {
lab.experiment('process IMTD thresholds test', () => {
lab.experiment('given post processing is set to false', () => {
lab.test('then thresholds should be returned as is', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, false)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, false, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
lab.test('then single null thresholds should not be returned', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: null }, 0, 0, false)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: null }, 0, 0, false, 1.1)
Code.expect(thresholds.length).to.equal(1)
Code.expect(thresholds[0]).to.equal({ ...alertExpectedText, value: '1.10' })
})
@@ -34,35 +35,35 @@ lab.experiment('process IMTD thresholds test', () => {
})
lab.experiment('given post processing is set to true', () => {
lab.test('then thresholds should be returned as is when stageDatum is zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, true, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
lab.test('then thresholds should be returned as adjusted when stageDatum is greater than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 2.2, 0, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 2.2, 0, true, -1.1)
expectThresholds(thresholds, '-0.10', '-1.10')
})
lab.test('then thresholds should be returned as is when stageDatum is less than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, 0, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, 0, true, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
lab.test('then thresholds should be returned as adjusted when stageDatum is less than zero and stationSubtract is greater than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, 0.5, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, 0.5, true, 0.6)
expectThresholds(thresholds, '1.60', '0.60')
})
lab.test('then thresholds should be returned as is when stageDatum is less than zero and stationSubtract is less than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, -2.2, -0.5, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, -2.2, -0.5, true, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
lab.test('then thresholds should be returned as is when stageDatum is equal to zero and stationSubtract is less than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, -0.5, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, -0.5, true, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
lab.test('then thresholds should be returned as adjusted when stageDatum is equal to zero and stationSubtract is greater than than zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0.5, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0.5, true, 0.6)
expectThresholds(thresholds, '1.60', '0.60')
})
lab.test('then thresholds should be returned as is when stageDatum is equal to zero and stationSubtract is zero', async () => {
- const thresholds = processImtdThresholds({ alert: 1.1, warning: 2.1 }, 0, 0, true)
+ const thresholds = processImtdThresholds({ alert: 1.1, warning: data.warning }, 0, 0, true, 1.1)
expectThresholds(thresholds, '2.10', '1.10')
})
})
diff --git a/test/models/station.js b/test/models/station.js
index 82dffb513..0d85f8888 100644
--- a/test/models/station.js
+++ b/test/models/station.js
@@ -37,7 +37,7 @@ lab.experiment('Station model test', () => {
Code.expect(Result.thresholds[0].values).to.equal([
{
id: 'warningThreshold',
- description: 'Property flooding is possible above this level. One or more flood warnings may be issued',
+ description: 'Property flooding is possible above this level',
shortname: 'Possible flood warnings',
value: '3.64'
}
@@ -45,12 +45,12 @@ lab.experiment('Station model test', () => {
Code.expect(Result.thresholds[2].values).to.equal([
{
id: 'alertThreshold',
- description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued',
+ description: 'Low lying land flooding possible above this level. One or more flood alerts may be issued',
shortname: 'Possible flood alerts',
value: '3.22'
}
])
- Code.expect(Result.thresholds[4].values).to.equal([
+ Code.expect(Result.thresholds[3].values).to.equal([
{
id: 'latest',
value: '0.81',
@@ -67,7 +67,7 @@ lab.experiment('Station model test', () => {
Code.expect(Result.thresholds[1].values).to.equal([
{
id: 'warningThreshold',
- description: 'Property flooding is possible above this level. One or more flood warnings may be issued',
+ description: 'Property flooding is possible above this level',
shortname: 'Possible flood warnings',
value: '3.22'
}
@@ -75,15 +75,9 @@ lab.experiment('Station model test', () => {
Code.expect(Result.thresholds[2].values).to.equal([
{
id: 'alertThreshold',
- description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued',
+ description: '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: '2.84'
- },
- {
- description: 'This is the top of the normal range',
- id: 'pc5',
- shortname: 'Top of normal range',
- value: '2.84'
}
])
})
@@ -224,7 +218,7 @@ lab.experiment('Station model test', () => {
Code.expect(Result.thresholds[0].values).to.equal(
[
{
- description: 'Property flooding is possible above this level. One or more flood warnings may be issued',
+ description: 'Property flooding is possible above this level',
id: 'warningThreshold',
shortname: 'Possible flood warnings',
value: '4.20'
@@ -235,7 +229,7 @@ lab.experiment('Station model test', () => {
[
{
id: 'alertThreshold',
- description: 'Low lying land flooding is possible above this level. One or more flood alerts may be issued',
+ description: 'Low lying land flooding possible above this level. One or more flood alerts may be issued',
shortname: 'Possible flood alerts',
value: '3.88'
}
From 4fe556d331754fcfede086490213ce313f880615 Mon Sep 17 00:00:00 2001
From: Keyurx11 <58322871+Keyurx11@users.noreply.github.com>
Date: Wed, 9 Oct 2024 16:58:11 +0100
Subject: [PATCH 6/7] Feature/fsr 1287 (#850)
* auto refresh. updated TA tests
* sonar fixes
* some sonar fixes
* refactor autorefresh. add unit tests
* conditional callback
---------
Co-authored-by: Ash
---
.../components/latest-levels-auto-refresh.js | 188 ++++++++++++++++++
server/views/partials/latest-levels.html | 25 ++-
test/routes/target-area.js | 10 +-
.../components/latest-levels-auto-refresh.js | 125 ++++++++++++
webpack.config.js | 1 +
5 files changed, 339 insertions(+), 10 deletions(-)
create mode 100644 server/src/js/components/latest-levels-auto-refresh.js
create mode 100644 test/src/js/components/latest-levels-auto-refresh.js
diff --git a/server/src/js/components/latest-levels-auto-refresh.js b/server/src/js/components/latest-levels-auto-refresh.js
new file mode 100644
index 000000000..c09323ee5
--- /dev/null
+++ b/server/src/js/components/latest-levels-auto-refresh.js
@@ -0,0 +1,188 @@
+/* global DOMParser */
+
+class LatestLevelsAutoRefresh {
+ constructor (targetMinutes = [3, 18, 33, 48]) {
+ this.timeout = null
+ this.timeAgoInterval = null
+ this.liveStatusMessages = []
+ this.targetMinutes = targetMinutes
+
+ this.latestLevels = document.querySelectorAll('.defra-live__item')
+ }
+
+ updateLiveStatus = () => {
+ if (this.liveStatusMessages.length === 0) {
+ return
+ }
+
+ const element = document.querySelector('[data-live-status]')
+
+ if (!element) {
+ return
+ }
+
+ element.innerHTML = ''
+
+ const p = document.createElement('p')
+ p.innerText = this.liveStatusMessages.join('. ')
+ element.append(p)
+ }
+
+ updateTimeAgo = () => {
+ console.log('--updateTimeAgo() started')
+
+ setTimeout(() => {
+ this.renderTimeAgo()
+ this.timeAgoInterval = setInterval(this.renderTimeAgo, 60000)
+ }, (60 - new Date().getSeconds()) * 1000)
+ }
+
+ renderTimeAgo = () => {
+ const elements = document.querySelectorAll('[data-item-time]')
+
+ elements.forEach((element, index) => {
+ console.log('element', index)
+ const timeAgoText = element.textContent
+ const timeAgoValue = parseInt(timeAgoText, 10)
+
+ console.log('--currentTime', timeAgoText)
+
+ if (!timeAgoText.includes('hour')) {
+ element.textContent = `${timeAgoValue + 1} minutes ago`
+ console.log('--newTime', element.textContent)
+ }
+ })
+ }
+
+ fetchRiverLevels = (callback) => {
+ console.log('fetchRiverLevels...')
+
+ this.liveStatusMessages = []
+
+ fetch(window.location.href)
+ .then(res => {
+ if (!res.ok) {
+ throw new Error('Failed to fetch data')
+ }
+
+ return res.text()
+ })
+ .then(html => {
+ this.updateRiverLevels(html)
+
+ if (callback) {
+ callback()
+ }
+ })
+ .catch(error => {
+ console.error('Error updating levels:', error)
+
+ this.liveStatusMessages.push('There was an error getting the latest level')
+ })
+ .finally(() => {
+ this.updateLiveStatus()
+ })
+ }
+
+ updateRiverLevels = (html) => {
+ const parser = new DOMParser()
+ const doc = parser.parseFromString(html, 'text/html')
+
+ // Get elements from fetched content
+ const fetchedElements = Array.from(doc.querySelectorAll('.defra-live .defra-live__item'))
+
+ // Check if any elements are missing in the fetched data
+ const isMissingElements = this.latestLevels.length !== fetchedElements.length
+ console.log('isMissingElements', isMissingElements, this.latestLevels.length, fetchedElements.length)
+ console.log('latestLevels', this.latestLevels)
+ console.log('fetchedElements', fetchedElements)
+
+ if (isMissingElements) {
+ return this.liveStatusMessages.push('Warnings have been removed, please refresh the page.')
+ }
+
+ fetchedElements.forEach((fetchedElement) => {
+ const itemId = fetchedElement.getAttribute('data-item-id')
+ const itemRiverName = fetchedElement.getAttribute('data-item-name')
+ const itemRiverAgency = fetchedElement.getAttribute('data-item-agency')
+
+ const fetchedTime = fetchedElement.querySelector('[data-item-time]')
+ const fetchedValue = fetchedElement.querySelector('[data-item-value]')
+ const fetchedStatus = fetchedElement.getAttribute('data-item-status')
+
+ const currentItem = document.querySelector(`[data-item-id="${itemId}"]`)
+
+ console.log('fetchedElement', itemId, itemRiverName, itemRiverAgency)
+
+ if (currentItem) {
+ const currentTime = currentItem.querySelector('[data-item-time]')
+ const currentValue = currentItem.querySelector('[data-item-value]')
+ const currentStatus = currentItem.getAttribute('data-item-status')
+
+ console.log('--currentValue', currentValue?.textContent, currentStatus)
+ console.log('--fetchedValue', fetchedValue?.textContent, fetchedStatus)
+ console.log('--requires update?', fetchedValue?.textContent !== currentValue?.textContent)
+
+ if (fetchedStatus === currentStatus) {
+ if (fetchedValue?.textContent !== currentValue?.textContent) {
+ console.log('--new value fetched')
+ clearInterval(this.timeAgoInterval)
+ console.log('--interval cleared')
+ currentValue.textContent = fetchedValue.textContent
+
+ this.liveStatusMessages.push(`The ${itemRiverName} at ${itemRiverAgency} level was ${fetchedValue.textContent} metres ${fetchedTime.textContent}`)
+
+ this.updateTimeAgo()
+ } else {
+ console.log('--no change')
+ }
+
+ currentTime.textContent = fetchedTime.textContent
+ } else {
+ this.liveStatusMessages.push('Please refresh the page')
+ }
+ } else {
+ this.liveStatusMessages.push('Please refresh the page')
+ }
+
+ console.log('-------------------\n')
+ })
+ }
+
+ nextUpdate = () => {
+ clearTimeout(this.timeout)
+
+ const now = new Date()
+ const nowMinute = now.getMinutes()
+
+ // Find the next target minute
+ const nextTargetMinute = this.targetMinutes.find(minute => minute > nowMinute) ?? this.targetMinutes[0]
+
+ console.log('--Current minute:', nowMinute)
+ console.log('--Next target minute:', nextTargetMinute)
+
+ // Create the next target date based on the next target minute
+ const nextTargetDate = new Date(now)
+ nextTargetDate.setMinutes(nextTargetMinute)
+ nextTargetDate.setSeconds(0)
+ nextTargetDate.setMilliseconds(0)
+
+ if (nowMinute >= this.targetMinutes[this.targetMinutes.length - 1]) {
+ nextTargetDate.setHours(nextTargetDate.getHours() + 1)
+ }
+
+ // Calculate delay correctly from now
+ const delay = nextTargetDate.getTime() - now.getTime()
+
+ console.log('--delay (calc)', Math.round(delay / 1000 / 60))
+ console.log(`Next update scheduled in ${Math.round(delay / 1000 / 60)} minutes at ${nextTargetDate.toLocaleTimeString()}\n\n`)
+
+ // Schedule the next update
+ this.timeout = setTimeout(() => {
+ this.fetchRiverLevels()
+ this.nextUpdate()
+ }, delay)
+ }
+}
+
+window.LatestLevelsAutoRefresh = LatestLevelsAutoRefresh
diff --git a/server/views/partials/latest-levels.html b/server/views/partials/latest-levels.html
index c00f58800..dd7cd7f49 100644
--- a/server/views/partials/latest-levels.html
+++ b/server/views/partials/latest-levels.html
@@ -1,13 +1,19 @@
+
+
Latest level{% if model.latestLevels.length > 1 %}s{% endif %}
{% for warnings in model.latestLevels %}
-
- {% if warnings.isSuspendedOrOffline %}
-
{{ warnings.formatted_time }}
+
+ {% if warnings.status == 'Suspended' or (warnings.status == 'Active' and warnings.latest_level == null) %}
+
+ Latest Level
+
The {{ warnings.river_name }} level at {{ warnings.agency_name }} is currently unavailable.
{% else %}
-
{{ warnings.formatted_time }}
-
The {{ warnings.river_name }} level at {{ warnings.agency_name }} was {{ warnings.latest_level | toFixed(2) }} metres. Property flooding is possible when it goes above {{ warnings.threshold_value | toFixed(2) }} metres.
+
+ {{ warnings.formatted_time }}
+
+
The {{ warnings.river_name }} level at {{ warnings.agency_name }} was {{ warnings.latest_level | toFixed(2) }} metres. Property flooding is possible when it goes above {{ warnings.threshold_value | toFixed(2) }} metres.
{% if model.latestLevels.length > 1 %}
Monitor the {{ warnings.river_name }} level at {{ warnings.agency_name }}
{% endif %}
@@ -22,3 +28,12 @@
Latest level{% if model.latestLevels.length > 1 %}
{% endfor %}
{% if model.latestLevels.length > 1 %}These levels{% else %}This level{% endif %} will update automatically
The River Derwent level at Derby St Marys was 0.54 metres. Property flooding is possible when it goes above 3.30 metres.')
Code.expect(response.payload).to.contain('Monitor the latest level at Derby St Marys')
Code.expect(response.payload).to.match(/