logos-sdk 0.0.25.dev27__tar.gz → 0.0.25.dev29__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.
Files changed (28) hide show
  1. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/PKG-INFO +1 -1
  2. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/big_query/BigQuery.py +51 -1
  3. logos_sdk-0.0.25.dev29/logos_sdk/services/GA4.py +98 -0
  4. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk.egg-info/PKG-INFO +1 -1
  5. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk.egg-info/SOURCES.txt +1 -0
  6. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/LICENSE +0 -0
  7. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/README.md +0 -0
  8. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/__init__.py +0 -0
  9. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/big_query/__init__.py +0 -0
  10. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/logging/LogosLogger.py +0 -0
  11. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/logging/__init__.py +0 -0
  12. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/CampaignManager.py +0 -0
  13. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/Collabim.py +0 -0
  14. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/DV360.py +0 -0
  15. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/Facebook.py +0 -0
  16. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/GoogleAds.py +0 -0
  17. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/GoogleSheets.py +0 -0
  18. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/MarketMiner.py +0 -0
  19. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/MerchantCenter.py +0 -0
  20. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/MicrosoftAdvertising.py +0 -0
  21. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/MicrosoftAdvertisingMerchantCenter.py +0 -0
  22. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/Sklik.py +0 -0
  23. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk/services/__init__.py +0 -0
  24. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk.egg-info/dependency_links.txt +0 -0
  25. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk.egg-info/requires.txt +0 -0
  26. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/logos_sdk.egg-info/top_level.txt +0 -0
  27. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/setup.cfg +0 -0
  28. {logos_sdk-0.0.25.dev27 → logos_sdk-0.0.25.dev29}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logos-sdk
3
- Version: 0.0.25.dev27
3
+ Version: 0.0.25.dev29
4
4
  Summary: SDK for Logos platform
5
5
  Home-page: https://bitbucket.org/databy/logos-sdk-pip/src/master/
6
6
  Author: Databy.io
@@ -2,7 +2,7 @@ import os
2
2
  from datetime import datetime
3
3
 
4
4
  import google.auth.exceptions
5
- from typing import List, Dict, Union, Optional
5
+ from typing import List, Dict, Union, Optional, Sequence
6
6
 
7
7
  from google.api_core.exceptions import NotFound
8
8
  from google.cloud.logging import Client as LoggerClient
@@ -218,3 +218,53 @@ class BigQuery:
218
218
  errors = self._service.insert_rows(bq_table, records)
219
219
  if errors:
220
220
  raise BigQueryException(errors)
