Skip to content

Commit 8a5658d

Browse files
[executors] feat: move convert_date and common macros instantiation to core
1 parent ce5b0ec commit 8a5658d

File tree

6 files changed

+121
-95
lines changed

6 files changed

+121
-95
lines changed

libs/core/garf_core/query_editor.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,10 @@ def macros(self) -> QueryParameters:
405405
"""Returns macros with injected common parameters."""
406406
common_params = dict(self.common_params)
407407
if macros := self.args.macro:
408-
common_params.update(macros)
408+
converted_macros = {
409+
key: convert_date(value) for key, value in macros.items()
410+
}
411+
common_params.update(converted_macros)
409412
return common_params
410413

411414
def generate(self) -> BaseQueryElements:
@@ -564,3 +567,50 @@ def _is_invalid_field(field) -> bool:
564567
is_constant = _is_constant(field)
565568
has_operator = any(operator in field for operator in operators)
566569
return is_constant or has_operator
570+
571+
572+
def convert_date(date_string: str) -> str:
573+
"""Converts specific dates parameters to actual dates.
574+
575+
Returns:
576+
Date string in YYYY-MM-DD format.
577+
578+
Raises:
579+
GarfMacroError:
580+
If dynamic lookback value (:YYYYMMDD-N) is incorrect.
581+
"""
582+
if isinstance(date_string, list) or date_string.find(':Y') == -1:
583+
return date_string
584+
current_date = datetime.date.today()
585+
base_date, *date_customizer = re.split('\\+|-', date_string)
586+
if len(date_customizer) > 1:
587+
raise GarfMacroError(
588+
'Invalid format for date macro, should be in :YYYYMMDD-N format'
589+
)
590+
if not date_customizer:
591+
days_lookback = 0
592+
else:
593+
try:
594+
days_lookback = int(date_customizer[0])
595+
except ValueError as e:
596+
raise GarfMacroError(
597+
'Must provide numeric value for a number lookback period, '
598+
'i.e. :YYYYMMDD-1'
599+
) from e
600+
if base_date == ':YYYY':
601+
new_date = datetime.datetime(current_date.year, 1, 1)
602+
delta = relativedelta.relativedelta(years=days_lookback)
603+
elif base_date == ':YYYYMM':
604+
new_date = datetime.datetime(current_date.year, current_date.month, 1)
605+
delta = relativedelta.relativedelta(months=days_lookback)
606+
elif base_date == ':YYYYMMDD':
607+
new_date = current_date
608+
delta = relativedelta.relativedelta(days=days_lookback)
609+
else:
610+
raise GarfMacroError(
611+
'Invalid format for date macro, should be in :YYYYMMDD-N format'
612+
)
613+
614+
if '-' in date_string:
615+
return (new_date - delta).strftime('%Y-%m-%d')
616+
return (new_date + delta).strftime('%Y-%m-%d')

libs/core/tests/unit/test_query_editor.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import datetime
1818

1919
import pytest
20+
from dateutil.relativedelta import relativedelta
2021
from garf_core import query_editor
2122

2223

@@ -129,3 +130,67 @@ def test_generate_returns_builtin_query(self):
129130
)
130131

