Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion driving_log/templates/imports.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% block content %}
<h1>Imports and exports</h1>
<section class="actions">
{% if not is_read_only %}<a class="button primary" href="/csv/export" data-file-export data-export-filename="driving-log.csv">Download CSV backup</a>
{% if not is_read_only %}<a class="button primary" href="/csv/export" data-file-export data-export-filename="{{ csv_export_filename }}">Download CSV backup</a>
<span class="muted" data-export-status aria-live="polite"></span>{% endif %}
<a class="button" href="/dmv">DMV driving record</a>
{% if not is_read_only %}<a class="button secondary" href="/archives">Archives</a>{% endif %}
Expand Down
14 changes: 12 additions & 2 deletions driving_log/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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")
Expand All @@ -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",
},
)
Expand Down
6 changes: 5 additions & 1 deletion tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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()
Expand Down
13 changes: 11 additions & 2 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -654,12 +658,17 @@ async def scenario() -> None:
)
self.assertIn('href="/archives">Archives</a>', 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)

Expand Down
Loading