garf-core 0.0.9__py3-none-any.whl → 0.0.11__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/api_clients.py +3 -1
- garf_core/exceptions.py +1 -1
- garf_core/query_editor.py +3 -3
- garf_core/report.py +68 -7
- {garf_core-0.0.9.dist-info → garf_core-0.0.11.dist-info}/METADATA +3 -3
- garf_core-0.0.11.dist-info/RECORD +13 -0
- {garf_core-0.0.9.dist-info → garf_core-0.0.11.dist-info}/WHEEL +1 -1
- garf_core-0.0.9.dist-info/RECORD +0 -13
- {garf_core-0.0.9.dist-info → garf_core-0.0.11.dist-info}/entry_points.txt +0 -0
- {garf_core-0.0.9.dist-info → garf_core-0.0.11.dist-info}/top_level.txt +0 -0
garf_core/__init__.py
CHANGED
garf_core/api_clients.py
CHANGED
@@ -22,6 +22,8 @@ from collections.abc import Sequence
|
|
22
22
|
import requests
|
23
23
|
from typing_extensions import override
|
24
24
|
|
25
|
+
from garf_core import exceptions
|
26
|
+
|
25
27
|
|
26
28
|
@dataclasses.dataclass
|
27
29
|
class GarfApiRequest:
|
@@ -35,7 +37,7 @@ class GarfApiResponse:
|
|
35
37
|
results: list
|
36
38
|
|
37
39
|
|
38
|
-
class GarfApiError(
|
40
|
+
class GarfApiError(exceptions.GarfError):
|
39
41
|
"""API specific exception."""
|
40
42
|
|
41
43
|
|
garf_core/exceptions.py
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
23
23
|
# See the License for the specific language governing permissions and
|
24
24
|
# limitations under the License.
|
25
|
-
"""
|
25
|
+
"""Defines common exceptions for the garf-core library."""
|
26
26
|
|
27
27
|
from __future__ import annotations
|
28
28
|
|
garf_core/query_editor.py
CHANGED
@@ -231,15 +231,14 @@ class BaseQueryElements:
|
|
231
231
|
"""Contains raw query and parsed elements.
|
232
232
|
|
233
233
|
Attributes:
|
234
|
-
|
235
|
-
|
234
|
+
title: Title of the query that needs to be parsed.
|
235
|
+
text: Text of the query that needs to be parsed.
|
236
236
|
resource_name: Name of Google Ads API reporting resource.
|
237
237
|
fields: Ads API fields that need to be fetched.
|
238
238
|
column_names: Friendly names for fields which are used when saving data
|
239
239
|
column_names: Friendly names for fields which are used when saving data
|
240
240
|
customizers: Attributes of fields that need to be be extracted.
|
241
241
|
virtual_columns: Attributes of fields that need to be be calculated.
|
242
|
-
is_constant_resource: Whether resource considered a constant one.
|
243
242
|
is_builtin_query: Whether query is built-in.
|
244
243
|
"""
|
245
244
|
|
@@ -256,6 +255,7 @@ class BaseQueryElements:
|
|
256
255
|
virtual_columns: dict[str, VirtualColumn] = dataclasses.field(
|
257
256
|
default_factory=dict
|
258
257
|
)
|
258
|
+
is_builtin_query: bool = False
|
259
259
|
|
260
260
|
def __eq__(self, other: BaseQueryElements) -> bool: # noqa: D105
|
261
261
|
return (
|
garf_core/report.py
CHANGED
@@ -27,7 +27,7 @@ import json
|
|
27
27
|
import warnings
|
28
28
|
from collections import defaultdict
|
29
29
|
from collections.abc import MutableSequence, Sequence
|
30
|
-
from typing import Generator, Literal
|
30
|
+
from typing import Generator, Literal, get_args
|
31
31
|
|
32
32
|
from garf_core import exceptions, parsers, query_editor
|
33
33
|
|
@@ -213,13 +213,22 @@ class GarfReport:
|
|
213
213
|
) from e
|
214
214
|
return pd.DataFrame(data=self.results, columns=self.column_names)
|
215
215
|
|
216
|
-
def
|
216
|
+
def to_jsonl(self) -> str:
|
217
|
+
"""Converts report to JSON Lines."""
|
218
|
+
return self.to_json(output='jsonl')
|
219
|
+
|
220
|
+
def to_json(self, output: Literal['json', 'jsonl'] = 'json') -> str:
|
217
221
|
"""Converts report to JSON.
|
218
222
|
|
223
|
+
Args:
|
224
|
+
output: Format of json file (json or jsonl).
|
225
|
+
|
219
226
|
Returns:
|
220
227
|
JSON from report results and column_names.
|
221
228
|
"""
|
222
|
-
|
229
|
+
if output == 'json':
|
230
|
+
return json.dumps(self.to_list(row_type='dict'))
|
231
|
+
return '\n'.join(json.dumps(row) for row in self.to_list(row_type='dict'))
|
223
232
|
|
224
233
|
def get_value(
|
225
234
|
self, column_index: int = 0, row_index: int = 0
|
@@ -414,8 +423,8 @@ class GarfReport:
|
|
414
423
|
import polars as pl
|
415
424
|
except ImportError as e:
|
416
425
|
raise ImportError(
|
417
|
-
'Please install garf-
|
418
|
-
'- `pip install garf-
|
426
|
+
'Please install garf-core with Polars support '
|
427
|
+
'- `pip install garf-core[polars]`'
|
419
428
|
) from e
|
420
429
|
return cls(
|
421
430
|
results=df.to_numpy().tolist(), column_names=list(df.schema.keys())
|
@@ -438,11 +447,63 @@ class GarfReport:
|
|
438
447
|
import pandas as pd
|
439
448
|
except ImportError as e:
|
440
449
|
raise ImportError(
|
441
|
-
'Please install garf-
|
442
|
-
'- `pip install garf-
|
450
|
+
'Please install garf-core with Pandas support '
|
451
|
+
'- `pip install garf-core[pandas]`'
|
443
452
|
) from e
|
444
453
|
return cls(results=df.values.tolist(), column_names=list(df.columns.values))
|
445
454
|
|
455
|
+
@classmethod
|
456
|
+
def from_json(cls, json_str: str) -> GarfReport:
|
457
|
+
"""Creates a GarfReport object from a JSON string.
|
458
|
+
|
459
|
+
Args:
|
460
|
+
json_str: JSON string representation of the data.
|
461
|
+
|
462
|
+
Returns:
|
463
|
+
Report build from a json string.
|
464
|
+
|
465
|
+
Raises:
|
466
|
+
TypeError: If any value in the JSON data is not a supported type.
|
467
|
+
ValueError: If `data` is a list but not all dictionaries
|
468
|
+
have the same keys.
|
469
|
+
"""
|
470
|
+
data = json.loads(json_str)
|
471
|
+
|
472
|
+
def validate_value(value):
|
473
|
+
if not isinstance(value, get_args(parsers.ApiRowElement)):
|
474
|
+
raise TypeError(
|
475
|
+
f'Unsupported type {type(value)} for value {value}. '
|
476
|
+
'Expected types: int, float, str, bool, list, or None.'
|
477
|
+
)
|
478
|
+
return value
|
479
|
+
|
480
|
+
# Case 1: `data` is a dictionary
|
481
|
+
if isinstance(data, dict):
|
482
|
+
column_names = list(data.keys())
|
483
|
+
if not data.values():
|
484
|
+
results = []
|
485
|
+
else:
|
486
|
+
results = [[validate_value(value) for value in data.values()]]
|
487
|
+
|
488
|
+
# Case 2: `data` is a list of dictionaries, each representing a row
|
489
|
+
elif isinstance(data, list):
|
490
|
+
column_names = list(data[0].keys()) if data else []
|
491
|
+
for row in data:
|
492
|
+
if not isinstance(row, dict):
|
493
|
+
raise TypeError('All elements in the list must be dictionaries.')
|
494
|
+
if list(row.keys()) != column_names:
|
495
|
+
raise ValueError(
|
496
|
+
'All dictionaries must have consistent keys in the same order.'
|
497
|
+
)
|
498
|
+
results = [
|
499
|
+
[validate_value(value) for value in row.values()] for row in data
|
500
|
+
]
|
501
|
+
else:
|
502
|
+
raise TypeError(
|
503
|
+
'Input JSON must be a dictionary or a list of dictionaries.'
|
504
|
+
)
|
505
|
+
return cls(results=results, column_names=column_names)
|
506
|
+
|
446
507
|
|
447
508
|
class GarfRow:
|
448
509
|
"""Helper class to simplify iteration of GarfReport.
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: garf-core
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.11
|
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>
|
6
6
|
License: Apache 2.0
|
@@ -18,7 +18,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
18
18
|
Requires-Python: >=3.8
|
19
19
|
Description-Content-Type: text/markdown
|
20
20
|
Requires-Dist: python-dateutil
|
21
|
-
Requires-Dist: jinja2
|
21
|
+
Requires-Dist: jinja2
|
22
22
|
Requires-Dist: typing-extensions
|
23
23
|
Requires-Dist: requests
|
24
24
|
Requires-Dist: pyyaml
|
@@ -0,0 +1,13 @@
|
|
1
|
+
garf_core/__init__.py,sha256=yZsVFJAqd5GWGzVYTAL99fSC0wOE6wdiWLQQmnzjw58,599
|
2
|
+
garf_core/api_clients.py,sha256=S7Ldmgf0H9Vew2BdfG3Yh6imkhdnr8ix_YHKJmGln-U,2280
|
3
|
+
garf_core/base_query.py,sha256=ZDAw2ojmismXRO0HXEvKDukpS7OAc7390LnM8kvCSCY,1201
|
4
|
+
garf_core/exceptions.py,sha256=Gzvkl2M-rA_XQRAMd3CC62KHeFQE_b6uby0fD0pouw4,1269
|
5
|
+
garf_core/parsers.py,sha256=Uj7aT7roYUofivrBKVKWfcSeG37oYGGLIJ-op2C3hZc,3238
|
6
|
+
garf_core/query_editor.py,sha256=MVfrZdNlJ0EBDPc2ux7XxSArRyOo1QoXvrnKYn6aHTs,16381
|
7
|
+
garf_core/report.py,sha256=2z4tUR5mg29CkwodGaFIXs8Vpo2DCyyzwJWXhBrT0R4,19910
|
8
|
+
garf_core/report_fetcher.py,sha256=1PtJ8iQwlEcUeW6k1lVZiXh-3hop5x2YHQe-EaKYGNs,4405
|
9
|
+
garf_core-0.0.11.dist-info/METADATA,sha256=MtxuWeqHxia8dJB76s_ZgO6rIXBt7XGN6ZzGzj7brTk,2425
|
10
|
+
garf_core-0.0.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
11
|
+
garf_core-0.0.11.dist-info/entry_points.txt,sha256=ODxSZXwWEIXQAjRwBQsBJ6GYw8oPK6DYTo0oDVkKCpo,39
|
12
|
+
garf_core-0.0.11.dist-info/top_level.txt,sha256=Gj-Zp7fM2turGut5vTJuo5xEcKfow7cTLX3y3WFfNgA,10
|
13
|
+
garf_core-0.0.11.dist-info/RECORD,,
|
garf_core-0.0.9.dist-info/RECORD
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
garf_core/__init__.py,sha256=yBtrakQ_ly__BHdpAtsmIizhSJvauvxZNhDIafLx3b8,598
|
2
|
-
garf_core/api_clients.py,sha256=CIwiBHTwPb5aaGdcy4shkREEMxKTkWbYdpzXddNs6MI,2235
|
3
|
-
garf_core/base_query.py,sha256=ZDAw2ojmismXRO0HXEvKDukpS7OAc7390LnM8kvCSCY,1201
|
4
|
-
garf_core/exceptions.py,sha256=7HvJPFSUKQCfhlAZ8KYxZKi6Zf-Ifa73PPz48iFFWHk,1248
|
5
|
-
garf_core/parsers.py,sha256=Uj7aT7roYUofivrBKVKWfcSeG37oYGGLIJ-op2C3hZc,3238
|
6
|
-
garf_core/query_editor.py,sha256=uOai_N5MeU0hEv8fVSMDsH-sw57rpHiHi83xM1nAG9M,16432
|
7
|
-
garf_core/report.py,sha256=4ZsqeyNyZFu46CQQJEFDL7y-_8i1tqqyjljv6cOMXPI,17852
|
8
|
-
garf_core/report_fetcher.py,sha256=1PtJ8iQwlEcUeW6k1lVZiXh-3hop5x2YHQe-EaKYGNs,4405
|
9
|
-
garf_core-0.0.9.dist-info/METADATA,sha256=d19Mcu0BeoKVyAxQzNArvmqdSgBWZaQjEImi7T3vEpM,2431
|
10
|
-
garf_core-0.0.9.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
11
|
-
garf_core-0.0.9.dist-info/entry_points.txt,sha256=ODxSZXwWEIXQAjRwBQsBJ6GYw8oPK6DYTo0oDVkKCpo,39
|
12
|
-
garf_core-0.0.9.dist-info/top_level.txt,sha256=Gj-Zp7fM2turGut5vTJuo5xEcKfow7cTLX3y3WFfNgA,10
|
13
|
-
garf_core-0.0.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|