131132
assert test_query_spec.query == expected_query_elements
133+
134+
def test_generate_returns_macros(self):
135+
query = 'SELECT test FROM resource WHERE start_date = {start_date}'
136+
test_query_spec = query_editor.QuerySpecification(
137+
text=query,
138+
title='test',
139+
args=query_editor.GarfQueryParameters(
140+
macro={'start_date': ':YYYYMMDD'},
141+
),
142+
)
143+
assert test_query_spec.macros.get(
144+
'start_date'
145+
) == datetime.date.today().strftime('%Y-%m-%d')
146+
147+
148+
def test_convert_date_returns_correct_datestring():
149+
current_date = datetime.datetime.today()
150+
current_year = datetime.datetime(current_date.year, 1, 1)
151+
current_month = datetime.datetime(current_date.year, current_date.month, 1)
152+
last_year = current_year - relativedelta(years=1)
153+
last_month = current_month - relativedelta(months=1)
154+
yesterday = current_date - relativedelta(days=1)
155+
tomorrow = current_date + relativedelta(days=1)
156+
next_month = current_month + relativedelta(months=1)
157+
next_year = current_year + relativedelta(years=1)
158+
159+
non_macro_date = '2022-01-01'
160+
date_year = ':YYYY'
161+
date_month = ':YYYYMM'
162+
date_day = ':YYYYMMDD'
163+
date_year_minus_one = ':YYYY-1'
164+
date_month_minus_one = ':YYYYMM-1'
165+
date_day_minus_one = ':YYYYMMDD-1'
166+
date_day_plus_one = ':YYYYMMDD+1'
167+
date_month_plus_one = ':YYYYMM+1'
168+
date_year_plus_one = ':YYYY+1'
169+
170+
non_macro_date_converted = query_editor.convert_date(non_macro_date)
171+
new_date_year = query_editor.convert_date(date_year)
172+
new_date_month = query_editor.convert_date(date_month)
173+
new_date_day = query_editor.convert_date(date_day)
174+
new_date_year_minus_one = query_editor.convert_date(date_year_minus_one)
175+
new_date_month_minus_one = query_editor.convert_date(date_month_minus_one)
176+
new_date_day_minus_one = query_editor.convert_date(date_day_minus_one)
177+
new_date_day_plus_one = query_editor.convert_date(date_day_plus_one)
178+
new_date_month_plus_one = query_editor.convert_date(date_month_plus_one)
179+
new_date_year_plus_one = query_editor.convert_date(date_year_plus_one)
180+
181+
assert non_macro_date_converted == non_macro_date
182+
assert new_date_year == current_year.strftime('%Y-%m-%d')
183+
assert new_date_month == current_month.strftime('%Y-%m-%d')
184+
assert new_date_day == current_date.strftime('%Y-%m-%d')
185+
assert new_date_year_minus_one == last_year.strftime('%Y-%m-%d')
186+
assert new_date_month_minus_one == last_month.strftime('%Y-%m-%d')
187+
assert new_date_day_minus_one == yesterday.strftime('%Y-%m-%d')
188+
assert new_date_day_plus_one == tomorrow.strftime('%Y-%m-%d')
189+
assert new_date_month_plus_one == next_month.strftime('%Y-%m-%d')
190+
assert new_date_year_plus_one == next_year.strftime('%Y-%m-%d')
191+
192+
193+
@pytest.mark.parametrize('date', [':YYYYMMDD-N', ':YYYYMMDD-2-3', ':YYYMMDD'])
194+
def test_convert_date_raise_garf_macro_error(date):
195+
with pytest.raises(query_editor.GarfMacroError):
196+
query_editor.convert_date(date)

libs/executors/garf_executors/entrypoints/utils.py

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,11 @@
1515

1616
from __future__ import annotations
1717

18-
import dataclasses
19-
import datetime
2018
import logging
21-
import os
2219
import sys
23-
from collections.abc import MutableSequence, Sequence
20+
from collections.abc import Sequence
2421
from typing import Any
2522

26-
import smart_open
27-
import yaml
28-
from dateutil import relativedelta
29-
from garf_core import query_editor
3023
from rich import logging as rich_logging
3124

3225

@@ -96,43 +89,6 @@ class GarfParamsException(Exception):
9689
"""Defines exception for incorrect parameters."""
9790

9891

99-
def convert_date(date_string: str) -> str:
100-
"""Converts specific dates parameters to actual dates.
101-
102-
Returns:
103-
Date string in YYYY-MM-DD format.
104-
105-
Raises:
106-
ValueError:
107-
If dynamic lookback value (:YYYYMMDD-N) is incorrect.
108-
"""
109-
if isinstance(date_string, list) or date_string.find(':YYYY') == -1:
110-
return date_string
111-
current_date = datetime.date.today()
112-
date_object = date_string.split('-')
113-
base_date = date_object[0]
114-
if len(date_object) == 2:
115-
try:
116-
days_ago = int(date_object[1])
117-
except ValueError as e:
118-
raise ValueError(
119-
'Must provide numeric value for a number lookback period, '
120-
'i.e. :YYYYMMDD-1'
121-
) from e
122-
else:
123-
days_ago = 0
124-
if base_date == ':YYYY':
125-
new_date = datetime.datetime(current_date.year, 1, 1)
126-
delta = relativedelta.relativedelta(years=days_ago)
127-
elif base_date == ':YYYYMM':
128-
new_date = datetime.datetime(current_date.year, current_date.month, 1)
129-
delta = relativedelta.relativedelta(months=days_ago)
130-
elif base_date == ':YYYYMMDD':
131-
new_date = current_date
132-
delta = relativedelta.relativedelta(days=days_ago)
133-
return (new_date - delta).strftime('%Y-%m-%d')
134-
135-
13692
def init_logging(
13793
loglevel: str = 'INFO', logger_type: str = 'local', name: str = __name__
13894
) -> logging.Logger:

