55from importlib .util import find_spec
66from typing import Any , Optional , Union
77
8+ import attrs
89from gooddata_api_client .model .inline_filter_definition_inline import InlineFilterDefinitionInline
910
1011if find_spec ("icu" ) is not None :
@@ -153,8 +154,35 @@ def description(self, labels: dict[str, str], format_locale: Optional[str] = Non
153154}
154155
155156
157+ @attrs .define
158+ class BoundedFilter :
159+ granularity : str
160+ from_shift : Optional [int ] = None
161+ to_shift : Optional [int ] = None
162+
163+ def __attrs_post_init__ (self ) -> None :
164+ # Validate that exactly one of from_shift or to_shift is set
165+ if (self .from_shift is None ) == (self .to_shift is None ):
166+ raise ValueError ("BoundedFilter requires exactly one of `from_shift` or `to_shift` to be set." )
167+
168+ def as_api_model (self ) -> afm_models .BoundedFilter :
169+ return afm_models .BoundedFilter (
170+ granularity = self .granularity ,
171+ _from = self .from_shift ,
172+ to = self .to_shift ,
173+ _check_type = False ,
174+ )
175+
176+
156177class RelativeDateFilter (Filter ):
157- def __init__ (self , dataset : ObjId , granularity : str , from_shift : int , to_shift : int ) -> None :
178+ def __init__ (
179+ self ,
180+ dataset : ObjId ,
181+ granularity : str ,
182+ from_shift : int ,
183+ to_shift : int ,
184+ bounded_filter : Optional [BoundedFilter ] = None ,
185+ ) -> None :
158186 super ().__init__ ()
159187
160188 if granularity not in _GRANULARITY :
@@ -166,6 +194,7 @@ def __init__(self, dataset: ObjId, granularity: str, from_shift: int, to_shift:
166194 self ._granularity = granularity
167195 self ._from_shift = from_shift
168196 self ._to_shift = to_shift
197+ self ._bounded_filter = bounded_filter
169198
170199 @property
171200 def dataset (self ) -> ObjId :
@@ -183,17 +212,26 @@ def from_shift(self) -> int:
183212 def to_shift (self ) -> int :
184213 return self ._to_shift
185214
215+ @property
216+ def bounded_filter (self ) -> Optional [BoundedFilter ]:
217+ return self ._bounded_filter
218+
186219 def is_noop (self ) -> bool :
187220 return False
188221
189222 def as_api_model (self ) -> afm_models .RelativeDateFilter :
190- body = RelativeDateFilterBody (
191- dataset = self .dataset .as_afm_id (),
192- granularity = self .granularity ,
193- _from = self .from_shift ,
194- to = self .to_shift ,
195- _check_type = False ,
196- )
223+ body_params = {
224+ "dataset" : self .dataset .as_afm_id (),
225+ "granularity" : self .granularity ,
226+ "_from" : self .from_shift ,
227+ "to" : self .to_shift ,
228+ "_check_type" : False ,
229+ }
230+
231+ if self .bounded_filter is not None :
232+ body_params ["bounded_filter" ] = self .bounded_filter .as_api_model ()
233+
234+ body = RelativeDateFilterBody (** body_params )
197235 return afm_models .RelativeDateFilter (body )
198236
199237 def description (self , labels : dict [str , str ], format_locale : Optional [str ] = None ) -> str :
0 commit comments