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)