Adds testing coverage plus a check for the locale being valid

This commit is contained in:
Trenton Holmes
2025-08-11 13:20:00 -07:00
parent 61813fab65
commit 7b92db189f
3 changed files with 172 additions and 18 deletions

View File

@@ -465,9 +465,6 @@ The `get_cf_value` filter retrieves a value from custom field data with optional
<!-- With default value --> <!-- With default value -->
{{ custom_fields | get_cf_value('phone', 'Not provided') }} {{ custom_fields | get_cf_value('phone', 'Not provided') }}
<!-- Handling missing fields -->
{{ custom_fields | get_cf_value('optional_field', 'N/A') }}
``` ```
##### Datetime Formatting ##### Datetime Formatting
@@ -510,7 +507,7 @@ for the possible codes and their meanings.
##### Date Localization ##### Date Localization
The `localize_date1 filter formats a date or datetime object into a localized string using Babel internationalization. The `localize_date` filter formats a date or datetime object into a localized string using Babel internationalization.
This takes into account the provided locale for translation. This takes into account the provided locale for translation.
###### Syntax ###### Syntax
@@ -558,7 +555,7 @@ This takes into account the provided locale for translation.
<!-- Output: "15/01/2024" --> <!-- Output: "15/01/2024" -->
``` ```
See the [supported locale codes]() for more options, See the [supported format codes](https://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns) for more options.
### Format Presets ### Format Presets
@@ -567,18 +564,6 @@ See the [supported locale codes]() for more options,
- **long**: Long format with full month name (e.g., "January 15, 2024") - **long**: Long format with full month name (e.g., "January 15, 2024")
- **full**: Full format including day of week (e.g., "Monday, January 15, 2024") - **full**: Full format including day of week (e.g., "Monday, January 15, 2024")
## Usage Notes
- All filters handle `None` values gracefully
- Date strings are automatically parsed when needed
- Timezone-aware datetime objects are recommended for `localize_date`
- Custom field data should follow the expected dictionary structure for `get_cf_value`
- Invalid format strings will raise appropriate Python exceptions
````
#### Additional Variables #### Additional Variables
- `{{ tag_name_list }}`: A list of tag names applied to the document, ordered by the tag name. Note this is a list, not a single string - `{{ tag_name_list }}`: A list of tag names applied to the document, ordered by the tag name. Note this is a list, not a single string
@@ -606,7 +591,7 @@ somepath/
{% endif %} {% endif %}
{% endif %} {% endif %}
/{{ title }} /{{ title }}
```` ```
For a document with an ASN of 205, it would result in `somepath/asn-201-400/asn-2xx/Title.pdf`, but For a document with an ASN of 205, it would result in `somepath/asn-201-400/asn-2xx/Title.pdf`, but
a document with an ASN of 355 would be placed in `somepath/asn-201-400/asn-3xx/Title.pdf`. a document with an ASN of 355 would be placed in `somepath/asn-201-400/asn-3xx/Title.pdf`.

View File

@@ -7,6 +7,7 @@ from datetime import datetime
from pathlib import PurePath from pathlib import PurePath
import pathvalidate import pathvalidate
from babel import Locale
from babel import dates from babel import dates
from django.utils import timezone from django.utils import timezone
from django.utils.dateparse import parse_date from django.utils.dateparse import parse_date
@@ -116,6 +117,11 @@ def localize_date(value: date | datetime, format: str, locale: str) -> str:
Raises: Raises:
TypeError: If `value` is not a date or datetime instance. TypeError: If `value` is not a date or datetime instance.
""" """
try:
Locale.parse(locale)
except Exception as e:
raise ValueError(f"Invalid locale identifier: {locale}") from e
if isinstance(value, datetime): if isinstance(value, datetime):
return dates.format_datetime(value, format=format, locale=locale) return dates.format_datetime(value, format=format, locale=locale)
elif isinstance(value, date): elif isinstance(value, date):

View File

