logos-sdk 0.0.25.dev8__tar.gz → 0.0.25.dev10__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.
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/PKG-INFO +1 -1
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/big_query/BigQuery.py +28 -10
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/CampaignManager.py +47 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/DV360.py +53 -1
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/Facebook.py +6 -1
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/GoogleAds.py +50 -4
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/MerchantCenter.py +36 -6
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/MicrosoftAdvertising.py +3 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/Sklik.py +125 -35
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk.egg-info/PKG-INFO +1 -1
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/LICENSE +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/README.md +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/__init__.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/big_query/__init__.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/logging/LogosLogger.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/logging/__init__.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/Collabim.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/GoogleSheets.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/MarketMiner.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk/services/__init__.py +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk.egg-info/SOURCES.txt +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk.egg-info/dependency_links.txt +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk.egg-info/requires.txt +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/logos_sdk.egg-info/top_level.txt +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/setup.cfg +0 -0
- {logos_sdk-0.0.25.dev8 → logos_sdk-0.0.25.dev10}/setup.py +0 -0
|
@@ -36,6 +36,19 @@ class BigQuery:
|
|
|
36
36
|
except DefaultCredentialsError:
|
|
37
37
|
self.logger = None
|
|
38
38
|
|
|
39
|
+
def parse_fields(self, fields):
|
|
40
|
+
result = []
|
|
41
|
+
for row in fields:
|
|
42
|
+
result.append(
|
|
43
|
+
bigquery.schema.SchemaField(
|
|
44
|
+
row["name"],
|
|
45
|
+
row["col_type"],
|
|
46
|
+
mode=row["mode"],
|
|
47
|
+
fields=self.parse_fields(row["fields"]) if "fields" in row else [],
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
return result
|
|
51
|
+
|
|
39
52
|
def get_dataset(self, dataset_id: str):
|
|
40
53
|
return self._service.get_dataset(dataset_id)
|
|
41
54
|
|
|
@@ -57,17 +70,17 @@ class BigQuery:
|
|
|
57
70
|
return self._service.get_table(sql_format)
|
|
58
71
|
|
|
59
72
|
def insert_into_table(
|
|
60
|
-
|
|
73
|
+
self, dataset_id: str, table_id: str, records: List[Dict]
|
|
61
74
|
) -> None:
|
|
62
75
|
bq_table = self.get_table(dataset_id, table_id)
|
|
63
76
|
self._insert_into_table(bq_table, records)
|
|
64
77
|
|
|
65
78
|
def insert_create_table(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
self,
|
|
80
|
+
dataset_id: str,
|
|
81
|
+
table_id: str,
|
|
82
|
+
records: List[Dict],
|
|
83
|
+
schema_columns: List[Dict],
|
|
71
84
|
) -> None:
|
|
72
85
|
bq_table = self.check_table_exists(dataset_id, table_id)
|
|
73
86
|
if bq_table is None:
|
|
@@ -87,10 +100,15 @@ class BigQuery:
|
|
|
87
100
|
return None
|
|
88
101
|
|
|
89
102
|
def create_table(
|
|
90
|
-
|
|
103
|
+
self, dataset_id: str, table_id: str, schema_columns: List[Dict]
|
|
91
104
|
) -> Union[bool, Table]:
|
|
92
105
|
table_schema = [
|
|
93
|
-
bigquery.schema.SchemaField(
|
|
106
|
+
bigquery.schema.SchemaField(
|
|
107
|
+
row["name"],
|
|
108
|
+
row["col_type"],
|
|
109
|
+
mode=row["mode"],
|
|
110
|
+
fields=self.parse_fields(row["fields"]) if "fields" in row else [],
|
|
111
|
+
)
|
|
94
112
|
for row in schema_columns
|
|
95
113
|
]
|
|
96
114
|
try:
|
|
@@ -132,13 +150,13 @@ class BigQuery:
|
|
|
132
150
|
|
|
133
151
|
@retry_on_not_found
|
|
134
152
|
def _insert_into_table(
|
|
135
|
-
|
|
153
|
+
self, bq_table: Table, records: List[Dict], attempts: int
|
|
136
154
|
) -> None:
|
|
137
155
|
if len(records) > self.BQ_ROWS_LIMIT:
|
|
138
156
|
for index in range(0, len(records), self.BQ_ROWS_LIMIT):
|
|
139
157
|
errors = self._service.insert_rows(
|
|
140
158
|
bq_table,
|
|
141
|
-
records[index: (index + self.BQ_ROWS_LIMIT)],
|
|
159
|
+
records[index : (index + self.BQ_ROWS_LIMIT)],
|
|
142
160
|
retry=Retry(
|
|
143
161
|
total=2, connect=4, backoff_factor=2, allowed_methods=None
|
|
144
162
|
),
|
|
@@ -30,6 +30,8 @@ class CampaignManagerService:
|
|
|
30
30
|
self._GET_FILE = self._URL + "/get-file"
|
|
31
31
|
self._GET_FILE_MEDIA_REQUEST = self._URL + "/get-file-media-request"
|
|
32
32
|
self._QUERY_DIMENSION_VALUES = self._URL + "/query-dimension-values"
|
|
33
|
+
self._GET_ACCESSIBLE_PARENT_ACCOUNTS = self._URL + "/get-parent-accounts"
|
|
34
|
+
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-advertisers"
|
|
33
35
|
|
|
34
36
|
def create_report(
|
|
35
37
|
self,
|
|
@@ -397,3 +399,48 @@ class CampaignManagerService:
|
|
|
397
399
|
report_id=report["id"],
|
|
398
400
|
secret_id=secret_id,
|
|
399
401
|
)
|
|
402
|
+
|
|
403
|
+
def get_accessible_parent_accounts(self, secret_id):
|
|
404
|
+
"""
|
|
405
|
+
Returns accessible parent accounts
|
|
406
|
+
:param secret_id: The ID of the secret in secret manager
|
|
407
|
+
:return: list of parent accounts
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
header = get_headers(self._GET_ACCESSIBLE_PARENT_ACCOUNTS)
|
|
411
|
+
body = {"secret_id": secret_id}
|
|
412
|
+
|
|
413
|
+
response = request(
|
|
414
|
+
"post", url=self._GET_ACCESSIBLE_PARENT_ACCOUNTS, json=body, headers=header
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
if response.status_code == HTTPStatus.OK:
|
|
418
|
+
service_response = response.json()
|
|
419
|
+
return service_response["data"]
|
|
420
|
+
else:
|
|
421
|
+
raise CampaignManagerServiceException(response.content)
|
|
422
|
+
|
|
423
|
+
def get_accessible_advertisers(self, secret_id):
|
|
424
|
+
"""
|
|
425
|
+
Returns accessible advertisers
|
|
426
|
+
:param secret_id: The ID of the secret in secret manager
|
|
427
|
+
:return: list of advertisers
|
|
428
|
+
"""
|
|
429
|
+
|
|
430
|
+
header = get_headers(self._GET_ACCESSIBLE_ADVERTISERS)
|
|
431
|
+
body = {
|
|
432
|
+
"secret_id": secret_id,
|
|
433
|
+
"filters": {
|
|
434
|
+
"hide_inactive": True,
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
response = request(
|
|
439
|
+
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
if response.status_code == HTTPStatus.OK:
|
|
443
|
+
service_response = response.json()
|
|
444
|
+
return service_response["data"]
|
|
445
|
+
else:
|
|
446
|
+
raise CampaignManagerServiceException(response.content)
|
|
@@ -34,6 +34,8 @@ class DV360Service:
|
|
|
34
34
|
self._GET_QUERY_METADATA = self._URL + "/get-query-metadata"
|
|
35
35
|
self._RUN_QUERY = self._URL + "/run-query"
|
|
36
36
|
self._DELETE_QUERY = self._URL + "/delete-query"
|
|
37
|
+
self._GET_ACCESSIBLE_PARTNERS = self._URL + "/get-accessible-partners"
|
|
38
|
+
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-accessible-advertisers"
|
|
37
39
|
|
|
38
40
|
def list_line_items(self, advertiser_id, secret_id, filter_string=None):
|
|
39
41
|
"""
|
|
@@ -301,6 +303,56 @@ class DV360Service:
|
|
|
301
303
|
else:
|
|
302
304
|
raise DV360ServiceException(response.content)
|
|
303
305
|
|
|
306
|
+
def get_accessible_partners(self, secret_id):
|
|
307
|
+
"""
|
|
308
|
+
Returns accessible partners
|
|
309
|
+
:param secret_id: The ID of the secret in secret manager
|
|
310
|
+
:return: list of partners
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
header = get_headers(self._GET_ACCESSIBLE_PARTNERS)
|
|
314
|
+
body = {
|
|
315
|
+
"secret_id": secret_id,
|
|
316
|
+
"filters": {
|
|
317
|
+
"hide_inactive": True,
|
|
318
|
+
},
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
response = request(
|
|
322
|
+
"post", url=self._GET_ACCESSIBLE_PARTNERS, json=body, headers=header
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if response.status_code == HTTPStatus.OK:
|
|
326
|
+
service_response = response.json()
|
|
327
|
+
return service_response["data"]
|
|
328
|
+
else:
|
|
329
|
+
raise DV360ServiceException(response.content)
|
|
330
|
+
|
|
331
|
+
def get_accessible_advertisers(self, secret_id):
|
|
332
|
+
"""
|
|
333
|
+
Returns accessible advertisers
|
|
334
|
+
:param secret_id: The ID of the secret in secret manager
|
|
335
|
+
:return: list of advertisers
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
header = get_headers(self._GET_ACCESSIBLE_ADVERTISERS)
|
|
339
|
+
body = {
|
|
340
|
+
"secret_id": secret_id,
|
|
341
|
+
"filters": {
|
|
342
|
+
"hide_inactive": True,
|
|
343
|
+
},
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
response = request(
|
|
347
|
+
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
if response.status_code == HTTPStatus.OK:
|
|
351
|
+
service_response = response.json()
|
|
352
|
+
return service_response["data"]
|
|
353
|
+
else:
|
|
354
|
+
raise DV360ServiceException(response.content)
|
|
355
|
+
|
|
304
356
|
def _check_report_ready_with_exponential_backoff(
|
|
305
357
|
self, secret_id: str, query_id: str, report_id: str, backoff_attempts: int = 10
|
|
306
358
|
) -> bool:
|
|
@@ -393,5 +445,5 @@ class DV360Service:
|
|
|
393
445
|
|
|
394
446
|
if response.status_code == HTTPStatus.OK:
|
|
395
447
|
return True
|
|
396
|
-
|
|
448
|
+
|
|
397
449
|
return False
|
|
@@ -38,7 +38,12 @@ class FacebookService:
|
|
|
38
38
|
:return: all accessible accounts List(Dict)
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
body = {
|
|
41
|
+
body = {
|
|
42
|
+
"secret_id": secret_id,
|
|
43
|
+
"filters": {
|
|
44
|
+
"hide_inactive": True,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
42
47
|
|
|
43
48
|
header = get_headers(self._ACCESSIBLE_ACCOUNTS)
|
|
44
49
|
|
|
@@ -21,6 +21,7 @@ class GoogleAdsService:
|
|
|
21
21
|
self._SEARCH = self._URL + "/search"
|
|
22
22
|
self._EXCLUDE_FOR_ACCOUNT = self._URL + "/exclude-for-account"
|
|
23
23
|
self._EXCLUDE_FOR_AD_GROUP = self._URL + "/exclude-for-ad-group"
|
|
24
|
+
self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/list-accessible-accounts"
|
|
24
25
|
|
|
25
26
|
@staticmethod
|
|
26
27
|
def fetch_with_retry_on_timeout(url, json, headers):
|
|
@@ -28,8 +29,10 @@ class GoogleAdsService:
|
|
|
28
29
|
try:
|
|
29
30
|
return request("post", url, json=json, headers=headers, timeout=25)
|
|
30
31
|
except Timeout:
|
|
31
|
-
delay = 2 * (2
|
|
32
|
-
print(
|
|
32
|
+
delay = 2 * (2**attempt) + random.randint(0, 9)
|
|
33
|
+
print(
|
|
34
|
+
f"there was a timeout when contacting the service, going to sleep for {delay} seconds"
|
|
35
|
+
)
|
|
33
36
|
time.sleep(delay)
|
|
34
37
|
|
|
35
38
|
raise Exception("The service is not able to reply within 30 seconds.")
|
|
@@ -53,7 +56,9 @@ class GoogleAdsService:
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
header = get_headers(self._SEARCH_STREAM)
|
|
56
|
-
response = self.fetch_with_retry_on_timeout(
|
|
59
|
+
response = self.fetch_with_retry_on_timeout(
|
|
60
|
+
url=self._SEARCH_STREAM, json=body, headers=header
|
|
61
|
+
)
|
|
57
62
|
|
|
58
63
|
if response.status_code == HTTPStatus.OK:
|
|
59
64
|
service_response = response.json()
|
|
@@ -84,7 +89,9 @@ class GoogleAdsService:
|
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
header = get_headers(self._SEARCH)
|
|
87
|
-
response = self.fetch_with_retry_on_timeout(
|
|
92
|
+
response = self.fetch_with_retry_on_timeout(
|
|
93
|
+
url=self._SEARCH, json=body, headers=header
|
|
94
|
+
)
|
|
88
95
|
|
|
89
96
|
if response.status_code != HTTPStatus.OK:
|
|
90
97
|
raise GoogleAdsServiceException(response.content)
|
|
@@ -163,3 +170,42 @@ class GoogleAdsService:
|
|
|
163
170
|
return
|
|
164
171
|
else:
|
|
165
172
|
raise GoogleAdsServiceException(response.content)
|
|
173
|
+
|
|
174
|
+
def get_accessible_accounts(
|
|
175
|
+
self,
|
|
176
|
+
secret_id: str,
|
|
177
|
+
page_size: int = 1000,
|
|
178
|
+
) -> List[Dict]:
|
|
179
|
+
body = {
|
|
180
|
+
"secret_id": secret_id,
|
|
181
|
+
"filters": {
|
|
182
|
+
"hide_inactive": True,
|
|
183
|
+
},
|
|
184
|
+
"page_token": None,
|
|
185
|
+
"page_size": page_size,
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
header = get_headers(self._GET_ACCESSIBLE_ACCOUNTS)
|
|
189
|
+
response = self.fetch_with_retry_on_timeout(
|
|
190
|
+
url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if response.status_code != HTTPStatus.OK:
|
|
194
|
+
raise GoogleAdsServiceException(response.content)
|
|
195
|
+
|
|
196
|
+
service_response = response.json()
|
|
197
|
+
all_results = service_response["data"]["results"]
|
|
198
|
+
|
|
199
|
+
while service_response["data"]["next_page_token"]:
|
|
200
|
+
body["page_token"] = service_response["data"]["next_page_token"]
|
|
201
|
+
response = request(
|
|
202
|
+
"post", url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if response.status_code != HTTPStatus.OK:
|
|
206
|
+
raise GoogleAdsServiceException(response.content)
|
|
207
|
+
|
|
208
|
+
service_response = response.json()
|
|
209
|
+
all_results.extend(service_response["data"]["results"])
|
|
210
|
+
|
|
211
|
+
return all_results
|
|
@@ -14,6 +14,7 @@ class MerchantCenterService:
|
|
|
14
14
|
load_dotenv()
|
|
15
15
|
self._URL = url or os.environ.get("MERCHANT_CENTER_SERVICE_PATH")
|
|
16
16
|
self._LIST_ACCOUNTS = self._URL + "/account-service/accounts"
|
|
17
|
+
self._LIST_ACCESSIBLE_ACCOUNTS = self._URL + "/account-service/list-accessible-accounts"
|
|
17
18
|
self._LIST_ACCOUNT_STATUSES = self._URL + "/account-service/account-statuses"
|
|
18
19
|
self._LIST_PRODUCTS = self._URL + "/product-service/products"
|
|
19
20
|
self._LIST_PRODUCT_STATUSES = self._URL + "/product-service/product-statuses"
|
|
@@ -36,6 +37,22 @@ class MerchantCenterService:
|
|
|
36
37
|
else:
|
|
37
38
|
raise MerchantServiceException(response.content)
|
|
38
39
|
|
|
40
|
+
def list_accessible_accounts(self, secret_id: str):
|
|
41
|
+
"""
|
|
42
|
+
Lists accessible accounts in Merchant Center
|
|
43
|
+
:param secret_id: The ID of the secret in secret manager
|
|
44
|
+
:return: List[Dict]
|
|
45
|
+
"""
|
|
46
|
+
body = {"secret_id": secret_id}
|
|
47
|
+
header = get_headers(self._LIST_ACCESSIBLE_ACCOUNTS)
|
|
48
|
+
response = request("post", url=self._LIST_ACCESSIBLE_ACCOUNTS, json=body, headers=header)
|
|
49
|
+
|
|
50
|
+
if response.status_code == HTTPStatus.OK:
|
|
51
|
+
service_response = response.json()
|
|
52
|
+
return service_response["data"]
|
|
53
|
+
else:
|
|
54
|
+
raise MerchantServiceException(response.content)
|
|
55
|
+
|
|
39
56
|
def list_account_statuses(
|
|
40
57
|
self, merchant_account_id: str, account_id: str, secret_id: str
|
|
41
58
|
):
|
|
@@ -92,26 +109,39 @@ class MerchantCenterService:
|
|
|
92
109
|
service_response = response.json()
|
|
93
110
|
yield service_response["data"]["results"]
|
|
94
111
|
|
|
95
|
-
def list_products_statuses(self, merchant_account_id: str, secret_id: str):
|
|
112
|
+
def list_products_statuses(self, merchant_account_id: str, secret_id: str, page_size: int = 250):
|
|
96
113
|
"""
|
|
97
114
|
Lists the statuses of the products in your Merchant Center account
|
|
98
115
|
:param merchant_account_id: The ID of the
|
|
99
116
|
account that contains the products. This account cannot be a multi-client account
|
|
100
117
|
:param secret_id: The ID of the secret in secret manager
|
|
118
|
+
:param page_size: size of the page
|
|
101
119
|
:return: List[Dict]
|
|
102
120
|
"""
|
|
103
|
-
body = {"merchant_account_id": merchant_account_id, "secret_id": secret_id}
|
|
121
|
+
body = {"merchant_account_id": merchant_account_id, "secret_id": secret_id, "page_size": page_size}
|
|
104
122
|
header = get_headers(self._LIST_PRODUCT_STATUSES)
|
|
105
123
|
response = request(
|
|
106
124
|
"post", url=self._LIST_PRODUCT_STATUSES, json=body, headers=header
|
|
107
125
|
)
|
|
108
126
|
|
|
109
|
-
if response.status_code
|
|
110
|
-
service_response = response.json()
|
|
111
|
-
return service_response["data"]
|
|
112
|
-
else:
|
|
127
|
+
if response.status_code != HTTPStatus.OK:
|
|
113
128
|
raise MerchantServiceException(response.content)
|
|
114
129
|
|
|
130
|
+
service_response = response.json()
|
|
131
|
+
yield service_response["data"]["results"]
|
|
132
|
+
|
|
133
|
+
while service_response["data"]["nextPageToken"] is not None:
|
|
134
|
+
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
135
|
+
response = request(
|
|
136
|
+
"post", url=self._LIST_PRODUCT_STATUSES, json=body, headers=header
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if response.status_code != HTTPStatus.OK:
|
|
140
|
+
raise MerchantServiceException(response.content)
|
|
141
|
+
|
|
142
|
+
service_response = response.json()
|
|
143
|
+
yield service_response["data"]["results"]
|
|
144
|
+
|
|
115
145
|
def reports_search(
|
|
116
146
|
self,
|
|
117
147
|
merchant_account_id: str,
|
|
@@ -46,13 +46,15 @@ def get_session_if_malformed(wrapped_function):
|
|
|
46
46
|
|
|
47
47
|
return inner
|
|
48
48
|
|
|
49
|
+
|
|
49
50
|
def get_report_results_if_expired(wrapped_function):
|
|
50
51
|
@wraps(wrapped_function)
|
|
51
52
|
def inner(*args, **kwargs):
|
|
52
53
|
try:
|
|
53
54
|
return wrapped_function(*args, **kwargs)
|
|
54
55
|
except SklikServiceException as err:
|
|
55
|
-
if json.loads(err.args[0].decode("utf8")).get(
|
|
56
|
+
if json.loads(err.args[0].decode("utf8")).get(
|
|
57
|
+
"detail") == "Requested report has expired. Please create a new report by calling createReport endpoint.":
|
|
56
58
|
print("Report expired, creating new report")
|
|
57
59
|
args[0].get_session(args[0]._secret_id, args[0]._account_email)
|
|
58
60
|
return wrapped_function(*args, **kwargs)
|
|
@@ -69,10 +71,12 @@ class SklikService:
|
|
|
69
71
|
self._CREATE_REPORT = self._URL + "/create-report"
|
|
70
72
|
self._READ_REPORT = self._URL + "/read-report"
|
|
71
73
|
self._API = self._URL + "/call-api"
|
|
74
|
+
self._GET_CLIENT = self._URL + "/get-client"
|
|
72
75
|
self._CHECK_DATA_READY = self._URL + "/check-data-ready"
|
|
73
76
|
self._LOGOUT = self._URL + "/logout"
|
|
74
77
|
self._API_LIMITS = self._URL + "/api-limits"
|
|
75
78
|
self._GET_SESSION = self._URL + "/get-session"
|
|
79
|
+
self._GET_SESSION_WITHOUT_ACCOUNT = self._URL + "/get-session-without-account"
|
|
76
80
|
self.session = None # dict {"sklik_session": "", "user_id": ""}
|
|
77
81
|
self._secret_id = None
|
|
78
82
|
self._account_email = None
|
|
@@ -98,6 +102,25 @@ class SklikService:
|
|
|
98
102
|
else:
|
|
99
103
|
raise SklikServiceException(response.content)
|
|
100
104
|
|
|
105
|
+
def get_session_without_account(self, secret_id: str) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Manages initial login of the sessions, returns dict with sklik session string without user id int
|
|
108
|
+
:param secret_id: The ID of the secret in secret manager
|
|
109
|
+
:return: Dict
|
|
110
|
+
"""
|
|
111
|
+
body = {"secret_id": secret_id}
|
|
112
|
+
response = execute_request("post", url=self._GET_SESSION_WITHOUT_ACCOUNT, json=body)
|
|
113
|
+
|
|
114
|
+
if response.status_code == HTTPStatus.OK:
|
|
115
|
+
service_response = response.json()
|
|
116
|
+
self.session = {
|
|
117
|
+
"sklik_session": service_response["data"]["sklik_session"],
|
|
118
|
+
"user_id": None,
|
|
119
|
+
}
|
|
120
|
+
self._secret_id = secret_id
|
|
121
|
+
else:
|
|
122
|
+
raise SklikServiceException(response.content)
|
|
123
|
+
|
|
101
124
|
def logout_of_current_session(self) -> None:
|
|
102
125
|
"""
|
|
103
126
|
Attempts to log out of the current session, nothing happens if not ok
|
|
@@ -112,15 +135,15 @@ class SklikService:
|
|
|
112
135
|
@get_session_if_malformed
|
|
113
136
|
@get_report_results_if_expired
|
|
114
137
|
def get_report_results(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
138
|
+
self,
|
|
139
|
+
secret_id: str,
|
|
140
|
+
account_email: str,
|
|
141
|
+
report_type: str,
|
|
142
|
+
date_from: str,
|
|
143
|
+
date_to: str,
|
|
144
|
+
columns: List[str],
|
|
145
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
146
|
+
read_params: Dict[str, Union[int, str]] = None,
|
|
124
147
|
) -> List[Dict]:
|
|
125
148
|
"""
|
|
126
149
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -165,14 +188,14 @@ class SklikService:
|
|
|
165
188
|
|
|
166
189
|
@get_session_if_malformed
|
|
167
190
|
def get_streamed_report_results(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
191
|
+
self,
|
|
192
|
+
secret_id: str,
|
|
193
|
+
account_email: str,
|
|
194
|
+
report_type: str,
|
|
195
|
+
date_from: str,
|
|
196
|
+
date_to: str,
|
|
197
|
+
columns: List[str],
|
|
198
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
176
199
|
) -> List[Dict]:
|
|
177
200
|
"""
|
|
178
201
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -212,13 +235,13 @@ class SklikService:
|
|
|
212
235
|
raise SklikServiceException(response.content)
|
|
213
236
|
|
|
214
237
|
def _create_report(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
238
|
+
self,
|
|
239
|
+
secret_id: str,
|
|
240
|
+
account_email: str,
|
|
241
|
+
report_type: str,
|
|
242
|
+
date_from: str,
|
|
243
|
+
date_to: str,
|
|
244
|
+
params: Dict[str, Union[int, str]] = None,
|
|
222
245
|
) -> Dict:
|
|
223
246
|
"""
|
|
224
247
|
Function creates sklik report with /create-report call
|
|
@@ -255,7 +278,7 @@ class SklikService:
|
|
|
255
278
|
|
|
256
279
|
@get_session_if_malformed
|
|
257
280
|
def call_api(
|
|
258
|
-
|
|
281
|
+
self, secret_id: str, account_email: str, method: str, params: List[Dict]
|
|
259
282
|
) -> Union[List, Dict]:
|
|
260
283
|
"""
|
|
261
284
|
Function to call SklikService get-api route
|
|
@@ -282,12 +305,79 @@ class SklikService:
|
|
|
282
305
|
else:
|
|
283
306
|
raise SklikServiceException(response.content)
|
|
284
307
|
|
|
308
|
+
@get_session_if_malformed
|
|
309
|
+
def get_client(
|
|
310
|
+
self,
|
|
311
|
+
secret_id: str,
|
|
312
|
+
account_email: str,
|
|
313
|
+
) -> Union[List, Dict]:
|
|
314
|
+
"""
|
|
315
|
+
Function to call SklikService get-client route
|
|
316
|
+
:param secret_id: The ID of the secret in secret manager
|
|
317
|
+
:param account_email: Account email to refers to Sklik accountId
|
|
318
|
+
:return:
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
if self.session is None:
|
|
322
|
+
self.get_session(secret_id, account_email)
|
|
323
|
+
|
|
324
|
+
response = execute_request(
|
|
325
|
+
"post",
|
|
326
|
+
url=self._GET_CLIENT,
|
|
327
|
+
json={"sklik_session": self.session["sklik_session"]},
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
if response.status_code == HTTPStatus.OK:
|
|
331
|
+
service_response = response.json()
|
|
332
|
+
return service_response["data"]
|
|
333
|
+
else:
|
|
334
|
+
raise SklikServiceException(response.content)
|
|
335
|
+
|
|
336
|
+
@get_session_if_malformed
|
|
337
|
+
def get_accessible_accounts(
|
|
338
|
+
self,
|
|
339
|
+
secret_id: str,
|
|
340
|
+
) -> Union[List, Dict]:
|
|
341
|
+
"""
|
|
342
|
+
Function to retrieve accessible accounts
|
|
343
|
+
:param secret_id: The ID of the secret in secret manager
|
|
344
|
+
:return:
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
if self.session is None:
|
|
348
|
+
self.get_session_without_account(secret_id)
|
|
349
|
+
|
|
350
|
+
response = execute_request(
|
|
351
|
+
"post",
|
|
352
|
+
url=self._GET_CLIENT,
|
|
353
|
+
json={
|
|
354
|
+
"sklik_session": self.session["sklik_session"],
|
|
355
|
+
"filters": {
|
|
356
|
+
"hide_inactive": True
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if response.status_code == HTTPStatus.OK:
|
|
362
|
+
accounts = []
|
|
363
|
+
service_response = response.json()
|
|
364
|
+
data = service_response["data"]
|
|
365
|
+
for account in data["foreignAccounts"]:
|
|
366
|
+
accounts.append({
|
|
367
|
+
"id": account['userId'],
|
|
368
|
+
"name": account['username'],
|
|
369
|
+
"active": account["relationStatus"] == "live"
|
|
370
|
+
})
|
|
371
|
+
return accounts
|
|
372
|
+
else:
|
|
373
|
+
raise SklikServiceException(response.content)
|
|
374
|
+
|
|
285
375
|
@get_session_if_malformed
|
|
286
376
|
def check_data_ready(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
377
|
+
self,
|
|
378
|
+
secret_id: str,
|
|
379
|
+
account_email: str,
|
|
380
|
+
date: str = None,
|
|
291
381
|
) -> int:
|
|
292
382
|
"""
|
|
293
383
|
Checks if data on server are ready
|
|
@@ -301,8 +391,8 @@ class SklikService:
|
|
|
301
391
|
self.get_session(secret_id, account_email)
|
|
302
392
|
|
|
303
393
|
date = date or (
|
|
304
|
-
|
|
305
|
-
|
|
394
|
+
datetime.now(timezone("UTC")).astimezone(timezone("Europe/Prague"))
|
|
395
|
+
- timedelta(days=1)
|
|
306
396
|
).strftime("%Y-%m-%d")
|
|
307
397
|
|
|
308
398
|
body = {"date": date}
|
|
@@ -323,9 +413,9 @@ class SklikService:
|
|
|
323
413
|
|
|
324
414
|
@get_session_if_malformed
|
|
325
415
|
def fetch_api_limits(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
416
|
+
self,
|
|
417
|
+
secret_id: str,
|
|
418
|
+
account_email: str,
|
|
329
419
|
) -> Union[List, Dict]:
|
|
330
420
|
"""
|
|
331
421
|
Function to call SklikService api/limits route
|
|
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
|