mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-08-12 00:19:48 +00:00
Initial work on a localize date filter
This commit is contained in:
@@ -434,6 +434,151 @@ provided. The template is provided as a string, potentially multiline, and rende
|
||||
In addition, the entire Document instance is available to be utilized in a more advanced way, as well as some variables which only make sense to be accessed
|
||||
with more complex logic.
|
||||
|
||||
#### Custom Jinja2 Filters
|
||||
|
||||
##### Custom Field Access
|
||||
|
||||
The `get_cf_value` filter retrieves a value from custom field data with optional default fallback.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ custom_fields | get_cf_value('field_name') }}
|
||||
{{ custom_fields | get_cf_value('field_name', 'default_value') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `custom_fields`: This _must_ be the provided custom field data
|
||||
- `name` (str): Name of the custom field to retrieve
|
||||
- `default` (str, optional): Default value to return if field is not found or has no value
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str | None`: The field value, default value, or `None` if neither exists
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Basic usage -->
|
||||
{{ custom_fields | get_cf_value('department') }}
|
||||
|
||||
<!-- With default value -->
|
||||
{{ custom_fields | get_cf_value('phone', 'Not provided') }}
|
||||
|
||||
<!-- Handling missing fields -->
|
||||
{{ custom_fields | get_cf_value('optional_field', 'N/A') }}
|
||||
```
|
||||
|
||||
##### Datetime Formatting
|
||||
|
||||
The `format_datetime`filter formats a datetime string or datetime object using Python's strftime formatting.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ datetime_value | format_datetime('%Y-%m-%d %H:%M:%S') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `value` (str | datetime): Date/time value to format (strings will be parsed automatically)
|
||||
- `format` (str): Python strftime format string
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str`: Formatted datetime string
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Format datetime object -->
|
||||
{{ created_at | format_datetime('%B %d, %Y at %I:%M %p') }}
|
||||
<!-- Output: "January 15, 2024 at 02:30 PM" -->
|
||||
|
||||
<!-- Format datetime string -->
|
||||
{{ "2024-01-15T14:30:00" | format_datetime('%m/%d/%Y') }}
|
||||
<!-- Output: "01/15/2024" -->
|
||||
|
||||
<!-- Custom formatting -->
|
||||
{{ timestamp | format_datetime('%A, %B %d, %Y') }}
|
||||
<!-- Output: "Monday, January 15, 2024" -->
|
||||
```
|
||||
|
||||
See the [strftime format code documentation](https://docs.python.org/3.13/library/datetime.html#strftime-and-strptime-format-codes)
|
||||
for the possible codes and their meanings.
|
||||
|
||||
##### Date Localization
|
||||
|
||||
The `localize_date1 filter formats a date or datetime object into a localized string using Babel internationalization.
|
||||
This takes into account the provided locale for translation.
|
||||
|
||||
###### Syntax
|
||||
|
||||
```jinja2
|
||||
{{ date_value | localize_date('medium', 'en_US') }}
|
||||
{{ datetime_value | localize_date('short', 'fr_FR') }}
|
||||
```
|
||||
|
||||
###### Parameters
|
||||
|
||||
- `value` (date | datetime): Date or datetime object to format (datetime should be timezone-aware)
|
||||
- `format` (str): Format type - either a Babel preset ('short', 'medium', 'long', 'full') or custom pattern
|
||||
- `locale` (str): Locale code for localization (e.g., 'en_US', 'fr_FR', 'de_DE')
|
||||
|
||||
###### Returns
|
||||
|
||||
- `str`: Localized, formatted date string
|
||||
|
||||
###### Examples
|
||||
|
||||
```jinja2
|
||||
<!-- Preset formats -->
|
||||
{{ created_date | localize_date('short', 'en_US') }}
|
||||
<!-- Output: "1/15/24" -->
|
||||
|
||||
{{ created_date | localize_date('medium', 'en_US') }}
|
||||
<!-- Output: "Jan 15, 2024" -->
|
||||
|
||||
{{ created_date | localize_date('long', 'en_US') }}
|
||||
<!-- Output: "January 15, 2024" -->
|
||||
|
||||
{{ created_date | localize_date('full', 'en_US') }}
|
||||
<!-- Output: "Monday, January 15, 2024" -->
|
||||
|
||||
<!-- Different locales -->
|
||||
{{ created_date | localize_date('medium', 'fr_FR') }}
|
||||
<!-- Output: "15 janv. 2024" -->
|
||||
|
||||
{{ created_date | localize_date('medium', 'de_DE') }}
|
||||
<!-- Output: "15.01.2024" -->
|
||||
|
||||
<!-- Custom patterns -->
|
||||
{{ created_date | localize_date('dd/MM/yyyy', 'en_GB') }}
|
||||
<!-- Output: "15/01/2024" -->
|
||||
```
|
||||
|
||||
See the [supported locale codes]() for more options,
|
||||
|
||||
### Format Presets
|
||||
|
||||
- **short**: Abbreviated format (e.g., "1/15/24")
|
||||
- **medium**: Medium-length format (e.g., "Jan 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")
|
||||
|
||||
## 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
|
||||
|
||||
- `{{ 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
|
||||
@@ -461,7 +606,7 @@ somepath/
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
/{{ title }}
|
||||
```
|
||||
````
|
||||
|
||||
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`.
|
||||
|
@@ -15,6 +15,7 @@ classifiers = [
|
||||
# This will allow testing to not install a webserver, mysql, etc
|
||||
|
||||
dependencies = [
|
||||
"babel>=2.17",
|
||||
"bleach~=6.2.0",
|
||||
"celery[redis]~=5.5.1",
|
||||
"channels~=4.2",
|
||||
|
@@ -2,10 +2,12 @@ import logging
|
||||
import os
|
||||
import re
|
||||
from collections.abc import Iterable
|
||||
from datetime import date
|
||||
from datetime import datetime
|
||||
from pathlib import PurePath
|
||||
|
||||
import pathvalidate
|
||||
from babel import dates
|
||||
from django.utils import timezone
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.utils.text import slugify as django_slugify
|
||||
@@ -90,19 +92,46 @@ def get_cf_value(
|
||||
return None
|
||||
|
||||
|
||||
_template_environment.filters["get_cf_value"] = get_cf_value
|
||||
|
||||
|
||||
def format_datetime(value: str | datetime, format: str) -> str:
|
||||
if isinstance(value, str):
|
||||
value = parse_date(value)
|
||||
return value.strftime(format=format)
|
||||
|
||||
|
||||
def localize_date(value: date | datetime, format: str, locale: str) -> str:
|
||||
"""
|
||||
Format a date or datetime object into a localized string using Babel.
|
||||
|
||||
Args:
|
||||
value (date | datetime): The date or datetime to format. If a datetime
|
||||
is provided, it should be timezone-aware (e.g., UTC from a Django DB object).
|
||||
format (str): The format to use. Can be one of Babel's preset formats
|
||||
('short', 'medium', 'long', 'full') or a custom pattern string.
|
||||
locale (str): The locale code (e.g., 'en_US', 'fr_FR') to use for
|
||||
localization.
|
||||
|
||||
Returns:
|
||||
str: The localized, formatted date string.
|
||||
|
||||
Raises:
|
||||
TypeError: If `value` is not a date or datetime instance.
|
||||
"""
|
||||
if isinstance(value, datetime):
|
||||
return dates.format_datetime(value, format=format, locale=locale)
|
||||
elif isinstance(value, date):
|
||||
return dates.format_date(value, format=format, locale=locale)
|
||||
else:
|
||||
raise TypeError(f"Unsupported type {type(value)} for localize_date")
|
||||
|
||||
|
||||
_template_environment.filters["get_cf_value"] = get_cf_value
|
||||
|
||||
_template_environment.filters["datetime"] = format_datetime
|
||||
|
||||
_template_environment.filters["slugify"] = django_slugify
|
||||
|
||||
_template_environment.filters["localize_date"] = localize_date
|
||||
|
||||
|
||||
def create_dummy_document():
|
||||
"""
|
||||
|
4
uv.lock
generated
4
uv.lock
generated
@@ -1,5 +1,5 @@
|
||||
version = 1
|
||||
revision = 3
|
||||
revision = 2
|
||||
requires-python = ">=3.10"
|
||||
resolution-markers = [
|
||||
"sys_platform == 'darwin'",
|
||||
@@ -1911,6 +1911,7 @@ name = "paperless-ngx"
|
||||
version = "2.17.1"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "babel", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "bleach", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "celery", extra = ["redis"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
{ name = "channels", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
|
||||
@@ -2044,6 +2045,7 @@ typing = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "babel", specifier = ">=2.17.0" },
|
||||
{ name = "bleach", specifier = "~=6.2.0" },
|
||||
{ name = "celery", extras = ["redis"], specifier = "~=5.5.1" },
|
||||
{ name = "channels", specifier = "~=4.2" },
|
||||
|
Reference in New Issue
Block a user