221
+
222
+ def upsert_into_table(
223
+ self,
224
+ dataset_id: str,
225
+ target_table: str,
226
+ records: List[Dict],
227
+ schema_columns: List[Dict],
228
+ key_columns: Sequence[str],
229
+ ) -> None:
230
+ if not records:
231
+ return
232
+ if not key_columns:
233
+ return
234
+
235
+ bq_table = self.check_table_exists(dataset_id, target_table)
236
+ if bq_table is None:
237
+ self.create_table(dataset_id, target_table, schema_columns)
238
+ table_id = f"{self.project_id}.{dataset_id}.{target_table}"
239
+ cols = [f["name"] for f in schema_columns]
240
+
241
+ staging_table_name = f"_staging_{target_table}"
242
+ staging_table_id = f"{self.project_id}.{dataset_id}.{staging_table_name}"
243
+ self.create_table(dataset_id, staging_table_name, schema_columns)
244
+
245
+ self.insert_into_table(dataset_id, staging_table_name, records)
246
+
247
+ q = lambda c: f"{c}"
248
+
249
+ on_clause = " AND ".join(f"T.{q(k)} = S.{q(k)}" for k in key_columns)
250
+ update_cols = [c for c in cols if c not in set(key_columns)]
251
+ update_set = ",\n ".join(f"{q(c)} = S.{q(c)}" for c in update_cols)
252
+
253
+ insert_cols = ", ".join(q(c) for c in cols)
254
+ insert_vals = ", ".join(f"S.{q(c)}" for c in cols)
255
+
256
+ merge_sql = f"""
257
+ MERGE `{table_id}` T
258
+ USING `{staging_table_id}` S
259
+ ON {on_clause}
260
+ WHEN MATCHED THEN
261
+ UPDATE SET
262
+ {update_set}
263
+ WHEN NOT MATCHED THEN
264
+ INSERT ({insert_cols})
265
+ VALUES ({insert_vals})
266
+ """
267
+ job = self._service.query(merge_sql)
268
+ job.result()
269
+
270
+ self.delete_table(dataset_id, staging_table_name)
@@ -0,0 +1,98 @@
1
+ from requests.exceptions import Timeout
2
+ from typing import List, Union, Dict
3
+ from logos_sdk.services import get_headers, get_retry_session
4
+ from http import HTTPStatus
5
+ from dotenv import load_dotenv
6
+ import os
7
+ import time
8
+ import random
9
+
10
+
11
+ class GA4ServiceException(Exception):
12
+ pass
13
+
14
+
15
+ class GA4Service:
16
+ def __init__(self, url=None):
17
+ load_dotenv()
18
+ self.session = get_retry_session()
19
+ self._URL = url or os.environ.get("GA4_SERVICE_PATH")
20
+ self._RUN_REPORT = self._URL + "/run-report"
21
+ self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/accessible-accounts"
22
+ self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
23
+
24
+ def get_accessible_accounts(
25
+ self,
26
+ secret_id: str,
27
+ ) -> List[Dict]:
28
+ body = {
29
+ "secret_id": secret_id,
30
+ }
31
+
32
+ header = get_headers(self._GET_ACCESSIBLE_ACCOUNTS)
33
+ response = self.session.request(
34
+ "post", url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
35
+ )
36
+
37
+ if response.status_code == HTTPStatus.OK:
38
+ service_response = response.json()
39
+ return service_response["data"]
40
+ else:
41
+ raise GA4ServiceException(response.content)
42
+
43
+ def get_account_accessibility(self, secret_id: str, account_id: str) -> bool:
44
+ """
45
+ Gets account accessibility
46
+ :param secret_id: The ID of the secret in secret manager
47
+ :param account_id: The ID of the account.
48
+ :return: True if account has access
49
+ """
50
+ body = {"secret_id": secret_id, "account_id": account_id}
51
+ header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
52
+ response = self.session.request(
53
+ "post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
54
+ )
55
+
56
+ if response.status_code == HTTPStatus.OK:
57
+ service_response = response.json()
58
+ return service_response["data"]
59
+ else:
60
+ raise GA4ServiceException(response.content)
61
+
62
+ def run_report(self, account_id, secret_id, start_date, end_date, dimensions, metrics, filters=None,
63
+ order_by_metric=None):
64
+ """
65
+ Runs report
66
+ :param account_id: The ID of the account,
67
+ :param secret_id: The ID of the secret in secret manager
68
+ :param start_date: report start date in format "YYYY-MM-DD"
69
+ :param end_date: report end date in format "YYYY-MM-DD"
70
+ :param dimensions: The dimesions for the query
71
+ :param metrics: The metrics for the query
72
+ :param filters: The filters for the query
73
+ :param order_by_metric: The metric to order by
74
+ :return: report result
75
+ """
76
+ header = get_headers(self._RUN_REPORT)
77
+ body = {
78
+ "property_id": account_id,
79
+ "secret_id": secret_id,
80
+ "start_date": start_date,
81
+ "end_date": end_date,
82
+ "dimensions": dimensions,
83
+ "metrics": metrics,
84
+ }
85
+ if filters is not None:
86
+ body["filters"] = filters
87
+ if order_by_metric is not None:
88
+ body['order_by_metric'] = order_by_metric
89
+
90
+ response = self.session.request(
91
+ "post", url=self._RUN_REPORT, json=body, headers=header
92
+ )
93
+
94
+ if response.status_code == HTTPStatus.OK:
95
+ service_response = response.json()
96
+ return service_response["data"]
97
+ else:
98
+ raise GA4ServiceException(response.content)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: logos-sdk
3
- Version: 0.0.25.dev27
3
+ Version: 0.0.25.dev29
4
4
  Summary: SDK for Logos platform
5
5
  Home-page: https://bitbucket.org/databy/logos-sdk-pip/src/master/
6
6
  Author: Databy.io
@@ -16,6 +16,7 @@ logos_sdk/services/CampaignManager.py
16
16
  logos_sdk/services/Collabim.py
17
17
  logos_sdk/services/DV360.py
18
18
  logos_sdk/services/Facebook.py
19
+ logos_sdk/services/GA4.py
19
20
  logos_sdk/services/GoogleAds.py
20
21
  logos_sdk/services/GoogleSheets.py
21
22
  logos_sdk/services/MarketMiner.py