findly.unified-reporting-sdk 0.7.13__tar.gz → 0.7.15__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.
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/PKG-INFO +3 -4
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/common_parser.py +78 -7
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/fb_ads_query_args_parser.py +0 -9
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/ga4/ga4_query_args_parser.py +2 -2
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/protos/findly_semantic_layer_pb2.pyi +29 -29
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/pyproject.toml +3 -4
- findly_unified_reporting_sdk-0.7.13/findly/unified_reporting_sdk/util/__init__.py +0 -0
- findly_unified_reporting_sdk-0.7.13/findly/unified_reporting_sdk/util/create_numeric_string_series.py +0 -16
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/LICENSE +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/README.md +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/date_range_helper.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/entities.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/reports_client.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/common/where_string_comparison.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/fb_ads_client.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/metadata/action_breakdowns.csv +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/metadata/breakdowns.csv +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/metadata/dimensions.jsonl +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/metadata/fields.csv +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/fb_ads/metadata/metrics.jsonl +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/ga4/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/ga4/ga4_client.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/ga4/metadata/dimensions.jsonl +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/gsc/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/gsc/gsc_client.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/data_sources/gsc/gsc_service.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/protos/.gitignore +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/protos/__init__.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/protos/findly_semantic_layer_pb2.py +0 -0
- {findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/unified_reporting_sdk/urs.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: findly.unified-reporting-sdk
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.15
|
|
4
4
|
Summary:
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
Requires-Python: >=3.9,<4.0
|
|
@@ -17,9 +17,8 @@ Requires-Dist: facebook-business (>=22.0.5,<23.0.0)
|
|
|
17
17
|
Requires-Dist: google-analytics-admin (>=0.24.0,<0.25.0)
|
|
18
18
|
Requires-Dist: google-analytics-data (>=0.18.18,<0.19.0)
|
|
19
19
|
Requires-Dist: google-api-python-client (>=2.169.0,<3.0.0)
|
|
20
|
-
Requires-Dist: oauth2client (>=4.1.3,<5.0.0)
|
|
21
20
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
22
|
-
Requires-Dist: protobuf (>=
|
|
21
|
+
Requires-Dist: protobuf (>=6.33.5,<7.0.0)
|
|
23
22
|
Requires-Dist: sqlglot (>=26.17.1,<27.0.0)
|
|
24
23
|
Description-Content-Type: text/markdown
|
|
25
24
|
|
|
@@ -2,6 +2,7 @@ import re
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from dateutil.relativedelta import relativedelta
|
|
5
|
+
import sqlglot
|
|
5
6
|
|
|
6
7
|
from typing import List, Optional, Callable, Tuple, Any, TypedDict
|
|
7
8
|
from findly.unified_reporting_sdk.protos.findly_semantic_layer_pb2 import (
|
|
@@ -16,6 +17,60 @@ from findly.unified_reporting_sdk.data_sources.common.date_range_helper import (
|
|
|
16
17
|
NONE_VALUE = "none"
|
|
17
18
|
RESERVED_TOTAL = "RESERVED_TOTAL"
|
|
18
19
|
|
|
20
|
+
# Regex pattern for valid SQL identifiers
|
|
21
|
+
_IDENTIFIER_PATTERN = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
|
|
22
|
+
# Regex pattern for ORDER BY column (identifier with optional ASC/DESC)
|
|
23
|
+
_ORDER_BY_PATTERN = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_]*)\s*(ASC|DESC)?$", re.IGNORECASE)
|
|
24
|
+
# Regex pattern for YYYY-MM-DD date format
|
|
25
|
+
_DATE_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _validate_identifier(name: str) -> str:
|
|
29
|
+
"""Validate that a string is a safe SQL identifier."""
|
|
30
|
+
if not _IDENTIFIER_PATTERN.match(name):
|
|
31
|
+
raise ValueError(f"Invalid SQL identifier: {name!r}")
|
|
32
|
+
return name
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _validate_order_by_column(col: str) -> str:
|
|
36
|
+
"""Validate an ORDER BY column (identifier with optional ASC/DESC)."""
|
|
37
|
+
if not _ORDER_BY_PATTERN.match(col):
|
|
38
|
+
raise ValueError(f"Invalid ORDER BY column: {col!r}")
|
|
39
|
+
return col
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _validate_date(date_str: str) -> str:
|
|
43
|
+
"""Validate that a string is a valid YYYY-MM-DD date."""
|
|
44
|
+
if not _DATE_PATTERN.match(date_str):
|
|
45
|
+
raise ValueError(f"Invalid date format (expected YYYY-MM-DD): {date_str!r}")
|
|
46
|
+
return date_str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _validate_positive_integer(value: Any) -> int:
|
|
50
|
+
"""Validate that a value is a positive integer."""
|
|
51
|
+
try:
|
|
52
|
+
int_value = int(value)
|
|
53
|
+
except (TypeError, ValueError) as e:
|
|
54
|
+
raise ValueError(f"Invalid integer value: {value!r}") from e
|
|
55
|
+
if int_value <= 0:
|
|
56
|
+
raise ValueError(f"Value must be positive: {int_value}")
|
|
57
|
+
return int_value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _validate_sql_clause(clause: str, clause_type: str) -> str:
|
|
61
|
+
"""Validate a SQL clause (WHERE/HAVING) using sqlglot parsing."""
|
|
62
|
+
if not clause or not clause.strip():
|
|
63
|
+
raise ValueError(f"Empty {clause_type} clause")
|
|
64
|
+
# Wrap in a minimal SELECT statement to validate the clause
|
|
65
|
+
test_query = f"SELECT 1 FROM t {clause_type} {clause}"
|
|
66
|
+
try:
|
|
67
|
+
parsed = sqlglot.parse(test_query)
|
|
68
|
+
if not parsed or parsed[0] is None:
|
|
69
|
+
raise ValueError(f"Failed to parse {clause_type} clause: {clause!r}")
|
|
70
|
+
except sqlglot.errors.ParseError as e:
|
|
71
|
+
raise ValueError(f"Invalid {clause_type} clause: {clause!r}") from e
|
|
72
|
+
return clause
|
|
73
|
+
|
|
19
74
|
|
|
20
75
|
class DefaultFormattedDateRange(TypedDict):
|
|
21
76
|
since: str
|
|
@@ -102,17 +157,24 @@ class CommonParser:
|
|
|
102
157
|
|
|
103
158
|
Returns:
|
|
104
159
|
str: The SQL query generated from the QueryArgs object.
|
|
160
|
+
|
|
161
|
+
Raises:
|
|
162
|
+
ValueError: If any input fails validation.
|
|
105
163
|
"""
|
|
106
164
|
query_parts = []
|
|
107
165
|
|
|
108
166
|
select_parts = []
|
|
109
167
|
# Metrics and Metrics Expression
|
|
110
168
|
if query.metrics:
|
|
111
|
-
|
|
169
|
+
validated_metrics = [_validate_identifier(m) for m in query.metrics]
|
|
170
|
+
select_parts.append(", ".join(validated_metrics))
|
|
112
171
|
elif query.metrics_expression:
|
|
113
172
|
select_parts.append(", ".join(query.metrics_expression))
|
|
114
173
|
if query.group_by_columns:
|
|
115
|
-
|
|
174
|
+
validated_group_cols = [
|
|
175
|
+
_validate_identifier(col) for col in query.group_by_columns
|
|
176
|
+
]
|
|
177
|
+
select_parts.append(", ".join(validated_group_cols))
|
|
116
178
|
if len(select_parts) > 0:
|
|
117
179
|
select_str = "SELECT " + ", ".join(select_parts)
|
|
118
180
|
query_parts.append(select_str)
|
|
@@ -123,13 +185,14 @@ class CommonParser:
|
|
|
123
185
|
where_clause_modified = re.sub(
|
|
124
186
|
r"\bwhere\b", "", query.where_clause, flags=re.IGNORECASE
|
|
125
187
|
).strip()
|
|
188
|
+
_validate_sql_clause(where_clause_modified, "WHERE")
|
|
126
189
|
conditions.append(f"WHERE {where_clause_modified}")
|
|
127
190
|
|
|
128
191
|
# Date Where clause
|
|
129
192
|
if query.date_ranges:
|
|
130
193
|
for date_range in list(query.date_ranges):
|
|
131
|
-
start_date = date_range.start_date
|
|
132
|
-
end_date = date_range.end_date
|
|
194
|
+
start_date = _validate_date(date_range.start_date)
|
|
195
|
+
end_date = _validate_date(date_range.end_date)
|
|
133
196
|
# Check if start and end dates are the same
|
|
134
197
|
if start_date == end_date:
|
|
135
198
|
# Use equality condition when dates are the same
|
|
@@ -143,7 +206,10 @@ class CommonParser:
|
|
|
143
206
|
|
|
144
207
|
# Group By
|
|
145
208
|
if query.group_by_columns:
|
|
146
|
-
|
|
209
|
+
validated_group_cols = [
|
|
210
|
+
_validate_identifier(col) for col in query.group_by_columns
|
|
211
|
+
]
|
|
212
|
+
group_by_str = ", ".join(validated_group_cols)
|
|
147
213
|
query_parts.append(f"GROUP BY {group_by_str}")
|
|
148
214
|
|
|
149
215
|
# Having
|
|
@@ -151,16 +217,21 @@ class CommonParser:
|
|
|
151
217
|
having_clause_modified = re.sub(
|
|
152
218
|
r"\bhaving\b", "", query.having_clause, flags=re.IGNORECASE
|
|
153
219
|
).strip()
|
|
220
|
+
_validate_sql_clause(having_clause_modified, "HAVING")
|
|
154
221
|
query_parts.append(f"HAVING {having_clause_modified}")
|
|
155
222
|
|
|
156
223
|
# Order By
|
|
157
224
|
if query.order_by:
|
|
158
|
-
|
|
225
|
+
validated_order_cols = [
|
|
226
|
+
_validate_order_by_column(col) for col in query.order_by
|
|
227
|
+
]
|
|
228
|
+
order_by_str = ", ".join(validated_order_cols)
|
|
159
229
|
query_parts.append(f"ORDER BY {order_by_str}")
|
|
160
230
|
|
|
161
231
|
# Limit
|
|
162
232
|
if query.limit:
|
|
163
|
-
|
|
233
|
+
validated_limit = _validate_positive_integer(query.limit)
|
|
234
|
+
query_parts.append(f"LIMIT {validated_limit}")
|
|
164
235
|
|
|
165
236
|
return "\n".join(query_parts)
|
|
166
237
|
|
|
@@ -11,10 +11,6 @@ from findly.unified_reporting_sdk.data_sources.common.common_parser import (
|
|
|
11
11
|
RESERVED_TOTAL,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from findly.unified_reporting_sdk.util.create_numeric_string_series import (
|
|
15
|
-
create_numeric_string_series,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
14
|
from findly.unified_reporting_sdk.data_sources.common.where_string_comparison import (
|
|
19
15
|
parse_where_column_condition,
|
|
20
16
|
WhereClauseInformation,
|
|
@@ -333,8 +329,6 @@ class FbAdsQueryArgsParser(CommonParser):
|
|
|
333
329
|
|
|
334
330
|
# remove duplicates from explosions and reset index
|
|
335
331
|
df = df.drop_duplicates().reset_index(drop=True)
|
|
336
|
-
# Also cap float columns to 2 decimal places
|
|
337
|
-
df = df.apply(create_numeric_string_series)
|
|
338
332
|
|
|
339
333
|
# At last, return that if there is only one date range,
|
|
340
334
|
# else, we need to split the dataframe into multiple dataframes
|
|
@@ -555,9 +549,6 @@ class FbAdsQueryArgsParser(CommonParser):
|
|
|
555
549
|
# Keep only the first row
|
|
556
550
|
summary_df = summary_df.head(1)
|
|
557
551
|
|
|
558
|
-
# Also cap float columns to 2 decimal places
|
|
559
|
-
summary_df = summary_df.apply(create_numeric_string_series)
|
|
560
|
-
|
|
561
552
|
return [summary_df]
|
|
562
553
|
|
|
563
554
|
async def parse_query_args_to_request_params(
|
|
@@ -83,14 +83,14 @@ class GA4QueryArgsParser(CommonParser):
|
|
|
83
83
|
# Check if the metric header ends with 'Rate'
|
|
84
84
|
if header.endswith("Rate"):
|
|
85
85
|
# Convert the value to a float and multiply by 100
|
|
86
|
-
value_number =
|
|
86
|
+
value_number = float(value) * 100
|
|
87
87
|
else:
|
|
88
88
|
# Cap float values to 2 decimal places
|
|
89
89
|
value_number = float(value)
|
|
90
90
|
if value_number.is_integer():
|
|
91
91
|
value_number = int(value_number)
|
|
92
92
|
else:
|
|
93
|
-
value_number =
|
|
93
|
+
value_number = value_number
|
|
94
94
|
row_dict[header] = value_number
|
|
95
95
|
|
|
96
96
|
rows.append(row_dict)
|
|
@@ -36,7 +36,7 @@ DATA_SOURCE_LOCATION_UNKNOWN: DataSourceIntegration.ValueType # 0
|
|
|
36
36
|
SEMANTIC_LAYER: DataSourceIntegration.ValueType # 1
|
|
37
37
|
GA4: DataSourceIntegration.ValueType # 2
|
|
38
38
|
FB_ADS: DataSourceIntegration.ValueType # 3
|
|
39
|
-
|
|
39
|
+
Global___DataSourceIntegration: typing_extensions.TypeAlias = DataSourceIntegration
|
|
40
40
|
|
|
41
41
|
class _DateGranularity:
|
|
42
42
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -59,7 +59,7 @@ WEEK: DateGranularity.ValueType # 2
|
|
|
59
59
|
MONTH: DateGranularity.ValueType # 3
|
|
60
60
|
QUARTER: DateGranularity.ValueType # 4
|
|
61
61
|
YEAR: DateGranularity.ValueType # 5
|
|
62
|
-
|
|
62
|
+
Global___DateGranularity: typing_extensions.TypeAlias = DateGranularity
|
|
63
63
|
|
|
64
64
|
class _Aggregation:
|
|
65
65
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -86,7 +86,7 @@ MIN: Aggregation.ValueType # 4
|
|
|
86
86
|
MAX: Aggregation.ValueType # 5
|
|
87
87
|
AVERAGE: Aggregation.ValueType # 6
|
|
88
88
|
MEDIAN: Aggregation.ValueType # 7
|
|
89
|
-
|
|
89
|
+
Global___Aggregation: typing_extensions.TypeAlias = Aggregation
|
|
90
90
|
|
|
91
91
|
class _DimensionType:
|
|
92
92
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -125,7 +125,7 @@ FB_ADS_FIELD: DimensionType.ValueType # 3
|
|
|
125
125
|
FB_ADS_BREAKDOWN: DimensionType.ValueType # 4
|
|
126
126
|
FB_ADS_ACTION_BREAKDOWN: DimensionType.ValueType # 5
|
|
127
127
|
FB_ADS_SUMMARY_ACTION_BREAKDOWN: DimensionType.ValueType # 6
|
|
128
|
-
|
|
128
|
+
Global___DimensionType: typing_extensions.TypeAlias = DimensionType
|
|
129
129
|
|
|
130
130
|
class _DimensionValueType:
|
|
131
131
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -152,7 +152,7 @@ BOOLEAN: DimensionValueType.ValueType # 4
|
|
|
152
152
|
DATE: DimensionValueType.ValueType # 5
|
|
153
153
|
DATETIME: DimensionValueType.ValueType # 6
|
|
154
154
|
TIMESTAMP: DimensionValueType.ValueType # 7
|
|
155
|
-
|
|
155
|
+
Global___DimensionValueType: typing_extensions.TypeAlias = DimensionValueType
|
|
156
156
|
|
|
157
157
|
class _MetricType:
|
|
158
158
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -204,7 +204,7 @@ Note that if an optional constraint string is applied, the constraint is applied
|
|
|
204
204
|
DERIVED: MetricType.ValueType # 4
|
|
205
205
|
SQL_EXPRESSION: MetricType.ValueType # 5
|
|
206
206
|
"""When you are building a metric that involves a SQL expression of multiple measures, you can use an expression metric."""
|
|
207
|
-
|
|
207
|
+
Global___MetricType: typing_extensions.TypeAlias = MetricType
|
|
208
208
|
|
|
209
209
|
class _MetricValueType:
|
|
210
210
|
ValueType = typing.NewType("ValueType", builtins.int)
|
|
@@ -285,7 +285,7 @@ METRIC_VALUE_TYPE_LIST_ADS_ACTION_STATS: MetricValueType.ValueType # 15
|
|
|
285
285
|
"""A single action for a Statistics result -> https://github.com/facebook/facebook-python-business-sdk/blob/main/facebook_business/adobjects/adsactionstats.py"""
|
|
286
286
|
METRIC_VALUE_TYPE_LIST_ADS_INSIGHTS_DDA_RESULT: MetricValueType.ValueType # 16
|
|
287
287
|
METRIC_VALUE_TYPE_LIST_ADS_HISTOGRAM_STATS: MetricValueType.ValueType # 17
|
|
288
|
-
|
|
288
|
+
Global___MetricValueType: typing_extensions.TypeAlias = MetricValueType
|
|
289
289
|
|
|
290
290
|
@typing.final
|
|
291
291
|
class Dimension(google.protobuf.message.Message):
|
|
@@ -311,7 +311,7 @@ class Dimension(google.protobuf.message.Message):
|
|
|
311
311
|
And we want to define a date range of interest for our queries
|
|
312
312
|
Use DATE_TRUNC(CAST(dimension.name, DATETIME), DateGranularity) to ensure we can use WHERE date range correctly
|
|
313
313
|
"""
|
|
314
|
-
type:
|
|
314
|
+
type: Global___DimensionType.ValueType
|
|
315
315
|
"""The type of the dimension, e.g. CATEGORICAL or TIME."""
|
|
316
316
|
value_type: builtins.str
|
|
317
317
|
"""The type of the values of the dimension, e.g. STRING, INTEGER, FLOAT, etc.
|
|
@@ -319,9 +319,9 @@ class Dimension(google.protobuf.message.Message):
|
|
|
319
319
|
"""
|
|
320
320
|
display_name: builtins.str
|
|
321
321
|
"""The display name."""
|
|
322
|
-
value_type_enum:
|
|
322
|
+
value_type_enum: Global___DimensionValueType.ValueType
|
|
323
323
|
@property
|
|
324
|
-
def type_params(self) ->
|
|
324
|
+
def type_params(self) -> Global___DimensionTypeParams:
|
|
325
325
|
"""The parameters of the dimension type."""
|
|
326
326
|
|
|
327
327
|
@property
|
|
@@ -338,18 +338,18 @@ class Dimension(google.protobuf.message.Message):
|
|
|
338
338
|
name: builtins.str = ...,
|
|
339
339
|
description: builtins.str = ...,
|
|
340
340
|
expr: builtins.str = ...,
|
|
341
|
-
type:
|
|
342
|
-
type_params:
|
|
341
|
+
type: Global___DimensionType.ValueType = ...,
|
|
342
|
+
type_params: Global___DimensionTypeParams | None = ...,
|
|
343
343
|
top_n_values: collections.abc.Iterable[builtins.str] | None = ...,
|
|
344
344
|
value_type: builtins.str = ...,
|
|
345
345
|
data_source_names: collections.abc.Iterable[builtins.str] | None = ...,
|
|
346
346
|
display_name: builtins.str = ...,
|
|
347
|
-
value_type_enum:
|
|
347
|
+
value_type_enum: Global___DimensionValueType.ValueType = ...,
|
|
348
348
|
) -> None: ...
|
|
349
349
|
def HasField(self, field_name: typing.Literal["type_params", b"type_params"]) -> builtins.bool: ...
|
|
350
350
|
def ClearField(self, field_name: typing.Literal["data_source_names", b"data_source_names", "description", b"description", "display_name", b"display_name", "expr", b"expr", "name", b"name", "top_n_values", b"top_n_values", "type", b"type", "type_params", b"type_params", "value_type", b"value_type", "value_type_enum", b"value_type_enum"]) -> None: ...
|
|
351
351
|
|
|
352
|
-
|
|
352
|
+
Global___Dimension: typing_extensions.TypeAlias = Dimension
|
|
353
353
|
|
|
354
354
|
@typing.final
|
|
355
355
|
class DimensionTypeParams(google.protobuf.message.Message):
|
|
@@ -357,7 +357,7 @@ class DimensionTypeParams(google.protobuf.message.Message):
|
|
|
357
357
|
|
|
358
358
|
TIME_GRANULARITY_FIELD_NUMBER: builtins.int
|
|
359
359
|
IS_PRIMARY_FIELD_NUMBER: builtins.int
|
|
360
|
-
time_granularity:
|
|
360
|
+
time_granularity: Global___DateGranularity.ValueType
|
|
361
361
|
"""The date granularity of the dimension if it is a time dimension."""
|
|
362
362
|
is_primary: builtins.bool
|
|
363
363
|
"""Note for Time dimensions: For data sources with a measure involved, a primary time dimension is required (notice the is_primary: True parameter).
|
|
@@ -366,12 +366,12 @@ class DimensionTypeParams(google.protobuf.message.Message):
|
|
|
366
366
|
def __init__(
|
|
367
367
|
self,
|
|
368
368
|
*,
|
|
369
|
-
time_granularity:
|
|
369
|
+
time_granularity: Global___DateGranularity.ValueType = ...,
|
|
370
370
|
is_primary: builtins.bool = ...,
|
|
371
371
|
) -> None: ...
|
|
372
372
|
def ClearField(self, field_name: typing.Literal["is_primary", b"is_primary", "time_granularity", b"time_granularity"]) -> None: ...
|
|
373
373
|
|
|
374
|
-
|
|
374
|
+
Global___DimensionTypeParams: typing_extensions.TypeAlias = DimensionTypeParams
|
|
375
375
|
|
|
376
376
|
@typing.final
|
|
377
377
|
class Metric(google.protobuf.message.Message):
|
|
@@ -428,7 +428,7 @@ class Metric(google.protobuf.message.Message):
|
|
|
428
428
|
"""
|
|
429
429
|
denominator: builtins.str
|
|
430
430
|
"""The denominator of the metric if it is a ratio metric. It should be a measure."""
|
|
431
|
-
type:
|
|
431
|
+
type: Global___MetricType.ValueType
|
|
432
432
|
"""The type of the metric, e.g. MEASURE_PROXY, CUMULATIVE, RATIO, etc.
|
|
433
433
|
If the type is MEASURE_PROXY, the name of the metric must match the measure name in the expression
|
|
434
434
|
"""
|
|
@@ -436,7 +436,7 @@ class Metric(google.protobuf.message.Message):
|
|
|
436
436
|
"""The window of the metric if it is a cumulative metric."""
|
|
437
437
|
grain_to_date: builtins.str
|
|
438
438
|
"""The grain of the metric if it is a cumulative metric."""
|
|
439
|
-
value_type:
|
|
439
|
+
value_type: Global___MetricValueType.ValueType
|
|
440
440
|
"""The value type of the metric, e.g. INTEGER, FLOAT, etc.
|
|
441
441
|
Always set this field to METRIC_VALUE_TYPE_UNKNOWN = 0
|
|
442
442
|
"""
|
|
@@ -463,10 +463,10 @@ class Metric(google.protobuf.message.Message):
|
|
|
463
463
|
measures: collections.abc.Iterable[builtins.str] | None = ...,
|
|
464
464
|
numerator: builtins.str = ...,
|
|
465
465
|
denominator: builtins.str = ...,
|
|
466
|
-
type:
|
|
466
|
+
type: Global___MetricType.ValueType = ...,
|
|
467
467
|
window: builtins.str = ...,
|
|
468
468
|
grain_to_date: builtins.str = ...,
|
|
469
|
-
value_type:
|
|
469
|
+
value_type: Global___MetricValueType.ValueType = ...,
|
|
470
470
|
display_name: builtins.str = ...,
|
|
471
471
|
is_numeric: builtins.bool = ...,
|
|
472
472
|
) -> None: ...
|
|
@@ -474,7 +474,7 @@ class Metric(google.protobuf.message.Message):
|
|
|
474
474
|
def ClearField(self, field_name: typing.Literal["cumulative_process", b"cumulative_process", "denominator", b"denominator", "description", b"description", "display_name", b"display_name", "expression", b"expression", "grain_to_date", b"grain_to_date", "id", b"id", "is_numeric", b"is_numeric", "measures", b"measures", "name", b"name", "numerator", b"numerator", "table_name", b"table_name", "type", b"type", "value_type", b"value_type", "view_id_of_table", b"view_id_of_table", "window", b"window"]) -> None: ...
|
|
475
475
|
def WhichOneof(self, oneof_group: typing.Literal["cumulative_process", b"cumulative_process"]) -> typing.Literal["window", "grain_to_date"] | None: ...
|
|
476
476
|
|
|
477
|
-
|
|
477
|
+
Global___Metric: typing_extensions.TypeAlias = Metric
|
|
478
478
|
|
|
479
479
|
@typing.final
|
|
480
480
|
class DateStrRange(google.protobuf.message.Message):
|
|
@@ -494,7 +494,7 @@ class DateStrRange(google.protobuf.message.Message):
|
|
|
494
494
|
) -> None: ...
|
|
495
495
|
def ClearField(self, field_name: typing.Literal["end_date", b"end_date", "start_date", b"start_date"]) -> None: ...
|
|
496
496
|
|
|
497
|
-
|
|
497
|
+
Global___DateStrRange: typing_extensions.TypeAlias = DateStrRange
|
|
498
498
|
|
|
499
499
|
@typing.final
|
|
500
500
|
class QueryArgs(google.protobuf.message.Message):
|
|
@@ -554,7 +554,7 @@ class QueryArgs(google.protobuf.message.Message):
|
|
|
554
554
|
"""The list of dimensions selected by the pipeline, but are incompatible for the request."""
|
|
555
555
|
|
|
556
556
|
@property
|
|
557
|
-
def date_ranges(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[
|
|
557
|
+
def date_ranges(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[Global___DateStrRange]:
|
|
558
558
|
"""The date ranges to be queried."""
|
|
559
559
|
|
|
560
560
|
def __init__(
|
|
@@ -570,7 +570,7 @@ class QueryArgs(google.protobuf.message.Message):
|
|
|
570
570
|
having_clause: builtins.str = ...,
|
|
571
571
|
incompatible_metrics: collections.abc.Iterable[builtins.str] | None = ...,
|
|
572
572
|
incompatible_dimensions: collections.abc.Iterable[builtins.str] | None = ...,
|
|
573
|
-
date_ranges: collections.abc.Iterable[
|
|
573
|
+
date_ranges: collections.abc.Iterable[Global___DateStrRange] | None = ...,
|
|
574
574
|
sql_explanation: builtins.str = ...,
|
|
575
575
|
level: builtins.str = ...,
|
|
576
576
|
time_increment: builtins.str = ...,
|
|
@@ -578,7 +578,7 @@ class QueryArgs(google.protobuf.message.Message):
|
|
|
578
578
|
) -> None: ...
|
|
579
579
|
def ClearField(self, field_name: typing.Literal["date_ranges", b"date_ranges", "date_where_clause", b"date_where_clause", "group_by_columns", b"group_by_columns", "having_clause", b"having_clause", "incompatible_dimensions", b"incompatible_dimensions", "incompatible_metrics", b"incompatible_metrics", "level", b"level", "limit", b"limit", "main_data_source_name", b"main_data_source_name", "metrics", b"metrics", "metrics_expression", b"metrics_expression", "order_by", b"order_by", "sql_explanation", b"sql_explanation", "time_increment", b"time_increment", "where_clause", b"where_clause"]) -> None: ...
|
|
580
580
|
|
|
581
|
-
|
|
581
|
+
Global___QueryArgs: typing_extensions.TypeAlias = QueryArgs
|
|
582
582
|
|
|
583
583
|
@typing.final
|
|
584
584
|
class DatasourceMetadata(google.protobuf.message.Message):
|
|
@@ -587,16 +587,16 @@ class DatasourceMetadata(google.protobuf.message.Message):
|
|
|
587
587
|
LOCATION_FIELD_NUMBER: builtins.int
|
|
588
588
|
PROPERTY_ID_FIELD_NUMBER: builtins.int
|
|
589
589
|
PROPERTY_NAME_FIELD_NUMBER: builtins.int
|
|
590
|
-
location:
|
|
590
|
+
location: Global___DataSourceIntegration.ValueType
|
|
591
591
|
property_id: builtins.str
|
|
592
592
|
property_name: builtins.str
|
|
593
593
|
def __init__(
|
|
594
594
|
self,
|
|
595
595
|
*,
|
|
596
|
-
location:
|
|
596
|
+
location: Global___DataSourceIntegration.ValueType = ...,
|
|
597
597
|
property_id: builtins.str = ...,
|
|
598
598
|
property_name: builtins.str = ...,
|
|
599
599
|
) -> None: ...
|
|
600
600
|
def ClearField(self, field_name: typing.Literal["location", b"location", "property_id", b"property_id", "property_name", b"property_name"]) -> None: ...
|
|
601
601
|
|
|
602
|
-
|
|
602
|
+
Global___DatasourceMetadata: typing_extensions.TypeAlias = DatasourceMetadata
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "findly.unified-reporting-sdk"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.15"
|
|
4
4
|
license = "GPL-3.0-only"
|
|
5
5
|
description = ""
|
|
6
6
|
authors = []
|
|
@@ -15,7 +15,7 @@ include = [
|
|
|
15
15
|
[tool.poetry.dependencies]
|
|
16
16
|
python = "^3.9"
|
|
17
17
|
pandas = "^2.2.3"
|
|
18
|
-
protobuf = "^
|
|
18
|
+
protobuf = "^6.33.5"
|
|
19
19
|
backoff = "^2.2.1"
|
|
20
20
|
aiocache = "^0.12.2"
|
|
21
21
|
facebook-business = "^22.0.5"
|
|
@@ -23,7 +23,6 @@ sqlglot = "^26.17.1"
|
|
|
23
23
|
google-analytics-admin = "^0.24.0"
|
|
24
24
|
google-analytics-data = "^0.18.18"
|
|
25
25
|
google-api-python-client = "^2.169.0"
|
|
26
|
-
oauth2client = "^4.1.3"
|
|
27
26
|
|
|
28
27
|
[tool.poetry.group.typing.dependencies]
|
|
29
28
|
mypy = "^1.15.0"
|
|
@@ -33,7 +32,7 @@ types-protobuf = "^6.30.2.20250506"
|
|
|
33
32
|
[tool.poetry.group.dev.dependencies]
|
|
34
33
|
pytest = "^8.3.5"
|
|
35
34
|
pytest-asyncio = "^0.26.0"
|
|
36
|
-
mypy-protobuf = "^3.
|
|
35
|
+
mypy-protobuf = "^3.7.0"
|
|
37
36
|
python-dotenv = "^1.0.1"
|
|
38
37
|
|
|
39
38
|
[build-system]
|
|
File without changes
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
|
|
3
|
-
DEFAULT_FLOAT_PRECISION = 2
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def create_numeric_string_series(
|
|
7
|
-
col: pd.Series, precision: int = DEFAULT_FLOAT_PRECISION
|
|
8
|
-
) -> pd.Series:
|
|
9
|
-
try:
|
|
10
|
-
numeric_col: pd.Series = pd.to_numeric(col)
|
|
11
|
-
if (numeric_col.dropna() % 1 == 0).all():
|
|
12
|
-
return numeric_col.astype(int).astype(str)
|
|
13
|
-
else:
|
|
14
|
-
return numeric_col.astype(float).round(precision).astype(str)
|
|
15
|
-
except Exception:
|
|
16
|
-
return col
|
|
File without changes
|
|
File without changes
|
{findly_unified_reporting_sdk-0.7.13 → findly_unified_reporting_sdk-0.7.15}/findly/__init__.py
RENAMED
|
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
|
|
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
|