mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05: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
	 Trenton Holmes
					Trenton Holmes