mirror of
https://github.com/apache/superset.git
synced 2026-05-12 19:35:17 +00:00
feat(time_comparison): Support all date formats when computing custom and inherit offsets (#30002)
This commit is contained in:
@@ -19,6 +19,7 @@ from __future__ import annotations
|
||||
import copy
|
||||
import logging
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, cast, ClassVar, TYPE_CHECKING, TypedDict
|
||||
|
||||
import numpy as np
|
||||
@@ -369,6 +370,38 @@ class QueryContextProcessor:
|
||||
axis=1,
|
||||
)
|
||||
|
||||
def is_valid_date(self, date_string: str) -> bool:
|
||||
try:
|
||||
# Attempt to parse the string as a date in the format YYYY-MM-DD
|
||||
datetime.strptime(date_string, "%Y-%m-%d")
|
||||
return True
|
||||
except ValueError:
|
||||
# If parsing fails, it's not a valid date in the format YYYY-MM-DD
|
||||
return False
|
||||
|
||||
def get_offset_custom_or_inherit(
|
||||
self,
|
||||
offset: str,
|
||||
outer_from_dttm: datetime,
|
||||
outer_to_dttm: datetime,
|
||||
) -> str:
|
||||
"""
|
||||
Get the time offset for custom or inherit.
|
||||
|
||||
:param offset: The offset string.
|
||||
:param outer_from_dttm: The outer from datetime.
|
||||
:param outer_to_dttm: The outer to datetime.
|
||||
:returns: The time offset.
|
||||
"""
|
||||
if offset == "inherit":
|
||||
# return the difference in days between the from and the to dttm formatted as a string with the " days ago" suffix
|
||||
return f"{(outer_to_dttm - outer_from_dttm).days} days ago"
|
||||
if self.is_valid_date(offset):
|
||||
# return the offset as the difference in days between the outer from dttm and the offset date (which is a YYYY-MM-DD string) formatted as a string with the " days ago" suffix
|
||||
offset_date = datetime.strptime(offset, "%Y-%m-%d")
|
||||
return f"{(outer_from_dttm - offset_date).days} days ago"
|
||||
return ""
|
||||
|
||||
def processing_time_offsets( # pylint: disable=too-many-locals,too-many-statements
|
||||
self,
|
||||
df: pd.DataFrame,
|
||||
@@ -409,6 +442,13 @@ class QueryContextProcessor:
|
||||
# time_offsets: ['1 year ago'],
|
||||
# filters: [{col: 'dttm_col', op: 'TEMPORAL_RANGE', val: '2020 : 2021'}],
|
||||
# }
|
||||
original_offset = offset
|
||||
if self.is_valid_date(offset) or offset == "inherit":
|
||||
offset = self.get_offset_custom_or_inherit(
|
||||
offset,
|
||||
outer_from_dttm,
|
||||
outer_to_dttm,
|
||||
)
|
||||
query_object_clone.from_dttm = get_past_or_future(
|
||||
offset,
|
||||
outer_from_dttm,
|
||||
@@ -454,9 +494,19 @@ class QueryContextProcessor:
|
||||
if flt.get("col") != x_axis_label
|
||||
]
|
||||
|
||||
# Inherit or custom start dates might compute the same offset but the response cannot be given
|
||||
# using cached data unless you are using the same date of inherited range, that's why we
|
||||
# set the cache cache using a custom key that includes the original offset and the computed offset
|
||||
# for those two scenarios, the rest of the scenarios will use the original offset as cache key
|
||||
cached_time_offset_key = (
|
||||
offset if offset == original_offset else f"{offset}_{original_offset}"
|
||||
)
|
||||
|
||||
# `offset` is added to the hash function
|
||||
cache_key = self.query_cache_key(
|
||||
query_object_clone, time_offset=offset, time_grain=time_grain
|
||||
query_object_clone,
|
||||
time_offset=cached_time_offset_key,
|
||||
time_grain=time_grain,
|
||||
)
|
||||
cache = QueryCacheManager.get(
|
||||
cache_key, CacheRegion.DATA, query_context.force
|
||||
@@ -471,7 +521,7 @@ class QueryContextProcessor:
|
||||
query_object_clone_dct = query_object_clone.to_dict()
|
||||
# rename metrics: SUM(value) => SUM(value) 1 year ago
|
||||
metrics_mapping = {
|
||||
metric: TIME_COMPARISON.join([metric, offset])
|
||||
metric: TIME_COMPARISON.join([metric, original_offset])
|
||||
for metric in metric_names
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user