diff --git a/app/templates/views/dashboard/dashboard.html b/app/templates/views/dashboard/dashboard.html
index 0a03daa88..dfdfc2a5f 100644
--- a/app/templates/views/dashboard/dashboard.html
+++ b/app/templates/views/dashboard/dashboard.html
@@ -39,7 +39,7 @@
What counts as 1 text message part?
- See pricing.
+ See Tracking usage.
{% endif %}
diff --git a/gulpfile.js b/gulpfile.js
index d96e2e7da..35c92bd0c 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -131,7 +131,7 @@ const javascripts = () => {
paths.src + 'javascripts/date.js',
paths.src + 'javascripts/loginAlert.js',
paths.src + 'javascripts/main.js',
- paths.src + 'javascripts/sampleChartDashboard.js',
+ paths.src + 'javascripts/chartDashboard.js',
])
.pipe(plugins.prettyerror())
.pipe(plugins.babel({
diff --git a/poetry.lock b/poetry.lock
index 5871c4460..b444e94bc 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -85,6 +85,17 @@ charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
+[[package]]
+name = "bidict"
+version = "0.23.1"
+description = "The bidirectional mapping library for Python."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"},
+ {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"},
+]
+
[[package]]
name = "black"
version = "24.4.2"
@@ -887,6 +898,24 @@ redis = ">=2.7.6"
dev = ["coverage", "pre-commit", "pytest", "pytest-mock"]
tests = ["coverage", "pytest", "pytest-mock"]
+[[package]]
+name = "flask-socketio"
+version = "5.3.6"
+description = "Socket.IO integration for Flask applications"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "Flask-SocketIO-5.3.6.tar.gz", hash = "sha256:bb8f9f9123ef47632f5ce57a33514b0c0023ec3696b2384457f0fcaa5b70501c"},
+ {file = "Flask_SocketIO-5.3.6-py3-none-any.whl", hash = "sha256:9e62d2131842878ae6bfdd7067dfc3be397c1f2b117ab1dc74e6fe74aad7a579"},
+]
+
+[package.dependencies]
+Flask = ">=0.9"
+python-socketio = ">=5.0.2"
+
+[package.extras]
+docs = ["sphinx"]
+
[[package]]
name = "flask-talisman"
version = "1.1.0"
@@ -1049,6 +1078,17 @@ setproctitle = ["setproctitle"]
testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
tornado = ["tornado (>=0.2)"]
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
[[package]]
name = "html5lib"
version = "1.1"
@@ -1641,7 +1681,6 @@ files = [
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
- {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
]
@@ -2385,6 +2424,25 @@ files = [
[package.extras]
cli = ["click (>=5.0)"]
+[[package]]
+name = "python-engineio"
+version = "4.9.1"
+description = "Engine.IO server and client for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "python_engineio-4.9.1-py3-none-any.whl", hash = "sha256:f995e702b21f6b9ebde4e2000cd2ad0112ba0e5116ec8d22fe3515e76ba9dddd"},
+ {file = "python_engineio-4.9.1.tar.gz", hash = "sha256:7631cf5563086076611e494c643b3fa93dd3a854634b5488be0bba0ef9b99709"},
+]
+
+[package.dependencies]
+simple-websocket = ">=0.10.0"
+
+[package.extras]
+asyncio-client = ["aiohttp (>=3.4)"]
+client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
+docs = ["sphinx"]
+
[[package]]
name = "python-json-logger"
version = "2.0.7"
@@ -2413,6 +2471,26 @@ text-unidecode = ">=1.3"
[package.extras]
unidecode = ["Unidecode (>=1.1.1)"]
+[[package]]
+name = "python-socketio"
+version = "5.11.2"
+description = "Socket.IO server and client for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "python-socketio-5.11.2.tar.gz", hash = "sha256:ae6a1de5c5209ca859dc574dccc8931c4be17ee003e74ce3b8d1306162bb4a37"},
+ {file = "python_socketio-5.11.2-py3-none-any.whl", hash = "sha256:b9f22a8ff762d7a6e123d16a43ddb1a27d50f07c3c88ea999334f2f89b0ad52b"},
+]
+
+[package.dependencies]
+bidict = ">=0.21.0"
+python-engineio = ">=4.8.0"
+
+[package.extras]
+asyncio-client = ["aiohttp (>=3.4)"]
+client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
+docs = ["sphinx"]
+
[[package]]
name = "pytz"
version = "2024.1"
@@ -2768,6 +2846,23 @@ numpy = ">=1.14,<3"
docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"]
test = ["pytest", "pytest-cov"]
+[[package]]
+name = "simple-websocket"
+version = "1.0.0"
+description = "Simple WebSocket server and client for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8"},
+ {file = "simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc"},
+]
+
+[package.dependencies]
+wsproto = "*"
+
+[package.extras]
+docs = ["sphinx"]
+
[[package]]
name = "six"
version = "1.16.0"
@@ -2945,6 +3040,20 @@ MarkupSafe = ">=2.1.1"
[package.extras]
watchdog = ["watchdog (>=2.3)"]
+[[package]]
+name = "wsproto"
+version = "1.2.0"
+description = "WebSockets state-machine based protocol implementation"
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
+ {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"},
+]
+
+[package.dependencies]
+h11 = ">=0.9.0,<1"
+
[[package]]
name = "wtforms"
version = "3.1.2"
@@ -3003,4 +3112,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.12.2"
-content-hash = "e96b87c048826ecb23d826c45b516aee60532cd2677a22332dc86fef498a411d"
+content-hash = "ce5863177e0d58f7e4affdb2d7c4a23721a6eb3199183cd92874c0315c0d6afa"
diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py
index 43a9e3ede..6daac653f 100644
--- a/tests/app/main/views/test_dashboard.py
+++ b/tests/app/main/views/test_dashboard.py
@@ -1887,26 +1887,22 @@ def app_with_socketio():
(
SERVICE_ONE_ID,
{"start_date": "2024-01-01", "days": 7},
- {"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7}
+ {"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7},
),
(
SERVICE_TWO_ID,
{"start_date": "2023-06-01", "days": 7},
- {"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7}
+ {"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7},
),
- ]
+ ],
)
def test_fetch_daily_stats(
- app_with_socketio, mocker,
- service_id,
- date_range,
- expected_call_args
+ app_with_socketio, mocker, service_id, date_range, expected_call_args
):
app, socketio = app_with_socketio
mocker.patch(
- "app.main.views.dashboard.get_stats_date_range",
- return_value=date_range
+ "app.main.views.dashboard.get_stats_date_range", return_value=date_range
)
mock_service_api = mocker.patch(
@@ -1914,9 +1910,9 @@ def test_fetch_daily_stats(
return_value={
date_range["start_date"]: {
"email": {"delivered": 0, "failure": 0, "requested": 0},
- "sms": {"delivered": 0, "failure": 1, "requested": 1}
+ "sms": {"delivered": 0, "failure": 1, "requested": 1},
},
- }
+ },
)
client = SocketIOTestClient(app, socketio)
@@ -1924,22 +1920,22 @@ def test_fetch_daily_stats(
connected = client.is_connected()
assert connected, "Client should be connected"
- client.emit('fetch_daily_stats', service_id)
+ client.emit("fetch_daily_stats", service_id)
received = client.get_received()
assert received, "Should receive a response message"
- assert received[0]['name'] == 'daily_stats_update'
- assert received[0]['args'][0] == {
+ assert received[0]["name"] == "daily_stats_update"
+ assert received[0]["args"][0] == {
date_range["start_date"]: {
"email": {"delivered": 0, "failure": 0, "requested": 0},
- "sms": {"delivered": 0, "failure": 1, "requested": 1}
+ "sms": {"delivered": 0, "failure": 1, "requested": 1},
},
}
mock_service_api.assert_called_once_with(
service_id,
start_date=expected_call_args["start_date"],
- days=expected_call_args["days"]
+ days=expected_call_args["days"],
)
finally:
client.disconnect()