libs/executors/tests/unit/entrypoints/test_utils.py

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,55 +13,10 @@
1313
# limitations under the License.
1414
from __future__ import annotations
1515

16-
from datetime import datetime
17-
from unittest import mock
18-
1916
import pytest
20-
import yaml
21-
from dateutil.relativedelta import relativedelta
22-
from garf_core.query_editor import CommonParametersMixin
2317
from garf_executors.entrypoints import utils
2418

2519

26-
def test_convert_date():
27-
current_date = datetime.today()
28-
current_year = datetime(current_date.year, 1, 1)
29-
current_month = datetime(current_date.year, current_date.month, 1)
30-
last_year = current_year - relativedelta(years=1)
31-
last_month = current_month - relativedelta(months=1)
32-
yesterday = current_date - relativedelta(days=1)
33-
34-
non_macro_date = '2022-01-01'
35-
date_year = ':YYYY'
36-
date_month = ':YYYYMM'
37-
date_day = ':YYYYMMDD'
38-
date_year_minus_one = ':YYYY-1'
39-
date_month_minus_one = ':YYYYMM-1'
40-
date_day_minus_one = ':YYYYMMDD-1'
41-
42-
non_macro_date_converted = utils.convert_date(non_macro_date)
43-
new_date_year = utils.convert_date(date_year)
44-
new_date_month = utils.convert_date(date_month)
45-
new_date_day = utils.convert_date(date_day)
46-
new_date_year_minus_one = utils.convert_date(date_year_minus_one)
47-
new_date_month_minus_one = utils.convert_date(date_month_minus_one)
48-
new_date_day_minus_one = utils.convert_date(date_day_minus_one)
49-
50-
assert non_macro_date_converted == non_macro_date
51-
assert new_date_year == current_year.strftime('%Y-%m-%d')
52-
assert new_date_month == current_month.strftime('%Y-%m-%d')
53-
assert new_date_day == current_date.strftime('%Y-%m-%d')
54-
assert new_date_year_minus_one == last_year.strftime('%Y-%m-%d')
55-
assert new_date_month_minus_one == last_month.strftime('%Y-%m-%d')
56-
assert new_date_day_minus_one == yesterday.strftime('%Y-%m-%d')
57-
58-
59-
def test_wrong_convert_date():
60-
date_day = ':YYYYMMDD-N'
61-
with pytest.raises(ValueError):
62-
utils.convert_date(date_day)
63-
64-
6520
class TestParamsParser:
6621
@pytest.fixture
6722
def param_parser(self):

libs/executors/tests/unit/test_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def test_from_file_returns_correct_context_from_data(self, tmp_path):
2626
'start_date': '2025-01-01',
2727
},
2828
'template': {
29-
'cohorts': '1',
29+
'cohorts': 1,
3030
},
3131
},
3232
'fetcher_parameters': {
@@ -53,7 +53,7 @@ def test_save_returns_correct_data(self, tmp_path):
5353
'start_date': '2025-01-01',
5454
},
5555
'template': {
56-
'cohorts': '1',
56+
'cohorts': 1,
5757
},
5858
},
5959
'fetcher_parameters': {

libs/executors/tests/unit/test_execution_context.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_from_file_returns_correct_context_from_data(self, tmp_path):
3333
'start_date': '2025-01-01',
3434
},
3535
'template': {
36-
'cohorts': [1, 2, 3, 4],
36+
'cohorts': 1,
3737
},
3838
},
3939
'fetcher_parameters': {
@@ -58,7 +58,7 @@ def test_save_returns_correct_data(self, tmp_path):
5858
'start_date': '2025-01-01',
5959
},
6060
'template': {
61-
'cohorts': [1, 2, 3, 4],
61+
'cohorts': 1,
6262
},
6363
},
6464
'fetcher_parameters': {

0 commit comments

Comments
 (0)