This is an automated email from the ASF dual-hosted git repository. vavila pushed a commit to branch feat/to_datetime_jinja_filter in repository https://gitbox.apache.org/repos/asf/superset.git
commit 369f1e3f11514b999ae6ad698d8cfd4549604e03 Author: Vitor Avila <[email protected]> AuthorDate: Thu Mar 20 18:27:03 2025 -0300 feat(Jinja): to_datetime filter --- superset/jinja_context.py | 19 +++++++++++++ tests/unit_tests/jinja_context_test.py | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/superset/jinja_context.py b/superset/jinja_context.py index a4d8e6d6a4..0f56886dd2 100644 --- a/superset/jinja_context.py +++ b/superset/jinja_context.py @@ -561,6 +561,24 @@ class WhereInMacro: # pylint: disable=too-few-public-methods return result +def to_datetime( + value: str | None, format: str = "%Y-%m-%d %H:%M:%S" +) -> datetime | None: + """ + Parses a string into a datetime object. + + :param value: the string to parse. + :param format: the format to parse the string with. + :returns: the parsed datetime object. + """ + if not value: + return None + + # This value might come from a macro that could be including wrapping quotes + value = value.strip("'\"") + return datetime.strptime(value, format) + + class BaseTemplateProcessor: """ Base class for database-specific jinja context @@ -596,6 +614,7 @@ class BaseTemplateProcessor: # custom filters self.env.filters["where_in"] = WhereInMacro(database.get_dialect()) + self.env.filters["to_datetime"] = to_datetime def set_context(self, **kwargs: Any) -> None: self._context.update(kwargs) diff --git a/tests/unit_tests/jinja_context_test.py b/tests/unit_tests/jinja_context_test.py index be6e5fb55e..8764aafc92 100644 --- a/tests/unit_tests/jinja_context_test.py +++ b/tests/unit_tests/jinja_context_test.py @@ -17,6 +17,7 @@ # pylint: disable=invalid-name, unused-argument from __future__ import annotations +from datetime import datetime from typing import Any import pytest @@ -38,6 +39,7 @@ from superset.jinja_context import ( metric_macro, safe_proxy, TimeFilter, + to_datetime, WhereInMacro, ) from superset.models.core import Database @@ -429,6 +431,54 @@ def test_where_in_empty_list() -> None: assert where_in([], default_to_none=True) is None +def test_to_datetime() -> None: + """ + Test the ``to_datetime`` Jinja2 filter. + """ + + result = to_datetime("2025-03-20 15:55:00") + assert result == datetime(2025, 3, 20, 15, 55) + + assert to_datetime(None) is None + + +def test_to_datetime_custom_format() -> None: + """ + Test the ``to_datetime`` Jinja2 filter when specifying a format. + """ + result = to_datetime("2025-03-20", format="%Y-%m-%d") + assert result == datetime(2025, 3, 20) + + +def test_to_datetime_including_quotes() -> None: + """ + Test the ``to_datetime`` Jinja2 when a string wrapped + in quotes is used. + + This might happen when passing a value from a temporal macro. + """ + result = to_datetime("'2025-03-20'", format="%Y-%m-%d") + assert result == datetime(2025, 3, 20) + + +def test_to_datetime_raises() -> None: + """ + Test the ``to_datetime`` Jinja2 raises with an incorrect + format. + """ + with pytest.raises( + ValueError, + match="time data '2025-03-20' does not match format '%Y-%m-%d %H:%M:%S'", + ): + to_datetime("2025-03-20") + + with pytest.raises( + ValueError, + match="unconverted data remains: 15:55:00", + ): + to_datetime("2025-03-20 15:55:00", format="%Y-%m-%d") + + def test_dataset_macro(mocker: MockerFixture) -> None: """ Test the ``dataset_macro`` macro.