@@ -4,6 +4,7 @@ import tempfile
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
import pytest
from auditlog.context import disable_auditlog from auditlog.context import disable_auditlog
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -22,6 +23,7 @@ from documents.models import Document
from documents.models import DocumentType from documents.models import DocumentType
from documents.models import StoragePath from documents.models import StoragePath
from documents.tasks import empty_trash from documents.tasks import empty_trash
from documents.templating.filepath import localize_date
from documents.tests.utils import DirectoriesMixin from documents.tests.utils import DirectoriesMixin
from documents.tests.utils import FileSystemAssertsMixin from documents.tests.utils import FileSystemAssertsMixin
@@ -1586,3 +1588,164 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
generate_filename(doc), generate_filename(doc),
Path("brussels-belgium/some-title-with-special-characters.pdf"), Path("brussels-belgium/some-title-with-special-characters.pdf"),
) )
class TestDateLocalization:
"""
Groups all tests related to the `localize_date` function.
"""
TEST_DATE = datetime.date(2023, 10, 26)
TEST_DATETIME = datetime.datetime(
2023,
10,
26,
14,
30,
5,
tzinfo=datetime.timezone.utc,
)
@pytest.mark.parametrize(
"value, format_style, locale_str, expected_output",
[
pytest.param(
TEST_DATE,
"EEEE, MMM d, yyyy",
"en_US",
"Thursday, Oct 26, 2023",
id="date-en_US-custom",
),
pytest.param(
TEST_DATE,
"dd.MM.yyyy",
"de_DE",
"26.10.2023",
id="date-de_DE-custom",
),
# German weekday and month name translation
pytest.param(
TEST_DATE,
"EEEE",
"de_DE",
"Donnerstag",
id="weekday-de_DE",
),
pytest.param(
TEST_DATE,
"MMMM",
"de_DE",
"October",
id="month-de_DE",
),
# French weekday and month name translation
pytest.param(
TEST_DATE,
"EEEE",
"fr_FR",
"jeudi",
id="weekday-fr_FR",
),
pytest.param(
TEST_DATE,
"MMMM",
"fr_FR",
"octobre",
id="month-fr_FR",
),
],
)
def test_localize_date_with_date_objects(
self,
value: datetime.date,
format_style: str,
locale_str: str,
expected_output: str,
):
"""
Tests `localize_date` with `date` objects across different locales and formats.
"""
assert localize_date(value, format_style, locale_str) == expected_output
@pytest.mark.parametrize(
"value, format_style, locale_str, expected_output",
[
pytest.param(
TEST_DATETIME,
"yyyy.MM.dd G 'at' HH:mm:ss zzz",
"en_US",
"2023.10.26 AD at 14:30:05 UTC",
id="datetime-en_US-custom",
),
pytest.param(
TEST_DATETIME,
"dd.MM.yyyy",
"fr_FR",
"26.10.2023",
id="date-fr_FR-custom",
),
# Spanish weekday and month translation
pytest.param(
TEST_DATETIME,
"EEEE",
"es_ES",
"jueves",
id="weekday-es_ES",
),
pytest.param(
TEST_DATETIME,
"MMMM",
"es_ES",
"octubre",
id="month-es_ES",
),
# Italian weekday and month translation
pytest.param(
TEST_DATETIME,
"EEEE",
"it_IT",
"giovedì",
id="weekday-it_IT",
),
pytest.param(
TEST_DATETIME,
"MMMM",
"it_IT",
"ottobre",
id="month-it_IT",
),
],
)
def test_localize_date_with_datetime_objects(
self,
value: datetime.datetime,
format_style: str,
locale_str: str,
expected_output: str,
):
# To handle the non-breaking space in French and other locales
result = localize_date(value, format_style, locale_str)
assert result.replace("\u202f", " ") == expected_output.replace("\u202f", " ")
@pytest.mark.parametrize(
"invalid_value",
[
"2023-10-26",
1698330605,
None,
[],
{},
],
)
def test_localize_date_raises_type_error_for_invalid_input(self, invalid_value):
with pytest.raises(TypeError) as excinfo:
localize_date(invalid_value, "medium", "en_US")
assert f"Unsupported type {type(invalid_value)}" in str(excinfo.value)
def test_localize_date_raises_error_for_invalid_locale(self):
with pytest.raises(ValueError) as excinfo:
localize_date(self.TEST_DATE, "medium", "invalid_locale_code")
assert "Invalid locale identifier" in str(excinfo.value)