diff --git a/driving_log/templates/imports.html b/driving_log/templates/imports.html
index eaebc86..a100326 100644
--- a/driving_log/templates/imports.html
+++ b/driving_log/templates/imports.html
@@ -2,7 +2,7 @@
{% block content %}
Imports and exports
- {% if not is_read_only %}Download CSV backup
+ {% if not is_read_only %}Download CSV backup
{% endif %}
DMV driving record
{% if not is_read_only %}Archives{% endif %}
diff --git a/driving_log/web.py b/driving_log/web.py
index ee44669..f1dea5a 100644
--- a/driving_log/web.py
+++ b/driving_log/web.py
@@ -80,6 +80,11 @@ def _format_local_date(value: str | datetime) -> str:
return f"{local.strftime('%A')}, {local.strftime('%b')} {local.day}, {local.year}"
+def _csv_export_filename(today: date | None = None) -> str:
+ export_date = today or datetime.now(ZONE).date()
+ return f"driving-log-{export_date.isoformat()}.csv"
+
+
def _format_date_in_zone(value: str | datetime, timezone_name: str) -> str:
local = _local_datetime_in_zone(value, timezone_name)
return f"{local.strftime('%A')}, {local.strftime('%b')} {local.day}, {local.year}"
@@ -1218,7 +1223,12 @@ async def imports_page(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
request,
"imports.html",
- common(request, title="Imports and exports", batches=batches),
+ common(
+ request,
+ title="Imports and exports",
+ batches=batches,
+ csv_export_filename=_csv_export_filename(),
+ ),
)
@app.get("/csv/export")
@@ -1227,7 +1237,7 @@ async def csv_download() -> Response:
export_csv(database),
media_type="text/csv; charset=utf-8",
headers={
- "Content-Disposition": 'attachment; filename="driving-log.csv"',
+ "Content-Disposition": f'attachment; filename="{_csv_export_filename()}"',
"Cache-Control": "no-store",
},
)
diff --git a/tests/test_browser.py b/tests/test_browser.py
index ec66b93..99c7572 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -390,6 +390,8 @@ def test_mobile_webkit_live_drive_recovery() -> None:
assert smallest_button >= 44
page.get_by_role("link", name="Import or export").click()
csv_download = page.get_by_role("link", name="Download CSV backup")
+ expected_filename = csv_download.get_attribute("data-export-filename")
+ assert expected_filename is not None
csv_download.click()
page.get_by_role("link", name="Save CSV backup").wait_for()
assert page.url == f"{url}/imports"
@@ -398,7 +400,9 @@ def test_mobile_webkit_live_drive_recovery() -> None:
in page.locator("[data-export-status]").text_content()
)
page.get_by_role("link", name="Save CSV backup").click()
- page.wait_for_function("window.sharedExportName === 'driving-log.csv'")
+ page.wait_for_function(
+ "expected => window.sharedExportName === expected", arg=expected_filename
+ )
assert page.evaluate("window.sharedExportKeys") == ["files"]
assert page.url == f"{url}/imports"
page.get_by_role("link", name="History").click()
diff --git a/tests/test_web.py b/tests/test_web.py
index c4e1689..7c6a16e 100644
--- a/tests/test_web.py
+++ b/tests/test_web.py
@@ -10,7 +10,7 @@
import tempfile
import unittest
import uuid
-from datetime import UTC, datetime, timedelta
+from datetime import UTC, date, datetime, timedelta
from pathlib import Path
from typing import cast
from unittest.mock import patch
@@ -27,6 +27,7 @@
from driving_log.migrations import LATEST_SCHEMA_VERSION
from driving_log.records import DriveInput, RecordService
from driving_log.web import (
+ _csv_export_filename,
_drive_group_key,
_duration_bucket,
_format_local_datetime,
@@ -62,6 +63,9 @@ def tearDown(self) -> None:
def run_async(self, function: object) -> None:
anyio.run(function) # type: ignore[arg-type]
+ def test_csv_export_filename_uses_iso_current_date(self) -> None:
+ self.assertEqual(_csv_export_filename(date(2026, 9, 28)), "driving-log-2026-09-28.csv")
+
def test_saved_locations_can_be_configured_and_removed(self) -> None:
async def scenario() -> None:
async with (
@@ -654,12 +658,17 @@ async def scenario() -> None:
)
self.assertIn('href="/archives">Archives', imports.text)
self.assertIn(
- 'href="/csv/export" data-file-export data-export-filename="driving-log.csv"',
+ 'href="/csv/export" data-file-export data-export-filename="'
+ f'{_csv_export_filename()}"',
imports.text,
)
csv_export = await client.get("/csv/export")
self.assertEqual(csv_export.status_code, 200)
self.assertIn("text/csv", csv_export.headers["content-type"])
+ self.assertEqual(
+ csv_export.headers["content-disposition"],
+ f'attachment; filename="{_csv_export_filename()}"',
+ )
self.run_async(scenario)