garf-core 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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/__init__.py +1 -1
- garf_core/parsers.py +9 -6
- garf_core/report.py +25 -19
- garf_core/report_fetcher.py +13 -2
- {garf_core-0.1.1.dist-info → garf_core-0.1.2.dist-info}/METADATA +1 -1
- {garf_core-0.1.1.dist-info → garf_core-0.1.2.dist-info}/RECORD +9 -9
- {garf_core-0.1.1.dist-info → garf_core-0.1.2.dist-info}/WHEEL +0 -0
- {garf_core-0.1.1.dist-info → garf_core-0.1.2.dist-info}/entry_points.txt +0 -0
- {garf_core-0.1.1.dist-info → garf_core-0.1.2.dist-info}/top_level.txt +0 -0
garf_core/__init__.py
CHANGED
garf_core/parsers.py
CHANGED
@@ -32,21 +32,26 @@ ApiRowElement: TypeAlias = Union[int, float, str, bool, list, None]
|
|
32
32
|
class BaseParser(abc.ABC):
|
33
33
|
"""An interface for all parsers to implement."""
|
34
34
|
|
35
|
+
def __init__(
|
36
|
+
self, query_specification: query_editor.BaseQueryElements
|
37
|
+
) -> None:
|
38
|
+
"""Initializes BaseParser."""
|
39
|
+
self.query_spec = query_specification
|
40
|
+
|
35
41
|
def parse_response(
|
36
42
|
self,
|
37
43
|
response: api_clients.GarfApiResponse,
|
38
|
-
query_specification: query_editor.BaseQueryElements,
|
39
44
|
) -> list[list[ApiRowElement]]:
|
40
45
|
"""Parses response."""
|
41
46
|
if not response.results:
|
42
47
|
return [[]]
|
43
48
|
results = []
|
44
49
|
for result in response.results:
|
45
|
-
results.append(self.parse_row(result
|
50
|
+
results.append(self.parse_row(result))
|
46
51
|
return results
|
47
52
|
|
48
53
|
@abc.abstractmethod
|
49
|
-
def parse_row(self, row
|
54
|
+
def parse_row(self, row):
|
50
55
|
"""Parses single row from response."""
|
51
56
|
|
52
57
|
|
@@ -57,7 +62,6 @@ class ListParser(BaseParser):
|
|
57
62
|
def parse_row(
|
58
63
|
self,
|
59
64
|
row: list,
|
60
|
-
query_specification: query_editor.BaseQueryElements,
|
61
65
|
) -> list[list[ApiRowElement]]:
|
62
66
|
return row
|
63
67
|
|
@@ -69,12 +73,11 @@ class DictParser(BaseParser):
|
|
69
73
|
def parse_row(
|
70
74
|
self,
|
71
75
|
row: list,
|
72
|
-
query_specification: query_editor.BaseQueryElements,
|
73
76
|
) -> list[list[ApiRowElement]]:
|
74
77
|
if not isinstance(row, Mapping):
|
75
78
|
raise GarfParserError
|
76
79
|
result = []
|
77
|
-
for field in
|
80
|
+
for field in self.query_spec.fields:
|
78
81
|
result.append(self.get_nested_field(row, field))
|
79
82
|
return result
|
80
83
|
|
garf_core/report.py
CHANGED
@@ -45,25 +45,25 @@ class GarfReport:
|
|
45
45
|
|
46
46
|
def __init__(
|
47
47
|
self,
|
48
|
-
results: Sequence[Sequence[parsers.ApiRowElement]],
|
49
|
-
column_names: Sequence[str],
|
48
|
+
results: Sequence[Sequence[parsers.ApiRowElement]] | None = None,
|
49
|
+
column_names: Sequence[str] | None = None,
|
50
50
|
results_placeholder: Sequence[Sequence[parsers.ApiRowElement]]
|
51
51
|
| None = None,
|
52
|
-
query_specification: query_editor.
|
52
|
+
query_specification: query_editor.BaseQueryElements | None = None,
|
53
53
|
auto_convert_to_scalars: bool = True,
|
54
54
|
) -> None:
|
55
55
|
"""Initializes GarfReport from API response.
|
56
56
|
|
57
57
|
Args:
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
results: Contains data from Ads API in a form of nested list
|
59
|
+
column_names: Maps in each element in sublist of results to name.
|
60
|
+
results_placeholder: Optional placeholder values for missing results.
|
61
|
+
query_specification: Specification used to get data from Ads API.
|
62
|
+
auto_convert_to_scalars: Whether to simplify slicing operations.
|
63
63
|
"""
|
64
|
-
self.results = results
|
65
|
-
self.column_names = column_names
|
66
|
-
self._multi_column_report = len(column_names) > 1
|
64
|
+
self.results = results or []
|
65
|
+
self.column_names = column_names or []
|
66
|
+
self._multi_column_report = len(column_names) > 1 if column_names else False
|
67
67
|
if results_placeholder:
|
68
68
|
self.results_placeholder = list(results_placeholder)
|
69
69
|
else:
|
@@ -238,6 +238,8 @@ class GarfReport:
|
|
238
238
|
Raises:
|
239
239
|
GarfReportError: If row or column index are out of bounds.
|
240
240
|
"""
|
241
|
+
if not self:
|
242
|
+
raise GarfReportError('Cannot get value from an empty report')
|
241
243
|
if column_index >= len(self.column_names):
|
242
244
|
raise GarfReportError(
|
243
245
|
'Column %d of report is not found; report contains only %d columns.',
|
@@ -298,6 +300,10 @@ class GarfReport:
|
|
298
300
|
Raises:
|
299
301
|
GarfReportError: When incorrect column_name specified.
|
300
302
|
"""
|
303
|
+
if not self:
|
304
|
+
if isinstance(key, (MutableSequence, str)):
|
305
|
+
raise GarfReportError(f"Cannot get '{key}' from an empty report")
|
306
|
+
raise GarfReportError('Cannot get element from an empty report')
|
301
307
|
if isinstance(key, (MutableSequence, str)):
|
302
308
|
return self._get_columns_slice(key)
|
303
309
|
return self._get_rows_slice(key)
|
@@ -342,6 +348,8 @@ class GarfReport:
|
|
342
348
|
Raises:
|
343
349
|
GarfReportError: When incorrect column_name specified.
|
344
350
|
"""
|
351
|
+
if not self:
|
352
|
+
return self
|
345
353
|
if isinstance(key, str):
|
346
354
|
key = [key]
|
347
355
|
if set(key).issubset(set(self.column_names)):
|
@@ -356,15 +364,13 @@ class GarfReport:
|
|
356
364
|
results.append(rows)
|
357
365
|
# TODO: propagate placeholders and query specification to new report
|
358
366
|
return GarfReport(results, key)
|
359
|
-
non_existing_keys = set(key).
|
367
|
+
non_existing_keys = set(key).difference(set(self.column_names))
|
360
368
|
if len(non_existing_keys) > 1:
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
message =
|
366
|
-
f"Column '{non_existing_keys.pop()}' " 'cannot be found in the report'
|
367
|
-
)
|
369
|
+
missing_columns = ', '.join(list(non_existing_keys))
|
370
|
+
else:
|
371
|
+
missing_columns = non_existing_keys.pop()
|
372
|
+
|
373
|
+
message = f"Columns '{missing_columns}' cannot be found in the report"
|
368
374
|
raise GarfReportError(message)
|
369
375
|
|
370
376
|
def __eq__(self, other) -> bool:
|
garf_core/report_fetcher.py
CHANGED
@@ -39,6 +39,17 @@ class ApiReportFetcherError(exceptions.GarfError):
|
|
39
39
|
"""Base exception for all ApiReportFetchers."""
|
40
40
|
|
41
41
|
|
42
|
+
class MissingApiReportFetcherError(ApiReportFetcherError):
|
43
|
+
"""Exception for not found report fetcher."""
|
44
|
+
|
45
|
+
def __init__(self, source: str) -> None:
|
46
|
+
"""Initializes MissingApiReportFetcherError."""
|
47
|
+
self.source = source
|
48
|
+
|
49
|
+
def __str__(self) -> str:
|
50
|
+
return f'No fetcher available for the source "{self.source}"'
|
51
|
+
|
52
|
+
|
42
53
|
class ApiReportFetcher:
|
43
54
|
"""Class responsible for getting data from report API.
|
44
55
|
|
@@ -69,7 +80,7 @@ class ApiReportFetcher:
|
|
69
80
|
Mapping between query name and function for generating GarfReport.
|
70
81
|
"""
|
71
82
|
self.api_client = api_client
|
72
|
-
self.parser = parser
|
83
|
+
self.parser = parser
|
73
84
|
self.query_specification_builder = query_specification_builder
|
74
85
|
self.query_args = kwargs
|
75
86
|
self.builtin_queries = builtin_queries or {}
|
@@ -135,7 +146,7 @@ class ApiReportFetcher:
|
|
135
146
|
return builtin_report(self, **kwargs)
|
136
147
|
|
137
148
|
response = self.api_client.get_response(query, **kwargs)
|
138
|
-
parsed_response = self.parser.parse_response(response
|
149
|
+
parsed_response = self.parser(query).parse_response(response)
|
139
150
|
return report.GarfReport(
|
140
151
|
results=parsed_response, column_names=query.column_names
|
141
152
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: garf-core
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.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
|
@@ -1,16 +1,16 @@
|
|
1
|
-
garf_core/__init__.py,sha256=
|
1
|
+
garf_core/__init__.py,sha256=GIvTQD8k9INUEIQO5HBfLShtzbins5jjqQfi0z2J9h0,598
|
2
2
|
garf_core/api_clients.py,sha256=k4vjFsA3JOx7Hp_pXIQNwzMXqq4u4dDcz-ddkJC0JhI,4651
|
3
3
|
garf_core/base_query.py,sha256=ZDAw2ojmismXRO0HXEvKDukpS7OAc7390LnM8kvCSCY,1201
|
4
4
|
garf_core/exceptions.py,sha256=Gzvkl2M-rA_XQRAMd3CC62KHeFQE_b6uby0fD0pouw4,1269
|
5
|
-
garf_core/parsers.py,sha256
|
5
|
+
garf_core/parsers.py,sha256=-178UL2hbrC83GkMqAPgrmpGpJitR7T9xT3OgJ8w16Y,3189
|
6
6
|
garf_core/query_editor.py,sha256=lA1aMFc_1wo6e2CyVISarnZxtH2jnKkuZfJecQqslDg,17191
|
7
|
-
garf_core/report.py,sha256=
|
8
|
-
garf_core/report_fetcher.py,sha256=
|
7
|
+
garf_core/report.py,sha256=O0SPxgVQckxi_QuD3vy1h_1mi5aMjQNw0LYP-jgN2Do,20262
|
8
|
+
garf_core/report_fetcher.py,sha256=dgErrkRv1bdPN0npX3atZbJrOpzy9OmoPC38e2FpBSQ,4883
|
9
9
|
garf_core/fetchers/__init__.py,sha256=_cSjg1D5RhUKxaVeVbaDdb8AAoI9glKJXgN5H4qXFkw,783
|
10
10
|
garf_core/fetchers/fake.py,sha256=fgJjxuHyd6EIUflUtj8r_HfaMS1YTDrOqDlaj6Kvbjs,2584
|
11
11
|
garf_core/fetchers/rest.py,sha256=-5B2-Ck_t3hG99ym59AKwlzctiDxRFI2Nnc8STBxRDo,2201
|
12
|
-
garf_core-0.1.
|
13
|
-
garf_core-0.1.
|
14
|
-
garf_core-0.1.
|
15
|
-
garf_core-0.1.
|
16
|
-
garf_core-0.1.
|
12
|
+
garf_core-0.1.2.dist-info/METADATA,sha256=dGX17SeAetNBUlagvqWDGHX4yzIura36Bl1CP0Bd0OU,2443
|
13
|
+
garf_core-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
14
|
+
garf_core-0.1.2.dist-info/entry_points.txt,sha256=u4h-ujHO1hbxVXRQzwcC4ftju9_KBYtq5mCLKEBHMj0,69
|
15
|
+
garf_core-0.1.2.dist-info/top_level.txt,sha256=Gj-Zp7fM2turGut5vTJuo5xEcKfow7cTLX3y3WFfNgA,10
|
16
|
+
garf_core-0.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|