garf-core 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {garf_core-0.2.0 → garf_core-0.2.2}/PKG-INFO +1 -1
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/__init__.py +1 -1
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/api_clients.py +3 -3
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/query_editor.py +56 -5
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/report_fetcher.py +1 -1
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/PKG-INFO +1 -1
- {garf_core-0.2.0 → garf_core-0.2.2}/README.md +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/base_query.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/exceptions.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/fetchers/__init__.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/fetchers/fake.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/fetchers/rest.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/parsers.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core/report.py +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/SOURCES.txt +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/dependency_links.txt +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/entry_points.txt +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/requires.txt +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/garf_core.egg-info/top_level.txt +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/pyproject.toml +0 -0
- {garf_core-0.2.0 → garf_core-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: garf-core
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: Abstracts fetching data from API based on provided SQL-like query.
|
5
5
|
Author-email: "Google Inc. (gTech gPS CSE team)" <no-reply@google.com>, Andrei Markin <andrey.markin.ppc@gmail.com>
|
6
6
|
License: Apache 2.0
|
@@ -20,12 +20,12 @@ import contextlib
|
|
20
20
|
import csv
|
21
21
|
import json
|
22
22
|
import os
|
23
|
-
import pathlib
|
24
23
|
from collections.abc import Sequence
|
25
24
|
from typing import Any, Union
|
26
25
|
|
27
26
|
import pydantic
|
28
27
|
import requests
|
28
|
+
import smart_open
|
29
29
|
from typing_extensions import TypeAlias, override
|
30
30
|
|
31
31
|
from garf_core import exceptions, query_editor
|
@@ -124,7 +124,7 @@ class FakeApiClient(BaseClient):
|
|
124
124
|
GarfApiError: When file with data not found.
|
125
125
|
"""
|
126
126
|
try:
|
127
|
-
with
|
127
|
+
with smart_open.open(file_location, 'r', encoding='utf-8') as f:
|
128
128
|
data = json.load(f)
|
129
129
|
return FakeApiClient(data)
|
130
130
|
except FileNotFoundError as e:
|
@@ -144,7 +144,7 @@ class FakeApiClient(BaseClient):
|
|
144
144
|
GarfApiError: When file with data not found.
|
145
145
|
"""
|
146
146
|
try:
|
147
|
-
with
|
147
|
+
with smart_open.open(file_location, 'r', encoding='utf-8') as f:
|
148
148
|
reader = csv.DictReader(f)
|
149
149
|
data = []
|
150
150
|
for row in reader:
|
@@ -29,7 +29,7 @@ from typing_extensions import Self, TypeAlias
|
|
29
29
|
|
30
30
|
from garf_core import exceptions
|
31
31
|
|
32
|
-
QueryParameters: TypeAlias = dict[str, Union[str, float, int]]
|
32
|
+
QueryParameters: TypeAlias = dict[str, Union[str, float, int, list]]
|
33
33
|
|
34
34
|
|
35
35
|
class GarfQueryParameters(pydantic.BaseModel):
|
@@ -386,7 +386,7 @@ class QuerySpecification(CommonParametersMixin, TemplateProcessorMixin):
|
|
386
386
|
self,
|
387
387
|
text: str,
|
388
388
|
title: str | None = None,
|
389
|
-
args: GarfQueryParameters | None =
|
389
|
+
args: GarfQueryParameters | None = None,
|
390
390
|
**kwargs: str,
|
391
391
|
) -> None:
|
392
392
|
"""Instantiates QuerySpecification based on text, title and optional args.
|
@@ -405,7 +405,10 @@ class QuerySpecification(CommonParametersMixin, TemplateProcessorMixin):
|
|
405
405
|
"""Returns macros with injected common parameters."""
|
406
406
|
common_params = dict(self.common_params)
|
407
407
|
if macros := self.args.macro:
|
408
|
-
|
408
|
+
converted_macros = {
|
409
|
+
key: convert_date(value) for key, value in macros.items()
|
410
|
+
}
|
411
|
+
common_params.update(converted_macros)
|
409
412
|
return common_params
|
410
413
|
|
411
414
|
def generate(self) -> BaseQueryElements:
|
@@ -441,14 +444,15 @@ class QuerySpecification(CommonParametersMixin, TemplateProcessorMixin):
|
|
441
444
|
"""Removes comments and converts text to lines."""
|
442
445
|
result: list[str] = []
|
443
446
|
multiline_comment = False
|
444
|
-
for
|
447
|
+
for raw_line in self.query.text.split('\n'):
|
448
|
+
line = raw_line.strip()
|
445
449
|
if re.match('\\*/', line):
|
446
450
|
multiline_comment = False
|
447
451
|
continue
|
448
452
|
if re.match('/\\*', line) or multiline_comment:
|
449
453
|
multiline_comment = True
|
450
454
|
continue
|
451
|
-
if re.match('^(#|--|//) ', line):
|
455
|
+
if re.match('^(#|--|//) ', line) or line in ('--', '#', '//'):
|
452
456
|
continue
|
453
457
|
cleaned_query_line = re.sub(
|
454
458
|
';$', '', re.sub('(--|//) .*$', '', line).strip()
|
@@ -564,3 +568,50 @@ def _is_invalid_field(field) -> bool:
|
|
564
568
|
is_constant = _is_constant(field)
|
565
569
|
has_operator = any(operator in field for operator in operators)
|
566
570
|
return is_constant or has_operator
|
571
|
+
|
572
|
+
|
573
|
+
def convert_date(date_string: str) -> str:
|
574
|
+
"""Converts specific dates parameters to actual dates.
|
575
|
+
|
576
|
+
Returns:
|
577
|
+
Date string in YYYY-MM-DD format.
|
578
|
+
|
579
|
+
Raises:
|
580
|
+
GarfMacroError:
|
581
|
+
If dynamic lookback value (:YYYYMMDD-N) is incorrect.
|
582
|
+
"""
|
583
|
+
if isinstance(date_string, list) or date_string.find(':Y') == -1:
|
584
|
+
return date_string
|
585
|
+
current_date = datetime.date.today()
|
586
|
+
base_date, *date_customizer = re.split('\\+|-', date_string)
|
587
|
+
if len(date_customizer) > 1:
|
588
|
+
raise GarfMacroError(
|
589
|
+
'Invalid format for date macro, should be in :YYYYMMDD-N format'
|
590
|
+
)
|
591
|
+
if not date_customizer:
|
592
|
+
days_lookback = 0
|
593
|
+
else:
|
594
|
+
try:
|
595
|
+
days_lookback = int(date_customizer[0])
|
596
|
+
except ValueError as e:
|
597
|
+
raise GarfMacroError(
|
598
|
+
'Must provide numeric value for a number lookback period, '
|
599
|
+
'i.e. :YYYYMMDD-1'
|
600
|
+
) from e
|
601
|
+
if base_date == ':YYYY':
|
602
|
+
new_date = datetime.datetime(current_date.year, 1, 1)
|
603
|
+
delta = relativedelta.relativedelta(years=days_lookback)
|
604
|
+
elif base_date == ':YYYYMM':
|
605
|
+
new_date = datetime.datetime(current_date.year, current_date.month, 1)
|
606
|
+
delta = relativedelta.relativedelta(months=days_lookback)
|
607
|
+
elif base_date == ':YYYYMMDD':
|
608
|
+
new_date = current_date
|
609
|
+
delta = relativedelta.relativedelta(days=days_lookback)
|
610
|
+
else:
|
611
|
+
raise GarfMacroError(
|
612
|
+
'Invalid format for date macro, should be in :YYYYMMDD-N format'
|
613
|
+
)
|
614
|
+
|
615
|
+
if '-' in date_string:
|
616
|
+
return (new_date - delta).strftime('%Y-%m-%d')
|
617
|
+
return (new_date + delta).strftime('%Y-%m-%d')
|
@@ -96,7 +96,7 @@ class ApiReportFetcher:
|
|
96
96
|
|
97
97
|
async def afetch(
|
98
98
|
self,
|
99
|
-
query_specification: str | query_editor.
|
99
|
+
query_specification: str | query_editor.QuerySpecification,
|
100
100
|
args: query_editor.GarfQueryParameters | None = None,
|
101
101
|
**kwargs: str,
|
102
102
|
) -> report.GarfReport:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: garf-core
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: Abstracts fetching data from API based on provided SQL-like query.
|
5
5
|
Author-email: "Google Inc. (gTech gPS CSE team)" <no-reply@google.com>, Andrei Markin <andrey.markin.ppc@gmail.com>
|
6
6
|
License: Apache 2.0
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|