garf-core 0.2.0__tar.gz → 0.2.1__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
1
  Metadata-Version: 2.4
2
2
  Name: garf-core
3
- Version: 0.2.0
3
+ Version: 0.2.1
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
@@ -26,4 +26,4 @@ __all__ = [
26
26
  'ApiReportFetcher',
27
27
  ]
28
28
 
29
- __version__ = '0.2.0'
29
+ __version__ = '0.2.1'
@@ -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 pathlib.Path.open(file_location, 'r', encoding='utf-8') as f:
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 pathlib.Path.open(file_location, 'r', encoding='utf-8') as f:
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 = GarfQueryParameters(),
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
- 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)
409
412
  return common_params
410
413
 
411
414
  def generate(self) -> BaseQueryElements:
@@ -564,3 +567,50 @@ def _is_invalid_field(field) -> bool:
564
567
  is_constant = _is_constant(field)
565
568
  has_operator = any(operator in field for operator in operators)
566
569
  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')
@@ -96,7 +96,7 @@ class ApiReportFetcher:
96
96
 
97
97
  async def afetch(
98
98
  self,
99
- query_specification: str | query_editor.QueryElements,
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.0
3
+ Version: 0.2.1
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