garf-core 0.0.9__tar.gz → 0.0.11__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: garf-core
3
- Version: 0.0.9
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==3.1.4
21
+ Requires-Dist: jinja2
22
22
  Requires-Dist: typing-extensions
23
23
  Requires-Dist: requests
24
24
  Requires-Dist: pyyaml
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = '0.0.9'
15
+ __version__ = '0.0.11'
@@ -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(Exception):
40
+ class GarfApiError(exceptions.GarfError):
39
41
  """API specific exception."""
40
42
 
41
43
 
@@ -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
- """Module for defining exceptions."""
25
+ """Defines common exceptions for the garf-core library."""
26
26
 
27
27
  from __future__ import annotations
28
28
 
@@ -231,15 +231,14 @@ class BaseQueryElements:
231
231
  """Contains raw query and parsed elements.
232
232
 
233
233
  Attributes:
234
- query_title: Title of the query that needs to be parsed.
235
- query_text: Text of the query that needs to be parsed.
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 (
@@ -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 to_json(self) -> str:
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
- return json.dumps(self.to_list(row_type='dict'))
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-io with Polars support '
418
- '- `pip install garf-io[polars]`'
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-io with Pandas support '
442
- '- `pip install garf-io[pandas]`'
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.2
1
+ Metadata-Version: 2.4
2
2
  Name: garf-core
3
- Version: 0.0.9
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==3.1.4
21
+ Requires-Dist: jinja2
22
22
  Requires-Dist: typing-extensions
23
23
  Requires-Dist: requests
24
24
  Requires-Dist: pyyaml
@@ -1,5 +1,5 @@
1
1
  python-dateutil
2
- jinja2==3.1.4
2
+ jinja2
3
3
  typing-extensions
4
4
  requests
5
5
  pyyaml
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  name = "garf-core"
7
7
  dependencies = [
8
8
  "python-dateutil",
9
- "jinja2==3.1.4",
9
+ "jinja2",
10
10
  "typing-extensions",
11
11
  "requests",
12
12
  "pyyaml",
File without changes
File without changes